[PATCH v2 01/18] ptpd: Add files in rtemsbsd
Gabriel Moyano
gabriel.moyano at dlr.de
Mon Apr 17 07:59:14 UTC 2023
---
rtemsbsd/ptpd/COPYRIGHT | 49 +
rtemsbsd/ptpd/ChangeLog | 429 ++
rtemsbsd/ptpd/INSTALL | 153 +
rtemsbsd/ptpd/Makefile.am | 53 +
rtemsbsd/ptpd/Makefile.old | 33 +
rtemsbsd/ptpd/README.md | 43 +
rtemsbsd/ptpd/README.repocheckout | 18 +
rtemsbsd/ptpd/TODO | 49 +
rtemsbsd/ptpd/configure.ac | 777 +++
.../ptpd/doc/IEEE1588v1_vs_IEEE1588v2.pdf | Bin 0 -> 272225 bytes
rtemsbsd/ptpd/doc/PTPBASE-MIB.txt | 5710 +++++++++++++++++
.../ptpd/doc/draft-ietf-tictoc-ptp-mib-01.txt | 3693 +++++++++++
rtemsbsd/ptpd/doc/index.html | 125 +
.../ptpd/doc/ptpd-2.3.0-migration-guide.html | 243 +
.../doc/ptpd_2005_1588_conference_paper.pdf | Bin 0 -> 323600 bytes
rtemsbsd/ptpd/m4/version.m4 | 4 +
rtemsbsd/ptpd/packagebuild/rpm-rh/README.RH | 44 +
rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.conf | 62 +
rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.init | 205 +
.../ptpd/packagebuild/rpm-rh/ptpd.service | 17 +
rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.spec | 241 +
.../ptpd/packagebuild/rpm-rh/ptpd.sysconfig | 10 +
rtemsbsd/ptpd/packagebuild/rpm-rh/rpmbuild.sh | 101 +
rtemsbsd/ptpd/src/Doxyfile | 1511 +++++
rtemsbsd/ptpd/src/Makefile.am | 102 +
rtemsbsd/ptpd/src/Makefile.old | 109 +
rtemsbsd/ptpd/src/arith.c | 373 ++
rtemsbsd/ptpd/src/bmc.c | 779 +++
rtemsbsd/ptpd/src/constants.h | 470 ++
rtemsbsd/ptpd/src/datatypes.h | 715 +++
rtemsbsd/ptpd/src/def/README | 11 +
.../ptpd/src/def/derivedData/clockQuality.def | 8 +
.../ptpd/src/def/derivedData/faultRecord.def | 11 +
.../src/def/derivedData/physicalAddress.def | 13 +
.../ptpd/src/def/derivedData/portAddress.def | 8 +
.../ptpd/src/def/derivedData/portIdentity.def | 7 +
rtemsbsd/ptpd/src/def/derivedData/ptpText.def | 7 +
.../ptpd/src/def/derivedData/timeInterval.def | 6 +
.../src/def/derivedData/timePropertiesDS.def | 13 +
.../ptpd/src/def/derivedData/timestamp.def | 7 +
rtemsbsd/ptpd/src/def/derivedData/tlv.def | 8 +
.../managementTLV/announceReceiptTimeout.def | 7 +
.../src/def/managementTLV/clockAccuracy.def | 7 +
.../def/managementTLV/clockDescription.def | 35 +
.../src/def/managementTLV/currentDataSet.def | 8 +
.../src/def/managementTLV/defaultDataSet.def | 14 +
.../src/def/managementTLV/delayMechanism.def | 7 +
.../ptpd/src/def/managementTLV/domain.def | 7 +
.../src/def/managementTLV/errorStatus.def | 8 +
.../ptpd/src/def/managementTLV/initialize.def | 6 +
.../def/managementTLV/logAnnounceInterval.def | 7 +
.../managementTLV/logMinPdelayReqInterval.def | 7 +
.../src/def/managementTLV/logSyncInterval.def | 7 +
.../src/def/managementTLV/managementTLV.def | 8 +
.../src/def/managementTLV/parentDataSet.def | 14 +
.../src/def/managementTLV/portDataSet.def | 16 +
.../ptpd/src/def/managementTLV/priority1.def | 7 +
.../ptpd/src/def/managementTLV/priority2.def | 7 +
.../ptpd/src/def/managementTLV/slaveOnly.def | 7 +
rtemsbsd/ptpd/src/def/managementTLV/time.def | 6 +
.../managementTLV/timePropertiesDataSet.def | 8 +
.../def/managementTLV/timescaleProperties.def | 7 +
.../managementTLV/traceabilityProperties.def | 7 +
.../unicastNegotiationEnable.def | 7 +
.../src/def/managementTLV/userDescription.def | 8 +
.../src/def/managementTLV/utcProperties.def | 8 +
.../src/def/managementTLV/versionNumber.def | 8 +
rtemsbsd/ptpd/src/def/message/header.def | 20 +
rtemsbsd/ptpd/src/def/message/management.def | 12 +
rtemsbsd/ptpd/src/def/message/signaling.def | 6 +
.../acknowledgeCancelUnicastTransmission.def | 9 +
.../cancelUnicastTransmission.def | 9 +
.../signalingTLV/grantUnicastTransmission.def | 12 +
.../requestUnicastTransmission.def | 9 +
.../src/def/signalingTLV/signalingTLV.def | 6 +
rtemsbsd/ptpd/src/dep/alarms.c | 427 ++
rtemsbsd/ptpd/src/dep/alarms.h | 100 +
rtemsbsd/ptpd/src/dep/configdefaults.c | 675 ++
rtemsbsd/ptpd/src/dep/configdefaults.h | 52 +
rtemsbsd/ptpd/src/dep/constants_dep.h | 251 +
rtemsbsd/ptpd/src/dep/daemonconfig.c | 3192 +++++++++
rtemsbsd/ptpd/src/dep/daemonconfig.h | 89 +
rtemsbsd/ptpd/src/dep/datatypes_dep.h | 179 +
rtemsbsd/ptpd/src/dep/eventtimer.c | 133 +
rtemsbsd/ptpd/src/dep/eventtimer.h | 77 +
rtemsbsd/ptpd/src/dep/eventtimer_itimer.c | 239 +
rtemsbsd/ptpd/src/dep/eventtimer_posix.c | 239 +
rtemsbsd/ptpd/src/dep/iniparser/AUTHORS | 7 +
rtemsbsd/ptpd/src/dep/iniparser/LICENSE | 21 +
rtemsbsd/ptpd/src/dep/iniparser/README | 12 +
rtemsbsd/ptpd/src/dep/iniparser/dictionary.c | 520 ++
rtemsbsd/ptpd/src/dep/iniparser/dictionary.h | 189 +
rtemsbsd/ptpd/src/dep/iniparser/iniparser.c | 771 +++
rtemsbsd/ptpd/src/dep/iniparser/iniparser.h | 311 +
rtemsbsd/ptpd/src/dep/ipv4_acl.c | 486 ++
rtemsbsd/ptpd/src/dep/ipv4_acl.h | 52 +
rtemsbsd/ptpd/src/dep/msg.c | 2757 ++++++++
rtemsbsd/ptpd/src/dep/net.c | 2300 +++++++
rtemsbsd/ptpd/src/dep/ntpengine/ntp_isc_md5.c | 273 +
rtemsbsd/ptpd/src/dep/ntpengine/ntp_isc_md5.h | 99 +
rtemsbsd/ptpd/src/dep/ntpengine/ntpdcontrol.c | 874 +++
rtemsbsd/ptpd/src/dep/ntpengine/ntpdcontrol.h | 362 ++
rtemsbsd/ptpd/src/dep/outlierfilter.c | 369 ++
rtemsbsd/ptpd/src/dep/outlierfilter.h | 125 +
rtemsbsd/ptpd/src/dep/ptpd_dep.h | 516 ++
rtemsbsd/ptpd/src/dep/servo.c | 1262 ++++
rtemsbsd/ptpd/src/dep/snmp.c | 2186 +++++++
rtemsbsd/ptpd/src/dep/startup.c | 1020 +++
rtemsbsd/ptpd/src/dep/statistics.c | 1047 +++
rtemsbsd/ptpd/src/dep/statistics.h | 230 +
rtemsbsd/ptpd/src/dep/sys.c | 2661 ++++++++
rtemsbsd/ptpd/src/display.c | 1152 ++++
rtemsbsd/ptpd/src/leap-seconds.list | 250 +
rtemsbsd/ptpd/src/management.c | 1896 ++++++
rtemsbsd/ptpd/src/protocol.c | 3534 ++++++++++
rtemsbsd/ptpd/src/ptp_datatypes.h | 592 ++
rtemsbsd/ptpd/src/ptp_primitives.h | 41 +
rtemsbsd/ptpd/src/ptp_timers.c | 160 +
rtemsbsd/ptpd/src/ptp_timers.h | 95 +
rtemsbsd/ptpd/src/ptpd.c | 139 +
rtemsbsd/ptpd/src/ptpd.h | 494 ++
rtemsbsd/ptpd/src/ptpd2.8.in | 397 ++
rtemsbsd/ptpd/src/ptpd2.conf.5.in | 3100 +++++++++
rtemsbsd/ptpd/src/ptpd2.conf.default-full | 841 +++
rtemsbsd/ptpd/src/ptpd2.conf.minimal | 38 +
rtemsbsd/ptpd/src/signaling.c | 1376 ++++
rtemsbsd/ptpd/src/templates.conf | 22 +
rtemsbsd/ptpd/src/timingdomain.c | 969 +++
rtemsbsd/ptpd/src/timingdomain.h | 131 +
rtemsbsd/ptpd/test/client-e2e-8023.conf | 447 ++
rtemsbsd/ptpd/test/client-e2e-pcap.conf | 447 ++
rtemsbsd/ptpd/test/client-e2e-socket.conf | 447 ++
rtemsbsd/ptpd/test/testing.org | 22 +
rtemsbsd/ptpd/tools/README.md | 80 +
rtemsbsd/ptpd/tools/cleanup.sed | 11 +
rtemsbsd/ptpd/tools/cleanup_sf2.sed | 9 +
rtemsbsd/ptpd/tools/compare.R | 62 +
rtemsbsd/ptpd/tools/filter_response.m | 29 +
rtemsbsd/ptpd/tools/graph.R | 52 +
rtemsbsd/ptpd/tools/ntplib/DESCRIPTION | 13 +
rtemsbsd/ptpd/tools/ntplib/NAMESPACE | 7 +
rtemsbsd/ptpd/tools/ntplib/R/ntplib.R | 184 +
rtemsbsd/ptpd/tools/ntplib/man/ntpGraph.Rd | 19 +
.../ptpd/tools/ntplib/man/ntpHistogram.Rd | 21 +
rtemsbsd/ptpd/tools/ntplib/man/ntpLoopRead.Rd | 20 +
.../ptpd/tools/ntplib/man/ntpLoopStats.Rd | 17 +
rtemsbsd/ptpd/tools/ntplib/man/ntpPeerRead.Rd | 18 +
rtemsbsd/ptpd/tools/ntpoffset.R | 53 +
rtemsbsd/ptpd/tools/offset.R | 53 +
rtemsbsd/ptpd/tools/offset_stats.m | 102 +
rtemsbsd/ptpd/tools/ptplib/DESCRIPTION | 13 +
rtemsbsd/ptpd/tools/ptplib/NAMESPACE | 12 +
rtemsbsd/ptpd/tools/ptplib/R/ptplib.R | 397 ++
rtemsbsd/ptpd/tools/ptplib/man/ptpCompare.Rd | 22 +
rtemsbsd/ptpd/tools/ptplib/man/ptpGraph.Rd | 19 +
.../ptpd/tools/ptplib/man/ptpHistogram.Rd | 16 +
.../tools/ptplib/man/ptpHistogramCompare.Rd | 12 +
rtemsbsd/ptpd/tools/ptplib/man/ptpLogRead.Rd | 20 +
.../ptpd/tools/ptplib/man/ptpOffsetStats.Rd | 21 +
.../ptpd/tools/ptplib/man/ptpQualityGraph.Rd | 22 +
.../ptplib/man/ptpQualityGraphCompare.Rd | 22 +
.../ptpd/tools/ptplib/man/ptpQualityRead.Rd | 18 +
.../ptpd/tools/ptplib/man/ptpQualityStats.Rd | 15 +
rtemsbsd/ptpd/tools/ptplib/man/ptpStats.Rd | 22 +
rtemsbsd/ptpd/tools/snmptpq | 303 +
rtemsbsd/ptpd/tools/stats.R | 48 +
166 files changed, 58963 insertions(+)
create mode 100644 rtemsbsd/ptpd/COPYRIGHT
create mode 100644 rtemsbsd/ptpd/ChangeLog
create mode 100644 rtemsbsd/ptpd/INSTALL
create mode 100644 rtemsbsd/ptpd/Makefile.am
create mode 100644 rtemsbsd/ptpd/Makefile.old
create mode 100644 rtemsbsd/ptpd/README.md
create mode 100644 rtemsbsd/ptpd/README.repocheckout
create mode 100644 rtemsbsd/ptpd/TODO
create mode 100644 rtemsbsd/ptpd/configure.ac
create mode 100644 rtemsbsd/ptpd/doc/IEEE1588v1_vs_IEEE1588v2.pdf
create mode 100644 rtemsbsd/ptpd/doc/PTPBASE-MIB.txt
create mode 100644 rtemsbsd/ptpd/doc/draft-ietf-tictoc-ptp-mib-01.txt
create mode 100644 rtemsbsd/ptpd/doc/index.html
create mode 100644 rtemsbsd/ptpd/doc/ptpd-2.3.0-migration-guide.html
create mode 100644 rtemsbsd/ptpd/doc/ptpd_2005_1588_conference_paper.pdf
create mode 100644 rtemsbsd/ptpd/m4/version.m4
create mode 100644 rtemsbsd/ptpd/packagebuild/rpm-rh/README.RH
create mode 100644 rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.conf
create mode 100644 rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.init
create mode 100644 rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.service
create mode 100644 rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.spec
create mode 100644 rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.sysconfig
create mode 100755 rtemsbsd/ptpd/packagebuild/rpm-rh/rpmbuild.sh
create mode 100644 rtemsbsd/ptpd/src/Doxyfile
create mode 100644 rtemsbsd/ptpd/src/Makefile.am
create mode 100644 rtemsbsd/ptpd/src/Makefile.old
create mode 100644 rtemsbsd/ptpd/src/arith.c
create mode 100644 rtemsbsd/ptpd/src/bmc.c
create mode 100644 rtemsbsd/ptpd/src/constants.h
create mode 100644 rtemsbsd/ptpd/src/datatypes.h
create mode 100644 rtemsbsd/ptpd/src/def/README
create mode 100644 rtemsbsd/ptpd/src/def/derivedData/clockQuality.def
create mode 100644 rtemsbsd/ptpd/src/def/derivedData/faultRecord.def
create mode 100644 rtemsbsd/ptpd/src/def/derivedData/physicalAddress.def
create mode 100644 rtemsbsd/ptpd/src/def/derivedData/portAddress.def
create mode 100644 rtemsbsd/ptpd/src/def/derivedData/portIdentity.def
create mode 100644 rtemsbsd/ptpd/src/def/derivedData/ptpText.def
create mode 100644 rtemsbsd/ptpd/src/def/derivedData/timeInterval.def
create mode 100644 rtemsbsd/ptpd/src/def/derivedData/timePropertiesDS.def
create mode 100644 rtemsbsd/ptpd/src/def/derivedData/timestamp.def
create mode 100644 rtemsbsd/ptpd/src/def/derivedData/tlv.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/announceReceiptTimeout.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/clockAccuracy.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/clockDescription.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/currentDataSet.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/defaultDataSet.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/delayMechanism.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/domain.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/errorStatus.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/initialize.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/logAnnounceInterval.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/logMinPdelayReqInterval.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/logSyncInterval.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/managementTLV.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/parentDataSet.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/portDataSet.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/priority1.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/priority2.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/slaveOnly.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/time.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/timePropertiesDataSet.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/timescaleProperties.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/traceabilityProperties.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/unicastNegotiationEnable.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/userDescription.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/utcProperties.def
create mode 100644 rtemsbsd/ptpd/src/def/managementTLV/versionNumber.def
create mode 100644 rtemsbsd/ptpd/src/def/message/header.def
create mode 100644 rtemsbsd/ptpd/src/def/message/management.def
create mode 100644 rtemsbsd/ptpd/src/def/message/signaling.def
create mode 100644 rtemsbsd/ptpd/src/def/signalingTLV/acknowledgeCancelUnicastTransmission.def
create mode 100644 rtemsbsd/ptpd/src/def/signalingTLV/cancelUnicastTransmission.def
create mode 100644 rtemsbsd/ptpd/src/def/signalingTLV/grantUnicastTransmission.def
create mode 100644 rtemsbsd/ptpd/src/def/signalingTLV/requestUnicastTransmission.def
create mode 100644 rtemsbsd/ptpd/src/def/signalingTLV/signalingTLV.def
create mode 100644 rtemsbsd/ptpd/src/dep/alarms.c
create mode 100644 rtemsbsd/ptpd/src/dep/alarms.h
create mode 100644 rtemsbsd/ptpd/src/dep/configdefaults.c
create mode 100644 rtemsbsd/ptpd/src/dep/configdefaults.h
create mode 100644 rtemsbsd/ptpd/src/dep/constants_dep.h
create mode 100644 rtemsbsd/ptpd/src/dep/daemonconfig.c
create mode 100644 rtemsbsd/ptpd/src/dep/daemonconfig.h
create mode 100644 rtemsbsd/ptpd/src/dep/datatypes_dep.h
create mode 100644 rtemsbsd/ptpd/src/dep/eventtimer.c
create mode 100644 rtemsbsd/ptpd/src/dep/eventtimer.h
create mode 100644 rtemsbsd/ptpd/src/dep/eventtimer_itimer.c
create mode 100644 rtemsbsd/ptpd/src/dep/eventtimer_posix.c
create mode 100644 rtemsbsd/ptpd/src/dep/iniparser/AUTHORS
create mode 100644 rtemsbsd/ptpd/src/dep/iniparser/LICENSE
create mode 100644 rtemsbsd/ptpd/src/dep/iniparser/README
create mode 100644 rtemsbsd/ptpd/src/dep/iniparser/dictionary.c
create mode 100644 rtemsbsd/ptpd/src/dep/iniparser/dictionary.h
create mode 100644 rtemsbsd/ptpd/src/dep/iniparser/iniparser.c
create mode 100644 rtemsbsd/ptpd/src/dep/iniparser/iniparser.h
create mode 100644 rtemsbsd/ptpd/src/dep/ipv4_acl.c
create mode 100644 rtemsbsd/ptpd/src/dep/ipv4_acl.h
create mode 100644 rtemsbsd/ptpd/src/dep/msg.c
create mode 100644 rtemsbsd/ptpd/src/dep/net.c
create mode 100644 rtemsbsd/ptpd/src/dep/ntpengine/ntp_isc_md5.c
create mode 100644 rtemsbsd/ptpd/src/dep/ntpengine/ntp_isc_md5.h
create mode 100644 rtemsbsd/ptpd/src/dep/ntpengine/ntpdcontrol.c
create mode 100644 rtemsbsd/ptpd/src/dep/ntpengine/ntpdcontrol.h
create mode 100644 rtemsbsd/ptpd/src/dep/outlierfilter.c
create mode 100644 rtemsbsd/ptpd/src/dep/outlierfilter.h
create mode 100644 rtemsbsd/ptpd/src/dep/ptpd_dep.h
create mode 100644 rtemsbsd/ptpd/src/dep/servo.c
create mode 100644 rtemsbsd/ptpd/src/dep/snmp.c
create mode 100644 rtemsbsd/ptpd/src/dep/startup.c
create mode 100644 rtemsbsd/ptpd/src/dep/statistics.c
create mode 100644 rtemsbsd/ptpd/src/dep/statistics.h
create mode 100644 rtemsbsd/ptpd/src/dep/sys.c
create mode 100644 rtemsbsd/ptpd/src/display.c
create mode 100644 rtemsbsd/ptpd/src/leap-seconds.list
create mode 100644 rtemsbsd/ptpd/src/management.c
create mode 100644 rtemsbsd/ptpd/src/protocol.c
create mode 100644 rtemsbsd/ptpd/src/ptp_datatypes.h
create mode 100644 rtemsbsd/ptpd/src/ptp_primitives.h
create mode 100644 rtemsbsd/ptpd/src/ptp_timers.c
create mode 100644 rtemsbsd/ptpd/src/ptp_timers.h
create mode 100644 rtemsbsd/ptpd/src/ptpd.c
create mode 100644 rtemsbsd/ptpd/src/ptpd.h
create mode 100644 rtemsbsd/ptpd/src/ptpd2.8.in
create mode 100644 rtemsbsd/ptpd/src/ptpd2.conf.5.in
create mode 100644 rtemsbsd/ptpd/src/ptpd2.conf.default-full
create mode 100644 rtemsbsd/ptpd/src/ptpd2.conf.minimal
create mode 100644 rtemsbsd/ptpd/src/signaling.c
create mode 100644 rtemsbsd/ptpd/src/templates.conf
create mode 100644 rtemsbsd/ptpd/src/timingdomain.c
create mode 100644 rtemsbsd/ptpd/src/timingdomain.h
create mode 100644 rtemsbsd/ptpd/test/client-e2e-8023.conf
create mode 100644 rtemsbsd/ptpd/test/client-e2e-pcap.conf
create mode 100644 rtemsbsd/ptpd/test/client-e2e-socket.conf
create mode 100644 rtemsbsd/ptpd/test/testing.org
create mode 100644 rtemsbsd/ptpd/tools/README.md
create mode 100644 rtemsbsd/ptpd/tools/cleanup.sed
create mode 100644 rtemsbsd/ptpd/tools/cleanup_sf2.sed
create mode 100755 rtemsbsd/ptpd/tools/compare.R
create mode 100755 rtemsbsd/ptpd/tools/filter_response.m
create mode 100755 rtemsbsd/ptpd/tools/graph.R
create mode 100644 rtemsbsd/ptpd/tools/ntplib/DESCRIPTION
create mode 100644 rtemsbsd/ptpd/tools/ntplib/NAMESPACE
create mode 100644 rtemsbsd/ptpd/tools/ntplib/R/ntplib.R
create mode 100644 rtemsbsd/ptpd/tools/ntplib/man/ntpGraph.Rd
create mode 100644 rtemsbsd/ptpd/tools/ntplib/man/ntpHistogram.Rd
create mode 100644 rtemsbsd/ptpd/tools/ntplib/man/ntpLoopRead.Rd
create mode 100644 rtemsbsd/ptpd/tools/ntplib/man/ntpLoopStats.Rd
create mode 100644 rtemsbsd/ptpd/tools/ntplib/man/ntpPeerRead.Rd
create mode 100755 rtemsbsd/ptpd/tools/ntpoffset.R
create mode 100755 rtemsbsd/ptpd/tools/offset.R
create mode 100755 rtemsbsd/ptpd/tools/offset_stats.m
create mode 100644 rtemsbsd/ptpd/tools/ptplib/DESCRIPTION
create mode 100644 rtemsbsd/ptpd/tools/ptplib/NAMESPACE
create mode 100644 rtemsbsd/ptpd/tools/ptplib/R/ptplib.R
create mode 100644 rtemsbsd/ptpd/tools/ptplib/man/ptpCompare.Rd
create mode 100644 rtemsbsd/ptpd/tools/ptplib/man/ptpGraph.Rd
create mode 100644 rtemsbsd/ptpd/tools/ptplib/man/ptpHistogram.Rd
create mode 100644 rtemsbsd/ptpd/tools/ptplib/man/ptpHistogramCompare.Rd
create mode 100644 rtemsbsd/ptpd/tools/ptplib/man/ptpLogRead.Rd
create mode 100644 rtemsbsd/ptpd/tools/ptplib/man/ptpOffsetStats.Rd
create mode 100644 rtemsbsd/ptpd/tools/ptplib/man/ptpQualityGraph.Rd
create mode 100644 rtemsbsd/ptpd/tools/ptplib/man/ptpQualityGraphCompare.Rd
create mode 100644 rtemsbsd/ptpd/tools/ptplib/man/ptpQualityRead.Rd
create mode 100644 rtemsbsd/ptpd/tools/ptplib/man/ptpQualityStats.Rd
create mode 100644 rtemsbsd/ptpd/tools/ptplib/man/ptpStats.Rd
create mode 100755 rtemsbsd/ptpd/tools/snmptpq
create mode 100755 rtemsbsd/ptpd/tools/stats.R
diff --git a/rtemsbsd/ptpd/COPYRIGHT b/rtemsbsd/ptpd/COPYRIGHT
new file mode 100644
index 00000000..86a257d1
--- /dev/null
+++ b/rtemsbsd/ptpd/COPYRIGHT
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2015 Wojciech Owczarek.
+ * Copyright (c) 2014 Perseus Telecom.
+ * Copyright (c) 2013-2014 Harlan Stenn,
+ * George N. Neville-Neil,
+ * Wojciech Owczarek,
+ * Jan Breuer.
+ * Copyright (c) 2011-2012 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Wojciech Owczarek,
+ * Gael Mace,
+ * Alexandre Van Kempen,
+ * Inaqui Delgado,
+ * Rick Ratzel,
+ * National Instruments.
+ * Copyright (c) 2009-2010 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen.
+ *
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/rtemsbsd/ptpd/ChangeLog b/rtemsbsd/ptpd/ChangeLog
new file mode 100644
index 00000000..775bd399
--- /dev/null
+++ b/rtemsbsd/ptpd/ChangeLog
@@ -0,0 +1,429 @@
+2013-10-23 Wojciech Owczarek <wojciech at owczarek.co.uk>
+
+ * 2.3.2 release
+
+ * Bug fixes / improvements since 2.3.1:
+
+ - systemd service file added and RPM spec updated
+ to handle systemd
+ - fixed segfault when shutting down NTP time service
+ - ptpengine:always_respect_utc_offset is now enabled
+ by default, to protect from clock jumps when GM
+ stops announcing currentUtcOffsetValid flag
+ - re-arranged order of InterfaceInfo fields to
+ fix memory alignment issues (SF bug #70)
+ - corrected logMessageInterval field values
+ for hybrid mode (SF bug #69)
+ - fixed NTPd check failures resulting from signals
+ interrupting select()
+ - increased statistics file line buffer size to prevent
+ truncation and merging of lines
+ - added Sequence ID to statistics log
+ - Solaris build fixes - linking lnsl and lsocket when
+ building without SNMP support
+ - critical: added minimum POSIX timer interval to prevent from
+ timers firing to quickly for the process to handle,
+ resulting in 100% CPU and endless signal queue
+ - improved processing of unicast destination, domain
+ and local preference lists
+ - CPU affinity setting moved to a separate function
+ - fixed incorrect PTP_UNICAST flag setting for multicast
+ - fixed missing management or signalling message acceptance
+ condition (clockid==clockid, portid=0xffff): ISPCS 2015 plugfest,
+ fourth missing: (clockid=0x(f), portid==portid)
+ - signaling.c: fixed order of grant processing for PTP messages
+ (ann,syn,fup,dresp,pdelayresp) instead of numeric loop,
+ to assist with interop where transmission request order
+ makes a difference to the master
+ - signaling.c: do not reset known clock ID when changing GMs,
+ to allow to keep accepting announce from other masters
+ - signaling.c: update unicast index when transport address
+ match only: fix for cache misses for non-best masters
+ - daemonconfig:c - reworked all config mapping macros
+ to functions, parseConfig now handless all operations:
+ parsing, quiet parsing, reload check, help - no hidden
+ values in dictionary anymore
+ - closed sf bug #64 - leaving mcast group with 0x0 address
+ - peer multicast group only joined when needed, not always
+ - logMessage now keeps hash of the last message body,
+ preventing from same message being repeated
+ - null checks getInterfaceInfo set of helper functions
+ in case if some data returned by getifaddrs() is null:
+ fixes segfaults on some exotic interface types
+ - signaling.c: use message type <-> smaller index number
+ conversion so grant table is 5-element, not 16-element:
+ significantly reduced memory footprint
+ - added dummy Makefile to src/dep to allow make commands
+ from that directory
+ - added buffer overflow protection for management
+ TLVs: protects against read past message length,
+ and against read past buffer space
+
+ * New features since 2.3.1:
+
+ - support for user variables in configuration:
+ - variables specified variables:name=val, and
+ used as @name@
+ - built-in variables: @pid@, @hostname@
+ - support for configuration templates:
+ - built-in
+ - template files
+ - unicast port number mask to assist with nodes
+ using different port IDs for negotiation
+ - option to accept any GM when unicast negotiation
+ is enabled
+ - UDP checksums disabled on Linux by default:
+ enables interop with TCs that don't correct checksums
+ - Added QNX support:
+ - clean build on QNX 6.5 out of the box
+ - adjfreq emulation using native ClockAdjust
+ - binding to CPU cores
+ - Experimental (but yield best results):
+ * interpolated getTime attached to IRQ0
+ * RX/TX timestamps in software on send or receive,
+ bypassing pcap or socket timestamping
+ - support for systems without getopt_long (like QNX)
+ - adjfreq-like behaviour on systems with adjtime only:
+ properly scaled adjtime() pulled into PI servo
+ (vastly improved sync on systems like OpenBSD and OSX),
+ support for drift file for those systems
+ - fixed preserving incorrect master address when
+ best master lowers priority (GM change as a result
+ of announce from current master, not foreign)
+ - PTP MIB enhanced with string values, counter support,
+ clearing counters and more, added some missing items
+ from previously supported items
+ - SNMP trap support for common alarm conditions
+ - enhanced management message support: enable/disable port,
+ some missing values, PTPd now properly applies
+ configuration when receiving SET messages
+ - added simple SNMP poller script: snmptpq
+ - SNMP MIB now using PTPd's own Enterprise number
+
+2013-06-10 Wojciech Owczarek <wojciech at owczarek.co.uk>
+
+ * 2.3.1 release
+
+ * New features since 2.3.0:
+
+ - full unicast support both in master and in slave mode:
+ + support for multiple unicast destinations for masters
+ + feature-complete Telecom Profile support:
+ * unicast negotiation for masters and slaves
+ * disabling BMCA for masters (also works for multicast)
+ * support for slaves with masters in multiple domains
+ * influencing slave BMCA with local preference
+ * serving multiple Telecom slaves at different rates
+ * slaves correctly negotiate message rates over supported
+ range of intervals until an acceptable interval is granted
+ + support for unicast Peer Delay target - exotic use case
+ - greatly improved network robustness
+ - added support for slave-only builds (configure --slave-only):
+ allows building a binary incapable of running as master
+ - simple failover mechanism - ptpengine:backup_interface
+ - ptpengine:max_delay_max_rejected setting to unblock ptpd
+ rejecting all messages when max_delay set
+ - ptpengine:max_delay_stable to only use max_dely when clock
+ is stable
+ - selectable log timestamp format (unix or datetime)
+ - ptpd now compiles and runs out of he box on NetBSD, OpenBSD, FreeBSD,
+ OSX and Solaris and its derivatives (OpenIndiana, OmniOS etc.)
+ - major rework of the ACL code
+ - outlier filter auto-tune to improve operation in noisy environments(high PDV)
+ - added support for statistical filters (min/mean/median) for sync
+ and delay messages - min or median can be used in envifonments with high PDV
+ - added small step detection (millisecond level)
+ - added ptpengine:clock_update_timeout setting to reset slave
+ if no clock updates for a period of time
+ - when running slave-only in unicast mode, unicast address
+ does not have to be specified - ptpd will wait for a GM and latch onto it
+ - automatic Delay Request override for Hybrid and Unicast modes
+ - added options allowing to step the clock on startup
+ - improved sequence and re-ordering checks
+ - added support for "any domain" operation where ptpd will sync with
+ any available domain
+ - idle timeout (PTPd hands over clock control)
+ - clock update timeout (PTPd restarts slave on timeout)
+ - maximum state machine reset count - full network restart when exceeded:
+ allows ptpd to survive situations where network interface is removed,
+ shut down or IP address changes
+ - support for NIST/IETF leap seconds file both for master and slave
+ - configurable leap second handling: accept, ignore, step
+ - support for leap second smear for slave operation
+ - option to step clock on startup
+ - TimingDomain API: all NTP failover code removed
+ in favour of a generic interface for multiple time controllers.
+ - configurable port number - useful when running multiple instances
+ - periodic log file updates with slave or master information - separate
+ from statistics file: allows sending statistics into syslog
+ - support for software transmit timestamping on Linux
+ - lookup tables used for matching Sync and FollowUp when
+ no transport information available
+ - added periodic status updates to log file (global:periodic_updates)
+ - status file will always display counters for common error events
+ if they are non-zero (includes denied unicast grants and ignored
+ Announce resulting from rejected UTC properties)
+ - reporting on message receive and transmit rates
+ in the status file
+ - restored compatibility with 802.1AS
+
+ * Bug fixes / improvements since 2.3.0:
+
+ - added CentOS / RHEL 7.x support
+ - statistics code enabled by default - can be disabled
+ - NTPd failover code enabled by default - can be disabled
+ - Improved status file - more error conditions included,
+ message performance statistics included
+ - Rewritten timer subsystem allowing for easily implementing
+ other implementations: included is the old interval timer implementation,
+ and new (now default), POSIX timer implementation. This allows for
+ fully standards-compliant message rates. This can be disabled with
+ a configure flag
+ - ptpd now usable on systems with no adjtimex() call like OSX,
+ OpenBSD and LynxOS ()
+ - moving standard deviation algorithm fix improving the outlier
+ filters
+ - major servo.c spaghetti code cleanups
+ - network code cleanup
+ - fixed bug #66 - Ethernet interfaces no longer need IP addresses
+ to run PTP Ethernet transport
+ - fixed bug / feature request #21: timestamping initialisation on
+ PHC kernels
+ - fixed bug where ptpd would cause 100% CPU usage because of a
+ late TX timestamp
+ - fixed bug / feature request #22: prevent FMR from overwriting
+ best master
+ - improved servo stability detection: servo no longer reporting
+ stable when discarding most samples or heavily slewing
+ - ptpd will now join and leave multicast groups only when running
+ multicast or hybrid: allows running unicast and multicast
+ independently
+ - ptpd will now ignore unicast packets in multicast-only mode
+ and vice versa. In hybrid mode, only unicast Delay Request /
+ response is accepted; multicast management messages are always
+ accepted
+ - further autoconf improvements across all supported OSes
+ - added missing files to distribution
+ - ptpd will now recover even from critical network failures,
+ such as reloading drivers
+ - remaining memory leaks fixed
+ - more code cleanup, no more ajtimex() and similar calls in protocol.c
+ - some refactoring, constified rtOpts wherever possible
+ - less "dramatic" warnings about drift file
+ and clock stability detection
+ - fixed unnecessary TX timestamp attempts in PCAP mode
+ - fixed issue where drift file would not be correctly
+ written on exit under certain conditions
+ - other minor bugs fixed
+
+2013-11-22 George Neville-Neil <gnn at neville-neil.com>, Wojciech Owczarek <wojciech at owczarek.co.uk>
+
+ * 2.3.0 release
+
+ * New Features:
+
+ - IEEE 802.3 Support
+ - Support for reading from the Berkeley Packet Filter, resulting in lower jitter
+ - Configuration file support, multiple previously unsupported options
+ - Rewritten command line parameter handling, new parameters
+ - Command line options to print default configuration, print lock file, check configuration and more,
+ - Full help (-H) for all configuration options,
+ - Configuration reload support with SIGHUP,
+ - Rewritten logging subsystem,
+ - Separate log file and statistics file, SIGHUP reprints the headers
+ - Support for mode and interface specific lock files
+ - Network interface checks before startup and during config reload
+ - Added ./configure --sigusr2=counters - will dump PTP engine countersto current log target when SIGUSR2 received
+ - Support for presets - groups of PTP protocol options defining ptpd behaviour
+ presets available: slaveonly, masteronly, masterslave
+ - Support for tick adjustment as well as frequency
+ maximum frequency offset is now configurable and can be above 512 ppm - tick is used above 512 ppm,
+ - Added a compatibility option to always honour the UTC offset announced by a GM when in slave state
+ - Added an announce receipt timeout "grace period" for slave state
+ slave can wait n times timeout without fully resetting
+ allows a seamless failover without resetting delay values etc.,
+ - Added support for DELAY_DISABLED delay mode - syntonisation only
+ - Initial support for informing the clock subsystem about the sync status
+ STA_UNSYNC is unset when adjusting the clock (allows syncing hardware RTC clocks with the system clock)
+ also maxerror and esterror are set,
+ - Support for RTC clock control on Linux
+ - Support for legacy command line switches from previous versions
+ - Configurable log level for normal logs, not only debug logs.
+ - Generic log file handler mechanism with a simple built-in logrotate implementation
+ for each log file container you can specify max size and max number of files
+ - Extensive support for realtime statistics
+ online long term statistics and moving statistics
+ mean and std dev.
+ Some new features included depend on statistics support, enabled with the --enable-statistics
+ configure switch. Statistics rely on double precision so may be disabled for simple embedded systems
+ - Outlier filters based on Peirce's criterion
+ blocks / filters spikes from delayMS and delaySM
+ - Generic PI controler model
+ - P and I components changed from attenuations to gains
+ - Double precision servo allows for tighter clock control
+ - Clock stabilisation detection based on the standard dev of observed drift
+ - Clock "calibration delay"
+ allows a configurable delay between seeing a GM for the first time and enabling clock control
+ - Realtime statistics - mean and std dev of one-way delay, ofm and observed drift
+ - Status file: one-screen dump of information about the current state of ptpd, updated in configurable intervals.
+ - Support for "panic mode"
+ if ptpd sees offset from master above 1 second, it will pause clock updates for a given amount of time
+ - NTPd integration and failover
+ ptpd can now connect to local ntpd using mode 7 packets (code derived from ntpdc)
+ and using authenticated requests can enable/disable ntpd clock control.
+ Enable with configure --enable-ntpdc
+ - Dump counters + clear counters using a SIGUSR2 handler to dump all counters to the log file
+ configure --enable-sigusr2=counters
+ - Support for ACLs for management and timing messages
+ - Basic support for SO_TIMESTAMPING (RX, TX) on Linux
+ - Support for non-Ethernet interfaces on Linux (Infiniband, GRE etc.)
+ - Support for offset / delay asymmetry correction
+ - Ptpd can now prefer or require GMs with valid UTC time
+ - Support for setting IP DSCP values
+ - Periodic IGMP joins also in master mode - improved robustness against network port failures
+ - Support for binding PTPd to a CPU core
+ - Control over tick rate on Linux, allowing faster clock slewing if needed
+
+
+
+ * Bug fixes:
+
+ - TTL no longer defaults to 64 in P2P mode - in P2P it's set to 1,
+ - Fixed an issue whereby observed drift would be reset to 0 if it
+ could not be read from the drift file (empty or nonexistent drift file),
+ now the kernel value is used if read from file fails.
+ - Support for updating utmp/wtmp when stepping the clock (FR #38)
+ - Mean Path Delay filtering now done in float (patch #49)
+ - Multiple other SourceForge bugs closed: Bugs closed:
+
+ #25, #34, #26, #42, #39, #33, #46, #45, #28,
+ #27, #48, #54, #47, #40, #37, #53, #50, #52, #59, #60
+
+ * Cleanups and other changes:
+
+ - Major startup.c cleanup, removed the parallel daemon checks,
+ in favour of better lock handling and the automatic lock file
+ name support,
+ - Some minor refactoring,
+ - Added a TimePropertiesDS structure and removed its fields
+ from PtpClock
+ - Improved log file format with full date and interface name,
+ removed the "===" indents
+ - Delay Request interval no longer mandatory in hybrid mode,
+ but a warning will be issued if it's not explicitly defined -
+ default of 0 is used,
+ - Removed the experimental status of hybrid mode,
+ - Manual stepping of the clock no longer just resets the observed
+ drift: resets it or restores it based on the drift handling setting,
+ and sets the time immediately before writing any logs - this allows
+ to re-sync and stabilise time almost instantly
+ - Up to date man pages written
+
+2012-06-20 George Neville-Neil <gnn at neville-neil.com>
+
+ * 2.2.2 release
+
+ * Leap second fix
+
+2012-01-05 George Neville-Neil <gnn at neville-neil.com>
+
+ * 2.2.0 release
+
+ * Patches: 3134556, 3296405
+
+ * Added support for Mac OS X (tested on Snow Leopard and Lion)
+
+ * Protocol implementation Fixes:
+
+ * Bugs Fixed
+
+ - Client now correctly accepts the Master DelayReq rate
+ - DelayMS and delaySM now correctly show the sign when negative
+ - Sanity Flags: client now requires -g or -G for slave/master operation.
+ - Client can print the MAC address of its master (instead of EEUI)
+ - master now sends ARB timescale only with utc_offset of zero
+ - slave now only uses the last UTC_Offset if UTC_Valid bit is on.
+ - passive masters no longer become active every 12s,
+ - first delayreq is postponed until we receive the first sync
+ - -G (master with ntpd) now announces a better clock class of 13
+ - delayReq period is now uniformly randomized from range
+ - updated to the PTPv2 messages rates (sync / delayreq / announce )
+ - operator is warned once when the we slew the clock at maximum speed
+ - and several others too minor too mention
+
+ * System fixes and new features
+
+ - Frequency adjustment is now calculated in double precision
+ - Kernel timestamps are now in nanoseconds precision
+ - Timer system overhead was reduced to 16 alarms per second (was 1000)
+ - each reset now generates an IGMP leave/join operation to
+ the multicast group
+ - Log file is now appended, with the right permissions
+ - Debug messages show a timestamp
+ - Signals are now processed synchronously (to avoid race conditions)
+ - Configurable amount of logging (to avoid filling up /var/log)
+ - client now checks own filelock, $0 and well-known daemons.
+ - unicast messages can use DNS
+ - syslog support (-S)
+ - quality file can be generated with received syncs (-R)
+ - messages can be dumped for debug (-P)
+ - gnore packets that result in large deltas (-M)
+ - SIGUSR1 now steps the clock to the current PTP offset
+ - SIGUSR2 now cycles the domain number (useful for testing)
+ - reverted R135 timer change from integer back to floating point
+ - rand() is now seeded with last digits of our own mac address
+ - IGMP_refresh waits 100ms between drop() and add()
+ - checked to run without leaks inside valgrind 3.5.0
+ - last message received is identified by a column on the statistics log
+ - messages are sent to Syslog by default. reversed -S flag
+ - statistcs file now display /etc/ethers names (besides mac address)
+ - option -C is console mode with full verbosity
+ - startup warnings are also duplicated in stdout
+ - startup: lockfile is checked twice: once at init, to return
+ correct errorlevel, and a second time after deaemon()
+ - check for root uid()
+ - improvements in parallel daemons checking
+ - command line parameters are dumped at init
+ - Set the unicast flag when sending unicast packets
+ (experimental, hybrid mode only).
+
+ - Reimplemented integer64_to_internalTime not to use doubles
+ - Replaced divTime by div2Time
+ - Replaced all time.seconds = time.nanoseconds = 0 by clearTime(&time)
+ - Replaced all hex values by named flags
+ - Optimized comparison of clockIdentity in bmc.c
+ - Resolved issue of comparison of offsetScaledLogVariance
+ - Optimized bmcStateDecision not to call bmcDataSetComparison so often with the same parameters
+ - displayStats now uses getTime instead of gettimeofday
+
+2011-02-01 George Neville-Neil <gnn at neville-neil.com>
+
+ * Add support for DNS lookup of timeserver for unicast.
+
+ * Add support for unicasting delay requests.
+
+ * Add code to dump packets on demand via the -P flag as well as in
+ response to updates that violate either the -M or -O flags.
+
+2010-10-12 George Neville-Neil <gnn at neville-neil.com>
+
+ * 2.1.0 First main line release of PTPv2 code base
+ (IEEE-1588-2008)
+
+ * Add code to limit how much of an offset or delay the client is
+ willing to tolerate.
+
+ * Add support for BINTIME on FreeBSD which gives more accurate
+ packet timestamps.
+
+ * Add quality file support
+
+ * Fix significant bugs that prevented correct operation in
+ End-to-End mode.
+
+ * Add support for syslog.
+
+ * Add support for user configurable TTL.
+
+ * Clean up code formatting, headers, comments etc.
+
diff --git a/rtemsbsd/ptpd/INSTALL b/rtemsbsd/ptpd/INSTALL
new file mode 100644
index 00000000..0e218aa0
--- /dev/null
+++ b/rtemsbsd/ptpd/INSTALL
@@ -0,0 +1,153 @@
+PTP Daemon Version 2 Build and Installation Instructions
+
+15 June 2015
+
+George V. Neville-Neil, Wojciech Owczarek
+
+The PTP Daemon is known to work on FreeBSD, NetBSD, OpenBSD,
+OpenSolaris and its derivatives, Mac OS X and Linux systems.
+
+*) Requirements
+
+You will need the following tools and libraries to build ptpd2:
+
+1. GNU autotools (automake, autoconf, libtool etc.) - essential
+
+2. PCAP libraries (libpcap/bpf) and development headers
+ (optional but recommended) - required for Ethernet transport
+ and generally recommended for message performance
+
+ *NOTE: Libpcap seems to be broken on OpenSolaris / OmniOS,
+ builds and runs, but no data is being received.
+
+3. SNMP libraries (optional) - will allow building with SNMP
+ support
+
+*) Building
+
+1. cd into the root of the tree (which is where the file you're
+ reading resides)
+
+2. If building from a repository such as Subversion, CVS or GIT, run:
+
+ autoreconf -vi
+
+3. If building from a distribution .tar.gz package, you may run autoreconf -vi,
+ but in most cases running ./configure should be enough - you must run
+ autoreconf if configure or result in errors.
+
+4. ./configure
+
+ The final section of the configure script output will show
+ the results of all library dependency checks and feature checks:
+ features and dependencies can be controled using the options
+ described below. Running ./configure --help will show all available
+ options.
+
+ *NOTE* On QNX systems, the configure script may not accept the
+ 'rm' command implementation available, so if it complains,
+ run ./configure with the following environment variable:
+
+ export ACCEPT_INFERIOR_RM_PROGRAM=yes
+
+ ========= configure script options for ptpd ========================
+
+ * To disable the statistics code in order to lower computational
+ expense and RAM usage, configure the build with the following options:
+
+ ./configure --disable-statistics
+
+ * To build a slave-only version (with GM logic disabled), build with:
+
+ ./configure --enable-slave-only
+
+ This will build a version incapable of running as master.
+
+ * As of 2.3.1, the default timer implamentation used for periodic
+ actions uses POSIX timers if only the OS supports it. To disable
+ POSIX timer support and use the old interval timer implementation,
+ the following switch can be used:
+
+ ./configure --disable-posix-timers
+
+ * As of 2.3.1, support was added for multiple unicast destinations
+ (both GMs and slaves) - with negotiation (signaling) and without.
+ The default maximum number of unicast destinations (also the
+ maximum number of slaves for a signaling GM) is set to 128. This
+ setting has an impact on the amount of RAM used by PTPd. The
+ unicast table capacity can be changed to a maximum of 2048 using:
+
+ ./configure --with-max-unicast-destinations=[16...2048]
+
+ It is possible to change this limit to more than 2048, but realistically
+ due to message scheduling, timer signal usage and single-threaded
+ architecture, anywhere above 1000 ptpd will show high CPU utilisation.
+ Ptpd has been successfully tested as unicast Telecom (negotiation) master
+ at 1000 slaves and high message rates (32/sec) and provided adequate
+ performance. When running high numbers of slaves, it is recommended
+ to use libpcap (ptpengine:use_libpcap=y) to allow easier matching
+ of Sync and FollowUp, unless the NIC supports software transmit
+ timestamps. Without those and without libpcap, ptpd loops packets
+ back into its own socket, so the followUp destination cannot be
+ matched with the Sync destination, and a lookup table is used.
+ Hash collissions are low, but they may happen, in which case
+ ptpd will iterate over a table of last timestamps. This is only and issue
+ in extreme scenarios.
+
+ * With certain NIC drivers on Linux providing the SO_TIMESTAMPING
+ functionality with software transmit timestamps (SOF_TIMESTAMPING_
+ TX_SOFTWARE), unpredictable transmit timestamp failures can occur,
+ which may cause unstable operation and in some cases high CPU usage
+ (see https://sourceforge.net/p/ptpd/discussion/469208/thread/4aea6e47).
+ While fixes have been implemented and have shown success,
+ there is still some risk. To disable SO_TIMESTAMPING support on Linux
+ completely, regardless of OS support, use:
+
+ ./configure --disable-so-timestamping
+
+ * To enable experimental options use:
+
+ ./configure --enable-experimental-options
+
+ This enables:
+
+ - running SO_TIMESTAMPING without verifying if NIC driver
+ supports the given timestamping options
+ - running message intervals outside of PTP specification
+
+ *NOTE* On QNX systems, when experimental options are enabled,
+ a clock_gettime approximation using CPU clock counter and
+ attaching to IRQ0 is used, and this is also used to retrieve
+ packet RX and TX timestamps, ignoring PCAP timestamps and
+ socket options. This is recommended for best performance,
+ but requires more testing before becoming the default.
+ by default in QNX, clock is incremented in ticks that
+ can be as long as 10 milliseconds, and PCAP and socket
+ timestamps are only accurate to several milliseconds.
+
+5. make
+
+6. Read the manual pages ptpd2(8) and ptpd2.conf(5). The man pages
+ are the most complete source of configuration information.
+ ptpd2 itself also provides an extensive help:
+ run ptpd2 --help to see the short help
+ run ptpd2 --long-help to see the long help for all settings
+ run ptpd2 -e [key:setting] to display help for a single setting
+
+7. Update test/client-e2e-socket.conf so that its
+ "ptpengine:interface = " setting points to a network interface on
+ your test machine that can see PTP packets from a grandmaster.
+
+8. Test it in place: ./src/ptpd2 -c test/client-e2e-socket.conf
+
+9. Check the log output of the daemon in /var/run/ptpd2.event.log
+ Check statistics output of the daemon in /var/run/ptpd2.stats.log
+ Check the status file /var/run/ptpd2.status.log
+
+10. If the results look good, make install
+
+The daemon may work on other POSIX based systems but this is not
+guaranteed. Patches and fixes are welcome on the SourceForge page:
+
+http://ptpd.sf.net
+
diff --git a/rtemsbsd/ptpd/Makefile.am b/rtemsbsd/ptpd/Makefile.am
new file mode 100644
index 00000000..41422be6
--- /dev/null
+++ b/rtemsbsd/ptpd/Makefile.am
@@ -0,0 +1,53 @@
+ACLOCAL_AMFLAGS = -I m4
+
+NULL =
+
+SUBDIRS = \
+ src \
+ $(NULL)
+
+EXTRA_DIST = \
+ COPYRIGHT \
+ ChangeLog \
+ README.md \
+ \
+ doc \
+ tools \
+ test \
+ packagebuild \
+ src/ptpd2.conf.default-full\
+ src/ptpd2.conf.minimal\
+ src/ptpd2.8\
+ src/ptpd2.conf.5\
+ src/leap-seconds.list\
+ src/dep/iniparser/AUTHORS\
+ src/dep/iniparser/LICENSE\
+ src/dep/iniparser/README\
+ src/templates.conf\
+ \
+ $(NULL)
+
+dist-hook:
+ rm -f $(distdir)/packagebuild/rpm-rh/*.rpm
+
+CLEANFILES =
+#DISTCLEANFILES = .gcc-warning
+
+BUILT_SOURCES = \
+ libtool \
+ $(NULL)
+
+ptpd2dir = $(datadir)/$(PACKAGE)
+ptpd2_DATA = src/leap-seconds.list \
+ src/ptpd2.conf.minimal \
+ src/ptpd2.conf.default-full \
+ src/templates.conf \
+ doc/PTPBASE-MIB.txt \
+ $(NULL)
+
+#dist-hook:
+# @find $(distdir) -type d -name SCCS -print | xargs rm -rf
+
+libtool: $(LIBTOOL_DEPS)
+ ./config.status --recheck
+
diff --git a/rtemsbsd/ptpd/Makefile.old b/rtemsbsd/ptpd/Makefile.old
new file mode 100644
index 00000000..3de584ca
--- /dev/null
+++ b/rtemsbsd/ptpd/Makefile.old
@@ -0,0 +1,33 @@
+# Root Makefile for ptpd, used for cutting releases and release candidates
+
+NAME = ptpd
+VERSION = ${NAME}-2.2.2
+RC = ${NAME}-2-RC-0
+
+rc:
+ (cd src; make clean)
+ mkdir $(RC)
+ (cd $(RC); \
+ ln -s ../src .; \
+ ln -s ../doc .; \
+ ln -s ../tools .; \
+ ln -s ../COPYRIGHT .; \
+ ln -s ../ChangeLog .; \
+ ln -s ../Makefile .; \
+ ln -s ../README .; \
+ ln -s ../RELEASE_NOTES .)
+ tar cvzf $(RC).tar.gz -L --exclude .o --exclude Doxygen --exclude .svn --exclude .dep --exclude core $(RC)
+
+release:
+ (cd src; make clean)
+ mkdir $(VERSION)
+ (cd $(VERSION); \
+ ln -s ../src .; \
+ ln -s ../doc .; \
+ ln -s ../tools .; \
+ ln -s ../COPYRIGHT .; \
+ ln -s ../ChangeLog .; \
+ ln -s ../Makefile .; \
+ ln -s ../README .; \
+ ln -s ../RELEASE_NOTES .)
+ tar cvzf $(VERSION).tar.gz -L --exclude .o --exclude Doxygen --exclude .svn --exclude .dep --exclude core $(VERSION)
diff --git a/rtemsbsd/ptpd/README.md b/rtemsbsd/ptpd/README.md
new file mode 100644
index 00000000..469a64ab
--- /dev/null
+++ b/rtemsbsd/ptpd/README.md
@@ -0,0 +1,43 @@
+PTPd
+===
+
+PTP daemon (PTPd) is an implementation the Precision Time Protocol (PTP) version
+2 as defined by 'IEEE Std 1588-2008'. PTP provides precise time coordination of
+Ethernet LAN connected computers. It was designed primarily for instrumentation
+and control systems.
+
+Use
+---
+
+PTPd can coordinate the clocks of a group of LAN connected computers with each
+other. It has been shown to achieve microsecond level coordination, even on
+limited platforms.
+
+The 'ptpd' program can be built from the included source code. To use the
+program, run 'ptpd' on a group of LAN connected computers. Compile with
+'PTPD_DBG' defined and run with the '-C' or -V argument to watch what's going on.
+
+If you are just looking for software to update the time on your desktop, you
+probably want something that implements the Network Time Protocol. It can
+coordinate computer clocks with an absolute time reference such as UTC.
+
+Please refer to the
+[INSTALL](https://raw.githubusercontent.com/ptpd/ptpd/master/INSTALL) file
+for build instructions and configuration options. Please refer to the
+[README.repocheckout](https://github.com/ptpd/ptpd/blob/master/README.repocheckout)
+file for information on how to build from source code repositories.
+
+Legal notice
+---
+
+PTPd was written by using only information contained within 'IEEE Std
+1588-2008'. IEEE 1588 may contain patented technology, the use of which is not
+under the control of the authors of PTPd. Users of IEEE 1588 may need to obtain
+a license for the patented technology in the protocol. Contact the IEEE for
+licensing information.
+
+PTPd is licensed under a 2 Clause BSD Open Source License. Please refer to the
+[COPYRIGHT](https://github.com/ptpd/ptpd/blob/master/COPYRIGHT) file for
+additional information.
+
+PTPd comes with absolutely no warranty.
diff --git a/rtemsbsd/ptpd/README.repocheckout b/rtemsbsd/ptpd/README.repocheckout
new file mode 100644
index 00000000..422dc448
--- /dev/null
+++ b/rtemsbsd/ptpd/README.repocheckout
@@ -0,0 +1,18 @@
+If you are getting this code from a tarball distribution, this
+file does not apply to you.
+
+If you are getting this code from a repo checkout, you will need to
+have autoconf, automake, and libtool installed.
+
+Run:
+
+ % autoreconf -vi
+
+to generate the build environment. On some older systems you may get
+libtool errors (./libtool: line nnnn: X[command]: command not found) -
+in that case use:
+
+ % libtoolize --force --copy; aclocal; autoconf; automake
+
+
+
diff --git a/rtemsbsd/ptpd/TODO b/rtemsbsd/ptpd/TODO
new file mode 100644
index 00000000..b0277ef5
--- /dev/null
+++ b/rtemsbsd/ptpd/TODO
@@ -0,0 +1,49 @@
+# not part of distribution - TODO notes to implement before next release
+
+2.3.1.1 / 2.3.2:
+
+- man page updates for template files x
+- clock ID printed in reverse in L2 mode
+ with (grandmasterID present?) - not an issue, looks like a White Rabbit problem x
+- compact grantable message types and use smaller array to save memory x
+- 16.1.1: may be granted in listening state or passive state <- but g.8265 is no BMCA x
+- sig/msg message acceptance 4 options: all1,port, clockid,port, clock,all1, all1,all1 x
+- rework config option parsing x
+- rework parseConfig to support an opcode, get rid of remaining macros x
+- pointer to best master x
+- disqualification flag x
+- remove masteraddr in favour of bestMaster->sourceAddr x
+- use pointer to best master x
+- management TLV buffer guard and don't unpack GET x
+- SNMP - implement statistics tables x
+- SNMP - implement remaining traps x
+- add alarm events -> trigger SNMP traps x
+- add offset alarm threshold to config x
+- clean up object order in the MIB x
+- call alarm handlers before shutdown (on port disabled?) x
+
+- add alarm initial delay to config + man x
+- add alarm timeout to config + man x
+- add snmp traps enable/disable to config + man x
+- implement ofm threshold alarm triggering x
+- SNMP MIB: add domain mismatch trap + last domain x
+- add alarm messages to log notifications x
+
+- add alarm messages to alarm table (for fault record)
+- rework trigger and dependency macros: 2nd pass?
+- complete built-in templates
+- add dummy or populated default template file x [dummy]
+- NVRAM write?
+- use split token loop macros
+- SET messages to refresh the config properly x/-
+ add set + commit + rollback as snmp handles
+- remaining management message SET handling
+- add fault record and numberfaults to snmp and mgmt
+- update man page with alarm descriptions
+- improve alarm dump
+- add alarm descriptions to help output
+- eventually add support for an event queue
+- SNMP MIB: add alarm list so that
+ informs can also send date and time
+- MGMT clear and get fault record
+- configurable alarm severities?
diff --git a/rtemsbsd/ptpd/configure.ac b/rtemsbsd/ptpd/configure.ac
new file mode 100644
index 00000000..95e4ff48
--- /dev/null
+++ b/rtemsbsd/ptpd/configure.ac
@@ -0,0 +1,777 @@
+# Process this file with autoconf to produce a configure script.
+
+m4_include([m4/version.m4])
+AC_PREREQ(2.59)
+AC_INIT(
+ [ptpd],
+ [VERSION_NUMBER],
+ [ptpd-bugs at nwtime.org],
+ [],
+ [PTPD_URL]dnl
+)
+AC_CONFIG_SRCDIR([src/arith.c])
+AC_CONFIG_HEADER([config.h])
+AC_CONFIG_AUX_DIR([build-aux])
+AC_CONFIG_MACRO_DIR([m4])
+
+AM_INIT_AUTOMAKE([1.9 foreign -Wall])
+
+[RELEASE_DATE]="RELEASE_DATE"
+AC_SUBST([RELEASE_DATE])
+[VERSION_NUMBER]="VERSION_NUMBER"
+AC_SUBST([VERSION_NUMBER])
+
+# Checks for programs.
+AC_PROG_AWK
+AC_PROG_CC
+ifdef([AC_PROG_CC_C99], [AC_PROG_CC_C99])
+AC_PROG_LIBTOOL
+AM_PROG_CC_C_O
+
+# Check for svnversion, cut and tr
+
+AC_PATH_PROG(cutpath, cut)
+AC_PATH_PROG(trpath, tr)
+AC_PATH_PROG(datepath, date)
+
+if test -n "$datepath"; then
+ build_date=`$datepath +%Y%m%d`
+ AC_DEFINE_UNQUOTED(BUILD_DATE, ["$build_date"], [build date year-month-day])
+fi
+
+# Set CODE_REVISION if svnversion found, version is suffixed with "-svn" and code is versioned
+if test -n "$cutpath" && test -n "$trpath"; then
+ ver_prefix=`echo VERSION_NUMBER | $cutpath -s -d- -f2`
+ AC_PATH_PROG(svnverpath, svnversion)
+ if test "$ver_prefix" = "svn" && test -n "$svnverpath"; then
+ svn_rev=`$svnverpath 2>/dev/null | $cutpath -s -d: -f2 | $trpath -d MS`
+ if test -n "$svn_rev"; then
+ AC_DEFINE_UNQUOTED(CODE_REVISION, ["$svn_rev"], [source code revision])
+ else
+ svn_rev=`$svnverpath 2>/dev/null | $trpath -d MS`
+ if test -n "$svn_rev"; then
+ AC_DEFINE_UNQUOTED(CODE_REVISION, ["$svn_rev"], [source code revision])
+ fi
+ fi
+ fi
+fi
+
+case $host in
+*linux*)
+
+ AC_CHECK_HEADERS([linux/net_tstamp.h], [], [
+
+ if [ test -d "/usr/src/kernels/`uname -r`/include" ]; then
+ LINUX_KERNEL_INCLUDES="-I/usr/src/kernels/`uname -r`/include"
+ LINUX_KERNEL_HEADERS=yes
+ fi
+
+ if [ test -d "/usr/src/linux-headers-`uname -r`/include" ]; then
+ LINUX_KERNEL_INCLUDES="-I/usr/src/linux-headers-`uname -r`/include"
+ LINUX_KERNEL_HEADERS=yes
+ fi
+
+ ])
+
+ ;;
+esac
+
+AM_CONDITIONAL(LINUX_KERNEL_HEADERS, test x$LINUX_KERNEL_HEADERS = xyes)
+AC_SUBST(LINUX_KERNEL_INCLUDES)
+
+# Checks for libraries.
+AC_SEARCH_LIBS([pow], [m])
+AC_SEARCH_LIBS([clock_gettime], [rt])
+AC_SEARCH_LIBS([timer_create], [rt])
+AC_SEARCH_LIBS([connect], [socket])
+AC_SEARCH_LIBS([gethostbyname], [nsl])
+
+# Checks for header files.
+AC_HEADER_STDC
+
+AC_CHECK_HEADERS([arpa/inet.h fcntl.h limits.h netdb.h net/ethernet.h netinet/in.h netinet/in_systm.h netinet/ether.h sys/uio.h stdlib.h string.h sys/ioctl.h sys/param.h sys/socket.h sys/sockio.h ifaddrs.h sys/time.h syslog.h unistd.h glob.h sched.h utmp.h utmpx.h unix.h linux/rtc.h sys/timex.h getopt.h])
+
+AC_CHECK_HEADERS([endian.h machine/endian.h sys/isa_defs.h])
+
+AC_CHECK_HEADERS([math.h],[],[AC_MSG_ERROR([math.h is required to compile PTPd])],[])
+
+# MUST chck for cpuset AFTER the check for param as the latter needs
+# the former to pass the compile check.
+AC_CHECK_HEADERS([sys/cpuset.h], [], [],
+[#ifdef HAVE_SYS_PARAM_H
+ # include <sys/param.h>
+#endif
+])
+
+# Similar case for if.h
+AC_CHECK_HEADERS([net/if.h], [], [],
+[
+#ifdef HAVE_SYS_SOCKET_H
+ #include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+ #include <sys/types.h>
+#endif
+])
+
+# Similar case for if_arp.h
+AC_CHECK_HEADERS([net/if_arp.h], [], [],
+[
+#ifdef HAVE_SYS_SOCKET_H
+ #include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+ #include <sys/types.h>
+#endif
+#ifdef HAVE_NET_IF_H
+ #include <net/if.h>
+#endif
+])
+
+# Similar case for netinet/if_ether.h
+AC_CHECK_HEADERS([netinet/if_ether.h], [], [],
+[
+#ifdef HAVE_SYS_SOCKET_H
+ #include <sys/socket.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+ #include <arpa/inet.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+ #include <sys/types.h>
+#endif
+#ifdef HAVE_NET_IF_ARP_H
+ #include <net/if_arp.h>
+#endif
+#ifdef HAVE_NET_IF_H
+ #include <net/if.h>
+#endif
+])
+
+
+# ...and for if_ether.h
+AC_CHECK_HEADERS([net/if_ether.h], [], [],
+[
+#ifdef HAVE_SYS_TYPES_H
+ #include <sys/types.h>
+#endif
+#ifdef HAVE_NET_IF_H
+ #include <net/if.h>
+#endif
+])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_HEADER_STDBOOL
+AC_HEADER_TIME
+AC_C_VOLATILE
+
+# Automake 2.59 has a problem with those on RHEL5
+m4_version_prereq(2.60,[
+ AC_TYPE_UINT32_T
+ AC_TYPE_UINT64_T
+ AC_TYPE_UINT8_T
+ AC_TYPE_SIZE_T
+ AC_TYPE_SSIZE_T
+ AC_TYPE_INT64_T
+ ],
+ [AC_TYPE_SIZE_T]
+)
+
+# Check for tick in the timex structure
+AC_CHECK_MEMBERS([struct utmp.ut_time], [], [], [
+#ifdef HAVE_UTMP_H
+#include <utmp.h>
+#endif
+])
+AC_CHECK_MEMBERS([struct timex.tick], [], [], [[#include <sys/timex.h>]])
+AC_CHECK_MEMBERS([struct timex.tai], [], [], [[#include <sys/timex.h>]])
+AC_CHECK_MEMBERS([struct ntptimeval.tai], [], [],
+[
+#include <sys/time.h>
+#include <sys/timex.h>
+])
+
+
+# Check for ifr_hwaddr in the ifreq structure
+AC_CHECK_MEMBERS([struct ifreq.ifr_hwaddr], [], [], [[#include <net/if.h>]])
+
+# Check for ifr_index in the ifreq structure
+AC_CHECK_MEMBERS([struct ifreq.ifr_index], [], [], [[#include <net/if.h>]])
+
+# Check for ifr_ifindex in the ifreq structure
+AC_CHECK_MEMBERS([struct ifreq.ifr_ifindex], [], [], [[#include <net/if.h>]])
+
+# ether_add: octet vs. ether_addr_octet - FreeBSD, any others?
+AC_CHECK_MEMBERS([struct ether_addr.octet], [], [],
+[
+#ifdef HAVE_SYS_TYPES_H
+ #include <sys/types.h>
+#endif
+#ifdef HAVE_NETINET_ETHER_H
+ #include <netinet/ether.h>
+#endif
+#ifdef HAVE_NET_ETHERNET_H
+ #include <net/ethernet.h>
+#endif
+#ifdef HAVE_NET_IF_ETHER_H
+ #include <net/if.h>
+#endif
+#ifdef HAVE_NET_IF_ETHER_H
+ #include <net/if_ether.h>
+#endif
+])
+
+# Checks for library functions
+AC_PROG_GCC_TRADITIONAL
+AC_FUNC_MALLOC
+AC_FUNC_MEMCMP
+AC_FUNC_SELECT_ARGTYPES
+AC_TYPE_SIGNAL
+AC_FUNC_STRFTIME
+AC_FUNC_VPRINTF
+AC_CHECK_FUNCS([clock_gettime dup2 ftruncate gethostbyname2 gettimeofday inet_ntoa memset pow select socket strchr strdup strerror strtol glob pututline utmpxname updwtmpx setutent endutent signal ntp_gettime getopt_long])
+
+if test -n "$GCC"; then
+ AC_MSG_CHECKING(if GCC -fstack-protector is usable)
+ OLDCFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -fstack-protector"
+ AC_TRY_LINK(,,[
+ AC_MSG_RESULT(yes)
+ ], [
+ AC_MSG_RESULT(no)
+ CFLAGS="$OLDCFLAGS"
+ ])
+fi
+
+
+AC_MSG_NOTICE([************************************************************])
+AC_MSG_NOTICE([* PTPD BUILD FLAG AND LIBRARY DEPENDENCY CHECKS START HERE *])
+AC_MSG_NOTICE([************************************************************])
+
+AC_CHECK_DECLS([MSG_ERRQUEUE], [], [], [[#include <sys/socket.h>]])
+
+AC_CHECK_DECLS([POSIX_TIMERS_SUPPORTED], [posix_timers=true], [posix_timers=false], [
+#ifdef __sun && !defined(_XPG6)
+#define _XPG6
+#endif
+#include <unistd.h>
+#if _POSIX_TIMERS && (_POSIX_TIMERS - 200112L) >= 0
+#define POSIX_TIMERS_SUPPORTED
+#else
+#endif
+])
+
+# ALL USER-CONTROLLED KNOBS START HERE
+
+AC_ARG_ENABLE([posix_timers],
+ AS_HELP_STRING( [--disable-posix-timers (enabled by default if supported)],
+ [Disable POSIX timer support and use interval timers even if POSIX timers supported by the OS])
+ )
+
+AS_IF([test "x$enable_posix_timers" == "xno"], [
+posix_timers=false
+])
+
+AM_CONDITIONAL([PTIMERS], [test x$posix_timers = xtrue ])
+
+AC_MSG_CHECKING([if we want to build POSIX timer support])
+
+case "$posix_timers" in
+ "true")
+ PTP_PTIMERS="-DPTPD_PTIMERS"
+ AC_MSG_RESULT([yes])
+ ;;
+ *) PTP_PTIMERS=""
+ AC_MSG_RESULT([no])
+ ;;
+esac
+
+AC_SUBST(PTP_PTIMERS)
+
+
+AC_ARG_WITH(
+ [pcap-config],
+ [AS_HELP_STRING(
+ [--with-pcap-config],
+ [+ =pcap-config]
+ )],
+ [ans=$withval],
+ [ans=yes]
+)
+case "$ans" in
+ no)
+ ;;
+ yes)
+ ans=pcap-config
+ ;;
+ /*)
+ pcap_config_dirname=`dirname $ans`
+ pcap_config_basename=`basename $ans`
+ ans=abspath
+ ;;
+ */*)
+ AC_MSG_ERROR([--with-pcap-config takes either a name or an absolute path])
+ ;;
+ *)
+ ;;
+esac
+PROG_PCAP_CONFIG=$ans
+case "$PROG_PCAP_CONFIG" in
+ no)
+ ;;
+ abspath)
+ AC_PATH_PROG([PATH_PCAP_CONFIG], [$pcap_config_basename], [], [$pcap_config_dirname])
+ AS_UNSET([ac_cv_path_PATH_PCAP_CONFIG])
+ ;;
+ *)
+ AC_PATH_PROG([PATH_PCAP_CONFIG], [$PROG_PCAP_CONFIG])
+ AS_UNSET([ac_cv_path_PATH_PCAP_CONFIG])
+ ;;
+esac
+
+AC_MSG_CHECKING([if we want to build with libpcap support])
+AC_ARG_ENABLE(
+ [pcap],
+ [AS_HELP_STRING(
+ [--disable-pcap],
+ [disable support for PCAP (enabled by default / if we find pcap-config)]
+ )],
+ [try_pcap=$enableval],
+ [try_pcap=yes]
+)
+
+if test "x$try_pcap" = "xyes"; then
+
+case "$PATH_PCAP_CONFIG" in
+ /*)
+ AC_MSG_RESULT([yes, pcap-config])
+ AC_CHECK_HEADERS([pcap/pcap.h pcap.h])
+ try_pcap=yes
+ ;;
+ *)
+ AC_MSG_RESULT([yes, guessing flags])
+ AC_CHECK_HEADERS([pcap/pcap.h pcap.h])
+ try_pcap=guess
+ ;;
+esac
+
+fi
+
+ptpd_pcap_enabled=0
+
+case "$try_pcap" in
+ yes)
+ case "$PATH_PCAP_CONFIG" in
+ /*)
+ PCAP_LIBS=`$PATH_PCAP_CONFIG --libs`
+ AC_SUBST([PCAP_LIBS])
+ # Separate CPPFLAGS and CFLAGS
+ foo=`$PATH_PCAP_CONFIG --cflags`
+ PCAP_CPPFLAGS=
+ PCAP_CFLAGS=
+ for i in $foo; do
+ case "$i" in
+ -D*|-F*|-U*|-I*)
+ PCAP_CPPFLAGS="$PCAP_CPPFLAGS $i"
+ ;;
+ *) PCAP_CFLAGS="$PCAP_CFLAGS $i"
+ ;;
+ esac
+ done
+ AC_SUBST([PCAP_CPPFLAGS])
+ AC_SUBST([PCAP_CFLAGS])
+
+ save_CFLAGS=$CFLAGS
+ save_CPPFLAGS=$CPPFLAGS
+ save_LIBS=$LIBS
+ CFLAGS=$PCAP_CFLAGS
+ CPPFLAGS=$PCAP_CPPFLAGS
+
+
+
+ CFLAGS=$save_CFLAGS
+ AS_UNSET([save_CFLAGS])
+ CPPFLAGS=$save_CPPFLAGS
+ AS_UNSET([save_CPPFLAGS])
+ LIBS=$save_LIBS
+ AS_UNSET([save_LIBS])
+
+ AC_MSG_CHECKING([if we can build with libpcap support])
+ case "$ac_cv_header_pcap_pcap_h" in
+ yes)
+ ptpd_pcap_enabled=1
+ PTP_PCAP="-DPTPD_PCAP"
+ AC_MSG_RESULT([yes])
+ ;;
+ no)
+ case "$ac_cv_header_pcap_h" in
+ yes)
+ ptpd_pcap_enabled=1
+ PTP_PCAP="-DPTPD_PCAP"
+ AC_MSG_RESULT([yes])
+ ;;
+ no)
+ PTP_PCAP=""
+ PCAP_LIBS=""
+ PCAP_CPPFLAGS=""
+ PCAP_CFLAGS=""
+ AC_MSG_RESULT([no])
+ ;;
+ esac
+ ;;
+
+ esac
+ ;;
+ *)
+ # This case can't be triggered - we won't get here unless we
+ # have an absolute path to pcap-config.
+ AC_MSG_WARN([Cannot build with libpcap support - pcap-config cannot be found])
+ ;;
+ esac
+ ;;
+ guess)
+ PCAP_LIBS="-lpcap"
+ AC_SUBST([PCAP_LIBS])
+
+ AC_MSG_CHECKING([if we can build with PCAP support])
+ case "$ac_cv_header_pcap_pcap_h" in
+ yes)
+ ptpd_pcap_enabled=1
+ PTP_PCAP="-DPTPD_PCAP"
+ AC_MSG_RESULT([yes])
+ ;;
+ no)
+ case "$ac_cv_header_pcap_h" in
+ yes)
+ ptpd_pcap_enabled=1
+ PTP_PCAP="-DPTPD_PCAP"
+ AC_MSG_RESULT([yes])
+ ;;
+ no)
+ PTP_PCAP=""
+ PCAP_LIBS=""
+ PCAP_CPPFLAGS=""
+ PCAP_CFLAGS=""
+ AC_MSG_RESULT([no])
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ no)
+ PTP_PCAP=""
+ PCAP_LIBS=""
+ PCAP_CPPFLAGS=""
+ PCAP_CFLAGS=""
+ AC_MSG_RESULT([no])
+;;
+
+esac
+
+
+
+AC_SUBST(PTP_PCAP)
+AM_CONDITIONAL([PCAP], [test x$ptpd_pcap_enabled = x1])
+
+AC_ARG_WITH(
+ [net-snmp-config],
+ [AS_HELP_STRING(
+ [--with-net-snmp-config],
+ [+ =net-snmp-config]
+ )],
+ [ans=$withval],
+ [ans=yes]
+)
+case "$ans" in
+ no)
+ ;;
+ yes)
+ ans=net-snmp-config
+ ;;
+ /*)
+ net_snmp_config_dirname=`dirname $ans`
+ net_snmp_config_basename=`basename $ans`
+ ans=abspath
+ ;;
+ */*)
+ AC_MSG_ERROR([--with-net-snmp-config takes either a name or an absolute path])
+ ;;
+ *)
+ ;;
+esac
+PROG_NET_SNMP_CONFIG=$ans
+case "$PROG_NET_SNMP_CONFIG" in
+ no)
+ ;;
+ abspath)
+ AC_PATH_PROG([PATH_NET_SNMP_CONFIG], [$net_snmp_config_basename], [], [$net_snmp_config_dirname])
+ AS_UNSET([ac_cv_path_PATH_NET_SNMP_CONFIG])
+ ;;
+ *)
+ AC_PATH_PROG([PATH_NET_SNMP_CONFIG], [$PROG_NET_SNMP_CONFIG])
+ AS_UNSET([ac_cv_path_PATH_NET_SNMP_CONFIG])
+ ;;
+esac
+
+AC_MSG_CHECKING([if we want to try building SNMP support])
+AC_ARG_ENABLE(
+ [snmp],
+ [AS_HELP_STRING(
+ [--disable-snmp],
+ [disable support for SNMP (enabled if we find net-snmp-config)]
+ )],
+ [try_snmp=$enableval],
+ [case "$PATH_NET_SNMP_CONFIG" in
+ /*)
+ try_snmp=yes
+ ;;
+ *)
+ try_snmp=no
+ ;;
+ esac]
+)
+AC_MSG_RESULT([$try_snmp])
+
+ptpd_snmp_enabled=0
+case "$try_snmp" in
+ yes)
+ case "$PATH_NET_SNMP_CONFIG" in
+ /*)
+ SNMP_LIBS=`$PATH_NET_SNMP_CONFIG --agent-libs`
+ AC_SUBST([SNMP_LIBS])
+ # HMS: we really want to separate CPPFLAGS and CFLAGS
+ foo=`$PATH_NET_SNMP_CONFIG --base-cflags`
+ SNMP_CPPFLAGS=
+ SNMP_CFLAGS=
+ for i in $foo; do
+ case "$i" in
+ -D*|-F*|-U*|-I*)
+ SNMP_CPPFLAGS="$SNMP_CPPFLAGS $i"
+ ;;
+ *) SNMP_CFLAGS="$SNMP_CFLAGS $i"
+ ;;
+ esac
+ done
+ AC_SUBST([SNMP_CPPFLAGS])
+ AC_SUBST([SNMP_CFLAGS])
+
+ save_CFLAGS=$CFLAGS
+ save_CPPFLAGS=$CPPFLAGS
+ save_LIBS=$LIBS
+ CFLAGS=$SNMP_CFLAGS
+ CPPFLAGS=$SNMP_CPPFLAGS
+
+ AC_CHECK_HEADER([net-snmp/net-snmp-config.h])
+
+ CFLAGS=$save_CFLAGS
+ AS_UNSET([save_CFLAGS])
+ CPPFLAGS=$save_CPPFLAGS
+ AS_UNSET([save_CPPFLAGS])
+ LIBS=$save_LIBS
+ AS_UNSET([save_LIBS])
+
+ AC_MSG_CHECKING([if we are building SNMP support])
+ case "$ac_cv_header_net_snmp_net_snmp_config_h" in
+ yes)
+ ptpd_snmp_enabled=1
+ PTP_SNMP="-DPTPD_SNMP"
+ AC_MSG_RESULT([yes])
+ ;;
+ no)
+ PTP_SNMP=""
+ SNMP_LIBS=""
+ SNMP_CPPFLAGS=""
+ SNMP_CFLAGS=""
+ AC_MSG_RESULT([no])
+ ;;
+ esac
+ ;;
+ *)
+ # This case can't be triggered - we won't get here unless we
+ # have an absolute path to net-snmp-config.
+ AC_MSG_WARN([Cannot build with SNMP support - net-snmp-config cannot be found])
+ ;;
+ esac
+ ;;
+esac
+
+AC_SUBST(PTP_SNMP)
+AM_CONDITIONAL([SNMP], [test x$ptpd_snmp_enabled = x1])
+
+
+
+AC_MSG_CHECKING([for RUNTIME_DEBUG])
+AC_ARG_ENABLE(
+ [runtime-debug],
+ [AS_HELP_STRING(
+ [--enable-runtime-debug (disabled by default)],
+ [Enable all debug messages]
+ )],
+ [],
+ [enable_runtime_debug=no]
+)
+AC_MSG_RESULT([$enable_runtime_debug])
+case "$enable_runtime_debug" in
+ yes)
+ PTP_DBL="-DRUNTIME_DEBUG"
+ ;;
+ *)
+ AC_MSG_CHECKING([for (old-style) debug message level])
+ AC_ARG_ENABLE(
+ [debug-level],
+ [AS_HELP_STRING(
+ [[[[--enable-debug-level={basic,medium,all}]]]],
+ [debug message level: basic, medium, all]
+
+ )],
+ [ptp_dblevel=$enableval],
+ [ptp_dblevel=no]
+ )
+ AC_MSG_RESULT([$ptp_dblevel])
+ case "$ptp_dblevel" in
+ "basic")
+ PTP_DBL="-DPTPD_DBG"
+ ;;
+ "medium")
+ PTP_DBL="-DPTPD_DBG2"
+ ;;
+ "all")
+ PTP_DBL="-DPTPD_DBGV"
+ ;;
+ *) PTP_DBL=""
+ ;;
+ esac
+ ;;
+esac
+AC_SUBST(PTP_DBL)
+
+AC_MSG_CHECKING([for maximum unicast destination table size])
+AC_ARG_WITH(
+ [max-unicast-destinations],
+ [AS_HELP_STRING(
+ [--with-max-unicast-destinations = [ 16 .. 2048 (default: 128)]],
+ [Change maximum supported number of unicast destinations -
+ this determines the maximum supported number of slaves
+ in unicast mode (with and without signaling)]
+ )],
+ [max_destinations=$with_max_unicast_destinations],
+ [max_destinations=128]
+)
+
+test $max_destinations -lt 16 && max_destinations=16
+test $max_destinations -gt 2048 && max_destinations=2048
+
+PTP_UNICAST_MAX="-DPTPD_UNICAST_MAX=$max_destinations"
+AC_SUBST([PTP_UNICAST_MAX])
+AC_MSG_RESULT([$max_destinations])
+
+AC_MSG_CHECKING([for daemon mode])
+AC_ARG_ENABLE(
+ [daemon],
+ [AS_HELP_STRING(
+ [--disable-daemon (enabled by default)],
+ [Disable daemon mode]
+ )],
+ [],
+ [enable_daemon=yes]
+)
+AC_MSG_RESULT([$enable_daemon])
+case "$enable_daemon" in
+ no)
+ PTP_DAEMON="-DPTPD_NO_DAEMON"
+ ;;
+esac
+AC_SUBST(PTP_DAEMON)
+
+AC_MSG_CHECKING([for experimental options])
+AC_ARG_ENABLE(
+ [experimental-options],
+ [AS_HELP_STRING(
+ [--enable-experimental-options (disabled by default)],
+ [Enable experimental options]
+ )],
+ [],
+ [enable_experimental_options=no]
+)
+AC_MSG_RESULT([$enable_experimental_options])
+case "$enable_experimental_options" in
+ yes)
+ PTP_EXP="-DPTPD_EXPERIMENTAL"
+ ;;
+esac
+AC_SUBST(PTP_EXP)
+AM_CONDITIONAL([EXP], [test x$enable_experimental_options = xyes])
+
+AC_MSG_CHECKING([for realtime statistics support])
+AC_ARG_ENABLE(
+ [statistics],
+ [AS_HELP_STRING(
+ [--disable-statistics (enabled by default)],
+ [Disable support for realtime statistics and statistics based filtering]
+ )],
+ [],
+ [enable_statistics=yes]
+)
+AC_MSG_RESULT([$enable_statistics])
+case "$enable_statistics" in
+ yes)
+ PTP_STATISTICS="-DPTPD_STATISTICS"
+ ;;
+esac
+AC_SUBST(PTP_STATISTICS)
+
+AM_CONDITIONAL([STATISTICS], [test x$enable_statistics = xyes])
+
+AC_MSG_CHECKING([if we want to enable SO_TIMESTAMPING support on Linux if available])
+AC_ARG_ENABLE(
+ [so-timestamping],
+ [AS_HELP_STRING(
+ [--disable-so-timestamping (enabled by default)],
+ [Disable support for SO_TIMESTAMPING on Linux builds]
+ )],
+ [],
+ [enable_so_timestamping=yes]
+)
+AC_MSG_RESULT([$enable_so_timestamping])
+case "$enable_so_timestamping" in
+ no)
+ PTP_DISABLE_SOTIMESTAMPING="-DPTPD_DISABLE_SOTIMESTAMPING"
+ ;;
+esac
+AC_SUBST(PTP_DISABLE_SOTIMESTAMPING)
+
+AM_CONDITIONAL([DISABLE_SOTIMESTAMPING], [test x$enable_so_timestamping = xno])
+
+AC_MSG_CHECKING([if we're building a slave-only build])
+
+AC_ARG_ENABLE(
+ [slave-only],
+ [AS_HELP_STRING(
+ [--enable-slave-only (disabled by default)],
+ [Build a slave-only version incapable of running as PTP master]
+ )],
+ [],
+ [enable_slave_only=no]
+)
+AC_MSG_RESULT([$enable_slave_only])
+case "$enable_slave_only" in
+ yes)
+ PTP_SLAVE_ONLY="-DPTPD_SLAVE_ONLY"
+ ;;
+esac
+
+AC_SUBST(PTP_SLAVE_ONLY)
+AM_CONDITIONAL([SLAVE_ONLY], [test x$enable_slave_only = xyes])
+
+AC_MSG_NOTICE([************************************************************])
+AC_MSG_NOTICE([* END OF PTPD BUILD FLAG AND LIBRARY DEPENDENCY CHECKS *])
+AC_MSG_NOTICE([************************************************************])
+
+
+AC_CONFIG_FILES([Makefile])
+AC_CONFIG_FILES([src/Makefile])
+AC_CONFIG_FILES([src/ptpd2.8])
+AC_CONFIG_FILES([src/ptpd2.conf.5])
+
+AC_OUTPUT
diff --git a/rtemsbsd/ptpd/doc/IEEE1588v1_vs_IEEE1588v2.pdf b/rtemsbsd/ptpd/doc/IEEE1588v1_vs_IEEE1588v2.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..8f26177eb488a6a1167e9fd04fd08c0cebd7b7a5
GIT binary patch
literal 272225
zcma&MQ<NxSmu;E0ZQHhOXP&fq(zb2ewr$(CZCic*+f{uZ>Y?i)Mnvp*jWO1o-`b=K
zB4V_RbSzM$qgSy7P^<(D1h$5Mp?G-cWlU_$oXiQ>{!NtV#Vo9xOdJU4#jFgROhinK
zY>iF$_ at Eq}983(Xq1>{XwKiS0T2OVmd8Yr|jg#UB9r(DbSP#n at a!2qu<0sYBs3)MZ
zrx=U382K9Z7W2;oBMoulIG936IjD6p5YU}A)Z5zu_?B at XHyqse_Sm^JA1o`s{W?4w
z9H&S4ZooHrxu||ycYj=lSK>S_P-s-|R&hWRnr}8EB*P{<23Y7+*Q!0^XFU6=<!kas
z4)^x;?VRG9EQ?<*$1<db3k~c#4#6aw%vhK-rcEGI0xxDws!dTvPzDE`%;Q`>E?anS
zLG%Zq=l`^Gh7IQld#|gT8bkE-j1^bJ&-9=D=w03>q`ewSVq{6mAM;fIuHl%ICA0SY
zC=;V3Dut+10x{FAeauF)rPv!OrhGD`17W-Io3Kp_al at olS9bg%?qk={n68p*Y)OkK
zxB<<VQscJ5NyNfhz0D&Da0dHA&;A?e at SIA<KqnfvX94;MLVIzk%{5!RTW}xk!sXFh
zTZP3CxumpOn*luQ#^9GWC?;hZpzs_S!h9PAxU0fYXej2U=bPKJDz1YcSM9T74rXKt
zJ2sgte(28vgVLS(M;x@$C>F6e{1OKYziM)uZE43Z0+^dG8f>oLE}Q1Kx$-wv`c~7R
zSwuA&pcx=j7Xw<%V)<cYIaV?Kx20!ZjHJ0qe{13*Pq|iHWh|q`7uIRimiC6Ps_DVc
zsD`t#rbE+$*8S(b<`eR#?%d2Oir-h`yRoB(*fct*U%wb)G)mO5b>W-C$O<qJ!&K!-
zmA~yOHD4vcUT=MdyIN~SUlz3uGzmbpU+Hv$TBmp9!nIx#>%2OwZ&tQXzE4~CL19<_
zk{wp!xTr7}pZtnjQcHIfOv;`5GS-`%>t~O9O^;f@&3m(XZY1>RN$nJ%QW4n`N@!&T
zOF*);2EdNdgy;Z6p;jI^jH<>AohK$w1`F^_L!C(iykff%Y<M7VwzYlMWfeS6o--~5
zp|69}4;Ku_{320Ioh;b(8GbvXK>$pCqhw>_1Ap6WwKhpz+`~7M^E6bf&lUE?d!B>x
zbyEprjbAK9VTL&sLz|S<b$yXyH=u4TZw~|<Y?%<)b#ELPIs&RR<&FJJEsN&~$37^`
z4`<8~r`lpq{u^=(qiiBm;zK}RZs6vQ4+*0xzfHZ5zt5e`kP`};X8ndD0&sjHXTlFI
z{~f3kjWXe?Lf|0VB{}jsBb2V5I{uU)BN7 at yRo4u;C#T^KRP)i17<EzT{lnTZPvj`3
zjX$tT+e1Im+;Y}GeWxIV-F at m(yQ7=G7A2i9K`koE$}9kMM7}5)oRWa{%8 at 6q3FOp1
zvh*N_z!LX(@0?MM2)KJlAC#(aY!5={FFljLs>rR*n}4)N*{eqk+y<OyHj;e~u(KE+
zY7B^q<8VFGh+HvQbIv+!<V%uVlrRTwIu2!Slh=wm;fpkNyOGgU-<u2&0r%J?&=|UZ
zEin;tEUgF7vn#n9PKRGu8P6=C8tTZ|i$rQz$Q^itSa9-bdqR^J@~35(t&WzAiqrh!
zqThlNE}ReB7PC_P6^ZJm4i-mA*9<qT#RsC<FD}9nhU>zCOwqCPiB`dU<=eJ2dI7ak
zuFr%5f5d$ki&|XApY0Lzak2uk^Ckc-<e8++Y{-2&r+d4yIOVyE>EtIs(pHt3K&_yd
zqQ_}OML%K!<5D5hF4 at m?6koZ`B&KH*u!%PGdTw?*?W=1Ws4G4ts;f=;sKa`*hR>ql
z*BVEqOak^z%)adiC`MtMFl_VC)6dvWdtm?V?1~5r;H{y$8_EP`n|QY{{*1FOn;>H~
z5P3(yb3Yh0l5xM(cJ_6>^Q~`Fdl4+nOD at M7hNz&&W}I~;u(}=%zhb0Z5*p0R!)tCM
zNxESrG()<Sy1_|K^@TwVImy1KB%F0Pec$C3%YzLw&g_7DXreO<OY&ODjc}owSZiH5
zLJN2&vjcDmxHY3iKd3c{=JJ6s0xiEhBzY1Li{T?)KnYq<1^PklucH at Gl^_o#`e<q3
zQJ1^4ARxn&%<{HJko~Z%p!6wLvAoVZLa$Coxa&fF{WVv#KBL7Mp=$<~SSeMp5H{X6
z7y|poY_Kt>$@IE#4>zI>8gCepU19H^9WXOFn3xHYG~QeoT3_jVqE<>bh5G at +@xQc*
z{O`nKb&;69A^b-UzQ*%XUxH8W>A;7zjLYYnX<t~!oP(N3e*70ecX_PU-b}}vY>v<Y
zmevfPf+ruZYm)=>fP7Xb{9rw<pgr`Bst>$>?w`Kwdj3p$=ZaF_^Uj}Uda-o&?`3~P
zrr0XX>=F}mBPSjVdFIx=`Pd;{v3{3}{rU|3W!iwvL4>QjrX2!DC`}ivjr49L-P7dY
zgj<%NBiQwgMqcZ)W>?kvC|{T4>;Z5wvANJM^$ufafCuO9#&Ds~1iUKa(=rzt;&v at R
zGp)-ebLa`kq5o{TWsl8}*7;NR*CE>yxGK~!TjQpKMl<{}jBnJvb#1Mb+5PH;NF(>p
zVtQ~7DP=-F9XXRoYvN$5 at nV7T+?%xSp4QtwGH#pgX9~+()Hm7wCu%RbJ<3q!Q0I`h
zPlH59^n%*Gr?jkOGo)82kXPzH;r`|XL}ch-t4_=<jV5GIIA7 at c4hcwynAhp{;o{vR
zhk6Ib`y*W5Zi4l;pEn51Kn{sxF98X7*)8E`xvg8IUaJrvYq6y%<B at pzX6VQ#2L2~g
zizYYH=j_bdL@}mTRxN>%bnq5h&}+gxh8LIBh2=fd0~8f3z^c}o)~8gL0onRjE62QZ
z3>|$ZP7m_;B>prfGGTU!yeD)<2Kz|b58;MPD%yIbV>!~js9L%7vSI4UyOg$6kXM$D
z)K?h$+mK1G+(*(DtM7I9uTNZ|{1aPzVK2XUbX6U at JXr6z_4rk`*tX>gUT|7EB{6P{
zshMVZMH-Dalk;<zrN^ORmlTQmY+PT3L8-EbHMgT7c?eu)amVBRicgux%pU3`o=8_0
z?GkU>zwa3$TV5cT!G!B$hv8qxk~VGb-iFaiPaR%4Y6R?sWI%Lll}Syw*9gT7<hc#S
zd^!uQO5XhR_BWz9wvN1GX_v`H5eU!}1;*v15qqZN!#gaZG~H2xgeOWME*v at Xh$73x
zAAM-`tNe3m?S=$c3%~}Z^}+ZqT%?e1?ZyV1qJK<5Y7b04=ODUImcZPfXSDUTt=jBD
z5D$oxVPo^|&x<Y5N;)s?b{1Rb$LN#T9!`5YS)tv4y}0eJX?7rldtuy(d$Z`w;POl%
z+}TpYRS^0h_zmIO{W!&_?QBuX3~fp|AeN*9`*?H&VI)L&27*3Q!^wjJ!I_`-_uj(i
zWPKC at 0lD039G8ivCHW;X;}9NGdHCG#{e|O!0n&4$I!Fn-W(;{RN0bVDPmq)GasgX|
zODb at 9jdriRiA%C}9>uKgjZ|?u_M$P8!SMkL6&dZty?EU>$ae9M9l`CfIQtxF;2OEV
z0R-$24nTK9Wbb!T?B>lDo9&_=KkfD6`>o1QrsjIDRF7>ga+(ha9&?)$_?UMOHvQ{n
z1=aU-p}}~`ZfPHO{8CR6;l=c1c!&9|_Vi6 at s;J9?0HU<|22(=o9`Vk!a0}aHKPfm&
z?+C=;R-tipjX#?7RmUh3tigwhq1oVd8uY_?7NOZ3cR9V6*6FKfzv&3rYQ<Ztt4KrW
z7HBXvE3(?w&`jxBo$xw_=0tM1F*W&$@*`OyQAV77(u>iR)$xk_2I^+_r&8a22(B4<
z1#Umg0$9HNV`uT?MJJAqS8qb!uN#YGn^k$%JUAZ;b at oPyB3+=xgr53KwcTA!2lAoO
zV3JRbsnTU~V&HSn`tl{HtxIxkb7l3mr?r%zwHa5uM<GGgP==nK^3^_CV}8Lih$Hk&
zY>fX4t^Ym!2gm;d;s3caad5Ij{nw1~zlZ;q)fw6UH?V%KsbP!Fjv!Wx{vF|cX#-e)
zWasu=9g3ZYo~3pcIS>A4W_d%+P+VXmciwk-cY%L?AMdEDsb#giNW#KUBu{dtpYSwO
z*hf(!KQHdS6nl!Eties-H&;eirfcqAue)#bG4ddC{b&(ics~fdFh7kqM*>kuVDm~L
zxLC9l#E`KwEh{Paj~u|l$wQVHmAcu4__7M6L7>ZVB^3lDm4LgZg#lw875(BFO>|8K
zB~&0BucB+BXt4w=2#s$|iSC$yg9L#egW&l?s1(#~6l98vTr!%NyPi+xN-Wd-D=L&>
zj5^-wc%ieLM3zsww=kKVYg(U>WxzxC-xq9+rBA~bov-V|@p-F<ICGgMcJG+RHq at E?
z7Rjs^jf_+wDahtFq6lEn0dR^EC4^zj?Lm7{vOl_ at OioQbP#6>B$*5pLVPe-}qM$SJ
z0Qj@(=3E~-Tc#+6bai%5^M5^*d{F37n^=3k@(Mly{S<oSFF*}&wl_jiv8apcHIij9
zWfvRGI-Jym_fhZ7qgG^WmOz?9f<--gtG7PvqZ}xvQB;l5;v^;|r^krn<Oz-Pz4YoL
zjngNu4d=0y5=Er_d)jNk?|T?9!n0d16n>OrdAJ<z&gdc5x1)$=3pSsM7&uL at HFnw_
zCa>guSKs`^nsb7sB&6Y6?bB|Y@?Ww8yh?v0c2IoBEeov(gn7`I0}51$qhfA06a@<w
z<Txm*Y(>PVrzG5Qi%7Kz{uH!QG_R`vXu9XC+)22kB>=m}5fbTaC>))OZhWStX)Fct
zdVOEg9B3Q?fOV-J)ieoP+U9GY?5h@=hdOgfJs!zxZaj1#Mva7mXtZL4?xYp7pTzgp
zyV)^aUnL?dK^!i-FP<Sw7_lhgd!&->d^5q<9w40G=)gW*(`3&qWK%0CKbxMr`{n1|
zX^5ugDNyx1m$ofiO4dCy&E|;FB<{UyQ%CRwFY2i%8`c(&$gsqgJU08tGb8e^nzTBn
zogY>`5!vdG+Pq5n(Kx61tkdQ)-(O>Ni_5*x+=vG4l;^-Rx~X*H1Lo#eYf=<?@0qn5
zzf`h9 at +P(@YwQFh%K4WBHWZiTqEe6-wZfug&ly+OmZWXrx5$Xg*m!<NLv+_Fom4l7
zcu_#Hl2UrS<zH#?HhN}?vYf44PaNr7x*{sV$w2B*)Qfr)N2KX@?lG at eE*f_Gr-7<Z
zfG$Zf*;%>?tdC5?iUVWd!<ssKJ+j&D`3xbn6#DShP2JVPFXPUy10aeoFGf}Psvrc3
zDP;BdYX)YMKuvcmEH?05`&*1M23|Xt5N3g68z!u8yEX-k5q&mRV!ObAJF~>;s at Lk$
zV}14B>C4mB2O_+9%<I!zH?ZbshNI~#U<90#9kwJ?z-V9^bv*cJ7fFoqGb#BYI$*~O
z#m1tDx7)N$xp}f7P^m-KV~j0hiwUg1=|rt8$8uw4n1$VnW-M3nVBoQZP{*Ze(9}YG
z+?N)gv(21SMJ1|u!=uywab|c>dXWltFRp?B8$|)5ju=_Znv4Z))y1VcOQ*;d0<F}b
zIHLkptPXV9=T*I^6VgGZh6tInA}?RlSlYxKsQe`M61s|qpclTlwME&t=Na%twFERX
z1irel>K<XJ<ossP?j0L6F^vOZV39|-nzX<dL0)w(SgOVZo4M??%Ux!ziqa>%Iw#QR
zWnZf3JL1zgMBP2-ss)?B+(~?hT0Y-Eq**qf1UqR)S<pZ28Z_<AmmY3b(uK0Cq-(d`
z at r@H=M>1ts;GsICB=+OAB)vhFBj3UeT_iheDeg7MVmq||;4nLwYDIRct7^>M-APHp
zKP~DSeN55)nvUC at oNF|hQTq@>F~cB;9-r$ZJoONc*|F9+hi!&8sosDHI at 5vL`Q0Ik
z|HW(c#x61u8HBAV3AYVSE)Va;<DP%SV3;NFb)*#(R#>YgMZ$z;{sM^f-%ENv9Tw at d
zZA;*a^=94>n0*A;ce72dVBFx$_wddXzAAvvV~n!RHdDui9Gr=KV~4;FHRv?K==me=
zMKH=&yUGfp&l`v*L5gziZPvLWt|!kxJnbOxS!8yVy~s;d38uW0I-N>Jz`nrw8$0_Q
z$9?Ka=0u;ofwAbC{m70*c#~Ocwug^NK?BhmEwHaktx$)P!iuM^@J9V}jOg#NS2pEV
zuaQDd2d+G#E7qO!Fw0q)e^h&!-S`N5T2i%cKLITKLByu at IlIJk>#DWlc^mm9Lqv}`
z{XnN}rCQ$lI_cjHfzIwLYq!R>uF=-_85#-lOFRgwL_6}b>RbM0_dq|hW%K?&!twv4
z-ha}Ok)4V2ztfS4;lCpD|E41o<Nqcdn>9Bba9R<owW;90BD^PM4sZ{B#xskQ+K<NQ
zHVnRq-VH>hP>3U_HHwOl*bj0iT93#Vq5<ckF=- at KIn^{+2LQICd$M|_dUnGkE28`O
zkN2Oh<JY`Ee{=D>`Joa=eixvBygpnW?~cwUZx2W01vRSK$ZKPUP>fh?b_m(wbUipR
z8#&!QpRC*%J|r>Mc(ex+qh>bukK2+R927tI7ct25ja3G1mJ*rg$M!4uvX at J9N2=4S
z?#cSj8`>~Q!wixzeK1-LkPzPhgp0|ri}8AJhf%tk=lf@>mlBf+VSKA^@fY)L#h3x@
zjY5nxy}C+_{T5=$^#Y;3aH;I5D)rx{-5%iQ%po!pOW$hw at p$<-Kj3E6m)O6&`J+3>
zJqvYAN`dX64UvS(hHL__l2nw#{x+f_XK?^?<HY7UU>bvn7QUFU*mK}-8>>c at Ej=`0
zCcpL(?D3jO-d>}(Qi6ubn;!1h(wsN$!<(xVXTuKOiq*v`QNi_s-X9qAs$ahr*4rSh
z#8dR9EU>ikCKj7d%LoERYgjxU)K&nwnX%3I{hIcSbKamTWI$30(n?{t=?sftAsS9F
zATc#X@!}CvwKlT$2c-3B%8*UHd_Y~B6rh%fRK^VYE-EwN0pT1DQ$Vp(6E^f^0D^?{
z$;6{p;o~Xte3S17)-#Ex#3$PAwK?`BZ4RSwV~1rw{fQ>~0`LBO at PteV<e1Uyh$rIh
zz)*lcXL8u$QlJ`oWr(&@Eq<nHtN$A?On#^)zff)t7`L}GfUzHPi6}$`LJ4Z91Vwoo
z%G(#p4#jY2wNp`zW#fk?LiqM=#|4DNp&s&xpf&Kpplw_dpIs9LVb&*)NbL>dL~OmD
z+?9C|t-wM|kVWRkB4)9-R%O$VwgDcE9*tNaOtc3Ge3&?-zoSn80chi2{vro=dm<`u
z_TAQ;2>v*^FWk|=4ChB2pPDb_6^UQa1y at I9_h&@k)5GbZ)NByaBMgcTt~S26<xCZQ
z4C)q0rWH>|(gS7JA6&9l at zCTl2T$JOR at Olbz?HQNR4;iYBgtQ%d<gXn=L{Nku4%7g
zLMT8paWwmC4qPGPSzmnStaOM10x^A#p2^jE^Rc^9d-3oRRjc%E1^P+&4kQex4K+>G
zA|Do`60g&k8{<?ARHKgCV_IabSe3ksvJ#7l)~t)Z!U%+|$yvU#w!wzjcebpiwlnXU
zgfmqMPFs4q?G*;8DM&?MN9tG|N<?C>31XDhtsanclBk^Y!XE|Uq*CY%UdxMRoGIcZ
z!a<h?o)*-bFAg2&k78dVsxfMqLrYt^)Vu$XnM`gh3G`u~)fA7-?i#~lHQ0pig?3|n
zCj{i9eNvoLu~pT;^h{W{jZ++C6%m?i1N4<z1hUk9XIIv6%iye}H4;G)B0=Kv45O~6
z!)oQaM&H}S#sQ7`s=yB-)UI_jC=Q!VqEx;%5#yVisDYiO*TGKe)7T3hdE#F}=Cm18
zV^2&wC&HTUbuJ}L`T<!kki5LU_u at p+?EDM}gqW_n`+QauCRYQbTWT^|ud=YD0Rp{`
zw60GOJ7g(8{dXen5PtyD{2*fzTi75_foI(6*zMX6eYHn`O^=afUSI?j^!hfG!53e{
zIT1^sNZ^RURp<@V6y{!UA)xJO^LLoV7}_Cz_&JZ*zgIzy6V7xq!}7XDgxb#|NyEC#
zr)Jn>9ZZZF*@Kqe5N;9)cxy*26#eZxsOfZxQcVFDCdt4$G&b>qfu)@y+x?<UNb?KT
zVMcb7--xHgU^T6P2VrAO>GYRG+87zcQ9UZN#xU(S=pas40p{P8DKwbyUc%1es!GsI
zCc>ie@;HTRc~O(#Llrp=J0^W`CpxX>9S6Y*oX-ZJQf<#Ab%tjndy&M3geuU)Vkc2F
zUcyAE&l#fCOv(>D{5M=N5-SFzYfu(>535yiVjC-&2b#Y(1((jP5**mqTY{~{oCN6+
z9bA|6BK5E{4wc}r`l+f=k|Xiu!RG-ySrvks+|~$Pae7N!8hsh1a02d?`_29v15J*r
z!e<jyqpZt5h@)&?LvomRf)90FNA|PK7M#xkUUe&?&&}iJeTf7qqQi|i9%_6~pv#)H
zOqAws`H|Lf7OS)>E2scA>cU=|tvd4u?#k4nuza+{qmkeFu*G;9B at 0!D4T<R`xvOLc
z!&OsRpSIDt=pC}on)+wF;8`{!nyEgytAUJ8gM`7!U>UdcVk<JFfccM~EnMie2NAZe
zNyW0Y0_)X7WBGz+CtMUVWEVhqvOQq>kOJkj<3)7}c*9rGniFyc;aavSnhS(G8`bPm
z`V<|vJ8u=8`O=rb=v2(!f at W)%CERp!zS&d7G`;iLV1)X{3yk=NTAw+?Q?4V{4G^N*
zJp4yU*77<2rF<#f14$jIfF?C+`$^Qx6(dM4F3B92mN$ARk^unHvHtuiAE1A=j!zJ?
zDk1}1QfzP|uv~l=qidIs0#|XMgKxNwhc!wk4S$rR;X?AGW~V0_Q=Fyp$91Y!i(IaK
zq{{sb&f$!fCAL|-lF6srFkGP~zNHR0(Hb^iPX)e-{NEk$ed#Wq$S=6Q&t-Ezy!8`r
zS0A2F6)Hytsrx&*cid32ad3J~B&C&aaI2g9^gb(5r)WQ8iak$;PLg_GuhxV}DzrjJ
zFJkuZfpM&$p%1#p1`R at qXPFpHjgC^?w8f#Ne%j$;>5<6Km4D+o+Wqh%gzYeCeP=^v
z|9HX4E}nKg`vjUx?DYkM&2#wua6cPeQaojV16B6nPXjYAFHugF!beH`MCs83^5W`w
zB`vh2UhA8nj!C at 0^8N&*VRxHLR999EuZ5ZTnF^OPXHY5#LX`xVb_t=rSIGBV5F!!#
zq?$3O;8%KNaNX at W!5Dq?5qxz(1He+nD<OYDM9U3@@~e7kpppGz3lR;3{%9Y;`aAkc
zZOlG{h~9FYhxNOm8gEO_MyLxD;qTTf=l*S7eG0SF6_Kc#6~mH8^?k3e;l-~Mx;#g!
z+AP?*{^AYm`7705fT2sIJFOSl;-qmx=qCYa2c~a)Gr9osvVWF*(bNZ5t1g|`i;(<u
z-nWlF^;G7ZmbI?~G`T7}aEveR!8n>B($mggzU8{`J7{W`DLeEbDR$mPzt#Z;Bc?pF
z_pyTpo3A00w!C!Ft$k$>J!LE9`bc5I9o}2vHplMAMvyc&1#li1Er*N;%c&0p?;$~T
z>EnOt+ghn~^_H&N>`HJ%J<mr%&tE;|zg3=wrxJL7c#1n_4y)NpH<1|S45~5!IElJk
zaNN}~?a;2 at +R`+xPRw7jXTiskbpKrl3>K{nqxniKC8!K=*Dt;)>)!41!mYgaEg63X
zL*g1LEwL$=dn0nl%}2~%as6(BPpl6A6%yUvSu!ov!%Y$ES;G6npOFGfoNlA*^00V{
zLKRwuXCA+rJNyAFX7q&A{@ToGc;aE27)p8^uhp%5$ig-o3x4Qs;#Bv3;mVK2{~@ya
z!%#+8L>7w=8i_eB{8q|&`n3T_k~E?zasp8G$pUzk7-J at F+Uzs7^$psDP5C_$H)QLJ
zRsYj3zFVJh|FBR6y=pa*$Fe7{-TWuip*%f+^#w0X#??Kd8uUp?ecCCy;4U&T-n&p8
zx;67OZJ6RX6k^W#NIoSlZO2*YIkFzyNWOD_^|Njfd;P_tA;G+LiEQJpgcz5ZhhEiH
z2b-1Or_IE!ktV^RgjbKrn#`w5jN0-QSLkrZ6hemz^aV5PqWcMzXIz`XP^&7IF1Xjy
z(x>rld;S*yZ1n~If57+uf$IPFXo`j7|ATLq{|Vp!X_o(gd*o`Z&6t1iElMZg_Z{Ib
zWeOx4`6|7WCBl});ZG0UJLax_EgMm+BiTv4{sVhj`at_0j<Vi`)3BaVZ-9xWX~m{N
zqn6F3depUE7aj=<{d*kx_=nu;5%SyD`{86rmgy^;{_g#2`DC_~Jht}<o0~Jgcr8me
zyKdaGc4{lo7N)z&OX;uU$Hme4OJR0)P{GV*>eEnny at zM}rXwFF{q;*{c5d39uGOrI
z1JeX`PdBAJ`tkX-O)Xh<mBW*>&DIr1cz)1^PxoeLxO+w at 8H(XT&g3<}tCfSArES at 1
z&ZMN{X^SuYZSGR+PrWiSY|YZk<HnmYXF@^zkMjq+l0R=A5OD%jOYcVZPbSO7*2z=j
zT`hkW4=3+?K+nA3?$fJ17JFi|R0dPFlQY{W*+^R0qV<4NK;*c4{d!|~Pg|b%o0mA%
zX^f|m(;VOHgl(;>>B7Kg-qKgoI;5oq5c&$Iv`3SUmjortTM=8zk5mHR_79y^MVwgY
zwXI7NzYs+4H*5GQxwXZrPp9&Rj7%og1bs1!q$zxZadr8)J(iK$#@1V8h2Ktjw4!mZ
z#f~|SIA;=wAZNKKgJ^AJngNVFU$H*K%|^KOTUE@%zD4pP37PCP+40Hu2gz0HLq_>k
z^@8m$dTv(3(O$n^QrS%jr#sqp39<h;W1$(Rb*pdr&(m%g{J)bUjR^UvxcwQp#6PEn
zw^n1W2YSPq1N;}Aqk0YrSwoDPtEk}>;)gnO!rD=5*RYhyU!L68=H)^Y8g|9QBGDiq
zRz&QkAe^?YL}^gXx*_C)Nn1Iqw|ZAE4hLdx-QCo;dPRcYFDZS)RDxj+$S4x@%@EAT
zwsMo67-y&knJsi~WbTy;Bm3kU9M>vnF?~-ic)Aqb-Q>+4V!%fu45#jA<=J;@Z%h-E
z#)y)P8VWbSY^Y1;LnJsECU#k~a&o_KNob;46j}G1r$vjB7y5+uv@&Z9{knGClnKY9
z<}@)&*a8t=nLi+1r5HkB#zsvNyNNT_3}k=35n1+<Nfrr<m96TUHcFn^Vx(_s19t`y
zj>PN at ODwLTM22Q{uU~Qm!X=Z3ohhzQ4JefIl_(Cav-kMcf*Z5YAdyJMiy7BmP0|D?
z30r^5jrxsut<XCdk&bGtcd!1=imL))ToTDyrUg6eh*ptelffEG3j9fZzPCx)ohx#e
z8HWVr0#)3k9ssT>etdiPPCs*RqREFa8jjMFZxl-!S4zp2G9t^Cig;=rgsZi at TyPM&
zbWiT3Lq&5A&s2)u|HNfaT&>X9*I^(W&`UV^)OR0Y4*bhaA3Ymt7!CU<YLy&NHV1FA
zGAVrF#<x|?YVcTBXNLg|R+gzAqGUd}Y-d}k(f?_CX~ftMAUdzjvYrh$FE}~IMnT1Z
zSY?<yMf8w3q(xWp8mq&XnPM#f{K-dM_NV|3%ns`3zb-^Upi62Mblqj3?}NVnmTHr*
zm1cg8GA4VeZ9#(ZYqgf)8pFeNofW^<rtObtao9BAkEhUss1%^G8jYx`7$C~MY5rN|
z(mh at Q@EW{YN007Im9!Unz?53HSFDNB at v$C7x&T6J#sDb)>vFcMf6s8$n12K-M6(L9
z8*9*bu=80L9Q6p+FThA(8QJU}kcx_5!5jTYU1nApE|C~S=x)JocjeBW*$tK}bAp3F
zJC9*Xi*gmv;%35es-TA9^cX=@Gc@!B at XZ*Ttz$IF7!QK#UtOm@*~ULQYjs1*F6pqF
zlCy`=jQA#n{CwHKwj4e?!mI0&MCR;{Z_a+M?3BigLtATaVp^vYCX3#dXfLWu4`8Ef
zKj=<HI%a*RDB3`#pYl4)mMUdqR}r3q%6+jlF4mvQ at O4aIp{zZlE`%B7z-J^07iiO2
zdoY!;8p8h4zJwORSoA0JLgg$A9}7I?F>aS{l2KZW68T?scp^7=<QHx9z-20uJSVW4
znP1$Op&!!t()i at Yt4>ugdDUYf{^IDMxTqtwCyY4m at h1_voRHIi?c+1+QnBY8Wzhf#
z%~^2cU`=YnVWH#fVJCBZWGVR!!lY2phhMT*2O#&3x0NT2*kn-dJ&iMXHXA~anq6qN
z5+F9>^G at UgabYAuI%{VxPf=A&Gm%A5a&3x&(pdf20$A$L+eM92 at cTH{lQE_E>DqIT
z+-DAx at Q9G2l~1iODQw3<#RO0f9FRk8l$2(ozm`zw?kEXMok$#L50d0c)(O58ZF4Ex
zVo}P(@fK6eCCMRPJNDWp?nyWXO+Y_Yd^E!%&jh%It*|J8tKZ7z#%qOSl><0H at AF>j
zhmYnb-+HnL6HkSt9W8<G;GeFJjGDoYeWUlOJT$D06m3|Zdx{M(WIOh8^@B5}avZb*
zfS%Lla#Vq`z(i6ASjT4j2_WDta$Sl5Ip&l=5bF+nf}OW!93&C1)c_;Sb%tH*Ai<s_
z5<kUSO|A^CE^HwCgh at trwFUKD1{(aEvA-VK+)mWe#7XMBA+L`a?lD>Y_{k$DJ at Cor
z)0|QIHEXH!ln^7Y0<ct%{j{^(h9^(FaLI+T4e0`C!I`k9RX_lHVyTp5v2HqL_}0k`
z$gW_Ox|PqBg?kl`J(VqUO)NTRkwdmu`>U1Q1nb7be>LL_OiV>*aK>!8oN_cpWEa#t
ze7LnatM<A`nr5+~DJp5F46hVTRJ;iUv6u^G4Wsw_giMOscLdw@&xtJP(~zdiVl{Cj
zguHbMg9NxR*hC*voMQ-z<sUtJmX9+m)XYI=acr+JqWybfM|H}xqEB!O?X|eDRgeUZ
zUyXvR22B!LSH#hc*|0QW@#6Y~N;E@|G(gBeWVx|nyXzW4$YnH>TS$t+Ifj<DfVaR1
z&ULIo=|qsJQJL6+sv?A;dtu#45VGrcR|Go^#~achUEcXP8c8proFgNLcbuNi6_e}a
zpmj38%^{;LlnzR?o2F>_aT+t%0lPjQ%xt?VhqD-xj6HY{PT=&rZbQ5vozths?HEeC
z*@_61z-aC}xl|S+FElU+b{(|-e3=(J08VN+q~(p6#N2Z2jqTPtHj*4&0?q-!;Rk|B
zYr;rr_bVbGw)kob at Zb|7FL7i}kfSK#SO_0w&xf%jk3G$^_vm`q_Z4*_HJlV)0-rr9
zt1Gz7C<Wc{Nja6Q__0AlEhe9gcGVqo1X7`?+g5Z&l<0ykL59%20o?rhH7fl*vDPZA
z0eya1qUA5(r<rKk#$1*NJZua$p-`_ZU0P7I_^25#`e?OuUz<~Q$T<F+v3#KO!ooA=
zg<%(q`r4sOSE!c3U-|7vIvhA>jHAqBiiE`no^wkyyZRn5MX#+A=fxxyp{}CdQd8%9
zrbq)oW2Zu1eGdt>W>m70p_y~`*Q#o=3=ldMj#3~x<PdXYAFb*+tgt}v{i%aUCr4%-
zn5L)yGP&R+Ok_3=vn@?LCAWi5;BG>47f1%o-2<YBT{@YvQx_itAuKuKet)hVCd`OU
z8K(#OMq|y)xiI7L;j5=Qj)wRF!y#r~?8dIHs3`#v)yo}&s at w$D0391?Gn)@pD{4fj
z8?zCYD&%c*<TeX=OOUhxayTLw+HU$BHOp`pcKuh>(Z{4Ll-VTJ5MV|SddD at 3)A?E+
z5Y2290DsME$4 at iI$VuX%Cda%MW{$kF+ztSSHx at qt*i0q^^}voD!>KCZc-_o3?%A9I
z0O41t7(LWw|Ez41{qWQ#m_sNOztY~tN5}06Co#Nl-GQXX at Edwd&AJE3HR0hewm}H#
z*u at or{$v2wq2wT2UpzTS*m1kj>F?xF at w@<9v*6vsa>Xxx6~PnOG1s2v- at 7s>^NUT~
z1-5XPj%85Nun4l_z3wGimVsqae`?#UeJ28=IcHI9KjOX5Y%FMTtRw<}_Kn0>75AVT
z%hJN~Q$&k{eJ>(-Fay{RHqqOskL{1`AVo%+=-nmS5c2Z2s$vp?=K_?iQ{QFkKbFtb
z!}BWUU04Z0{s#xijhKN0*1JNfDH(rc<#@s<yBo7x<J3*Db?vaT at 6aJDxBv!AS^zRb
z<eSY)->4vpa{{G{_Mf~#!F)F$mC&xtRvFt0INLbA6de>#Z{A_!(9Z1QCy@>4s>zQ(
zrQxYl9ZiV^%`LWg2m3t+l3?+r5EY~Wu<jMWuyvm`sCaUvlBCYHCGT_)aQR~s-j7A0
zDe_7|Xz+DlBT3)~0WYq5 at 4s_pWdczTc}FkfosQiuC?ZllyYLR=>nDuLA(n)Nc0)L3
z0Hx#l$ADqXoDTd`9ajE&6k2lbJC1*zWqJB&;$I^AxDMsw?Nf8KnrZMUZeEyR<m~#W
zjJ{X2Q`NQMvxr5Pi1kJp9}@otMUkP+oA%0Hz&|5Xe at Hu9vj2dyeE1mDU#2=%;syfe
z!bYL14^*%|Wv<~Cns%U_d*{4_W#YA8VsX#rc<^UAU-#&{#K2ydjN{e#j&yjYT0F$4
z at L{8uhZb9_TLu4`mHP|~kRDt$Y>Vnas5Vm8DqP}t#^lXNek^bG(m-9ZH!GoTCW(l$
zCe@*dGMNI;SFL($X)^XfcM&9r;OTn$*Y3hxpxIp;c#&v{>6Le?VJVe{4o62inap{v
zwYjt|V8LIju^@`E_@!1z_EZ}_beCeNX_sKZX^8#^3Q`<~bgIZ1&s}kbB6%^%v71%)
zVPpU=KBT!Q)@cjL^`-i_qxq7&nGz4c&ko?S<sUwaRG-nCksA%(TH^asUY*po=AR<+
z>u7i5qAxI>uxW=&4%WWEEX)hg6j?C+JfYM`F=0eoIW){Dv9lF7X2%-IZVy)$fa=#k
zXYqq*smV?6TnkGPrQ9dQCBSd|sL06zboEpyUiEA!{jtGTt8PS91VKn_E}+fGS{*$-
z&e{|Gz9?aVJL^MTNZz`&zC(3 at x3zm|%$NCt%9}7 at eDwAHJ?Vj?_{Ad+cj+Fz8{?3=
zfNUs`@|wq`=a+jD`*2FM?$UHZFZToQP&~%+KaKalcchuwnVJ9Fc$u00hw(Bq|8I=9
zSz9CFpYg)d<HvA{`f?0x!?|F7KAejtu&-;5e8uC$%+rw2&cq>w78F~Kr9NnQL6X0K
z#4T?x%_+qxP5~ha92`SAc{dwCqhq7}(8H&;npr&pez`lmET?N3ztQ*f&8D_y?q=nF
zUm*9^M<Q!ROutWKYIVI%6rp(;?G+zKDrG)yHqN&6?yQSan0I-@$!}p-wyt*ao<`m;
z*!(s>LL+ZQUJuLBiMbEAx;~#*@OgJ{R*F(r;ylp}O~(wfBq?l;c6Mu}h1%;25TaeT
zZ~d5zqM0?nfN9>eYqPl8e3pLvbg!HbW^4MjD{aPN-FK%a0?GOE{}PAw;UW4XYJhCw
zU(ftPB01YQ{+!;`>ScAi`n)hc%?s|_y;>l#NB+}Yt}G{OmQl2Sx;tn+U>6WM{2$!~
z_0aC;^J)T)cO2z~;F8GoJZ_bDF`XayB=}c3{nK3o0Q8k~Qx7IOI7rYm`oBi|wLFWn
zE&R}#R7LYPpNY6vu?PdUzA%fAku7zW{M(e)XeF|l6P-vIAZvx~DCOnD_IvR`DPgxN
zAa`=sQ9xfO_QV`=N)+K_6M{4Wlx{kum~L)!l)>tDtMFFBts}>GbrTk8WH8fc#|u8*
z2rts^(<<|7=dHid@^TsqcX{+uEY6EKEYL2C{Xv3^g{J>(x7YgPDc2l7=|rOuMK29-
zk1Uw7@=?*1Esy)YwnBCv|9ZQYZ=b=jMZI`Ff<!#PxS~ev>cy~r3h)Fy?Utw+D0z=s
zrIn%(6x>ifW>rw}MJhlNIPq!Urq*DCiAw4E7m7dS2mSec+y<V>VG6;!3t at QyXYG%A
zygi*2a=?h8Xx!L4J^yA7cs#V(*NcUw^jgWKK4e-Q6Nc&<*=U;hkn9qFe!rYvlU)9J
zfxfVH(@r{+HLMj$s15t|{W{zf+!(Hl^XIgU?cmEJSIuB|k5z4M at TwFf<O at F>Kpr at _
z0=`~zNmzg6%ARQ{phJXxSCEzf`Ko5J2D#i8jV^Y$`$;nv;!AN;7a!Xfs!e99>!KL>
zsuM5WEJG!fGUUuv32(u1J}$-OV}C^;BaHO8)^#5(n_)^1BjRfCVj%<#p#X#u3HAdf
z-fuzGQ4LR?NlN|nOV6`_bcb+f7WunL=5Tl>PDTNwfdJx{JbHjRF>QD^A{+Ha4}>v1
zRZ-lvzi-CQ5Q{fV$eJ4iL!^~YOKwy at vQ?swwnf*OvIp!vjCz_;WB~@`R9S}%Ca^!d
znb)KE8}Ow4fNc{Kg5Ub|bvoE%S+ViH-s~(NZ~ve(5C#mAnS=kl3<N_%VkialR>dB}
zvTa4YAlng~5d9sL9JoL<L at X8#O#vt%3##WbA(#~g$GdUO!(n3H!Cul--axmP$L0j6
zUR4O|8OLi~`X^hV{2*}{oY0VM<+lJq4-JK5y1SSn(@@y_Z9p$d3N%misVe;(Q4i?2
z`C-4{S7GxPNnStJ2<gr5a$6ICjP;=*QcCIY5HBJFM4v2G2|Qhhb|71qp77#Aq?oQ2
zs6;09^=1i at r9=cf%#Y$`c;&)thvfBjiT`RmrpzM-K`%O-kZLizXV^U<MC8CXfz%R5
z<hNxY8`9#cU<=a8s_d=%Bjs2(zzZ-|GjrwIZ0x^lsBjIO+jLnhJ*^qW&b`;cOK`Hi
zADz`rIXIH!rxk2BmEuT!c#V4n`AAO}$&MKHWe`ay at 47;_ERp;8zyP{GPI4zO at 1?ia
zrE66SvevBo+d1$@$8jN<8tKQ<uxhg9&C>8bsiqqr(aHqfJiG2u_2ri$&=uRa;OLj$
z^aKJ#^G+r+EAd1Gh#9$&N%Ds-sT at Hb?VMW!(t=lhkpaqhthwdwP7)i3uq&iw_Rx~D
z3&U_i7jtv1NJ;wCv-;-B!pDodyC#-`M=alJ)mn_iLp!s7t-S558wM4Hd9;?CB!F;>
zsSoZ;29B|r&cOX|I!Gmmfq$~ZT^$V5=QvQ{Es}Rp0{UH9u$zG!=x0A`gk9~1X-?QI
zm%oh6o0Ayg8w`fqdRZ*js}`BC at FA2L0hWW@$Y&afY+oL(n7NK(#g^T)Wpar5M7Cr(
z<^VJcNdv`o`x;6~U&#=U!lvkW0+^wvj?Afa*B%6N>f=QoKR?3A&_M+c=nd;1JSi+J
z*Kv_Qs|8v8gY(#AbuK*w?FXO*Fh;tUmx-kpm96Y~A at R!nE|h>l=ajddxR;H)8(e0~
z3u)>N&d^3g$MqJ%M)B<A#er0(;!>$xCNU;FV081gy#$;|&ue>mebtO~JU+1^uB*@_
zjii`e1ev;Q!=e{K8 at em*zU#|>p#oE_nVd98jRGJURXm}<yO5jff>e;8n;cQgz?Q>_
zZH~S*^MNVhfb7yDk0|WCq$*JVetg%YyjM^amTQwU6A0JbM&e>r$fhqYvm`P7xJ~(S
z%-JA|mYB1ma|}(()CXl83uymtll3^^wN358tJ=LDeWF=3_W=RI(W5<o6r{@JTxXn>
z?Df(;WAZ9pJBTugcXxcKvvKp^TFZzuP97IX=wLm#i%+jeT?K*UHxMjBpygqoXkH_1
z;)A}7QX|90*%ecveH9urq(5e$5F4hUElN{V^6xBr#Ge77+z4%Cx$suFN7K{d!Vw(?
zy!ql2!Psih>c@~?R at 78(tWj#1b}qby(5Mn$Pt2Jp<s&^!OzG+ek5&C_CwW2`J`Bl0
z7*-flRccZ3h{?s)e5aWBTPc=BcRZ&M&F*Dt$AC3go}6aH*_&Z+&><hTI{XOWUdgbk
z)aR7qu;H1jb0P841hh2vQ^ZqFfR0zPdJEcY=`TH?WGY^MSEc4rH*hNn4C<- at d&oQJ
z^t1eBRIOKJ&`A^RUe+g!ztj*#i3HbcPaNZ5>ER00e at 50GI&kurj;ySaZk+LVK9Nip
ztu)V;x`%WBq~pONKntUQ(okL4c&p(h0%pth4|*kE3vY)ChS_3V%e&uQ8rZ1X1bts#
zi3wmdtkHRXN#;4ozedC=uarOsmRGGo__tn9r<2^?mYNXha^>hjGELM)D{(2Ku=zAo
zM=+UD-;0E?AO|Y$;<%MMXk4R4!>kFQB<XZlEFt<6z!Vx7o|#Cqf${B}`gecibzZ_J
zy3Hm3lBUHp*Ia>E?u??73r%FwvH=b|6|tOEVB!|P^i709nFvuQo{3ZklZ at j`@Z-Ko
zs2X1XoTgAF(nns4+F1JLlS98*3xRM#m!f@|FJg1%n2mT{Zdd`-<oU>@dB6}P=Sj2Q
zbLLP1HII9YswGGwItahvOCxiqBu;U_WY-LPUnyX&J5)9VG_Jro(#<f8wsVgz7BhjS
zC@;~7NJaqf$56NV$b7lBzRVms<&)rN?B<1Boz{^PCyTHpd%Ko6irbAL(o6jv(*E0P
zvm~slh^j-+p8C=TBU%O;F}UlmXvVB-^d~XS)srcSKM=a at P7@bZ`X<XJk_`zX<iAyz
zsm;|T=K7#sskv8wzvL&Fs$Gj5&fCA3**(t6S}iCydoLu)aixNoZX`j7V1q%rhwe%J
zf1oVAeNsd4ExExN|BN=%p5emitQaS0u=K{1CNHpzf0pGXS<Avodx&eFY+38rUs!gi
zmU!{#sqEu~N;1rlzV#CSVOKv*KldWwg!slyJ9RJZXfCkI?}aSY;tpQMShl>Cn*3CC
zmdgwZ1X5`d*ulmeP1XiVwhQk=F77fiLFa!h-p288&DZmaEo2DC{oPk0ep9}v!m%L1
z6^6+Hl(iI_McBld!4u1aC7x5OC>w%22cCz{CYZyL74M}u>mHj2JX4qEp8G^S)gilc
z+DVm*54map_eS?l^^Ed4);_F6%jzPVn5Y!b5B?>Ly!q^-8dWHJ<g`BuUeTgH8PZu=
z-)b_qfMc9b>ozE1*)PBh75C;PUe%5*#u-cCv4)LMiOv}a4no4ABLEJ3fz?Pz&g?L5
z2eyFr4rv;wf~X5dUnEtU_}GIX(n4AFsDGh=T10XZhUgQ=d3p!#Fu4mWW6d@{{R<m9
zX4zpE)L^r#ZzP+w=(5c87fd2P!?~R-Q>i&u-&?AB`QA`#&Kg%LxgquSQkU=5T^fJ)
z3e%a$DFQbKotH#V)iyN9!);a)%n8+zma8CG7M-iCMaSQWE=g{z%q5>+1SZZWu!c8J
z_{2)!#>KMb-#@Y{3ZFd#xB6QN{<8hNmY at Lx_qKQZb<le{`7X}x)0W+<DLH<qoKOM}
zr{J)eoG>9VCbcY@&nWF?(=y20Xx*bscc&xMyOgO_0O at H_cpt;H#_zU^sH~CigwEL7
z$Pfe&{AtJKbzy6a->JC0Eiamr+cO_+)7*l+?FJn_YIAyTy`dR(MIR5hs>#iHcxg6D
zF9KO2h^XQ#uxj(7_8S873eq?rL<Li0?<XkUq)W5^YS()I5xp;#P=4KE^VEFJh);JV
z->#<&GIu=lSnD2we9qJN at tv{OmwIMTv8`IeKy83_6NP(&cyyyuCN^CZnV?wz7!joI
z5(}o1J(=7%HuZvXm_t&w>NBZ$h&be8t-ExubG^bj&Zj*#|Frk^pnpVpJCTpi8=O3{
zD4!)39>rM-`3%yOqtc2xCF{D{(A#N+$=Lz%T(NOt!b&=u87X{1JmN>!7RnS1CZ7qT
zxp=p+cMYxENS;Wg0=E*=7+Mex*_-U_WRB_cXABK7p;{Mz5yC at ta$c-ut&Rp~y~f|t
z#_#LrGM_91-kVv#5 at kI*qxIeQPjnwE<6Ie<BFf30ZCTgQcBEU7P;!IUg4KR-^I2Is
z$BS*oQfPGWZv`g}Yz{&`1e3KmJ?GqF#iz8+4}RMIq=ZE8`QX+7YJEYY<wn5M2?f)m
z&P!Co;SYNPXiYJPL7k#H?<2iuSaJ at 7j(4-yu^0Y!k`*O$hu<^q!n2{E8vF%w;ZK18
z=5tUccltT(I%N5iV;7Z^`K}Pt1v}js>M`Ru(f7yq2xFf{S?*a2w1H(zlha?+a$#2z
zFkhG03h^Mg9er<E*Udf`2H~^MiFIG_6P}g)+^&!Sja$@M*~+R9H_k4MapjoqGU-ws
z!bTRifK?qdN0n_v;EuHV2kum*l`+XEO8%hT)adn6j-F4xK(@ccZAkeWGw)zupR228
z?7=k98paLF$uzjH&%p^fN6Kf&#Cx;dwbQJ^*X8Ks-yJV~;3Q4Zkg{!7`U|y|gn at _q
zT2M5)WsY-M;>Up?(h~WPu2>EZN at 1(X7)TtA5Tpl+<6GZ7-Y%rRR>MM48XlJ)M`7e=
z`u!UL8~io9eu&SFh-c^%k`Xbm1JgoTXZ7ugQD|KE2L`*1h%{c9pf0(=dTiqPza=%N
z8eb*Rtc3fNy9Ms3e)OQ^Xy<LXh)@b34baB<X;qadI~^N<9WP63co{7#IjSAUH9c`D
zh*nU}#>9k2HBWb*b3XK8=b at _W73g!ovdoLBq@@Pr4u*V=?rS}qpq?O|t9*Kf^Pzz+
z3tXK7a|Q^y)QYYm=yHJ5YKkI~&gdxd$|50QfK*6*V4SaHqZGc at _}c9<WeNAK)S~qn
zMDllu<Kvp-xORP6Hh*6;+Y{5=H2DwLq~y|62tLD%=-J(&v+bLkH`-#(kn5X3I#)2-
zgif{^(uMP1X#6T9 at Tl(b+$dNIgsfo?45+s?CGW<T{6m8k688wxRGDLiuH6Ba6)h2&
zrfv5K=c};c0Vp})6HnOh7|d6qL+!JU;#s=x&ZehDX0Z4Q$^6EHY|TlAS0M<}tEgD5
zKV)J)IT8<7WR6I#({VsK5({Na9kjnh<RWv&Ebbqv at Gnx-B~04P>b3E&Hg6CAB&&24
zDLA#g|5ko1?WELJH0S1eLfiYm`eZsQkJ;by2H8etSN~x~C<&HYo5w7U at TWkK@XS6N
zLv9Dy-pEq*0PA1MaU?&3gT!kG7Iz{)hlAvJgjmT(Z9`)*m5{{+aSXvB479!>$SxF@
zV8~hlm^eQ}L=uQBJmz)AVh4jy=L3f)&J at C3FWMtqJl*s66A^$i#x}l-J2$#xh(~@*
zpB!w09vHh5#m5Vn#o=Xv_hdYeg-XA#(yg8pe^w5lw>!04O7!tj)PBJKi(XZG?ifCC
zBv1U(We?LqugCet=KZJshf6!C60Ko}&AK1r5=AT2c%b^syY^t?jK(3Dg-6+lg8QoU
z1~(EODzh9-W~Of1+tR|_(mL<7?(<M(03?|{UBI at L*}G{Ine#rUod-dK&Bs3`5OjK7
zWE{PXFNCq{^j_I>ico|Fq~)t9|JvERLzpff=rSPSxRpC!0G8!}CVGGJV at WjY+{YgX
z<sQ>&-c{ge(vGDYj`0`<uwEvcFhvgrVR8x5(C8B6-zx!|JgHn9^n#nohj|y7!wIjw
zd)a-1^iu_o*At`RRV3B5QCJ!<I2J;_6+jKsiVaf;nMf0gdJvmhb{Ir=48#eA2t#W?
z*ps1%NWn}PhNhVa$wgF{1(iX75)~3+Iv<fC5%~^tDmanQ8R?oNBEPb0rtfYx`?JRe
zTa6;?*`=MXVK-a;(W>ri*!WMN^eJzETyB#~3-z{W7UG)^H6Jf at C>x>Aprec);z|rW
zG4R>3VJzWf6_NBx2cno-ae)=nNa*`;++*@d!2NXHcNBnOiJSnA7CC|G1;~*5i2<(=
zdhVK;NID-GGcxD2F;OX@*$MoR*&CnroB$&KRR-XO at 7+6qfaPQ%M088oy(p$@0c}HN
z7-9x;Sosnn<NL5*+JcwR)EW;VnKJoMR5h|8le5qi&tn~)%_t1(G|8g4xy;f3!`NE|
z#}#YaqK=uF*>TLw7-P)LOmWQ2%*@Qp%rP_DZDw}N%-qK7WWPGE>h6zwetNXJR;jh7
z=9-em8k!pLIJmhI=gs3+HXvoS-rYc6HaaW1fSw96yzx(7Q)c8q#KWMj<g*Drn!0x?
z|E0CmcVhci7QArGvh#JL=5gBS8q;93>Svgipgxt7N%v<DZH>&s;D7c|v~*SvlN_0J
ztQ(J|#n<+q&~5REf{3T1zw#%uR$^4ku5cU{q;nh=!sN*1JQeHHj~(i{893%jf~sct
z`Cf=5Y=eUTi{Si6T>h2xuraeS{|~|W- at DCN{#U!r2DR7h3!Ck#zjf^>jf<2s at 51`*
zdbUIotSEdPu9tZcogAbeEn;6Gdo&fRiEF-Tcp{qxY4aO02u|c`Dw<vFCh{H1>$Ya~
zCG7kYCR3gz{xLgzzXn(Lg7fO)a{d0*lKU+I?&<M<Z6l_J-utei<yK2xwPItT0<LZl
zJBo&FZ&ENXC4;HGq8aPY*~k7Chf(g$JA&uvQOCv~Li(b0?RoEy?vNH3vVzra&ZCJm
z)4lgD?~VByq!6hFja?bvMeqwm)L?_em5&Eo9CnmEq9ggT3DL604>x43t3^>LHin|j
zN_c}0qQ=*V#iK*020vTF2R)9f2#xqJ5+6?Ym$l&Uab&izDf%BSAr&9N7zhR~_bSh8
z{5qa?PS;YVhI8)yIdWQd&2BcoD>ja&-Qj+s36-vI&^whBt_mr)OVtTR;7#{0L8*T#
z>jQ&iD;NsGUfKiJD#MxRsJ+e+0Uk%hg>K1ld}&xEm=J+B#)sQT_{SVCMBj?2&n=ff
z`oG*2B!<6qhinf{xKFPlAr7_4s4oTESBuOEc(7w?vNb@!Faz>2t}3NKjEoQAudrSw
z_FUry&DiB(#_~U3^l*=VBlVZTAr}hnGR#S5qbg}<>+NIAv#3jxzq at ZJWI(H8{8hz$
z at sZf=f33eYsvBv3U`(ctY0A>$+soKs1abPvZ}xCEd`1+RWU3)*-S@>VqIdf2XHyoz
zvg~_-rqZ(%&C5)UK!q3<NmW7Z0uvSXZm|{alXHtBBjNGAt}`XPj)tu|Gfs at W{G@=B
zygQ4ck^|Y-NKkPM<=PiL3PC|Q#+D_SR~=0-3~g`<li8mF6IddyzV{XN8VRb`7sLcd
zeTnyj={86yu;SH1Bi0y0h`&~RzNHa0koQ1ZHhH{uR507P4f5LQ=-^W%{?1Ty%G<V~
zf7?F^8SPyPt2glD%$cY6N{2J;GnKbb2c_Tp9z9gb%cy<JQAuy(@~4~?WjZU^YQQu}
z^$Yi+P=+_g2{B(^BwgtrC7ZI>YOi|Lc3A``Xc^Gc$IpBn0Gl<TKUXSMKlvM{pfo?a
zQWHN&8adn`E!Q8%sJbjnlb%VC$e?q{6C}Mv=pEGnY-h!k^Iv3E+B$4<a)5mXYL at a#
z9bP-APfkxf&lNW6A4pbZx{dE<eu8UAaMy4;7!uI+B8Kss{yFigA%-d`iZ$V#kkIU1
z`Ejr8Xu2YX>Vs=V&U}qrcGLuZt#q@`buh2ie;_ at Q5SA?v`pY}Fdos6KOy|0EsE at QR
zh?g}<7qtYeR0_klEaa>|LVlz8+KeUmKWGXnKa!7vlP+bMHH$Lb=4M_ME0S3<UzUku
z>>Dg0Qzz-*!o`Kzwd42b>nq7rmNR-Wx3^g5NN?1f_UO_>FxHK4dgT1J{qZ4gQMzhA
zg(tC<N^<5BVqB$VQgsJ3deKh3-K#z6AaXLRyf|deH03g|=HAU6#f<VO^A+KQI(WG-
z3BY|ead_%h#(7B39V^vFk52HhD$SDWL%BlaLzfIgUqdZ0zFg0E9aDYRS+y--^NZOV
zEJt~UscqOKCJQWIJZ{X<q at 6yBL-}Rh!5wzGDZy}d|L8;#V~)DdGNyaBm?f;gi^p!k
z?8aWzu3{n;Ga%Z-S0UDB0j3}I4!le5RjtwkM2oLHcGRpX&pTxZNv`-QRy=T=J6x&b
z3b9*o$B4iOOhe(QV($pyR&HM-M|h|Qg^+LQI0?~~ns%KVpe8LwiTF?OBi*EUn2C#t
zm&gY#;=@Q<KUY6bFlxNCfoa1NJ*n$9nr+zEl^U|2PQi8lslN(`)C2bZXh2+H?B35%
z!lEo{3wNV#D)7l6Gf3zbg!HetSx~bg=-vM052Uxog1x!bIgP2tAcMRGd4^NcL at Z_Y
zIVvPN)faxT18dC(G7s#Fj4R^wrG$TI=wRQsIX{mn5Z$h79Cz^tvO?WOPbES9R9T+Y
z*oZlcfXhxfvSu7uIq!e+f?qf#YwW}`OW{w`)gx6CXbK&`h|CE(DL^+Z9w0Z&<W2BX
zryiMPsgpN*z!z4(cQ2el7MKuA31mLqDbRi?<MY?xjG3A%FM$=C%;w+~O#@39y|}Ja
zKm_{5=p)icH0Hg!bO2quaE-61 at oG_YfOUD5zZ$VGUVynPYMYUDW&;_%?`Qnl^#U?j
zNAz1OF4WI^S%4`ZRgR|5*>Y>gS*}F_-=;e4LB-HYh`Y!QNtrrWNZVNQ=9WVRejR;{
ztpD~H#j}vn``580>iPrJk#z0e?uC&o4O*>+450s!Fy^sRVDm1(&9V>pK2P1uf{34d
z%R}X&c*(b}+woN$r4o6us}C13v9ZTDkCm$&K%$LOo$9&EHp=0*R06sHOW6Gf;ykNm
z%gNh;rvhPf(Hjgd%bGhYDB;<vlhR9}+nPBqQ8gLE2P8*75ydP|&|uaGRlclBZ(BZF
z!FMIk|5YYXSNB(ECEbZRHIpo&MU2$$o;g(`Z+)WjRcG?e{JDdBkGz3N(9{JP-G0oe
z!}zJ=sy~QOi56Y@#qbZws=c4dnxS#7VFsFe667>xbMvTOrC*S1#D)iF6_3LM;<fx9
ztMqgRiMhkB^AMoE76{_7dM>O*p<?d;7z+t&F|)nEG|<`pnps#H&M#*tSlI4{CbP*B
zzsc)S(;nyxA=$Ud)TcUKEzkh!1SOf4&D85fZw^3I782xb!Gf1o{<3PRpU+6A0-C4y
zJ0<$Gj|So3sVv=)WiiYb_tD;VQy-{3Te14Our4mc?3I~DTcF9LfpKD*8%<?fTfYcN
z&WL<kpPmeJ1ug~iobe6a5&cW4762rtAvSJCA~?*OigW{SikX>v6q>p6m#<Q7L!^H+
zb3B*<v(RAFJsb{B)-T@|?dblO+JDz0Srs*624dmWFo!}84xude4~bXe;D?5K+Y+Y{
zLvws6B=YrZ#IR(lu=E-=ns72~!5^}vZ7~CJCKJS59c@|19Jn}fz|O8A5tFCQoNc>@
zv&0 at UI629Psw(gjq{Y?}LAP~$ui~jUmuhNGgS{5|{6U|Qr?K`@y!0+hxpS}}A|-*N
zoopW@{u)3e6j~tCsyi$Os1*~2bGQl?hFCQCsx&U4v;|1Xf~Nd33rB5VH6FU|1cFf7
z@;nF*2b=ut(>{J|#9U@?W`CQY!SQ-POg@{uc__U+jIblqqgFoLz~K?-ApFdrkaRS?
z_&B-_GMW2fu3>1!k^fci6EtCDjU-pjwNV2L-yvOAeOEgDe43uDJhH_tjRfCN>;A=i
zh-0|&tjA&#ewa`j$ZHPMv+{9mq61R&S{b)-Nv(CE$lQ41G=6QmE>+VSFNl>;rbPzS
z2`p)E at Q^Crk-0ktY9~U0Sm>VkR}-e~;@(!NK&PMx%lLficWw893DEDBr85+z`~+B8
z5;6C`*BMs6M0OD(GW)Wo$pN?81K^^w=F?BRh-8&);Ws;~7?SC>94mK>ppi|-=+n<t
z5Lq=#@n<yl)fqoQVm}m#x at 5HgU`81LIHiK^z$1boNT$|qE9^(=rEDiOhW4oj$Z%5@
zuSo;W44&JoB;VB05YmKAPdY2~CZ1O4UpEAM(>7P$cvNf1nc at dcg2#DHS8X5}q6tPD
zW2}3 at ewYyI_x6DOpdcjXyYe-U;mysv<Gt#mbMt)~0Sgo(j7 at B~?iq4CP25}yEGw1^
zx8{4f{VLS1Yem9ew)W)_K9L;2`QTQU!fIpJ`BRHWJQ~H|=fGRIpOyZZ!1H<jDf<G)
ze)<5-a8J9l*_{qMj6w}vQV=L70$*5<*kz(XgSp6-KRq<<e0UE;7R+Bz_xM^X4+qL4
zfyMZ?8T8cRLc)8Y^KIYLBSlUF7TIDxV6XtZT5vMHx4VpF%!WFpyYkD#=bp*w^_0z@
zb6q at TUQWs{vQI$|C|Qj$5CNa?JLW0ZtNgopPVlV7AIWY<z0jBoxa}FQxWrp4LwYg#
zTyQ{>SMkG|*^R+Mu}G7is)-|uDEqAC)<ZJW*^e;WMAW8DTn9;FZY5AS$WKrJz4k*@
z_9zUjdYsQhE$$MyL;P|*Lqg2U4X%_!G`Wv_BLddK$i$L=41Dv)YmV?TkcdA(v<8r;
zD8Uf6n+R+)OD*rtM1Co2xIoEr`fTGTIDWR#qx}0xcJS<}Zbhvl)Mpz(wr2=p6CGN?
z$Y$-3GMyD(?*SOB3r)8ES>kT^^3k33gzAdu4(1t4a<+GxX+g#)S!@vy0iVDc at sz9a
zdBe${H>_&b`P+OztU}cFw|TrheTAw}iIr{%Q4adPEWcG>$(eB#x_5p0m{!cFPd_BK
z*1 at is%xOV-usj-8{00tcI$PDt{orlxLAofbndakI^ze at fHkn4jxwa+9EJC18(!5=n
z*?^b~DX)2YVW!L+^M}1Cj~-at1L$4x&|3w|(Ooi(!ZT+7GrQSrAy>IXnc9+M=p_4R
zGbg at suh8xUOemhu7@JXcPJ|ar3>%0r;rT at C_L}iQPhGZs+f+V=mb1TEpxK^Vpc$Q!
zk at Yj-I=*NG;9TmSI;vm<w&Wk#m-27RNvd<+Cs4xhs5!pH^tCBR9~~$oV&v;m{%F(^
zq{<NbBtjBr0;Ln?Wae1~%V8vMI!WBc4PCvc<*T;f%kcT!SaNo5x`pV6UkAN^uiA4(
zTaN%v0el-(QPqV8vPvt(ecp6dbE9)hH+9<4dz^vN98ObX`||A45<kLzvQOXpAFwl+
zgT1eQHGkXOGa4i~%1rC1`T1lKp%;F=Ra}964Eb~J3d$%|-Y}`>{~l*pQiO|m$uW4{
zLzz<gU1Lo{v=L3a1WQ*A?e!=4Qo!ce+q4l=iI59?a6i?OZ%#x#@)J}kV4z#j$yyfy
zQ;Fj!dDgEE4qUnUDfU0 at -3K%oSDxydkJ8<d^@$2r=~4=O>(+9?&RSG_%d{$#!&WH;
zm+)bK7&cpgnI|(>I^0r1pNEiCVM*bjlyZ#;IL4wFB~<>5lGdNLPZl&~0F!S|G-$Aj
zVn!zTrEZoAuXbIVh?30QrwO^rIjY}^pIuh*crcLO>t|X62^A0HZ+lB5a{PC2x#L`#
zEKl32UuDU}vJLJ>rBBG&<^`Jh8r}RGSg3Bvk+y{qW-{RPORAbi|J2Buo-3FF+1Gg|
zYEIcF|HNT(u%2sdE$ozSrQ5`$D7J1jiOZqtE+4N*L2D5SWqIDMz&%({7?vV|-`ZX)
zD8WKO)i3iHZ$<vOyZ(k=v at v^aMlpK5V3{z!O at +k<uG<N*Abvqrg5mGhXf1Eb?*xKK
zTZxArVC&^hnWxl^B8w7OF?|pO$$DR*ywua||5MWetYW?`#I{h7a~5R6E)^#)Vql5g
zk4s&MNx1emazX$ZDhq_vkHp?3Dvsy2e2 at V#ES7#ZR_GY|;J}=TLDRGgkN9O|S-mIu
zY|!qjJPLr=r3ZZxhLBCR`GdkpVpMW?gs at -e-PD+9d!nLHPg%+$LyRVWCc^<wX!waU
zl%Hf{MN2MhSn(g};S=BVaPDCS3e6gL1estZ6<EdoS&{@D4a(ya55>c8$Q&5-V42&z
zsGq}+RLVDPmDg4q*kf6)?|0?Nw0W~U`tMO2Eb(Mr+`uQ>X6U+Z0vj5Y=eoPN$g;lj
z0vM%Q`3SMYaM{ZEJv44r6%x{lp_3q$(7$C3ZdHZzIF)*C{7LHV6Oq&dQo(grPM5IS
zYTGy&MdiVq9PgCx&g+cYZ5n}mk&3)N;r5rB at 5*B>FXIU{rPpH+MI$bn*(OkkW^Th#
zm0al#5T^|-NMx*k$WpzpOJ3xaX2KvtkyvwANfAsb2 at r_a=K>r?Nl84fzJep^7D{rZ
zU`g0uj}iqxGXtC5`UT!r4WE-{=pJx|DBctWYxIP~-cUDBn->Z1v_fW2IiBopDM<xA
z=NbA6yd?l!_Zj2bZSIdN)?BjP+OOz+pt%sVm<>;B$qw_HHRx6yy!-DP3L+uq6xuVP
zli`TJ__Fd*vd{@e%DF|9&J||zaPibwSv7zTeNaBKS6fpRiMK$9ENRZ~&|*$IWvxLm
zXX<N~>5XP<FUP-5bDC1?vn+VB<xjb&$SeU%ho`k$5}W|EORUkPz)9X-e?N*)SzbZU
zEtmyDHzDC;x1AFtR`-ytBMz`mVcR=A-%XO1^f at 2dkeub4CGUm{>queW9+eYPs>RM?
z9X8HtvmYihJoWvubD^^7&cCv=ipw5K&D5%rEmnhT=UI0OictvJWE3Zz1)z^PV&mW1
zj%|{HozEzHzr%Ay%bgd&vot!%IQuEFjoU^OxlYa+)e>swdP(}T(*n(F1Raf&5h^+%
zXTmhtD<5#nzcY^oFX$TXxYsb*wJxQ$tXoL)KI)<=atHq6-vEvsG(@cpm3cPpsAb1a
zlgp@|6NgT&4*Ltfgvzdpc~0{!5H`vQA2-(Qx<k#_wFVrutP48wULp#uHv7p&AC8*A
z$%TH47R~%lwr3`Z at 8z1}em_PaWFmP4OyMccM<ctt8~e;OBb_*1p?4vUv808tLrR8B
zNF}H^*}Ra7fNh}tR<)QRGBV;_GyP7nF_yYJs-`j_QF*ku!G?UjTyzrQ7jXQ6AfhG1
z%|Ia4j0bdN{ey_<9;YF3G){q0GA{Y)a&pwsX-RpbsZWJFUmKV=VzxnwknwAHNT*gc
zdd-C5&r{>OIbuEM21xl}0GG&thc3|XI|BZ!Xv$X_^6p`!{=d+a7X_O<zmc^0w}Bxc
zkiVv2eIU7{neqHLm-v?>{Kq9SG5(KOlJ&oHbFluea&s(buhkPY+f{LTUjxJ6&@bO?
zhg$Rq3#2^5c1KXpv3QbxQpOoeE5>7#@5tT#P0WG7N*{2sau}N-sZxmA*a~uY2Lg8l
z$N2P6$ZjPI`(eeO-Pxs7lGE?QaM9H%l`-fe+57skaylm_cj5iu#y3cZV>59)ddt(e
zpQ%&Ic1I;(FrVR2LQ!-1Zu>qgwH_G&I8?imIz)a0R*ZEnYK^dGf4szV7&)>ZmSD9*
zq-61RA9r`1b-&xUx at 5S_keP1u%!vfxPPW`$GCZ0o;bem$u^QiMtliqsn at i2S;O8E6
zHf1bPWqq`!_;BOCjSX56ueaWsfW0P4*%KfC$$o`xBj%fi=#|IH{J6N;<IfXQ;{Fa~
zdp|F6$>D2#q5FNk{O9U$*PYjri5u^E*AiDw%3Mh@?WP5dZ_>1($Wr#vJOER#kJHoH
zkA6wQ<jC~QD@%v3yCrZUBePCvt!8d;02)dBDO+Vu>~=@9#P at 0<>+7pv`up}P8osh&
zfZSnsch3{H;SEqMS9jAhP~qiz5Lin^6iiK2K1Vz4JzI32z7`LO(>O={B-joDSf}FL
zV)))1yQ_c(kPSCK!Ysf?cVSZy4d&V>N8vsv!mnG2wSTLlcaujWtYS|&aec!d9y6-Z
z&ZuI~`6zjnr0)YC2JVcfTI=?Y`_D+{GD?p#H^j#r#teHtYiST=F0nRbZYqQ9EW*rf
z(x<dK__r#(A2q;yFax|FT2+U$eBYq=_hsNITQp)cZBkx907s@%?jio0L;dDOb_bMT
z`)H;NL)NmFwOR5A-{Iu=ee7B}4B#Y(Z}k`!ZJ7FT?;zXz3S+0H%<-`G&wdw=@awzX
zQKjO%fqSg}umXTeR_R9FloT!bT at n(iEJz*t5UCg-L}fTFE?}gVdwa}$5nWzTC{W8Q
z!A*Mc=a`f7D4Y{)=845RnAMSjU@&$kfrh=+(~ge-5E+rXqsj77r{-T5-oqH5>UFhw
zZ0c|53)e;wG*QKX9!kgWv1Y;u0XO#{J-#nt!4m3H<<3>pVi2QnlY*y;=Gi|)7^L}1
zAb#sh{gMFhSPJp-99e0OBaxefm?TkK+KT-8i;lmS1gZdUBzbXkO)>|O%ct`Q+}#X`
z)f~f5#BGebqV>L6IO-aEBf{$=saVgyt^0D*{{l{$Yzx~HTgh&kQ at 4B#eNPY#*80{n
z(of|aT|eFgw?3u~>KYYFjtA-TR<ARlt6#%73R8DhltN)eG=`BDw5SqYprn1XCkylU
zJQPlWGc>`Y8?`te%ui^epNh)D at bfb-UHd1yT4~FNc<MD0a at 4kHx`gA`)OyH{Ys+L&
z0hsWwF_{URt!a;lqo9)#M|W8Q41U&7+}iT^>Ih$06&*TjU~VzAS=ju<v0MdNnXfcS
zR2VMxLV~<iM~#vMhh|=JS(59f%WsPL4Ap+b7Wz$WS at Sbs^jdB+Afq)7kxwg;fOn>d
z57MOd7FM#LjlxUQo-)HjN0F!x=&ohmz%RB{FGV_|BI8!}{<MP$r2on)-60`**78_B
zR;wc_Qud&%mK;})dRKZ>E(VZ|H;7!&hRlnA7wDwy52!P`6gs-0H+>Sp(ZCao39okl
zML>>o{5xk~Mzr9I%ne2{*Q7?kui4GJvP+6!<O at jc5S^Yec~onP4tS&*y>{&$DGqzC
zMhn-sRTxlRcfZ|6yuioK8R<M>5QWh2!HA*83#>sRC<cS{Nx7FAhL at TeV}{&;-L5ox
zu;?th*1&q?s3dyn;8#9>hB}<4*r*~Qo`aQb#k?$QDG?S*r)ipM3eIB%g6Ep;ACRhg
zcq|+v1-bPDRoTRW%uoo_UQcNC%s;_)Ff8;V8|(ulIg8`3k8=f$zWNBUTc0Q|z=h^l
zYixXP3oq*e-yTe1<Poj;+o|CUGXqb*IgdffVyOVnPP)q9%Y0=Rqt$@^xmpydD?K#V
zD*-cAXpu!Vu`d&EbJ#I#VjktdH|?&R$bJU;wCtnRj<r?-*0sIXmK+xVyJD-^DvIX1
zO!K8-r`F>a83F{!to5$LlvU(q=lKxp&NbJ{a$o;kM=eN)Y*w=tJVw6wevu~P{ieJ{
z2`kPdRCpnR<C+yz7*b$;A)lf6Ay%iWz76-ouRw)@C#O+j5QO~ktnH2O=d|goo-9oH
zDzCe4M-LlUXi#tzhjty)c)ede)oz+4fwx2q<KH-;L5UB#geLsTNX^?UQA>=4aoDta
zv(piD%0?|n+3?yFmE<8Xq{$=Zzj+ps2UJ+n?~9s?ri!Klj>?`sIOYc;Ne-|!p(jUr
zx-P!Dpj!_jDZ2jd5;CyifM*~SF>nz#z$W$U3$%w->J>5&!U at ti{|BwqrTVObg^b%Y
zK{(n*4pbbtibjm}5GndfzdNF at -;BTZF&~I`@;hB|Y_xjuBp~;%#r6UDe_b}&*K(B(
zrA{obyXsfp5EI-G<C0$a%1?q(tGfzN?tZDz`3Gtc7z5zqw?W2-H5e^03`qN~7!1&D
zc3fj94S-Ex7Xee$CmnF4uW$HZ0KbF35DB4QDbsxlo`%^P!^3%W-@)NF*+3CoBqSuA
z#b?Jds$CG{6G#T%mX<@sZf=?vvk{d>zGiIlj2wPf*B+;Bf%Ttn*GA>J{#^yQ19Ih0
z<eppmdH6Lu5P}@D!@I#_qgoMon?ZaG-yA4$(znm2jaBcIIy+lQRj&kaen};|Z*XK&
zv_GlJ?-*@pE14C)&86Ah9hql&&E~Io*!#JX9M$ix-Cib?y(UZ>kCgFvvhTxqFxg;X
zH?QbReN})W$Za4bZs+>OGvL*yDM6`6Q>L#$17l=CR-rQ at OQ}WkY46-Q?>c7{v_7?r
z(Jkr>saCeHR4e*6`WWz_Gi&ir$TM`1HSO+~!gp#LD!)W=R~`<pZ(Y3PPR<8_qDGka
zn(GwdR)Pp>T+U}HTy#G=5%%#5{DTYwnWO7g|AC!~ykNzCY06cSA$)axYoeA_T?pfM
ztBWiw#oiC at uh?sKsUt5Mf7D(ysR^rmR>A<-;M=O~59tzwmE7#3suI0ad6|OBwTz+i
zL&U1>|DYO{+H~Gef?bk*^q(c0tCbNSn{*z$B3|B7=f at A`*oZv`6z at g;*k*Gytku+^
zOAzj{jNZQ(9TFO5D(U0b3?T>D4Li?Jj?$w7aKs?scA<?T4IBT#?q^p~KlUTH4_MNu
zqcwo)4#N3c8)>g>25nz34e+dr;>Uumsk8sc>R5TMZbAurIlOj=b%e%RAQ%j2wenw{
zb~EL0Vx!?Ob+zPHNmj%Sw8vHK6%rQ0LHP9WztBpZn>(Qq#%c*cg8<PHngYI&%^HoZ
zWwavlB40Pc2FE>D3xSC=W6M at 7cDjQfk*LHl6G-)wP<Nv-Fv6jwls~#C1O_YP1t%x0
z2*HTf3c at 3q-O-4uCW_YT2%sp4q0Cs+G}`HKh1<pHivQQ%NcC0Qo}NfVm%>nEF%(Cf
z-yf8BUdISKCBs=P__QvdIGA_(lkr4HVSjx~Oo)TUWJU#yjF at Bn0~J9CL$SxMpET-T
zB47UcY%?me;pR}Uz#b8^m&5nZ2^Fe~Lyq2I!*Xy)qqh{bt^*3x3?hd^-b=<{`b{8$
zkz1c??Gzfj;{@8xYLIDQwVTl27Jef`qx(jN8nvs^L435R!=k7C!{WJV<%4TR6q>0K
zSlVDy>93e=OIfD{=BGZi$|dr6&51)>bYDmtb#ZC{i8T8?x_Eeq>ql>pC8ZwHr$-~f
z7 at d+-=$J}UY9amg=$M^q^?tS8 at qhm8qz2Ew(0f#I87{+{_vK-|{#=LCjP%E^-ABbX
zH^$;DA;0F4>+~Reif*I^s%e$JcXeSd(o%f`@VrQrO6Px|jiEd(Lc{ETm4xyiAHKCi
zl*8r17;DBD35CimcKfD<zKR*R7p>Wh>~d(|D7}u60tZ3RqWxkx&_FiudcR;O6CfX=
zP(Fwqffg<=OG&K`Eka3(UZL|3KBLm_H{zFU`6dF-KWO2-D(ortG<lN2148;%!ld&H
zmO(#Cbyod>qJPAbuN}6DPZDVeW8QTEopvs;RV_aFVbSM_2#UxOloWCQfE1-dNBjpR
zDavQXtb&MNVe}*h{+Yrp1tf(u!en>WBjM=469+}^ZbS5XEy-E|Xjalg=@BIYM48Ef
ziMJw6YzT$L#6$t8513>9gHKOz!iJh~k!`i^!z#)A*K^J<n2hoYbOhG{sm&RRx_4f!
z8TIf}!(xYE9m%(#878wT862U`_-|%^;3x3kPyhw|)av>CLK=6&=2-bCfPDVX=eX1}
zIjq<dGi>OZS{K=obVjIes)v at 4le8Ftg^ZhXNe*+Ag0GDWFNA*&jVP*3jDqioiXkXS
zfwzT<FxvBIKx}G at 5Fc0?Lh*9oAM(US_>0j$2;ltOBtzHzjhlu+BrNd8DFHUwDM7x;
zx_&iiGO*q$p#l^({cMo3Zad<B-!_g;yp>HdGL at -eG9?DAMn0{is8G4fQTyV2t9Og|
zL*Pq(yr- at 5E-Wb`9hGB}z62fsdj|F)()0)bt&4{<Go6Iu|2bma{7hW_AK$>q3&&`5
zW+BqIdXW|vm$Aeh2bKW$Tv<BijvLg5OFSd{g+21Wcl2cn%MnIa*lHH!WpaJSw-EUu
z{LlFI7e4KRo%KW|*spfL*aA3Ew4UhFvc*+ty*2QG)>@y!Dd^zIw4?iTx1ZZ at wI&Ls
zCGb*15NmPS0a4iOi44X_eIiEta|Xt^Dlao!xmG?-UQ|T2{U7|TIMZ?wTCZ&6KcOCd
zT8nA0jA+|6HfnB(`gJ;Opo9R=h6@@f!IadiMU!BQPe~z2iY{OVl@>wc$rA~yPyDSw
zg7Od#gVvKY at j7aO{vTBM!@5(1<W%WL!Gd>$V}Wk~T01a^a*38)X=sh7s!i&CJd3}#
z-ObEBiS94hyZ&TPPPOJ*E;LTWi3a8!B6N~~$0Hc|{i0chibv1^lhAFK51wX=MOPMt
z$RZ#mF!A*C%&+Nx5(2;FQAzjY7(larrWn7iqDC-jE*JNwo`l$Lp4ORmuL-{!QtsJ2
zhi1?*P`;%*e9;)^oNhiP at wW;V#NV+9vtrPHXwlNG98t@&S>ondvVky0Uw8M5XIL|I
zbtLs}flGwWT)ZbbfFyID^bAnM0v^#MMrR={WXW|BLH12tD0xiJNAxekZJI4t4^!ei
zvLm$kTD^E#dR|`2Yc`DlwN4TzZjwigec?<51CBSrFn-!F($f+<?zgBgx!||?pbwZr
zFa=gCO^Vu<wqx5oI+Rv)!;30fQAs<O6z4-U_TSghNcTyEA3sGsQY0;-PK7oPth~Q}
zgO~$(>gwi~@JrcEK24xL0JrpULxow_%MN^v@^0k~6L5Azh3>)I7FyXUPobDe5}WJV
zYuC;~|NdinrAm#$VDPb%TVfc0DX0xmuhT$m-xxUouW@)mg8r^;W0t%o7$g_7SF>NA
z83sc~1caN)&y2i}Ko*|7AXhqA$~m6NRvOa|2f~Osem0(POB?1=aYhAFv_#Rzw at Sr7
z(w`Y;l3PL4v9Db at aWvH1JLLjoVP(VUECm;GX=Ov*(D{G>dNDb7&v?-kz|WRg+Ox&o
z244-<)W- at 5U%dg?T2-QvW|-P~zWIcjHTR7xEvxv!Sf#^h=NxZ^Zj}gyyGx{MDa{9W
zfl}r-!`Ql}2@{-)1_ynl`)e-=%@&x3yDvI!WkWgwPNf*_3@~2fx*IWC5!6uXUa556
z=8UOgXIlvPy6r1?feP|&{ne|1T*MVWB7e#;uDZ4g?&I2kbk(ir*2k8CJFtXhNxmwN
ziHHT(M?}xhi>%w||Af|QEb|sd>wgUv4I8w9ZqtbG{3Gym!ok=AYoo}Q_{TS=V#l^*
z8mS>H=PoL9CtxWNeVACC>-tup{;2FOXotg6jMQf6q2n&y-`YXVqeJvX-~7_VF1E|b
z+SE>Y!+RvT=C|@ZK~%Y3$+D<@8NqHWicrc1WQ&NklD+7QH<!!}OJ$miJxGD6Kg94_
zGmTxkqyN46&<Xt4%qe2}l0NAi?x1vh_Mp|%box-+;%Sukwe}B)>@BO!|6->8F;0J(
zDOPs&|H&H5`rnDGSpRPK{eLE|+Sgu-FXV_r#H at a%I*F$K`Yd+XzYnUbkA-Q7Ykh_=
zJn&SzNzgt_h_@@Yr{NkVBaue+CaL1&)<(<@itjk at 8LwEvphQ)4=i?@y!49SJUO>da
z->np)*Z0BynR}Gps_C_cJhEHQ+xuyye?#uiSV#-mZWR}=r+rCNW&<2u2p_n3q`Sk#
z)fI5#W!UHY-~{K0rzb?WBjOAZKi`H$XOQLYHA6`URY3z+7v~CH%h}yE-1|Ms$McEf
zCHrN*lze(mzl1PLrrj`q*2P>g^E?;^ZUeCL{F-0xA2UUK_>Y+)_z0N$a~$ODVUGvx
za`9vh^(QVKU;Mcy#3zI#R1RK{krRBFD3kCVN|d#zzuyMX?d|zE*}rj_7s;@T;8TNl
zT|0PWxpiWB(n!`wRU}<?Az#k2YDtyR-?^GQAjXJvnXNzKG>Ze-7TGmPPsIN{=#!Wl
z%bl(1A02?kD4d;IoUL%PGm{s1IzU8u*O&Uk?|mhbrt-;54HISS`$sYXpBSdQI)Kn~
z4+ROg21<N)P3z|Kv{&Ay at CX@ynJKGzT-P}!Use}rMCizSRcGxY1!vRJU;`$AATvk9
z$VjfTHFDlu*N%F)6IDuu)2J{@o_S;IJo#CQQJ3}uAg*oI^Hu9DNgEu20d`k<iK9la
zQJ)=+6`Qr8T=nMq>9$(lrwX#qEvkI~t{9ZD*$n=kUTvSt_hz2IQg)x{`+3QwnUgm%
zfK$l*Jj at T$dND=SrsM at AYG#J;9x=!n59vS>Ye4~W7HT>>aw9XlHm at Z(E@(C8L)G0%
zab$Qn0g*A0zDK|=O|A1mj8TY at ca*3*$Z<=Y>dqhm2AtEoe_zjGQkdbrw+_)=k(#KR
zz6c-Q2j9p5V%zmk!XHPVa9#{gTji;doSyTR5njFH>z{19(&GR$)gn;5`MAzitftvY
zRfs!seEp7b%pJi~Fv?>!qCZ|jwPef0mZbgc6+JNeJ60)%>WJIPwk#t^<n^BX2PNQh
zXxZ$@0=;mUpse&iHpF4)nY36jF{g&#Z&mV=5g9jYT;K5GyT6-F)2UPN|LS}#F6p}9
ze>o}RDmm0MyFTq<LprT87M?b)pHijlSew9%#@#XIkC>zYT=b{rpi0ZRW=H%?gkr(Y
z6w7#izcx85@>F#%&T1A3D{Z%d9j=;*koDhBr_yLS)TXSp<6}wDhd6`4|7{o&9OkB4
zHsBh#X(!~U8BU at c`bCz7us)sDFP(oP=zgAtl8F_~ZrHX$wMT|if+>>UX0vBvW*fP3
z1D#&n?h4}43ScA}V*v5&HwfQ`VXBt960pU!$9W|4h2=;GT8zy%h`Q;Ct=2c#d^Vis
zp}i}-%W|L|0B`w{O6EC1{T$4^C`*=f2HCYre2hl_E<uM*0+dPVN`S#BSo8-h(oK}!
zx1}pU&<h5jxhf=nrtpAn>WFsm|9%OIy|@ZN^<_pPFj(a$Rli$v?98+VWNNHnun8|@
z``>~n{I0%wUsSjxK)^BnQyp_ZxZHxR at WTX$J<W(m=mri4A<HN*5eg^nx_IxKp^Y7Q
zq~t}iMe%kK_o*K-G>_<7p;XIcLEz~;bW2oI;3<e?OLSPE#UaExH^bJVTYoh8_Edu{
zcL{<fiu8sbI^1K$&K(ZHZf+mL(~{V0MTxgc)>tBj25~{prG1$d<|fgZ7IyN5M- at 6Y
zB<Y!2Gty95l9xcsGF0E1vf+blq%tdxK|;GyFh25*;Qg*V at k8JcqQaLEm{ZUwj{0Ei
zje3a at N`(Lgkr!16+@)V%Z23b#yC_&O0iYx?S=k#<8o6Asp;BT73b}3Ip;9RVn57Lh
zI@#!LY|z&l2H@*~35jb|(#t(m at UR2<ay_p|0gACuC1b}4kYr<5rxiQY!|+;j{4t{*
z`zP5}c6fOUrJ_84{1Q#7v^>~m&zRZ-Bao{Q3)W(4{;XK1S`V7ym^TwWP`Nl}&%~l_
zv*PXLwi*;Cm88iUSi<Z?Z>tvhW?07m#P&Im9(K<^a9DduN%O8t-`qYVow^%C^j3`_
zF%%cRPP$x>IVEy+m{#h>=TN(6oQraN@%QP}W;$IuTY`z!XrwY>h_4Jdp&d;`;tbiX
z2A`2+ZqHerKA4XFCj5K6eq-t>u{|DsMu at qc0`y!Wm>_sep-Noz=?626+6wKy at i(v(
z=ra#VJZpHpF(aqUL$aF9%HmfS3N-etdK?I^MZ|GsZf^iid!}rDNSDRsXbzUBB~>Ad
zbF~ejZY?3akuK7Fe&jxMSoBH?ro(a#Fa5~-%yhJUbxop;$#)N*LU`uNz$Lr&tlgF|
z5^-V4QpUo>T8_fN$zbIJdVC5;_QLhVED|TGDz50Kal0=ZE8q5w at m-2()A&SdSwGHb
zFB`knuYh*dbyh{~?8u`zioqUWQa_;2_Zk+ at Gv*L@{e1X}z>X^s3tMKt{3t at y?<)ig
zg(X04tw1EEcJxiJU-C<R`X&)$i*ABovNcN-xNn9Vmg8(kYjP70gQZ)a7*Lz2lxflz
zbUVJm=F#m!a)j?4Wv>To=c>13`CYN_EO`<_{5RBa at i)HLy-RddyNcKm2G}f_`QROR
zI at f_9-8=DK7fO~WzgT0f{83|g<&LuLm?KNB{bKP^zrx~6G}Bt$IL)Zxe2`&??=Vdo
zhX(F%#3t~pPlD6lF-BJW(unTkHZM$8E$)Bre&qHh_bY3yn`uLMD+Jj0uFW7^%mia?
z%)CX*Y=!Is;tV`kZQ75PJ{3h at ov?t)dw#o*K0BqaD<i@<VP at I3*yksefRk9a<&FP@
zI^(_Go^;pRyw|}VuzO=7*JYv?dQc?Q7cwj-xMXbgH%ZL~4Out(X;R2g`}rU{0_;a;
z at ez9Z0b&3X?$8)Hm|Y4f#n$w55hUf57%O%3`2aY_R^!hw4&b+uyFe32qVz7;?pBer
zUI^Qg_#rEP#bkkQD2F&@I#PR|5p%Wu3D|iWd2k|p({$J(Ep(5bqph)v1kaq20JU1d
zDCR5q9T*!t*IK~P%Ic~+pKesi{h2~xA{Phk0_o91wIsk9c^c_e>YJ#zfFo)ig7Uf%
z_)vi6sOb?cscD0yST#j)LuBwIv;8~`j=oetqTkWCe)WoEW&lZH;@_x-4e5 at J<l6=>
z#f&_-F1*P)veg52!r2&L>PyYnU%H)4H&QE_VHpHL1yAD1i0-ti=9&h;$UY6L>^Y6C
zxnhol>p(=<c4l_0cr`-DbR~eixSW8`q9RP;>0uT?3EjbLLGF?R&%d-giSFh!PIq}V
z`HD;uAQyiQgbo~aH=+cQ{YA++FJGd4yBcg8e2!=mvU12NP~bm;#7>L^(dH3?Khtr-
zWIco%4E=*NhBnqv7gInr3^W&Drjt7(gGu=%NTnn$B!8~fIoPNqF4uiqta1RI-~Z}F
zER`U~gei1#qQ)D1+dA}^`0aic_S+!+XCg%&rd|ZsD)k*1J{acj+pQ->6{H3`$p~V!
z4=GwT5$f~zYiUv_aFl94O)C=E^&u=Hci2eaumeR#{HIUM+upF{1fffq3pO9tId7CM
z&YY$;0`6IfB4UHW!fSBL2<ZORuDm0|`?q%B7wS}#D;Hq>3|&9wQDId-=I=(=_F9P6
zkk1e9aDUUu9{RVhQ`MoGJx3J~Q^-lY{efX7p|IIu>q#bw9tSvVMpG;~x9KLS#IEGm
z<k{dG(I!lm#ZRh6DI2a$n(2Di8fVr#Q>-2~b{qOQeJ7rtom;!D1=>o1r>+l_DKWUg
z1?!qavSO{Nb||&WHr(0p>hdBlzJq&RzIyq6jdy%_wZ0O1X$n8cI++aeIr9BCzq%B0
z>}Ma#$gIraX-?d2RsmDDAUjm4|NePe_La0dl==Yy?c+&K_!_zpP%Mb5^hyXSe={Fe
z?WAYL7vgEfEe|YIlAtG00ty*}o7i5BAa}h1QC{hRTVF{t!&MrYT=%bS+OonJOw4k@
zm}XJtXYKHpT+6<VO|MYnE*Uy)#JOsR5`ub389j5^5D`Hg%sEsD+H`5eHUuGB0SZX;
zxCE-8_>KvNS73QFf3NsYn$<k-?YusU6uA#Zx#EkO)G`iPv8!)@y3dqZwJ}IP&rE%p
z(0HpIrMm-DC?iT`Jch~_;{S0`PL$}eh-41X<(ICvc<TYrBSN*=k(fr%Id2)1&z845
z{%G(%`<}@E at R>#oZc|E5%f?F#i+=8`gXZTB0G^cEEEooD0vUr9t(9hi@==<_9FT3C
z4z{MbQb(2UKTA-PB-s(Rgr9s6csL8+WIq_h{0eX2X0h{f1)uU-;wt($J$V(`(zUnJ
z9x*H2?m<egE$iS+6EY*-RTOm^$=f|KhrdrGca2=Nmtl;L5K1r_VoLI7Ab`q+jA);J
zc-+G|76n%>MtR<X9~&)@i1wdzOd`p4t*?Gr8*e7IG1(jO@}{}baRkk76QatXw3feE
z@!Y2DYp>EC?}{!w#QKS`(<k(mn(iC9G$yKo0g1?kq$1$Embf`!QV7!~L?T^m{hFk;
z3tl(`I{Er%{AJr`7|(T|m1+sat^sFTzn(eAW+B%eh- at 7$mg)m1ImUlKv%yQ;UBeH4
zVOw4BnNkzalzUZm>e$|Ee_ at pd>t#oR*&-^TH*s;GbP_b?n3#m<63XBGPY8b3TBt|v
z3%p$-bY at T;-5)Y3w|mr0LhU|}+oK_ at yy-*%0QUk^fPSG3)JU0WIas^XVTXNe?JB%U
z^f5rH-n+m<K6SQz<koduMTstTvXbp|57NnB-*4-YkqvAahH0F=DH(qx+Shr94m0wh
z2OKb%*Ve5&6 at UWdl#|(hC)>~G>E+xL5Y<dAnwww;IrMC?FPRv&wu1#4P0HEGOXb31
zV!!uEK=>h(kK4%8xhh$E2dwrSBbd`zt8(4LCfdj^U at G$lK9hyubqYG^!*sUZK{~08
zK<DIiGlgnu$4=m-`v%9CDN<J3LkpLkx7hQIE)i=!Gt<LQ>`BH9#}ogez!OkAJ;@
zjb?s25f-L;j9sEPQr<v554E1b5J>T20?<9reoje``s*2n+#6#JLe~48P%o3up}l8P
zf?;ubYhDzcGL5cOV#{|yt+}gT>~2kS8^0n^0pr+U-%y0&U_q at L*7Vo5*$o`5Gr_(z
z8(`StQGIEioMLqL4*uNV-I4Ej&p}!3s(2Xg6KCxLQ8+L+zJqe&Dr$$Mbyv#n7E^@>
zJ;;=p<tNV->$gQiY*dXxl$@-5QS0z<T~~%s at tXT$M#;#>PIs3J8bA!y^;$1LHkluM
zv?ODm7!M&3^ohR`WSH42Gv-ZON=X7)WXi!+&@E;?ltas+L3d^qQU{T_IsQb0QEWgB
z)>URb(2#u!qob!T0(=!(+OAoKl=(~c@~z76DU~4~t~ZK2+Ob${2*PJkgcWuP;%h}F
zUXCwpN;`M at k}Vrd?%Wj(y0rPbGz5Qh=UVgY&3zxhM=xNyDNZf6^KSUr9=Y5+&DtKp
zLBpN_+)xcz at EknG|6FX%=8<1pPe~=Slp$>kc@^RZig=X2*Jf(?w6q(`y@>=&Zr}To
zh}PAaGTM|6IY0#(4D(;yG at RNGnd42i*IsLr-o0d7Q&8MR#9f?he4ycNp4^{W9=RR0
zQlPzlI}f8kBc5u>vUqiIV-nOFY|zpp7`XqA|HF=`#@NO0aCMl8pDZ=|YQ4(FjF+`g
z*T!ve5ew6 at tCQsku~xVFMZ{YDP`^9AML;hXcQps|E+aAgI^R;~HeM&xtP~w>?2!Z+
ziA&C5`)LhkmmA6&&L&}FS$aSx#G(<@x{r<s3FY(gWb;$j>vC*aB|p5O<dw at Y2jp%8
zWY>O;xBej)4x4y3%ZMDDC~wi-Ph8t;@pEyV)2U~YS7|v9fU(+lxq^+eO69jz?&|*0
z`a<98Z}6_DgbjXW$HoaSEBQJ1_+pCt>n^K_#tMTOmwU`s0g3s^4 at CXr_rJxHwDAIB
zLAd`EGji51kQ24tFN>@6zgQiUx0SV<3R;xD1;`=%n}2k~`%%lDAGPoO!eT6cy1`b;
zh$Vkf_*8c`qgl<8X&)-|Ihlj6i)!^bfsknrgB$RyaKyDc>Go9J&W~xWG}q_I7%|my
ztbbjj3wqtd)bQxmaV5)(%Hfb-;7)z`d>;d4pS$|wg-bmC<cE^;ASbiH-cl|1=R~6<
zWc|va2l_W}l|`s<pSo}*LzNN22!%ob)%Bv0-w>+;*weAMPOhE&BC?D&rhxM-ha$Oh
zU`P3>ud5S*v#dXIKq<144%9%!Y3*%LR8I3j5>>j_ZKpX2Uq!NShV4W&&`Mc~LLIYL
zG%aGuHM^y?^)-Lay4x}jVpR5J-<!a-RgYXS<zcLL>#(G0tkqLc at zkPeELZziPRKcP
z-h$c26u}C=Xq5r$^EMiiljPw#lhbxxO*iM9<&R;3-#d5-5>a#tj!f~}ntvBdgG*#{
zGJKfj1q`(QE4AB1Zg^)KgI2W_7c1kBM6LzR<-pUf&KJa*M at VvgU~q0{yr0^-R!Et~
zy2LkSX8DO`cp5Mi40!Zb8ZfHIae>CRgi?%dJPEK&xPsNj;F*&u{UfB_2&+%sFDVbr
zsC}^)@wPn?<vzMAS&6zs- at d5)_1foBMHiYo<m3I~1Ek>f$mhSAz`qRNKaCnA%m19N
z$jbTu{rLZ%AH~Y`zhMG5v{&l!hwTs;<d*oEY_jdZH5(aX68)3O#@HqFfI;@0$RK{X
zBK~mDl26P&0o{ZA+vA$`$>Xh$ieO at t(vW`J$K`X4nE(J_^5RnVWcr}+>d*V`;F8P9
zoz&Ci{f3LnaqW{_E8~av_5JbTqYM8FZO%?yWb=XJ$B8TfmkE|Rj2Eme(@B5p+}n~%
zP7Z(DKtCs?=R2(TF at FB1!pW`p9;H6&kYD#EFLLtW_4Q}WR{h)|$K5H}+w=R)IKk%U
zIeX4fa##oxwnJGT=Qp$j)csyt!9)0b79Q`15!{M*d{C4?>9zjT#K{lwC4R!B_v0Nz
zVwa28Q4szEX~fj`xA!GR at as4d=wgIkAOrp52MlTY?D0FtlaG(b<MrD{iTv=c>*t<V
zyldNmBg@>i71oR7`MgBteP at b}teFGxmCWoiF!PQ;x2HW&;p&7*#%a!H?siXaFPIPx
zuHD=Ry^@G96zka9oW>%##X;c!(7g~Ik*9*jyW878TZOoIp4=Y`5C_B92~$2W6JsGU
zj21p#c6|7NkeNgzHdD-B{EbC1={)g}(cGpOuerh?OP?~eX`6N%l;H%-#4Ox$1NBII
z+qCpBe4NF^3`$2$8~nOeeYdA3dN)(@bxi{L1>iH;WXhOsYnP at K--jG9NjnfC`gAa@
zZoMNo38En_x3`vY3?Rvu?ZdP<=Y#dK)nQFDb}S5mY^}rC>C#%6g;tq$164)-nqzlv
zl4IuNg=~wz?SA~p2NEtCR>X`o{N)3)@BM|^qo6E`?Di?amMErmBfn?rGV-e}G1l9<
z{HD+^tn?3hM2&>M<5vNLCNt19m_B-_Wt0K&fFHVg8+;}`IJ2XCxv$ygK3XYMhCcf;
z`*7GCVDSv}eD#dlwN at OtwJOP<Bk*<+UGD7|Y`r2Hp2BisL~Uw+eAy6znhu2vYGgYd
z8)JcE-_&q;N8w3G+-%#Z=&lhi`x%Ge-x}jaZTNXATxpj2snrI!E&quApiIq2yA+AI
zB-y8&l$M={nI2P<d=SJHO_tujOPJTp3-t8*4a=p5+mfde&yuv4eP0|avq5&}n3Gh<
z8>ynZ*Lxd<p4U28ABI;y&$DmgB8%U?RB9h>wMP&04adTBE#NdCs_0aHU(4BPy7whQ
zR-p0?$X~i;Vl)9|bFTbC_x6lw9^eMuyVImgi8hj}_i#rOwe}6vL at t+ODK@MejsIXq
zm|?AD#fTS2H=GB<G&Kc?O22-<gA?TOt%d5By^rh$5H+RFneh1*EDETl+|8zWR5)Rc
zMN>X&98>Ezk*#cOuw3TPV`WN(gkRt&9T>u^<J&2eYgYQF(8_Q&U}()M3VLE#H%&TT
z&Dq+deT%$fJ#{xpkGRe1q6aGA9Q9#{^ml7SLc#NR+1E>lR92~6JwdpKtFuCXRnRDT
zts3>7tiwuj<iVeL-XLQrNPjv8d~kSPBdF;r#tWP4?0s at E9-FXe-y#w5A!APm?!wEv
zsgu6CrrAKf^ua(BEJ}~di=nbu1`Q6Bdzw2yw<9Mxm+%HORu;tTB7o{!)h=#%F$M=)
zqx(X}^>4!(;A3oX>D$TSCtsy at _9)C`f$>6=5s3EtGDQbl<NLbDft|qpy(Du%Q4c<|
z5<EKm0^fEZgLBqQXFsc8ESK(vz}h0S0&=j>a5?4kB-nlmC54ht{hBhYYZ_tn5((Dl
zavW4QIC$3y8N4GBboi7J`mNI`qrNs<j=qAFonf138j6*B$c&4>M33QkspZ+r?m<y?
z0~g0A8lFZ&MTu29D}(*U<Y-#BKa~!zNeJpD|Cv7e6PK(+)P%F{!*?N<404Khgq39w
zDmxZuMls5zT_Lbr%lwJ3%Dr8K3iDk<$Z1u-On#_V%VP}F^aRtemr2B_`S$OXg2<9p
zjg-%D!(_1Znw7K6T<u8H!ez{`IzA$NmdvKp)ZzVzVU)vRC7sDvA)5L)b%7B1YpSV3
zN*wsmS6ixB*l at micinhhS3Cw`PM659S9L*Z{OduC7 at kb9h*x8%H4!9!HeDCf;tu?X
zU<JgP@@%ys=+#a6TnJOgfzu~H_Sd|Ehpo5Pp|v-@!sm$6VG4Rgs_8IcS6In+We9Pk
z1qWm1EYopcP2{6$D$CE5ex82Ww-LxKB%C`d!%|5#Enpk+k_=HYG!q}){$-brOS=Uk
zS!*9Un!Iuj<j^(VuyNBM&hX>@7aQVvN1lJp at e-$pH_IiS7YRJqTR3!4)xm^1Ie(<U
zRSKCl#PBVSr{2ztWDva|*mt#qFjU7FssF>;TL(nlbp6B9(kLKZDlHAtASETzNQa1Y
zv*ZFwgLIcHC0)|e-LQmovxIcVvc$XKb=}W>-Ov60p1S at xv%~B%J9Flo&x!rc4BmQ|
zBf4{}Y?*;3?bL{#PXo7HSynx=e~uwi3~xP0VXbP)cq6KDAgL}m>3Cuo>QWOBR6Uph
zv2u(IVj9v`t44b|Q-hY at y^CmE7qECW(D5TQ;(g9ikvDaG61z5T;D=fAvSaT<Vs`T8
z)I)RZ$7~2Hc#U#VJKMsY^tTT%5y9V=hCEeU?Yv%!`>Gxs1o9Y8i31iNj}hnsX;6im
zGcpOZuRJ5Nw5wf#nV&)5He=$srwmhCwJ7i(jwuR#40)igYfJj#Y6$m6C}E#ypw`Y2
zdFXxKVyosw%2T`RBl6G8BJMboh<0B+)Sxf~FD@@HDQB0fWY$|F@@up)+s*ae*8wT7
zP2F@$LLJ%%1-3Q9RxDT-*%IxSC$7OuH>RrDvPnpeo|wiQk7!s;6>y?84j*B+k$Bq9
ze|6BzZA65&P=x8|4)N`ymh1%OX(c?}*!(qQtT*Imx<vk&pL}lN#yXgoP^U&ke at 4q*
z+OB>Vhu2|TCnl{Sl`^?jb7ktuP5JAGeMui$Mk^D^jmmP2*bLq~6sPNa*B-_|84S at P
zE at z>ewA|mNJ|ew%^3L>1c0Q at ndv!N at xI-hs5wUjhl=eP)TTF|)7Km9RYvxD#o8
z8nnU6q_LK6^5O?U8EYQ*z!`FN`D$T at 86D!HQL)-87U4W&r4Onsc^As(tn|?KmM&)d
z+EtWrA5NTv@}5UVANi%lNTj)CS9{WJhLn}Mq9kJZLJ<<;?bVz%9&o=Q|9nv15bUd4
zT;4#Un@~fEi~n&pW0POb;8dN5spw^<?B`a1Gl?#JXe$7RST_(5I4i?Z>+BV%v*A(;
z&<C_bjjU1v#)w&y3~|SN?XJvj&G1UdbAXGv+^+GLQFy7SR8y35PT1S&<pe%RQ-JN^
zjpjE_?etn5*{|*KrKyV;J|zVDgVr#l$iz`jLOU^}c*Ic>vUD?k$$sxvUz)qj7bhhO
z8TAuf{naU`hJ7SRoVBZFlv+_BF#Fuf3#r-mluSVXD!_zgUx>maqhW=YyWs-6e`Y^<
zDwU6C!b^8X_6q}R&HVCfk%@^wbXQGZ{s&KR1NJ^Ym?Ib?7Av>SX_1I<XpDPmWi5*H
zk&bF_HON;+IU07@((sxyl?j2**QVNw`=A_o$^ENsf(a!-?arC at fP^5S;RDcPXWz|D
zv>pES5oIU0d4cnf^FFC5GO^E+OM<KT9xCG}b9`);>*UUVBZeEp^P|+uLav*lbuFHp
zb*|(6!)E1x5+Swfw^Ylm><42Io56jL7kd+2R0*{@5f|8maBZgwt3MR1C`-W+mhFzk
zl7h)h70Vx>8}TNR_S4GzVr}&@ilZi6r0oeILU(pbpdKlD?6*AKkd9%=xGCfFU4mlh
z_*v>`*6U^{yPDA~?n6@;0=)(MGbS?e?&*M6*F1%w=XiSUAOZ_F2C8Aw$qFYOeG}OR
z=-~YI;x*OULsUlTgJ+>(4Az__iSdQJUSSWX1(%tm2AdMIJ~}ninwG_-mY*zy%Yo4@
zlOJEM3blh0IT-w}7nF197T6eX_q#_5qii*g(2bVjS7qdgpFdeFe9qx=pxs7I$iV13
zMeF{stV<m*7-q?z+up{<(n*K?`pD0J#xP}&JO#O*?Z7GZ5sm0a%kvd^x8=6t at 4ZSZ
z40KF=_Lo6z?zXQV9E1_`0O_B&((I7Y&ls#Czq+yP+xHenOy__g90`2DV(Y9LKf=Q#
zvKc5Erky2y!%n$tgTdYQq_3Pr*;PBi)E6~1Xj2T6ufEZT0=&aqM?dn3c^@6u&Akc#
zI6WzCdL#Jb3}rNiZ&$Z9$(?J!*cUbEW%IFp_fx+$+0Mg~)zr21)#is`zN!+6LDSK<
z7KjV+SMS8wKI@!OH5C}1?=)+V$}EkI-eR2~*5Pw_9T+_g8$ol_9IUuy&)-)Zyx|Jb
z(NHTd82#QJA;+q^$p~Xp)p8hq>`oxb?qj*+;}8&DLSSde`Bd6IkXTxRH++TuF&fIG
zT$|!XS;xq*wtFl}7K<IRaM*n8^}CTO3RJ4thn}sxT*!AmfDNfxAsS_W$Sgl;oqY$p
zq7}JfV?XIWBxAR{OYqcU0A+khKLt>8SoS66mB7wJmKfF+z6iun)iEP^I}&UCI;`{u
zkVLy$r!>ceq2tkS$Kq_Z3Q)%j*O{e(Y_iNSk3K5$k5u3br0oK#hrDe@()(Q2{Ukk3
zFm}!x&gHT2cpuWvae{(EQYBa`(pk=rw#UMu?@@#>f?e|Jj97f6zII?JC>J3XEit^y
z(>7K>q%tmHZy3%I*Qa?cs at _w0K1^?PO6eEyWIqEk;PH#Q at SM6b#}!wy!UH$@x!qj!
z(QJ9%pw&fFEolQA>yU$SB0cRuE)Nq$ffQLMFjq8T*ia!<C2)V5b`n2p*l_9~mYOEi
z0#wc at J~pyaQU~ZeDaS?>2$ANIt4aE4`3ZFn4{n^XOBD|m2&@d%V%2LHnwOnh*5a|9
zk>A<Cub*^%O4+ycFo01^N=Q{;Lr_-wJ>KEqP7qJveogGS at HdYepPd`LO(fjd$;k7w
zSJQ at m9e_tHC01v|<lZWFxLs;J`P&Q$Z+8kkquxp+Ic3f`ZYc?1n~W|iw4$OaJdqe=
zF|Vbt`(ffs(HfUhF;|zro%Mnimwq5ucrB6FF%gvL$#=`3=k;S2lifxOGdOldEwE6P
zqVfYRX<u%g-c(LqhE)MslLA0uTJ0Ea<QwCQ{U$Tx4zw#Dr><!*XRup*%lg*RX4%Uv
zM6}Z?Z!YS}Dm)XdPmq#E7ApDDUe)6RnC#=`btm7H`ci8CbO~~eN}D5+vdLjVU&Edv
zt!ZF+hLnl(*IA{Fi6JIShJt|-)rB;p87&iRskmu%bPv_C&*79$ikK`fN;oHt&kFna
z`itEho)~Bj6C+2eP52+=^<C;9f9b<|QM0r<`G~$V^m>z7OnYqM0(N*jMq2t%OHpv$
zZ0%hvlm6PT82#1Swa{7DCI0l_Vc{2TPU?O`46Q$TVpHmzjko~VCOIX`DfyNRT!03~
zsaKXgGAwVs?=+I-P9x`1*V+>WaJ{cZIRPHrFvc7kzbB<#ZAKTgi}@M4CcAAp<pG>=
zJU~?~iKISP)xP#emQt#d2lGF9TzLU-Ud6 at z#IrsOyT-NNyf}8}Fxmc|yDy|&50o@?
zzg&=dsqa1#0IWJGtK3mHC()j+s3h(CX7c7q_P7!qi*Y%1aR8_JA)Nm_w5g at 2Ma%UF
zmIpR1eEnggeVq>G_>9nBj}$`ZvhtU%GC|a}Mm{AM`zK)~Y0(JL)lN%9U<(MeYcCO7
z*=!qGnLGANrrwm~h!R~$fNiE313ro^#!IEJP2KmBA(G!af(U}&2S2ip*O7Yk#S%rj
z<wh>=<jGg!HRwB<uNz-K6i<1ik2xcCA%+h5&MLV2C at 8I*6k3jzc5J?Tmtzn-R$B5#
z)E0xA9r8>s^wOrZ*+eLkXHE#7n->@Wl{<S0Hs1vB(KAJjhF8d#(`)N{_v4Q|VW!O#
z)?hlV)n8Jm3xFtH at GYR1VZLZQ7&S^!N&97z7S8q5uiaIN9HOfa_ty~p+Fn@))p^ov
z9W$!$&Gc)XbR8?+3XQrb-ZG3n<A2l`_3ru=Rjkk!Ia?=%b)G=Lj|RKtvxujRR~Bha
zhcPk>%F!E{lJ5ZT8<IpFR=Ly at o8BuayEj?^AIsu5c|At2Yc(hBGdn=X9Jx3w&VE at A
ze`07hiYU8n?(J-r77`opy6YNIkVpr86rz=kvO8?6k3Nc^Br_NIIRD9&s%Wqp=8 at u4
zF&A#H$Bfrn-j8}yUYMM_>KLTAcRR-7bI7q7NN$<rrat6(*j17T7*5T|6huk)S|R4g
zxS)+4GD={3s=UeQEv`r&JC|WxEiQ=BH}l95?oI`Kz7NY<u*+2pLQ}dH-x^3_G_Za8
z_?=<|-B??B0=}Q0<7*T0(neEgYLFqy)DPwYF-_Bo(yPqK0LA#sxAmVA@~vvW9&idb
z04W?0TvR>i(ge8Sbz}s#tQPx?l9TfZ;l59;4`+s5B1DJLq8dDegvCDt<R{0THBf?D
zlNJ0+_FML;B)2Y7d6vi2fUCZ|9A!s#mu0H{o3PjFwC1<&;%){GYKcwhYHRKLk)PBH
zhMhW at h^;H9vg!CHo<rg}lJ}J2diL?I=Qg7hN1n at p7<80gtLg;@lrA<fge;gd^_hn-
zkC-<s=vs+AepqOB4p5$*W-UYMI_zV^n<uqFTD)C#++%ACV#^7&3Is(`A0mk8`vEk4
zkYj^Ij*yPe)`;<OVX;>aOMUPZw$1GI6T&j|605&(8f8e0<=}S1k63rW at fC7CqMJD>
z&spk@<IK>`&u6^$NwI6l|3u^-{7d7wh}~jEOp-(B03mBl%moAiU8T=bZ3U0r_|2gb
zW6_8#D!<&lZWGw0n9u#1&}F{=OJrAE6;B~8oR(>@))@4#7V$3h!8NK_+qtqC=AGMR
ziy at kww*M$dn;GKxv@%VyQb+MF_}QN2xf+8%eHd%oIrpD)j(54l`<x>O_doW<^4#4R
za at Y7DIY*wm2NM6wrw^;X&PLPLBz<iUEW{D*z*)?z9+*KqpnCAekM((2cH;Lg8s*|7
z%y0>@?>Y+Du#OlrD31`^n(3iDanG}fnIDA(JmefQ<3pmj22Vd%c~M6t8Kkw*=BWO<
zE%FAQt!wdJmtVuSrW3$@QkS<mEf?pewr at o}Z+4`qCRLkaKSaL;-(I}XwJ6>?COuqb
zNrXt9o?H!9z_-vk3?*b+uhmcPmTC&m&i5aCh7y%V at 83-R&=(KknfG*$|6)1rdhQI{
z?mU}(x8c#4_>skW8?d}dP8LAd2<txf{NXnfK8BMx+G!D6e`}AE5ItB>!?T~I`y$@q
ze6i*Nd>(eQT$E7lI9_kCkLm5zNN??N2?F0X5sG<bKNMn-vOA|50^fd%Gh at u%Xx?h}
zI at s9&gRXO|jT;+gy+Il)UcFkKHSMzSlM>r#{UUSPzg{k at SZ%Mm*5M{pcYblE at ba{g
zqLeQxPWRaCE;!vApJt1<VCkn#dXhH8xZ&(Ks_69E)1lKfPfcZzh$UB-t**?=;hYL)
zqOhW|chi4q+5}VFfc at Mu2@)we0D&#M(ANbg&(-}#nF5jECnq}u?|z6^NhR_EOR1c!
z&M=)g%;i at L<@@(OAl2#3PG at N_I`jNF(c?5F^}=;&j(7I_=)kB-v)w8X=bH0v!hXW_
zmRVK0<#C+*`E5>f$oj;J2hwO|jp&!+{7!d9?AofI`T23qo!-^FWw(Op0ouCB$zAdX
zPIVxiVRvVM#hcuT#GG5J!NS`q<FRic#qdWpU!>hMUC+uD=+(}#HYBcn*%(WKCRCom
zFEsT!o2~9DG%Yr~<fNL6G=V?c`@{p=vdQbLo9Zo3ySzZK^jBVxS5>(I?(lZl<V-v*
z$wXv%(Xx%s4o?w31vPjh=I7f2d;Z`}(}HS>=Q*)6h?5-p1w)64+5m&BQv0<{YQ_TG
zdXQImy&^|%8H~SaSEN%z`uw`JZ*0$lq&xjI`^kF)CUr&6tWy0UlZ&6I6qUU&eJ%Bb
zKGIH)FE%_|<-{YU;86?lH;1eWJnXT2F9g5NCVv#$O9OSXW~`1fnH<Rz%1fNko1V`V
ze$&g$jaEE&vk20Ae_K61H|!@m-w2$a`J|;*4?AHq#mum^-!Go$J~h{QK?ZEFJy`2H
z+3hdYF+Ox|O5SU7gO30B>b%&%GmxIyz77WGBuu>Op9c*s_%RXIdpO2Bhh;-I`zruH
zu<T>{^M@^m8i(7}eiiF?r#97y+X9N>`T!Y$$vPS5Pu~n4otwyi&*4n%0}su7sO#dC
z^q#?(e<wYvfzykNq2H&nr!7I+-#GoYu-9Gvz|%LQXc3%{o<CAw!?x~V+nI35+DFSP
z)sGw3$R at RzJfL<m@#Q1iWqwt4NHurDaK}4~P#wo)qRQkNUV27=a@<c%7`W_)Tw at _c
zRq&%ua_;MRIX=$_qL=aT8&a)Nld73#3HVV8S!8tzSv6p1rlS*~Uby3_%xl1c6ph#D
z3YI*-a*U6C)KDpAzj4YHU7Koq<1pjWW+{9hr)Sx=tnBy&V<xc#Kz@!3$5nl}1n`(|
zZigXGW9&dQV577NKk>@|G-|yWwO%Lou~Dl{HP!1exsP)TM~e<q#yh{6cpKT-o2R^J
zNp`Ov;V0RipxOwt+W;n$)}32&9sWQB_GD<=56A|HEX(w6geAX at rKe&9RR8R=op~j)
z#BMWBC$h at EP?2K(rP9Q=F`hPf{&Tz9`K3v)!fw3#%UtTRu9TY!D^dDKkpwggA;~Kw
zD~}miee5?}{cGt$n2#8bNsdV1#DC0xPbTaAJqbyr)CNXPKEOCA(~<gmVauedbC4x_
zD87LuI$lJd{nD5uFc}##WWS2Pjt?i*RaH1~t#jRz<GSOj<9#n|&+dEihhp6I((^93
zbgkUD0q}(n2fon<oM|}6j{}Sk0!JIALXn|<aEgTz+*2qN3mOU~ho-`b^Vnpye;^^M
zl-)qAsSQX7Wja=0I(`MDyc at vMu?#T$W_gF|Zz!UsML92}Ki>BIqDs1cmgW87EF$#g
z-!uxT1yNEJw3AaYbr66FsF)-paL0{;1-oG2jF_qyN};A9xepEjt0obGUCf;u_L(eA
z&SPhMa7Hu!gV`kij at kadnf(mp_X%kI`Mr>;5NeS0a;<TLiV$rOJ%sNF{TLli=6<zI
z-CP|kN^@`q9AX at LE;;HM60;G2;mh`)7|w>e^hFHLWsb)Nq3|{5JLr at Ia*%W-DMl(L
z!YN5H>W;vP;7B+{fs?>dY+MGfn+AX+P>f^3Rpx<rQKg&)C`|-GY$&a at j^r0A&zlIa
zAsq>i558rSON$tZh~1Nkh?Izlh=hnSoJ>We at 94f0&flFx!aI>_>H`v9B<mYDpg->l
zR(yIOs}_y~dcG3#oY*D&8HgQ34PvLd3 at r10_&+HaE(6QG|14Cz7%`x0UZ%Ei)-&EJ
z^{~w<Likpx<e(-x>BOdg4A1!A4UhePc!>t28wml>Z}wj1|Bq@;Pwqf2oJ at O6;j}TZ
z0jGpN`G#`Ox57KVJqDpI9aEFLgyMswmyXTI(L at wGHe@-5fIh&sdkO*e0YiXYzyLUf
z1BdR3<X+z4e2;|7`#sB`OowX^fc(VWpV`JPB;gaGZ~C`l`9Elcnyg=O8hdH+HNQwS
zcrwI>xt`#E-0uT5S-vVa{M(v~5)Cwl*qGN7;*R at 6peAdF!66X*xNdr;a78srP#&L%
zvKugS<wElR@<ynlS^8mc^(*#0@>0B9c&&w9p8*Ggznkkoevc^sfhu>1f_0B5|A8v^
zTo$_%OgO51_O9HcJ(N0#IE%S>dLa$VCr4|Nxfyx#u<I+Nhkk$_PIt?md%x^|F1<9=
zWV at 6;NY!il3Bi5I(8bkB(`+T`4+*zlk&iT}26fpl$;TR0qlEnNcbk1T- at PWI|93IL
z_Yj?s6l8`;0O_D-7s(KZ;9Gw-PQ5_xqMv~a_#e1%hc5qtFu&paH*&&N`VKi!?vQge
z>kwU?7-aT)e5!Y~Fi1--x5&s7hC5h@*bc;j=GqCpn&a$L;GkqIL0K8YC%{w0OOUg_
zSM{b2AUgE%vDE690iq~2c7s<<EkJ~ehR2|HoqUIG2qJ9o=^noUrcprJ?BQnM&J_GM
z3wI{sj((dA`JRMCHX4HuO+mmY=+h&Vb<7^3ATZN`77rX02v;grh>748la-1hxZ>X9
z$vw8*;mdEV`RBpV{vHhHAA_lU4ASVXwIjc{#OESrgR^}M3z!@RfvaiYO~wvY$p50C
z*`e~e`M(#sqFpFDz+a0jnB<VQ!v}{M-ap_5<M^=VX8UN2FRiOqZ6#*we&z%#F`2~l
zBE2p2(|mtoLpU5KW-q#rxRiioqFP=|-OHEZ$I|gA0f&-@)>o#`O>$}Y>O6uzg|80g
z08u0x-l9nJT{BTAKD=fIy_0DBo6+(X_TON0*YO?r1l$8r-#>uqZh`;GQz+u$?lDpV
z2yXkyHmF!Ju^=5aU|U@;fo>GYJpgX}p%uS1B==rh0&h5my!D<?8@&Hxm8I at hIg#zb
zBA>UbmLVL-JiU&N#`sz9YbQsl{D02$Zcx9dK3_je_eMU8_;mAVN)+)@+VJ+_|6k)(
z)*AjZ1K8bE(L#K^D?xpS4-Zor(+D9^EBe%XU5Ci_g#^5Cp%8kd``hT<8$dP=1Mz!-
zzGE3&pd;DXsa6wI7 at Bn#`^vigAuaD1nyv2C?j1E;rSC=P4(<Os+27DW|68E{oa((m
z!>4Lc(veX3KJ~%@p-DFs(vrY+xV8gd;AbE%&>hi$IN-zydU{XyHt??_`mJKGfcl at o
zh4$nZ4R|1;d(lq4ZXXf--xoB^1X)+*EbO7GH%50DHDuq)w?>5uKZH6TZq<Kl+;3}c
zd~eyM?ybC0lOHe``uK=;{Ywu~65CS)!=_KbR0F2<gg)rE_g?w`RJ{Wf;x4#9apzOO
z>CV6S)8F{*d)%r2dw7R;>i>seP*a5|2DM!HJ-mPExM3<{fnzzW{(CiFsAAAC{0;ca
zhkHzM{KMV+jVY at 4ZYch${6In^d`%^rdhXQ#1+E5xH&Iifk1wU)-^%==3ch}p;r;w9
z!ta05$k+TP(SUA<4Szi$__*H>YO-?0Y2=l}*BqK?@Op^t*?L0MaeolhWc7;ky%!1p
z-_*<}^2inVbm>BJ=`i>c1pjk4Jz}^bG9~EY-~L9+g=7J at NEB13VzwLvKfmqY{%P_0
zFH-Rvga0ZOQh!Lr;==6(rT)@vRdcF4k-6L8_OW9jh%xRaZMKO!ww`wbHnq6HRM%Ab
z^Jso at c2=Po>ZHS01ohH%bYt7Ux>+y~_PA+R+-sUzbi1s+)?%yL+?uc7pEy6k!Gz}&
zCO%Z;a5t(q>{iLt!P=YJZZgM>#+&sPDlNwPlX87KHx({sUz3faRyEUJ^oQO2+8x-|
z at qxlGdjAD*Cxr5DPzjxV9GS?wTura@<XK|0HPK^z=l!e|t$`W9uXY at l+k6YC!z^i!
z64|6OFf$pgOL3vvO;}EGY^G|8>ux6p1Q_tAoj6ZHLt*k%?=M at p_V5KqUAu5={5Jj0
zkvtvFN6-~<#MVYZb3^@P7a&RX4OOlDQT)7Q&3Csh-wNDbMdwnF->gHtrLz|#EIxmg
zt2y6eEo}oWT&5nZP1l`c#$U|rCmPT+uI(N(XV-93Xe=1e%;z>$eH)`GdRe6LU~yJ3
zTc>Nz^!3 at B+pV=3m`4WDw1xYO0o{B at t!N6S?c{>g#r3}Ti0I&w at O7<6O|!rIZK3)0
zr88{t)@HU7*1Vap_p7{$oA2b at v842s_b6t%&iwFA!BPITa8-EC`=0zoO<iHL62)$S
ztbEZW!gxKc;<r%A^5zaVFWKXZt<!wY-UGU`#WNIym1suRkVBcn{HmS959Eq?`&IZW
z|7?5=@>+-r;Fu*&(*)0Nx7jaYCS8bYg#HG;XZOJOAK(Xf{y}$!;y1wjAxhM6fMGwX
zq6>w`L&my%l+^m1onPJysR$7TnZo^^KSS2L at Rj7=_hJ0Q7ea?a)V+uC^xnhx3`M;2
zFk0R*9mW;Fjg88uQUVCyLEIZ4tzeh`onvr^>ZbQ5?6+^9`-j7K4=3V(z{zi`8gcdP
zBiu(Eg9oumSMc8vLowl=d5m`@+`sz^GI8B|JO6 at Ae|Gg7nf{)*^<R21+Mx=#`QH~W
z|0%jv{6}<)*@e^v$iuU}R9xdbFsBh=3Ha?*eTDe^b=LnS9us|1WIB}Pfd at qdOED{V
zt_kSFQcThR)arb`P(f)@yZibTd6Wwk<!~f}zK(=)cqZX@)*$shgMjr%M&WmM!SCL5
zN!-fpP@!Kx`|kbxB%<RcYFP9!Oj`0bXou?2^|Nws$&-kln at 1y}PydF-hdWdV*U!ql
zkxwF8ZXOMbBEqC!-Tq%2SE&ehvZ6iI*dyc?lv at Ru_@z-Gd8=fER$0-OYV2WhY09mf
zO#H7=+VfV42zN50b=26u$muJ$GBNQ>p=9N);uBhBM$4<Q2g^|^w?1X!mqhW+Tg4^Z
z$%qytU=I{uP--E?;Fmx!W?02UvC4>KCSdm$Ph$^Pu4dpDN04AxMMK$1k0c{t_Z8Py
zYQezZ7ejc)u!@9Yl^%&n!2VgBQmF+IgZ~u*5<~Va%4&1uB at y23n;DSUB^RSN>M}SR
zM)E;4X3G at s`b{!O?1+oe3$+!TeNM6}5;J3p2YaIp65HZp^hC`7XCIS%5Q*tC#e=>f
z1&OV2F#=IPgR}QZR)u5gO!3a&%<QG_zWv(^oAlk+Gyf>wftr*>z~~3dt?7)xlq-7t
zGz*=Mq0%8PQ1TT$VU9HFh10)c-eRoe{vo4(7vF&bBX`FaIB4MjRWYy+|Hn~`(Q3*D
zGS;En+ZMRDh!&La<}i~uf&h%N^4D<S&QboIM);jl at cGaF865l$6tvEWL63Ji-U3w%
zggd^#K{-d4Y-O5*^<UKP|7F(ioxN46<J3>WX}zNB{0I`MSH@}h>~H{WNw{w?{!{sW
zr|<r%e0LGWy?e%a at 17w*5sx#x`Cx%re|td;3(WBT-xt_~D}$W$4^|}nT`LCAKV2x%
zEWB_;WM?JDC7#R>Z_rvtHYC5O!~;EP=Q}h<6&bv9o&QMZ{j(ur{3FEwiy?~o!w}KO
zU1XEOlVBJJvi9Ma at CxUFtj50t1swk-DELd<{R)_mXi#wX^@raOf%yz at Gi0(NKmFf`
z?f$I~oarrj77=vwXj&A0h{uQ9pkGu`*U$dXjsLyy?Y|%B-GA~Z0^>}%SC}bLnsz_W
zkRmQM%g0{rC=8=oxtE72 at ii?t&yXYze)^n|+R+ybXXRcNrbH>)(mX at LxYSG^WwoPV
zjF-y2v`mSTv=MoRkK)KPeZ<s`0x`~%dMPjxC1`9J46*%FGd^(;9Qh+wEA<d!B#P50
z5_dBs at FU=%Fdg|KIxF>HV<d{vurU~-_@}0SdPH#a8S$l34+=)&D;it|Lj-^F^iK!`
zqqm4h1~S)}LEdz`VCw6nSW({t)6r|Ja**sPSCALoAeb7OL?G(BWI77NasbKhas_$P
z6@#fylVU}DhfPPJSezi)HLf5aT^N}9Fo{6Kx5aey9P21ecAhK9gU;Gz*qQ!sFL;NY
zpIQG|)NkRRWzLCA at SW!eBybFQ7SJrn<3oH7UmfbFOEe at fbUo|vND>#ibF)4H at 7=7G
zluI=a;FhWp9AiWXJH8mD!e7Vn!2cUH;XTdoy7-w(3h98?0$lG^#db>-4yXTuTmNN{
ziU5TV;z`=28XjTL)7hW!>olYo1$-1d3pkRoGbPtvt=*H!D-%hRSKlO2J}UPzGigfE
z=H?j^!~rvX6x1AoG3b<gpD}4l(gx-k;>BHM_z0^x1R}00^^jp`O3;`x7-IPYGd{5p
zIQS#xDD~iDXo}NFGZ;Sfze at i^LEzwv_))0`6GKyshMvI?*&mqx37f#-Ga{W*4-$sv
zD;hKg!w3E+&7ZD_RBjPhK(a6{8E?8ZF!fcElc?{Gsme80CP?;}OU8??6HI-PbRyzA
zXQ~3jdJmG_=92NG`v#^yNpceL9WYgaVm$@PLbzmrbpBxKgQOE--v(2abF7s#*;$Ip
z`M<jetX{~9Xx`ateaH8tKKq8S1*+$=Ek9fLWFBa#b0~j%WSP8h&DZ#NFLwi`!j?gJ
zJ+C;l_oJEI#w&PsGeH%9tgI=(%d5=D8x!FDb at 7V0^YZD8TiB`auv>CdQ`4!J?(ykW
zU9*8mN|Phy<i6yIfy()0$2k$=Tb2*7=r}MKgT>NyGNaBb{=KfFK+CyG!!%6rM5GK@
zp&!3;-K*aq+PSjZs2{dX|2Ri~|I%^$H2m-;M>!Pk&UV6hBLZD-S2*Az3?qu5p^Ret
zBYz0bb)D+(<Lx)<haeg3pT2J(?g}nFckgy}hC}tA8IAj2On(1*0{;c&pOZ<yL!jTu
zB!dS{3)}v#`={cm7w)Alp+p<ChQDj3Yk$Tscd^UA#xH-E%0Dw1h{y1Fp=Z*g(WCa<
zor4Kas(N9YBJ)myPxaX(E5u^+$n#R~2`eu&4=WFTy4DLOHz}0Dyj23i(9CE>wX$G2
zR^`^`Om31WU-DM*2(dDwMb*jz<@S_X$T8d`5G)y1u~0%YBH0Ma{KZR at S_m-Q#1Z5f
zRv)5ZWkgaDl=+I=D79c=xQQV!Gpr(`gr-O05|n)wXH{xJ#&COufXT4>00k>O@`fn#
zR(#9g)fJbyH|i!h`-;R*G-l5<^7>5yNbH2m+zYiAoP9xpEE=<58VP%20TSEcGWSHy
z2WOv<_=&^}nMOk2FoDEYxy*s6!Qkuz5 at eB>Cez6CH(P09b6n>2zSV+%fAP7R%p8U<
zU|pO=$ofNZcR|2fVRaoxvp(RuBD2n^c@!$5r at dCRwl^x8F(MkRxxcbJ>cmO}kpIq7
zl#t{3((_!L)*v%}v!2L#Wb1Hmt$KZX)0jA3ZV%L=>2jG+;a81xxLHryzb85i*&jN0
z`2P56Z*ZEeIE!e1!S+j&@WvPQ?=u3a)?Z?Uy6Q!DVQ%%E237v<y*byal-s6Hw>mxC
z%i`YD7wPo6Qn at FJ+S+e@ou1gQ7D1{7X{Iem-S*BeDuB{u;)^L5yX0luE>&c<4fQ0e
zzOL6DbPlL-M|j`lcdN%ZT>%tfZ7#>j3g)?NQ7^pDuF>jG9^2|bzj5z=2H5)pYCzx@
z-}EB0cy1M8s;snrGpuR4+Ll4)x~HeFPd6(+YJ7Lz&8#Wb>k{@H=N&cUP at H8e^!l;C
zHG2(C;8LrqF9o0fQ1@*b$Ix5b^I9a at b)Ig8UqqOt&ph#Mf5Oqzqjp^x{ijCb>SG0r
z4$;|~OQ6B!=CZ%}X^Hx?T~M@@^fusyY=aXR-4WpC*krpwTg>HkUJ9GWdf3h=<X*>y
zJ7mxN!#>x;Q=mZdbdL%)8f{TnYCpdrEiHI?`a?GOwD1~usU*or3gdk?QEUX-E<lgq
z=fU#L6u;32(QdllNQ`@NOl10>R<vW*$Hvx1=z1Wq47SEwHvr7f2|X=v`j<Djtlo%}
z2wp+eMW>S`L`M%$qG6|7i|5Zabx{JkZXIrqPnF0mk4Gg3Etv$gn3eH_^K!ouZEY$^
z#|H#eIn7#>T;~`MHtZa6K6pVyrPI;x+yBDY(@XXUV&2fv{*n#HrH{7q#ZA8!TRe;?
zI^B=bwnyGb=o^jg>@JML;%K=wV7_`+wKjXW;6%f?d8W#l^+~Y3I#!JQKA1IxgWRmB
zZt|Nz(L({(Z2JDR8N%8{i at o8ah2qo4tLGE?xX!~`$+ at PRZ#_NxQuvxhUzEK=8+Q>s
z&rO8r8#0f$is7<NNy~`t(zJe**Llborf};}{pECnfw%KSS*mH8_wq_fK!!d3%OXnF
zsc<eSyT%nW?f%SR=R&}2R7 at 4B^7?aQ<){Rm$4BnkQ_b at sbZHi%^R-_k`kw^oT`yDH
z6CZ`_hnM at eUM%9MJI${}*6gGhqsCNzb2PH3QH3Qj4Hb~5Xkx#=*fsK;`eLQjqj-*K
zmlciW4&8ddtN?J)TK at P<$3;k{M+4PFS0KHa>Q)zHZvug?QE8LKZTw3iX{J6DD;|RT
zi5CDAn%ySUr*yy$bp1{=N~b2R!K>a at ExX-x={iMYaH+3Sjzru1s@<UV*2ds+a2aE9
z4}O_$ua63YaedcL7D%z^)OXGBN3|YKW+~&U%5mV1k=<eGk7z~a2GW at DH31-YdB#k|
z0Y}m^GJV#uF#Q&bwq}F#8+*Y`)M5<TzJyf$A at MO$ll*8(zRS5SszC&=X11$ZddBGx
z-xt{Q*IsM at f?Iup$5 at WJ+b52i$5W(%TW6;lKe{h30>5jXcMd<!$sFNF8<!6mG)p(D
ze$8Vk*8k*7iMHKTT^I9Dj2;Ps7wLe`<!rC!{a-{LE4MagBZY+xs4n`|P#e)v=aa?V
z{spQt{t0~1q3i&{CJ{B?Wq|=y<011l_fs1{#KcA1Th=h=O_8E7)AQ(s29D)lOcNLi
zzcd?xK%T_*Gf at NZ_v`khR&~epE|I*WvNk#zVP(c*NiNrYDvnJ9jf>9slZZeH_!$Oe
zkdp1P3Q?g<79}@rB8Cob#?y3??UJ+_3^EJGYJE?ugZ5;PeL}0vkGRG^1dPW>ewq}k
z)&xF{e`cjlnLM<t+})h<aKaZG`_N^!?Yw>j6wfNT?fj(qEjl}HQoj^`gos*pK3Rdu
zTxfu5^@sgDwr1;>;jY*AR(sp}py*ghTeL3Z_0}t|-JTco5ek!NdZcRWQGJD3SS(47
zpXS~xgn3Q|i6K-|bZ%o-gsZ<eq>*2<X#;+i)*}wsr~aX at 1GdlIGaP1_9X29<^8l^r
z&6ZSg#8}Bff84pd?)zVPKx9&5^UI|}WtYOz??2|&><Sf^nTidYr<|7Op|6Zk!FA;!
zD+1N0i at Ql3u)~mnz$3`b^TgY;4UJR%uMx<0f{YZ7bC8|e_SX`oT0IHotKJ1{I;B>G
z?RN7Bb$gMxejW)|^TVw=eXFhvt9<s0HJ7_-t2@!hXLc|IWC8TVIekLWRQ&TdFY!oH
z7T^#{G?}n?Br)j3bnrcnRU(*X0+- at pHdg((hs^iVm`fd~4)Z&f;<5}ap;liFn%4O5
zj~l2k?BgEo6;z at k3&)+thIe}@0VkbBdwS~|=We&0?NgU+k-nh?FBdWweT%8(37}c%
z4v03gtfj2H0h*_=2u|yHOhKD7RGs6F6es()vKVj81vAKws%?(_)&XjUiX3yk%P~eM
zJ>2m=X|EyT<3a~u{t?e3hs}zku6h><y|0s7H81)%p{17;%~S)tisyoUEO20GKw<8>
zJl{;vgjJRDUFTL!Y6JlU>fR!G91C}PhQU1JHX-A)OjMPdPD=o6p_H|Ta|7H|^V&Dx
zH$<;KSa~>lPT<)u8y|Wu<1O at GvtO<1d3lr1CVQ+O<a#a_S})h6_;~SHr+MHU9=*C5
zZ7pvm*(0!qQP#6>8`OU&!8Wo;lP3z@|MmiY7EC>r1q8vss&Z;fD`I}X9Y%ZRc+sz=
zO_&d04qZ4~cyqq&D6cn*lS8MXQ(scv_tVrv;ua_p+o9~g at YXa|YNAb-^dzKw$H2)l
zZyUjRHr4W2-M6r5Vx*8vrTx{UE>HY&@wxPJtEWAI<Mc0-q94RZgBf|t0bi?Df{eY%
zy4c^Hd$bMaWu%nh_~Swr1UJib4we=Knw){mM(t!{&9+ at 9u&#s2y4j2k_8cFbB(|^V
zE)YiR>GSx&mEvt^X(2h6gyNprrz^#Zq at 173r|RXHXj;;dWtupuOKdpDy+%~*nf8D(
zT8|JVl<-EYsg2=gin;>b4$mM9otJYa9^YAg+eGC01H;mDn;n4FF0S at L>jEsItn}mt
z!VKj`rrj>~)iG{(g8Qd7TjIr#qe(qg5KPliJu1GdlxOXWb(W{*Eu3)iud18ny>qt{
zmvJFh?ZS05)3+nzv0p9MwB9{vB;DK4v!1{*`8969+e5ay&Uo&i5pR?`*&gEbBb|&>
z_lML*I9JnlMi$_q1OHB77*El5CP4|z9cXt;4I{sl2>fwd3_Ef>xz2gRQ#!tThN8(j
z-UR(}m`UL at cM;Izj%Fkex$V<G;#rt{SAR~nd&CE#dQGxOfpJ{7A|mek+^#;4(uFQu
z+O6jO)vsSc-R-PsXZCZ*HnO8kRIuSCY6(10VUWJc{A-JFTY+?(HTgPza#x-dTT0U<
z)@O>m+GI=Px^b*@276?lUr2c-qVlh;>y*=klJ?J>VrtX?fxECCI)wsA_6M`=ZZthq
zVpYwGk?sv}^%s7H at rz+-#nHjlH0Wy$w4!vwRiCb{8cR5Q%(!{V*=gqdX3)u#8<`|$
za<Af}!Cs2D at U0fTqemb7f$`<1=7~@0ot!<MdRYos$z2mzk>AV+3b(fz&2)2q9xh5)
z)NVENk{cjW06684VKh+QGS&zLKtF7M^evr}`!vyzN(Xr(I6MMAd+98cyJGo369ZD;
zo-)mg_BO}I{JKmG`8C2WMbioiXGc~r8;H`%cZ at q{tiOUo5i=riJVCNhJvEYgB)J+o
z2 at T?zv<F3yoz$(ft2)+Z;V`b9KJPJlS91dUj_*m0l%aOv{(4OyC3~OuG`DXOp=noO
zzRy>5Y3oJRlBvhya_`UUw*(4CS_MZdb#nNIy+^pV9~BdByBc>|lLKI2O%7avo%R<s
z89#KvA#51C=8;#$4lNypxr4jfM`M6e-^*)Y{p8j0^~@e-V3M7^=%=rf9H4S>!X7 at f
zD|K6E{?D-Ikfk=jd{Z(%zh24rvfFcwsDKH3h}U)`p|Pm**G?WM{Zf!i9?TJaSoy0d
zcv9ZG^H$_})85hUOUpCEx9=(T5~&QDQ|6(K8B`merA;_uhr>pE>F3|+DDJve!oX<9
zH-XFo>^nGshB4do{?ouf{45AbXtrTL!mGAwrqkWzQk)9CfE2U#uUr}n`H;bi&5ap$
zriXmTnzZkV`I at fZc#6l#hrV2Y1dE- at z!cH%D(ubPZrNH6<4*n7M84;`v=4q4yz4Ib
zaoUQkB&$I9*M13cd;W5VL<7m`*?|mucf$Ik;ezzU$#KU**_Td~=6$D?^i;`v-!*ip
z<~dpy>wu2$EF6mMUpAiVNNygyTzbUOLQyj)rWx?@(yu;n+r~b8mQQNK-QS2&D16L7
zB?i73$7#UR_;MFi)9;|RebqI8gVdB&u*yrvw+~n at Gxp>OZrPZ<ZoKiJIBVuPPAjwv
z*lTwRp}bv*Z+M|@lIqc1Rb`}xn>#Q}&5ymd%$=~D*55AS2csxhe#o0EK_T?u;@3wA
zzP!#eZ>J_`HJy{n#4~K at if>rfEwn>tTf$uLG;HF+&gBZ~T!Bs{CrCWcZ}-K=?O+6N
z#-09ZOiEXKi&iHB)#BK_2NM11r~3~V&+7*`3xVGDc7U$@;z?uo2;Ksd6eBCV915Y!
z%0)5vPe}68)BRBsdJ`&|&RdM7=SY((3;W*Qqaap&So<O+0P|q|r at et;asQ;X|K&4w
zMBx*%>f<T3_cFyeUzSrKX at l1Ni|Y_;`M{wOs?wimj<*?&dyvYS4d_w!*JWe_szqXQ
zu+dn!{XC at N2WcIIQOH~5P1AY29iO7BrXz`&?fhaHIQNH}pvCm&@L9yJYgwd`e~K|f
zN{-%Q at ho7qtZnYs$408s?mE`^19#J~=)<*#i<BQc*wgHUupI0p at Lm&$ECnpJI$|5w
zxv2M=Jvk^2zJz5Vs!hP!O%G!CBeA?|9=bJE>ThVPhttP<a#*|!Z`zhR8uaS at K8yR~
zc!u(4AeSz^k~wKjU78SfDJI`uEcxO)k4g^*AZ(RzB6?egT8Fef&T)R9UGp*$T{Xgb
zs`b=QW!7Wo`CxZwdmUeu=O$UY3{J)4vkw<`(U+0LY#yX1#-gJ~>F~Q#8D=Gu=Zg#|
zr;gUj1UZGiIj1^P(K~eGYL2x}P3F4)JU%9473~?)7d9Z at Thh=G1=>Vp&t9 at byJvYg
z3FUEMy~f^N7{H<$se~L)v7OGaofS at ESy3cZ@p1||I9L)duQ_`tL7YAC=xBXewdzjT
zs>3Ogm&02ekh*Mzo^$67o$CE4FWfB_^lLwh4SJ&(z>BN3FV`mQiza3GAVbj86O_Mj
zeLO!7t?D|i5Z1Zc;Bo at S4+dNw-yDD;oA5J5g&D_N4*(>PVZe`5 at vv|1*le3qbWchY
zCC3|ejlBc6YN6#P-vuXOp9*D#dy))EyHBB_d||%&=m%nrWm)mg>Db#bT>UfwpTmq8
z2hwXNUXU-41xl2-0NCBb+-^<*KMvD^wrys!a7RaoN5Z+!Q&T}K`f4VH2POlbN<gVw
z63k^JxNT>ooK2%2%oghzum+uf$s3_o4&YrujEHvaSl4r<XAJ;qjVjNyH5!`sxE28V
zp}ek?$vx0vd~5|f8<F8M>UrSjF~AjiMacwYvFDp2uFy|+r0=C3CoPAA&Rk9>lRpL4
zxv^T_`fqM_ at xSO!ruvwTRwT?cK;hv6;J2SU<@4>FIJR?}>k)q&AtZEl(D2QkcvFT6
zbPy2p`SKS@(KkOav)ekG8TWK!8lOVXVceTz)*;=uF?_AQU`J5nx4~XM`m<{j1>Pzu
zj}MzGga%nhe);sYXZ63>6}H^X;Gw_z8hh2 at Xla>93BCj`3#ABN?Nu<YPzmr|gwVNq
zv#<~W_A53ZtR1EMYbH3buafel-S3y{<pv=4l2H*y5gA&V2K=7pQkS$o7^6B$>zeJB
z6p|%2iB3hi^wHEj_;&f=DQ at n?=7`2^70U_TjtY)#I0f%ubyT%yJiGe9%tlx+jw)SX
zO2j(|+9cSt1n7(3Kn7kD2qt;y?om5($aCY~;D_dteTG=ebn4q-nrJvJ at Sy7FY%ur?
zc!qTRZk_b#f*owhI&7r0a!CS4ViZHGr9^dS0MzaEl}=+eSZ}v_O`MW{TE4-d`_Nr_
zFvAsPly$usn{o~;iAe+Fg10W*J{uW;M9pg=X}-(@nTG{hE);jc6X!!+yDqLO at 2Mp9
z(cD6MZ^-+iz8LW00cw#vQ{z2lFnF`u+hN4Z`iFH}!}!<J6EkHd`;2p^1eFPB!95e_
zooCJGQ8 at b9$D3m7PN{wRi=!93t97?_v8l_#JPk}sW*5X=wi`Q{&AidJXbL*mn<FAR
zG&|slL%X*&nahzsv3QGakRg(25?p~0o=MxV9oP(gob|pgqkO<i&#=zAr(c^q=*GU(
zOO!#^I+8DSN)kZFv%%laaO20zOCF8h9G at OlsP{uR8jjA6k4<l#++nf2<lU<6rxRs?
z=>7e`wHxcCo2RA;{a190KXYFw(jTVe?$%WgRh^~n)ooYq){9<4=rK)_9(d7vZk+dC
z?rfUit-Z}PbuA`3Vm!57J at L2KS7zfWji4O-AbL4vCG_F;fl~e3To25#`XGZhi_{j=
zujI8YZ;_QlRo5bKqP|{#j|N#4UODM}o3yLB;T#?Qo_nh8VL7RyJ$))SLWLJ@@;C#q
z39FcxQS_*8866s^(XqG&46K{HWssQls80ouKGNs)7PO(9iF;^&`E!2|ADzfLKCNFv
zEpUS~Zgkgjq2S%El9)Nb_2P+g!k$Kv{5vPD_>^4BWK0vcU+x&Lmd2ya{gbS^Bonqw
zMB49hVP at MMH#av%N#~P|RdRDf;-khXyvsisAn@&;MncU6MEeE<n^n|c_;Um`03FOn
z2i`&`ec#m6Z#*l*ZoN=@SgtFoY;IgFv5Mhb$1W_TN{HV4y4w$)>{lGG`u3LDRunX&
za#-}MK(<h9?L%}zg!N#Djw<0M2x|&j+1%^Bk>a%n9P3+TGr(Jc9iqtw`v+z}zc at K9
zs$}Z*+EbXD5jALZr at O|N&R^f0-!CSa?v)eqJaxF?n<~upY~Eh?rBKm!$!M+;Ik|1e
zb%jca-+pMlK`5=YjwAD!7Ku1pCx<l-uf;h`?Aro_%~pPDYc<RA+?veOlF5yAq=}U*
zmkt58;)WV?O}5WB;?ueDL$@m!krWC!wI_A1T+kGQ$7C&z%Qq{?w`IMKS{{NATo<#Z
z+w0m6x9g28+LY_AFa&#Jsf5`ne~t!O_YGWKO<ldzb~DsGFLNAo%IvLaE8DY!*~@nY
zc%8=3@$7@`MV;H7^rK*SS0Pb_TD-!l9=P9}EkiCf>|kQH`tQ at eHY6|;yd2E;Tzb?<
zYnlRzz8uyrZwx#uEuz(x7hP>M&xHUM^$qIiD`U2kY+#r*6KSlA_L}aqj=k6r+-HMX
zSus at Xb?z?^sgCNB$-qeG-2+*n#Vfx$4Fidc8o)KQ%Y%<qZ^b_{^w)@Gdq1<khPq#q
zRD;I)w!yjDD at Yb~ny=+!=z29lXu9Nwyx<$r<^#O$|Fp;Pe(&Plc18|vZe9)y`1|)8
z9{=;fYCQj~gVm~Z=c9GItxK+ScU1 at UKB^=`Uf;Snto4*Ly-kfa(XjcD at MW6zU3oyZ
zOo+otkXw9%<O3(9umyf*8}e?ZUbM0K7Bp^BQBNG`+0x4cH0j%(_T54<mkaXS#%4DU
z)tXng5Cj+ywsoKgw(&YM5sWRqB)D3h?kB6Mns~p*wG?Sz*75|hJYASl8ZIgtHz=J7
zY*@mt)EzzS+{7wdd(sELD#6;3GFena)SspM$?}CZXzyfmev`b0K)$F}+e;|*fsrIt
z#I2XW<RDM1FJj}O3jCy#nwVJ&*?f~!w}3L*yweLdftzXFlWo<xNVm{#w)F^*o?}SQ
z<~i8QpN at fwL+q>_xw~0F81_uNa-zM$n|5p8M#RJUm`pdOXW?QoS4+tDTSuh2Zl0<(
zAjNU0q82Im_>&_B`H)RJp-U~ULF(|TQbtBb at 8$4&!3EG!JkSzgg?{VG2USrLr<D@?
zNiW!?2;UD`-s#R@{RL_JU3YVi^(jrR09F5??OW3buTAP%bZ?IkMqAdk;B7XOwogM*
zL}u^1$`F{iil)4;FjKO-B*NimHnDp2$Gp{Ke}%vT(d#hsmlYzVEH~49rH4YK{qo0E
z<&-9Vo8(A%byUs1<ILBo=r0aH3Bem)(Q(a#jxmG~ue4 at l*PrRuga{DXwJeRh8!6Cs
zn)wVnkRf?eQ at Uq?j-q0jdXRRfmiCU at 5r|X=Ihl^sZ9{!?FC*pqfbum5*Us3|!AAfw
zj4%~TGBqn_tZ}J6MY-COB$=vS+K;6|?VL{r>q>1MdTX(V&`F~~k^yW2&OhATka~W;
zGIe+={uoO)D4$d>a$ws`d*Ug-(cx-vl9qzMW+Vkqo(yde5Aj#j$m~pU-O3}8cUgqd
zgVd0bV6tATN^#DjEF(S3sKZ|!VIMfPX|yIr>de+2Rg73XkY!?O%T#}>pA>`rfZ}=l
zi<bwlKB$c1lf*hn`b4_wN2$3u_%XBEBt9w7&P}!Xg)LQ&oabdVxT6xP^+ at 34fsohO
z`D628x0p-HZqMM%rc%j(IAq$9%Gy#nx~GMwB at f34WjXUWdbpl3l&BGOnMjSh1iz8B
zS8xuNVtn%o_uxBAeYV`%z(7YL3RbwTE8P?ay=k(piAtqd0gi3&>S(^zOPN-k7vv<`
z)b6fI2=RP#-y<n&rNpH&eUv)7KPwF>{WwhIQ*;dIPe)cjiu2Mc!>fL5rkYAM#n9&e
z@`SC at C__DiQ`sjr)4)ubK{0j-XJiO#!2vyV7M)P-(xkO*N-iVL+|V-gb<v3HfL1|B
z=xdq69$UX090LabZprq?ED3MXlcS3QP%(`P2~fo;7POH1sV*m5jJ+~am45MNsAW7<
zAZb!!6!OCmH|h5ua?MlDd(T~|NlYens`OeY&;K>8;dr01eTL*~fF$~;3QL^Z8}2vb
z4jC~*RWgGXX7KT at _q4kUxdFx)+);z62FDVSOT?rydcKqs^=b7TYQnF*W375bimvJT
zCNrtCZR+VXU*KmJuE`AV{#7#B3ng|8MmA%gSRM_g=ukC3#D4$$7iO97HWN at VjSIDC
z--o({OUY#e!llJzXYkO&WgeGm#y*!(Zh5C6^|Ppzoiz4U!3)&^s|WT^9AX*8i+tEe
zT|=L8ADJmdSJN^r$$WzUEWYNOjk=*lp+1r8uTCEm?WR1DzQvCD<5jHF9#a_F*S8s5
zewEQo1&_DV^DMP`2C at q5SuqRW7NqMEg<&%VpcB9(i0w(JhielyTR6rO;@-$b8 at P#K
zH?cfF$jdhRX>YVfoG{k=)<{=Fje;&0fePiL5%v2=OP>nHKhjrm`-?B2gd}r*r{O0w
zcYA>TL?d`Nqn7XXBJTtD^VZeS2M+E+?0DaUHe?D-<mtaXd=-5qf5c<A%pto}gd9?c
z(vQI)j~29 at m%NbvJ~1uEH%5HOu?z725!gsWMI>5ix-{!;ugrK1Q)_Tneisc(5ucGf
zlsZej*J4MEoj;2YFEJJ<o$-2%amBs&=2vaF4B>cQ+E1I#81#ncfYrOH_QsD>6TZ{p
zd6`H2F at 1Gq_TdH|A|!hAvrlP4G=glw%fifXKkZSLl48tFH#Ke?B_fSCZ^ed-x}K;9
z$~K0U${K2PeH&wrVB!jCZOY4&%c~+mk8QzDa;s$Ae4_CcOU|dQwkXd%MohMYXjfB<
zdF=etCaK at groi2}s=*IP3ZtDS<;R%JXkV!1^qb5)ER_cJW$FVKqRb|TJ!r;wPD<Lr
zBB{E4;P7t;c?Po9Y13M#iIQh;dC6=^QN}}t%8jJHRZX_EG)D3kk4ScE)4udU4e02a
zY>AwjaZg6ujC&5*=8*jW9BzrcaJ4WD0t&^!Hz*Z<ra2nX2xBS0i3y=7{Y(=j|2b_^
zk`)F1ANhH`T4E8hPxrY$tSXQ5)%g=rSU(GjIQuyTmzn%k1Z=En4!oqa-fyoIkEtu7
zqk8h5YjTd5qhMnNhi`m{W12;ZR}MzTWC}>ikA{Dkzx=~@nQ$Qtxs|t{Q3FH at qNrog
zv1DoS%?x`)UK9zC#D-!($}{0T-+lN;`3D}QHD#06gaiatJnBm6=V(a^?7ag5j3udX
zUaINxGl%wvgcv!%{y)aPDLT at iTYDy&*tTukHakuxwrx+Wjy17u+qO9|Cbs{~_pSfj
zoSSpgy}DQJ{nma8 at 3X6F*XAW9nGkj%Iwq6KeSt0r5d^wQm)T{?mJ}*V0F@$<2d~Xl
z>%P=$CK|UIdPbJ at 7f3_YE%=0-3OmE4$={<D)B&j at s3|2Q(odCU at N-G=)uN5GHI#hd
zr5wnb&8-z(QWsQ3fNj+vpz$Y3r!+GkMsZ?NHw<(uG>iEOw9yi3dl1QL7E{pFlML86
zs;~*KR$<DIivxnjo^rg<Fr{Q3g@|xkHR@#&a8hi at lD#p;8X8JM at M~`ih-m+2Py=Q1
z0K!TY8(2}e1$Wk}f`~jsxHT+Q_Fht|+<>La#)1$uwEfi6<=HZ0Hqt)pL|4KQB+CG@
zNHOdm2In-`q2_d>!zJoK)dlff2j%nxB8D$ZlW6}7B-gn+)Ma!X`!Sx|9$|%#*w;r|
zvl2LXdJ0a4;90<sloU>C@@Zd$l#P;>)>o&0@=aK`_ea`d(r2M>HFv=cE?oMDCNl#t
zSnGxv7%X%8U7 at -o&RV+dYwqX-{$$nrhfZWrf|bUw=K6^<eBV^{mjRJT%cyLH?jnxD
zPGwBRPorfCWhiDfrL{u;jCFAQ5hJEWtXvedH`@Z#8<j0kVb+>c!-ta=A0Ja1a+`Z2
zO#An08Dwn5aDxLQH?g4r2LZBD9|XlkqzgBcN2Duv5b?#5lil`3Fn7(T(CtM28&uq~
zW299O0h(B0*kOisG&W>vs3GR+sE`?g`V4{&<HpkSztl{<dYGw`Mgq&aiF0Ik#~?4?
z<$h`F>vFbkpepMl*FqI69osQy7Fg`VewDwr?E*h3fDRNhinQz+#hjwFW7dHmcb0Nz
zczvb(yWc?3o5z#5D*3ZRLKz2Y0!?x#DHGg_%(07IW&}%>`GWxX at Qw?#C1TsxQ0<Hm
zuM|O7KlG(Q<#>$<Gp at -(k1=cQDOkoJl4=!FGmunPR2mIS)E~MN`+-=vN%$Z~bhN6*
zM!cws>8P~t-7e*yU^4 at T*llAEMim7qd})W2(}6}_UsUELYUeII at sRa1^^tqw{g+1q
zn<RL6FZToviRCW}sp?GOMCf07%tT4(V;v}{kSXAC54}X at QoUnS<)o)UL3-^`{G3n5
z+fcvKP>+wn{A+*@q#<b;$E0#~G7q;|4#cBcwMqn01wIs%jPx|rd{PFew0oqvlwQC<
zh}IN^kB5`lX~_~xL0yz&OB?T<oAr6Fc?v{20zcl6%Ae_Z2$}o!8S5X~iI)+Kk%@s+
zw^5C7OCxUwQxck)vsbBeA;VZC68Ws>FE9GP6{AJB%{$Tvo^_7bBacC6jMfAD;e&^2
zE&D)9aX+2=&YkER_Xt$yLR;V3uk$fBL`eS(&GnB=?tBs)<eAif^fn#hzM~Hn%XjBg
zD?_7oJ=7ObO?iJ~gL?@L|06T_Gr8M`605mvE+dl#-MLQ9ugduO8fo-!o)$TaqsxfT
z2#%)7SC$d5KIPV%w<!T0xw;)`bZ~agZu{cb_>w at xThlk)H4tnVvzw;H6g!BqExQ1J
zx;zcX;PLX|EdD$gYAD*tlhw)`Do8-1R at R}y#m|$4<X1H~55{|r>F>=YUsZh+OtsVg
zyyrwCxQqkZxug<UQ`6fUH9^2W_rsnHUmy!Gn4i$Jps3HyNW238_qJj`iSl85F&o?=
z%6^YNz!W1dBKZ52(S=aN9XNRyTXL1Dg!592!}$jN`M&p5b~-*itS;}cA!vNM4k4z`
zG`PVt{jot4xt<|^FE~M?%utoK>^`Kax;YBz0;tPV9gi#}3IT*U2c4lO^x6%F@^?wk
zUaBb0d3DWPVi*P;iw&Gl628*)X)N+Q_tv7Y#xv}hRIIAm9IR9{_=ar54$C{QWR|NW
zNmHs8jzkgNYqbhIY8Byd7WjtDso$LiOqB~H1l;0MnTlz;qmhKD!=6B-U}MUpVBg_n
zY9!v>NmDI+%sC)_GlVCW!9lT5JMrcjCKj%7mNczEuP;juAxR*P22jBV)%F+JaS8l>
zTsFVmLD}$^>b968Py&rb<Auz0HY0#0kA_sGvX5eB8WNCYrX)9#oCtM_;LZs2gn7oq
zpCZ3NBsTk%jByc0#-BQM^m5)i%-(fXz(MtUTy8mXXI;I71JK*;F2a(nh{gBPN##=~
z-RRpukYTu#33J>RBJjui;ZZ*qZ^~aOzKdwH<fU{a0gSciJ;}0C6<O;$$EF7^hd>|H
zo?9^CcrB<%=?VuxAI0YhPJ6IH^bf`v(ggOoyPNRYAolBUS<rO7N&$l^%_8NEb7~m>
z-rN3nqXR3v?)3~VECYN>?HmR~PBKIC2%@xu%l72LZ-+?F*UxXW6 at CWGFfW(S!>1ZX
zNuN|ciD$J41-w0LCaO|P8}eUKLBpo}F3qnIy3)}vK6LlAb!GzkQPenMO#{D3xda>P
zluX~`@%yi3W1Tg%H9~7a?VJ}qs9gsm;<n|i;ubL3{Y5JVATG&+j<SRr&$bf`XvRNn
zKiahPzICN`ccB9y8shzUTJ_9L2X<3nc564H{CEi9g0}IBS1ww*47ztHU~bE+NMenq
ztB{5cjRXZgE?Cn}mfRzUG56<ak`ZSYeMQw}TU)JDI)%I9gl(2kqE!$gO?h<*Jf9*R
z@?(EVnAO?MJbL8~u|p8V>Q9$43_O&%E!mh4+bwKWQ$iZfE6r;Jo*#c;yF}ShJYcjh
zF^%iX+uONr4o<KgGIjehHP=W)AcK5z=0&Di&b<9((&M>X7^Bsfz3w^G&>#IBk{Nnt
z7HV?*?(NM)5ZxU8?x2 at xA)A9e%(Xo~zv5HnBjL#$I<#{342xKOUy^bA9DOb8)YED|
zY{}s@{&+TOJp`AzR?-*BE~2<vYm-9mYYcxDIfxRxe5<S8x}NpASykTQ0oJGLZZAUS
zGQV9N-(Of+Xh~- at HL+l-OIA}sncqJ?YP~&d#^~PFl9LHL{x})a3>}OZz1M}Mrqa7s
zCb`1MVs`|kk$=W%Xu5%GzPGo_T*!+YZR<9bsy3L}YoVFH^lpTG5zI2aJRo0x5sA{|
zLUlmaF-_od3%5eBBv^Fcw}p=-rB3A3mc~TH$G@!xz<AG(7Vh&&x12Z7t<7RQGy?T}
zD<BzvM?k#R!XUu)O;1;zd1<q`Gc|G_(=|)8mSw(<%*S8)buW|?v4Yovnj&aonapwP
z0LQ-&JhXFdER)JaHcV at b&$rAh^g!h~exE!G40u4uiEn&jsFs#dx}Ve|tJ8;0GknS?
z>hV9vL8<+#J$Db}`>{T}w{x{vh>gSV*o}@`PJ`0-b$c7I2a8TUH>i?}sOg1?I9XV7
zNhY<=m)|ngv$bIR<4yysegVv<Ea_8H_QN$C$tVj-<JGf!YZI}4!k-mId at cMrfFHZG
z`r)B?p)=i*-DA6TiIFVXkJfygJdGe5wQ-nz<Fj=QvZ_3g9%3f2YU<3tVU{Ai-$>T{
z#h{4t_RK2W%<$10tyK{KjoZ5Kn at e$6kRjzTj%$BwK34VGnK#P1a`^U9kAHvZHhk&o
z_7J}&wj1iIW;%*(9r7N!S!2QzR6J1d70)EaU~T?18t2x0hJw&PVqZg0`e;jH7CFzm
zrl9L0PT<;$S=(L_738=59*c~ps-jV;=*R-Wh}5 at Y*S*_G&1|taU<~#iTJve&IO{ya
zzY3LS#oAFNXR4_D7ou>rNTFbc(Y+#i*l-W-GlQ}bVBh!I55KeNZ~oM+BfM=iYhKNB
z#<Xke=8ZnFo1*z%r`oB4OD~KxIJnxZzd6v6g|rt0hwsI7^y)!lX6sYM_uO0Ob0**%
zaCRY^H`FirwDWNQr6=e4W4j}%P=_O$0XK0{KU+{eN8gtpe~a&&?sm`5b^EyzpZUD!
z$>>Rcm1oW%YMTMk|0Bu_TVLys5r2nQuU$v>2Zt|jOsL)+4)43?<3u(KfI4p;@b;GQ
z%C8axJl at RR1~&T{eCH%7{P|%#T<&eNu}wL?IKnaid}&5<Cp0`vW?}8oNpdPdq2uv`
z=_M59vk;3uBQ4v)>fSQ~i(~MQkPRb^x{yI?ER7ifi~ju_LD{&s%0~7QZ47g#jLqn-
z7j~z<Zaqob at sbyfkY9VkjO#rkyRVoZkp$Jq&m-g6U~MmEH+BP|YNTUVe!cN?bt%If
zhwdx`HnbOwTmfdw{DkkXL$>KX%D1}mEX_5WCkjo!6w%v1l`nU?(~AG<ZDV_{v at dha
zo-vZ>cDy8M=z8HT5p>V2zcd2l#AJw&uDlzYI&4~ogkiTXa`Y~~*?=5w3aVOpZ~b}s
zFve*l`q`zG%Af)JS<W)bwmdZsf6KggmZjphdakN>2G~AJxUQ~#oI2_Xt#(_zDc>+L
zYKZOKFsa7pAFByDzo{GU+daQD6Hs}MpWN(!!qMZVV$f>~k{^qj{Cr!eh>dD}T1ZpC
z-DADW$oTv|Gkk9MzZXXT=khle#{Wq;!T!Hf&$6 at qUoL+e)LjQ=Hak>h+CESn((}+?
zf*l22;tskFhAZ>Ww8rbwf31jWiYOCPQTYz=%IFclv&}>gP0 at -CI~%6x-okx!U*~?m
z`+ltH>tjzr01y9}grK0e)zUwG>^E)K)n!o}_GusV{<eSchk#)7t-tyu_qWyv6E41w
z at 1A_;qv{aKhhj<Xi;8rp1cM!a`}=k6Z{fU9yXm{pN9Eh|`n{~j;yI(v_h}=6)*Y`F
zAMdDQpyy3<_1(P6``w{?iagii!m-Wsjs2Y{Nr?07<#4;JW7bP9ya?yP#ol>0-e&1y
zrdKY-r8lq1_S$*3`K!W`Og8>}cyO`v>sPvR1$r_v;9a|f00r8 at rIHYo3V5@hi}&4+
zIdiK}iBIUeM-BlG=UW+13X$!*PlBRr$1=9Tyv at NTPpgalOqh+$@0;KX)2e<gP86Pd
zRFnM-F$Ip>-V|aYuU*ByuWuUiTz6ZCiB~D1MkPs}=9tGhFTF5(K3Y(nzm<6we5Di4
z%$oh8E#PYRU0>a9YPUWnujyynoV!+jd_1H&a<%H9SH~9iNk-;dFNj88?^`gcMDC5p
zRsfUX$z}t8zu#Cs=is5kDJi5w(JR%@1xLwe&|)EVo#Ux at G!=AouP-pq7F6LC#TQ?i
zrDiSE%hdT8b-&wvEGQS^#lj`_t@*9a18(0J!Whq0w=APg-x3;oKO*78$9!!Y_Z6yw
z`!`pFnA at f@P6RW;KNXrWdMq<kLO#1Zd=rEVgbEy~@2`MyJi&+5GijbDf|`op(t$Ti
z$xV-CV_=qoHpHg^iWsO(P3Bu(4hdgpl`TvhMLruR_zh#tGp2og_e88s3Bl4{6DKOc
zBKr(iP-MNVHn!n at p*Pb{fz at Nh&6x8n5Irkq-uQBbALn>q`QoVAugW6`O$21mL7}T5
zioo_$gc#V}ueXoNBMXgZ;5W?=<TWk%<OP;1+G((cOIE7k$I5Fnm7#V(Q{csY@)S_R
zME(SYh33gok!+B$gRT#7au~Gva}v0TVX}?>n<L-ndoAl0_7bfFgA%t5>zi_hrWb1<
zunsocl*uGlHV$8*^HLE_p%y|Yi}eyVlW(}OZHDFCx1KpNDkY_MhV!ywh?YxP2wunx
zN|!BuGuQ3rah>)g2l<>r*a=X>f)N{#N6ErPsKm<NBix(z=L(FDDmAB$yc*{w8AkgT
z2;TsAE8B%WdAb?J5FD3~qDI^Y%?o5-dkw0AN at f!9+IT`cLxZOud}HLwDwe^{aR>kC
z$w^RqB#^O{$ks*=aV2<^hvyCnNN86Gs|vn~q(whLXCO5KJpx{7h~y}gfx!h{Pc%e=
z!Fb=7DDn45Yb1Q#IG9R{3KS}%A$rE%z!t9DK%+W^V~{E1+1kO}Nf*&nq(iQ^jNWkk
zBuJ>$AtS2Bgv5mKgs9n2!o0;Frxejn-b)d=K5aUA!e%RnHX|!jAdWF)RG0FKGBd}r
z5Ud6%K~hH5OTbPiRU0mDjl6m4J(8Wfxe!i at 01qJD<Y%#&7tzFjRFr62sHi9 at xuRO!
znax4J1E8WO<b4zlY0aa;szh6gSm?^7qy+I0+9IB|k&0_p{>f)?0|tKn-r-<iyU$xF
zu?v{hI(o7a%g}fVFNjSKDDcB~K-)?y9D)2po{~6`@77caE;JbRyQO#hfZK-Q**=Hs
z<FIuS>TA8lbX^(=jNPOv1ezlwGOC7x#Dv&jz8MLderIV1$}z1e1w<|9*?N3~{eX34
zR43R1VxCskV<3V!R#+S}GKo3v4)I^%3Ao?>#D}ABxgt}ys~0VFUz+y`A!X|s&nNSS
zY at BShY{bVz)fAVvrg1Rr&%$h-9)h$(rzep8omZsB_BlvpQBot0&abLqz-`9^Gn1GV
zG4>lDZ-+u!d%KQUABF8UK5_j*vnF=yDUh?+D{!tm at U~oX`)vA(z-S7ujzi?a*IqsL
z`g&I&we!G?=ovx75p;I|-9p!XV72Z8T*zP)*F&UOmAm**MRKT_w#kV8gpV0$fPBaq
zV{L-;8CqFU5Uyc%)&V?Ab!(N*I#aYTqX*oKqZ|@>6BO{z43RBumw{i4Iiv8zvI&1X
z_kJHZOhc${@C!{O#Cnz2JXC;$#OQ`CBQ;x!$(3}h at f-GAithZS@kUJl3m9}k5iM6)
zvX*7b%8$ZU)U1a_H at 3?g{f*#*GB{(^hLkn;HE1B at Tyf~3<Q{6|(FRNx_vx`}J6#vK
zkQA4$h9Lm9i=xFZatr=rk)1Qrxv=F)(-UMQ&$6e~u&;dKAt~PEWJ<pN>M}}BTB#SP
zXYg!iBXO1O<UcKe7fZ8F8;%wv(5G_b>q6b+>zWnf^l)bxd6WXq<iz>VnbRd?sfz7^
z>N0IsAPUhJ0BODNE(2_KdDLej9*cF+D>a7cNWVBOn6XGu;)jiPzX09DK!T?~sS2+M
z1dk^rTV(?~BOk-yDO#l_Y5Fok<9L72cW{jFZ12icicZM-t!7)x#Y-H}8yGT7YDGwl
z$e0H~3*G+D;#AYXuYH`q7-qkuni&X)$Tl~i9{5j|j`oTQ))VTLK=GQ%Lu^sleySWN
z4SATcnfFmsP-urG3{YBp?}aVti}M_;((QE;=L3~g6k3=MO`LnMht4P&2vXFeqK>|u
zvYZK4i}8U-H#SL2F(>7*b-_tIPAAlmIX>rFj*>%{wcIEpu^o*_SM+G?_>Y}f0Inu#
zRumMZ=>368gU~VTEf#<Y+$ScPD8;C`4fn1hyHXfi-aa<i8T(i3{?o1o-^7e-Sn=Y1
zwA3JS4vo(UFxgy#plLo0lUWx{YKtztvl;k>HcvZ}W0)0R!eAe!qzoBoo&>4=Q#tH_
zytehoctLQwQZsj5l*ld?7%%~lvSJ4Wk at HH??ly2XM*-y!2cXBOVad4<>zb*pzA&_x
zR_b#N^hFu0p{&MD{4Xl!1GL&<^AnWe;6k?d&`c_xS~~HoV&N-%#Z6!DLz(G^KXdtL
zY`Hh5QWlwR0NMJ!^8wPsES4!sE(jsp=r4g&4;o-ciEnEiG%J2d{BQwDjK>%10F5Rj
zoUh8%Ri+MfsJow+Tnh&w#b8^>mgo{~N1mo|IYeV4ER0oh?`l<+KqaZ$tcStT6s_(^
z8KT~9?KI{?Kt|it4Pm2DTNU^!fqt7ZfP?)4{J{t1AOlYYW-4Z-4x4o~0W3UD%ZT`e
z6UQK$jeMaB5ltNK8)p_<9A0|c3E+*qtcE|ZFNbxC=S%Zy!f~M_f~+X(+$y=%6Jrhp
zMwVOy#J9{<>=AgmO=mX;6!2IAHlwskF#{&VFV&o@!kH!Flib`m25<!28RqvL^=jdQ
z;6i%=Q}PyknK$GSd|l$i^^H{45P{_FU#Ej!IqwmGE{K2q&t7`JpWcJc^%DAW*$3jX
z;vYwU4gN|@C6FZbbqQ;Y%Jr~T<9&AG61#)-Fv6{!TL&Mp_()0uA&x;&8VCiRD1VF4
zbeFhl!V+wuY5FqgpO;yfNe;^gqBp^H^Be#Yid-d?Z)9C)&+s8!>0C49c`ZfbCE{Mm
zj7aK;N+gOp+=xJ#A9cM-pqru(+?K)7FySqMO8au8&YME$JxRuFJuWh;-<R>`hzB_6
z_FeX({D<D{7oL!mtQR(HH!ugyfd;3`W|KcH#G#h(s{S^ESOVp%Vso5>ZCy<MtWlnB
z^)@OkxP(}d2s_#sL0aI;id$viRi`wL1daX`W#PFK8<%%OcF_ at Y>Tt>1(uQ6L5q^y_
z#M7m0Si{(t<v86W75Y&ElL#_y&Jh2fnd?wK at 3|@$dU8Ow^xmIZW|8eNcTpAr{a2hz
z4!i?@rrF?Fb*$S#Vm#00$2F!xL4tn-S!uq9V+RS&S=b7#K4FlsgP_1hJc6Xn!mccq
zu{^)*5T0hkmSnkY$LBl=97+D^?)`NPyKym^ix>X`N at X-~l4AaCGd?rQsdf89kLSe{
z?cJNL8&=rs0dlQ&#g^(KM91SgiWpv6%;%HxeanScmggfTkajJ$-c{=UUV-`|?SK$3
zW38xgb-S9uFC;MObY1WIu92As@!Fl(4&MS;@bmP-T_Udx;r(GY9#Z*3vHzSRNGiN<
zLxod`O0iR<rvqu&@Od0%x_H9^4MyU{QK^7Pkus6{`onpq at Ocyxj7f=7m4(f)>fCx6
zI^XB0&!@?$Hu=X1^l<apY;_gV=UtdT?Xp<2dlW<`P@;W?Qxj925}ZmW<t{~sihw(?
z+<5Xv(@J^_ax at NmPh{>L3aXmo2maCN=UwfIK7S_)oSMkx<aNvhMl1Djg_ht&{CQrM
zg7MfzC4}C|*TG8f#w}0p?jOF}6;bye?Q9zPDGJW?Q)lDL<v|o{y(vTZ8}Z*Ai;Wvh
zxAZc*I`sxd6dX^Amtc|;`r+2yI6khNUbYELEmo?1*(2-sQwfQfGbxE%tx3=bvO~{2
z9a&}|p at wis)-mgbjwm=%HEsTI7d-zemC7r|jMf{%SsW=-S&GcipH8KdDb%p{&f@%u
zsV>+rhrp=NmAxU$DQ`ZcUHX;D)&Yb!>?=faZ{ENovKI(h()ow(z<?8ejh}R&5Lq9#
zSs)i7%<*MJkOJ~{6G|eEYuoXbF8Mf<H;hqfU7Ev=Ndp;6+U^a|gj`?0Cf>B`sABUY
zq(BL5pcIqwFu4)#_$cUYDNMR};Je29RKuSQiEYCgQs%_1C%QlXo>C(J{?TIA%=bub
zcsrT`TJ=bFh185ZR at r^jU=bsqwmZ&~nI|h_9VOp#xmQ{)Jd}fbVK3`)?cK2M-nQhO
zhmIPsG=4PC_xxDSPxS^1l+A65j4Qw!1(kx#dXlnRC!LhAQHOPeU<`B~eda>cSLQTM
zL5W)>FOB*`+^8FTR3vDFaZhw_6gCrtE4+iq1;It}^$(H3TBcaY7abkcqyn{ITV at yT
zH(78sX^?(b6)ayu0dJ5>4+%0B);a+%0wx>d*AfrgC<`0}CiO`o^QZ<?GHKH1H<j;B
zrupO=M#k~Nh at 6wXrb^Ou#uZ5^$EGs|Ava6Ct4af;4Y!J*Wj(+6sJM;#aK at L07QfPL
z#|?DaIc`#89X<IKC6p#a`H>U8SveYUXv=m;s0_eACiYdA+@;Ph9lIZezSK_IYjLG_
z5qdY&3&B{yQIq3rAg9eyiQm{AX9B1hPri65R-^8S<oX6o$x0r-C=Q;DV>@Gf=^ZiY
z`)$|(E%?!KuyOFBCRIqbb(0*uGHr^|IyjzsX|}NNSz{X{1)^RRDs}OyBOK#;L0gyv
z?eGcbHww~wl`U<ms0PE)^)Ftpye?)p<^Jsp&jQ~H$BdEEMO@{aCK}IHu at YWGuDqU7
z4k9dr7{HjI)@0)sv;V5j87^3(?qH_Zj=LM4*UsORIDn^`lg4ZZUt7uEov3&V-f!80
zMV-MId!7Ujhae|wAoaaU7-&bP^p0$b;#wTzpi)xGt;3hSsI7A1nB`y8cR^v$^vey1
zTBDzE2fk2+0$PK5glzA$l32fVU)yO0TU?^E*=9Hey;PEcf7P at heca;mD0;l)T-wv|
z4oK4kDfN!3Eyo3tDVa*#a<l89gkq|=!(MrK1CZXCwPI6eJOU(tmF|lQZN*Tm6_Y$2
zUa^dDBanRaFvFq#-FzE{hGsi&S!G_>v+Q~ORxd}r0E<z19<en(NdMJxmQY9;(w&g@
z))MNSNX at 5W{(BB*rNu^Oxf1W*;-;c-EfKAC|EO3)Q)E}1AYP;HLQ-;k?cxH8+OVmL
zf~wNGn0=CGKni{&&jXI<!sW^zdL_^*!z!OICR}meM?9McilBcAfI@;T(LRu<$+2C;
z`SUbaxy{*g%iWnH;e#%O=U at d`2p)@kpTw>;V6$o`#$R^Yo#<%5hH6@{_|p?yz5r9v
zIQGrO0A1X65lNys9J&Ibaw at _X=Rc*Hx01D~60MLn<V&*9;bAoz%YT1cHgH=Z#0Abn
zgR9zUDxq~W9DfV>=^FMciEDmI#f|^m&9i*UY)zi_O at i~CjH3S$N-iO19JCY2fANfr
z)t2taek5~Pc)s&^3RRIgqr{V`DgFG=PLgI8!KVStTzL~$Fz#yOWW_RbNGzP)nobn+
z+xdR{+ckc(5KgC@{0%aFvbwvbvpiWwwuFPF23&0%nUjHo(Y{=dvIi4eDS*IMyBs}5
z18^&0Ddz<t_Y{)8Oopy?uSfB-S-r9&wNe^Pui&&(-v6M&6AD#&OO$B=IabkKR*T16
zBRYbu%VBmb60^muQYvzzY|vrlT<ubo*ji{2CuM|shC1Z9MGa;}XHyie<lfpAr`F_)
z5*sg)^3(V6saD2 at C26Z+x_Zg2p?wJ`!|0?VF1&P47-=;I`Jk}n+>Ct*82m?oa!>~9
zBKI_l%J+{H<GJElGh(wkos8s`cG$Kh@&b~ZotRil@}kmTgMR|lOFw-q6KOgUpD$#_
zw-K$?D}SddYLWz3YbD3+Xu(Epl!03_+2-wNm2K at 8rT&+Z5_?u?Ib?97D1j4QRB7=s
z<+_HLcUht)ewDA-C_{_=k9W~;SE;3As5#lkO{otrKf25uuO>BI=^KP?U;_4_mY*|0
ztub?JVxg|Vp(N>5_RFYV?W?)r(m9h#<<CAXIXJ2lI~~fQMM~OynZ_#5F!Hge8EaOw
zwPUr}X{gt6^aFMOk==Y$*dMI*W~vq77n<X(KZ7G7bil>k-%A<@!x*qLQA={n6;*X=
z+}Z|PzkDmL>BEjrtA9RBOV%8jEG^`0B!nEec#C<D+Lz=n0`MadvHYCCpWWu2#!_EY
zR^H$ruF?+w!F6|~Nb=;n$;X7uc<Ym}E@%~0qc6;?U%Uu>;7;K^u+X7AriF~AjCCpW
z#?s at z!0t#%Co$IEoVNokmj=Ep*8<T*ddGnKMy=BkU~ewA=?rIrf_Af{RPsfv%Y{&W
z7ob*tTFHtMTy9{ywNRhbbg_ObvA>4s%SCgUZ)}iL{mu|AYFfSoj}6E-g!?MJDop1%
zt(5>lqSIhr&?|}oM{jyo_UjBMScUqu2xr-hRteOSFT+iGucIED^^dFc(u)A+b>2Ug
zPmgWIiuvch{VxPYw{PR71~`nV?cz)KUk)W$He?Gf%Kti7>r_%rijV&c=54kNL655W
zh)W~XunI6XL#vpRsiIcm*z|13BUjbqqxvVHTG4xcJl#r_B-}m(!{=nRl3O=qknj<f
zLFjJLX&lQ-j^{9uQS!Y}ncT5kE6`Nqt7p(g5F#N<FXVl|@^G^EAzMK%RM}q?bRUsJ
z>S-~o{&Tae*zzC)Q^bk0_*VYSFXqe);T%EzrOKLOF)L3mpa-#Wh`THC<q#-ifu`b=
zDf6FhY$zC5f3lSl@)_PuUF at uIct4O8<PehCC9{U!=OCsa_?&-PGwxQ$3^2uRT at z4#
zCCZ()<^SxJ<MEb8F}m1gA~;*!-(S`Ll=90t?d2j}9~g#!XW^1*EBGshyorA9QY`1(
z$a^eNmR4yh8T+G3{;k=HJzID7x}EIg{*}gS3pS4kpaOYq>2e2%#g+B!PI8dsnqj$p
zp{8$TZc;cV`6yDeeq{=h^vl!r<>Du!(ie$}78t!f{*i4Ft)O95tT*d&Ax<Ztg<V5|
z8hdW9Rvr0TZ`J=EHrCZFHj1lH-OkQXy?3|2+SuyZ3^!e(-ITBPC>H&*Rq^pX(kFs@
z^jiU_ZAwAj6z-le%TuER-uYOi at X(KyCGzfhf*({UFW){_#c^4CArV{rK^dohJXac=
ze{rbaQTi%X?7_P)=s{%hxpzZ$Pze1>Fr00s`8qxEs1U;vVpVjE^YHXLVc3DO&8Elf
zr1CxBxU&rotfc>#8kcU;mNY9b>~O4F4-)edmv4QcU&OSmXRE$pZ~X_`sxF6V=Jcen
zt2|6?c1Hoe)a~aTwt}xf^VO9;K|cXwzNm^jII)O4tRK@~Fi>eF!{}z;*W|v?IIFJc
znnilW_Ge~Xi)B)RgZmvuT4_fub1bu_57FL6f+T;HLl(5VU*Jl}y=-6CB#}JL%t#l@
z9gRa3YQ3pXn5OJMY*LxsK&g=1*uTaHFR2W~2Ds)z`CUaxracUwRjRLjB!B+ImZOV`
z at P2+Odox?!@(X1^fybdeQ(#0rLfJ_0Z5?_vmU?mY{(H!Pza~I&#F7DgHe0gyB!c)D
zmRZMi=V3c)Nwuf?2;d6an{6L_+nn35@}OD!K|iP`AEea(GoeD~91}fb6?UC>+Yg3~
zV;!T*$Gb7e2EVApO=hy_lS1YcOJSE{j#;?FcCA~7AliR>tIBrx20n|NjTXtuCuW&?
zu+LP^&v9RwTEgqFc~}N7WSv1g*K7d1Pg_f^)p-~vhe)-|#bVr>;~rb2&U1B%BPEh3
zHV4)PP_KZO64)MA8^0b}q9KrO3ztSA^Y=9|>VaWd0_<A at jyDwGZ)CaC=Y^zi2JXx`
zLN)S}uos(7%CD~wV0D42M>rH7hU#j|y%+C}F7ZF*<<ji2zU`>>E7qtOIJ&pf)h;H0
z6P=JWQ;+mRM(c9gd8YA at H!`HS7vapx32>t|vtmsStv>?#Ym~vTI$V+lvrqz;W^NoZ
zKPHPhbc$-osu$?f-tC4hVIeDMo|(rMDmMx%Q&6*>GZPxU+(Unf9=`sDP;<!E*XZ&}
zC@>_@{|nl2x5J#~bk{#g4~0c?)^YBq)|aRj=yNWTGCQ-IWMbUJwvG)A{|jjEfEA}T
zl2X0AkWm$d#F6*AWEB`*TmJNN=)tp$^Sq>J+<Br{KQZ2kcYiqIXX}P3>EZ11BHX|~
zT4-Rl-+QUH?J|A4;p;WM>HR4v);@cQ!UUeBna^}*P;5$iBOwaSS8RIHo$SWHJjFpL
zi}SpD>YPE568~Z7VvBhxuC>Jsc)LgYd_T3UJ|r_Dj at I;`HWkA#NTAT|Q5v^55 at pt-
z1^&$Tt~KV at W_AM=Gw`F&2w*mtJiDKzjoJLz89vKhq=`YUPW##C-M%#3yezk2I(77x
z(FGgy!wlVwbMx8jE5HDE?5-Ko<gLjMrl`AfN4F2H+E3xf<K%fK;gq+x_n>|nnf;pg
z3*xrzr-x6w9l6Jj=Zw7^1H$U0%&tc9kn;p#1naN5oz}PCI=r5-EB7{@A!GGy+bIFg
z?T|CY70aj0m&r^Yr@#Mr+1dMw1tEaPFQYbKjjo=%bk|hp%II1L<h$gnoZk01D2EqH
za)!NE?~Tn*$gAWKq%5NFXfI4HxlRK{GPSiG=(-qlqcpF!b<&afLvc5On{DK$TI9E`
z<5E`?p at 9s|pAIfDlUCc&`z&{_;|5htJ|f{O&DKm;D0?>S&M^#=1AO1L>k4B`z?o at U
zo^Hyr{3%IW==CNMvoEryC<F#zN9sk+YhBJPx4VYLbXR$atp<6I49FD8&=NGlL+1$0
z38M($s%m{>9hA|QUzhf78bkZYQKq(Lse5CQ0I)aCPP~0*I2OKEP6_2gcekJK3#kFR
zYr^;YLtT<T5Nw^=ohQZh)3 at 1QGTE)~X88NWvsE4`Y at MAPT_&s3vl_`pOcP`ed>r^P
z6mu!g#-)(7xpIfEZ=bFX`!9U)dEEyTlnl9cAdt+8IKAJj;g+j~-fWG8aQBEp9T3+0
zbFUT`vz_to8Oh$tF at MdzJ>q8a<SL)q;#Nj~a}u8mrDJY=QOfRk7*k_9g}8_4#eE2|
z?_*G=oaIaN3pC))b@=<3V)7_7`FtvMfahB#Tt8m+_b~gqr`tPd*G;X~8xMFJ-YtLr
z^0<e7;QEn))n0Yge*Rliz0kt)$}IXaqRY8$YkA1cuHSz|s8SXyzwmo4Gk0yv=2Efo
z4u%3B;dsh9)%>T|Z|1rytKf^5K^27S&VB~Qt6L2mjQ6j#mtya4`Xm&!#D80TpC>sj
zV?BMdJzI}F_X|5HMn&FY{|W{1TFjNpP%K(2$i%hcyI1~o=FJ!wNBk%oI0N;hsrJi7
z;8(-@%fvMWrhW5(EX>m_y<z at D_R*W870++IwvPzul13#7>Flr`7%xz$@Pso`rao|C
z2KbTYOQyd=Ce$?o4#o4h>9%L4+W0i-Cb0M$w)1vZp+GoyQ7+(wAT}OW_R>|2PxJR(
zqpna(Q$*E8o4#g5bgx<JO$gjT at H|aYYa1)JPiN`LAA_*>P7xQP3y#an?MRw}riuwZ
zma)?w{rFJ4^P!C`v~=G$@a}etLi+PtTU=answbME{g+$s^s})87|YR}3*Ryf8~uuz
zgI2w2Z>RI$b^_m$Q^pqUnxlO$r at oFcKrM7JTyApltI?+<{CYU|p041t;E46DZRwbk
z>1SY=SPsMZLwWtkxeWBSJ`Z$#iI!%@sC}90%&IKSqZO_FA^$Nm{ED8op|W+_jl-7p
zX1Q(0_Sp2;H6F0bVE6Gup`Q8Y|JpqN&!+glKq3w<M%MoyRK&sfzcQzBF#TUP&(HZ*
z9d()=lL~Kos#A-N$xZN$^nnzs>W*C3t~TPniWi__$_2#qKuPH(xdR&DSoJ>64c&7%
ztNl0Uy_`@Hn`UaY+VQG<h#B)c_#7tBM&txln)r`O0tpN+!+W?lx3))123hX+1f1i?
z*}>EDpiTzA7#aa^lSVB(-gOj}sM(g*P#rGwJ4a?y7yH^r<M*F?6ec;hFSuRC&RbOq
z?61o5^p at QpHYgOi#;Rxi(Z4gp#j6<BmmOX`J6Aap*tWnhbpy(y!I-0%&xSMJ8N(rO
zdxE9q*rhu^3`mN0Dx={&FJ$;MvHD&Jv|mMV at 7{-G{WntY6(GGi4VnE<5Pp1qqrKwM
z*ikj;eVJze at d<(pXW(+B`!c`%mydVll`pC`eEaJ2)nP+!3+H^znR~}tNLCm!saU)4
zBr%v at uj*kCj3h<~UckoxfO!NmN`50{cHsVGNDeTF86nAR->9_J4@*SnOn$!=;ees8
zdY1r2LqMTo?o;=1OA08=f;;Oj%l3W8$nXL8q$<eA`*1X>$Ov|kTrM`ug*dRsvq*~e
z1lFYX4PrfoYa&mKVJRLn+btz~X{M3~OYcM>L!{%RK}mp-uz-cYz7O!jiO{+!SOtPJ
z8(78<qz*pH0b{}esg_;bRi6}o;CsEh3E#0o9?Zy6)1y?r1@<b}mx9MVEt~)R%)<Ls
zTbG(1pI~p<D4?yVt2;5&0Ae)uX?2+N;cuh=K^x%6<Y6=#(Lfr7T2v`c)_6<o&m}8(
zSy1`iDUNo*y3aUM4cXs<3kek!-GcRr^Gaz)ixg5#6CA(lp<`5b2I*~9$}n)gW at CI~
zUK`VS(|_6_kLG7+^qTmZ6WjF3GKSQ*8EPQ-vPaC?G8Q~n0ZSldpuKatmCgY;CwO80
zfaZrsmR6FZauotIAK~r28{D*!zege2-Nrr}Hj&9~qFV_OWKT$3LxeeVjx-4G(>ets
zR at hg()x2@|LV;Q=2cZ(oCuyFKGFd7ol}2V_$T6 at 496Jy(p3uEH-Zo<C<bHeuc1q8r
zTD``%cGWE>aLy{CTo&dK at LDu2^Ts<kjWb~XOA-HaZ-J=9T-J3s95;)&KsG?-<laWW
z&SH8aYr?TN)XA(^IkDnASZ6M2hirnxC$`-HJFj6j0uh25l$jM9*7-eeI4;b&X%d}k
zZTqka9!zPafXLE~HUujeI;mnIVajFDRSvocl`?y|{>*Ex1R@@oRwZuhoQN8Lg~2r>
zCcP*JiVqN`*ZjcSrHCKD&cU|u%?zi10g at b)Nob?KyZ2L_o+;@cgH)Y at xEICGhT_OS
zL#fmEoXC`mXbCQ&Wtxk*57kC^&)uVFHJ$baZpyoZo<?My%`u<VcR`MDMwh)P2b<$G
z#u&E!>X`8)tGiTafGl(j1PhQU35M6<?<v$Qe1jsk7mu(#g5{eS<xRMZ4nvA>UP!Ta
z&_Tn%3eLR5Ga4M=DM85tL!0lgHsHe(p(L~24?FW5FC*lbpNHnLkpG*Balf7jpk!El
zxs1 at _lpR6~XPR5Ox|TV`3{f5A>NlISghAR~ku|a5&*QUtg%0 at f^sH;;r{h1{Ty^za
z?bKeS4Gw8eb&yHr8RkxZecP1VA-DtOOFSC77ol30w&ok+tg~A at alRJXXe8BTaef5p
z9hCi}@*NLEfA>hr>hpEzu+Du&rD5<Br7-m>7um0${-dGM4c8*OuJJaK$8I32W`fJ+
zoCA`#e_EpQolBP~=+hkndXJhwwZcMiEw-`Ya2PMbtxx({%1XctD<q1yFptctESO(p
z$4Ndy6ei~S7NIiBKE-_WkE6HY0A1+ktk<*xriD`%Kg-Rw{e2SoM(*$wg>3D~9};`b
znsdm>PSVSb*FWh`abVXRccr0_S$n_P7PyR>L>-%%^tJaj;O2>!lCQx$+x{5(;3$xj
z{fT!sLq={FqAOF6_2r}Wc)c_Bk$A5GV(2lC4k3{vs>?DD7HCZ$@g+t?Ovk|U#4LMA
z^TLz2UIvyH?DRlL4mcYA+y%>+MUW(wYVndvNN1k^Ll`1<Gp^v`N|8-svSpJMVH_OU
z*#n3^o}cCjON{HZGU_uep5}gY3D9n-OJu<kswd;0>>LO_hnQ!T4gKA?aN%K=ZZdJ*
z;^7dN%DU5 at 3^r9|(rr2B3uPyzdx|aiIj!X=zVYKHfSSM?Hu-lt!*zWbqdQOq9%Ubr
zF&=#_idQ3@`(X1 at 3xv(3D+kC%S~>o=#+6jB^wd8pE?ej~bQR}Zm7NB{TsewR2)7=R
z^|(cC)+RmDdd`V)eS(qemhWwORC;2Lr}Jo>J3~U&rc5etTMFJ*fFMEct>=_j^6eCV
z^6t$78M;V(Cs};gx?8qewJV~^E-#kLKpWbe+X9Qi1_(9mwZcGY3Lg+^;nNZNz)e~D
zt&hg}d$UW5B*>a!3Kz*S?+n`R>0^`{1`t%q01)OUvHb4if{3uDw2Z*`oioE(B%sG`
zt~L|><YC_+#`FY7oa2*G<d$h3Ve(~m>N7tWQpru3u63-q_&H~tyPkPSi26WXxvD^`
zlYi9<(4(hXL;KSXM<X;x3zTt`1b^`g)m<p~oyE5j+ZV;+b1kRF;8Cap)h%maJucZ+
zCNDK6C?IxBPWsfDm=N at Cx=W#KbQE(ez|C=_`1!A at g~|A)>h)Ys!j4ECf=#~mG)~7%
zpH)TBulM$c`xf0CgKjg<ClAoyaZYY0M2rDm0z**M>)+pLtrDxyyXrLEbm;9DcUs#U
z<gomW3jAyoSFRk?!{^pV+mfJivu6DkTdsNaDLhjiY?8p2M;}<jtaS=lZEHs=%I7Am
zV0$^TREJ901*?q_?qOsUhpd8O4zwcKnb>)Yc1snzYrt&2TZDk3ppz?~=tGJei^ql~
z&dlW?@hj-;K>T6(h>$esABJKGgF3$D-efVE^>DOnul5E*Nv<&~?jZy|SK7=QgRtT%
zy7dIA-1GI?oi^@sLxW5Ztpz+l!bM7Z-R~#*4Zu|8Yc{hQgK<oD%^X6If$3+?OMw$b
z?0PIg2;hX4AmtONV(re%;ZUeSgl6|3h62Py0(-Vh(WvY%tZK{z!3d{7lKaAtL;~m)
zzbzo*LWcGX at 1*9+;1bdWig0i~9sCmEz-4j1kb8wd4{ZkK8vUX_xmv2T3MXQAo|qxS
zW2&=Kl3tCE0nwew1u}Bs)`zQedxp*{KEfARMxPqC#LUs*NzTPVQ}_}`W(83wIbM<e
z{`x^#pLv$OGIBRvVen!`&iKxPk@}GaXVaNIF)cM>0X!7Bq7j2Th8_dw{ENH2|2D~$
za9fR?u-$l?k}~;M0o_3h!XB>37KeKq;^b#pDdwAo6B;-4q$uAv4t1p at 1N0YI3T}{R
zrjr<MwM2tsh7dL5Z{oFiWP9Jxz_EVT8UV7H1(91E-~2VJhp>1 at BFHQNEe2oMDJahl
z7(f&@9Q~M}=7!0gCT;OhA)nn5Ay>M-O9K;;pCYC99wAMaze@{#sGXhH*08{&t?2my
zQy#{j8*)N5NTGu4)IbfJoWhDTJ!mq7B*JSo89Vdx8->?q!b658Hsu$lN=R_mJn|wy
zW`at>w3_IbMlRJmE8qMPA8G*b0ujklBq=1-L at 5^?vQlvcQ{6pDag!e_?;HJ3(qnTf
zIP5>S1>m-b{Q1>ksO;VaAO&*%OV6HcR6Sy=X$Sq$N}$J6u$`8hL50z2sUV1>WeBGe
z&F5AN!I0~2e#PXemFYWyhOD{5KjbLkVB|_R|NBijBZNYou7^@#$HObc60`QNe`@{f
zpD+LEpKTy1x&cwG(#OOE1<{TYMEJhY{h{w>uyqk$^bjt9Gh#b3a(#B@%nqTBs16$E
zI5!K8k#<*teO+wy8~PccEi^d>yVTsQJxJ(gIxj`Hp!o=D_Z4aNH|$xACx3JJ05twz
zJ%OWyMIg;|MJ_@$J;Q_=DHVt+7MP4SLE91umu228!gF)uve2?fprxt}E4sy}3tA<R
z*qBi at rL<!E^RoF~$G(|!YL<Xm%*}n1GHZE>?5!eHRy96sG#19B_cmwIG({<fqnm~W
zv<cyZHUl$Uo6j?E=TkZM00_9s@>D4)egOMVBIt*`QgJU3VH;U8Y^PO+#tdDZ`8ZhT
zkm`MI&K<Jw9)*zX{aq2_yYIiVJ_55xn<}#^e#$pnHOgBY!2r=yTr2Ikj;m6Xx8_W{
z#6zpk_9!4B(Ol?5YU05ouMw^$QdOyZL$V7lnPiB${!qC2N1lo2Q&4RlQB{yg7Q-o0
z-~O at S|1;|gQeRT_wqCh^0B+m*+B$tON0}$CnuFq;@?OAO#_G53eq+yn at 0N~#N)i_Z
z<-SdV=v5EWxg}AAB=5pO5dtLX!c34%3XE-oNs at -&+Kh`V at h&{gvv*UaNxl|!gSs>6
zj3eUMQb!xwd{mZ-=lLCo90-ZWznlbOrK^%8Hk*B~0!@7f(J}@N`(n_ZNA$=HDa)^^
zu=EHYn)>13W~cLtoMIABCAyJ223PGqriRS8BT!3VVBv%mm}gNYY&vbSD8?(03%#o<
zpkz{!4}@WgE7N9^0J5r4A^%7V5%X}y6LXGltl5G)Qfg~w*#u~FCl?X_we(QX%9Km1
zN6zcaOSmyXuSV>(X?>TYt2{+DI~X?NI?qyn^W^6t2dIzk?jiauciuyE5U*=}D)U^4
z>dk{B;nhYF?%^WU-k8xcfzjN=RBQ3?+GX9|J$rX?BRKl3w->G5Q at IlQjDlZjx=@Xu
zZEIqcJQAMt*p$EIjOo9ZjDEBy`7Bp8ALF(uv!32tZflL1;?k`{SNg!>L{12~eH~VP
zxdI`XZz?mAckWn1hTY-58E2nWngXBIOTe&#ZBLN|l5kb9kD>$0oy#<q&h$B6A$sJ4
z>GAoc)u70ayGpXW29V+}95$uqx0~Wn)hgtJ)ju)l8g48UpO2NZm%=XbsFAyJ@?>C>
zO#lv*^;&H~TD_Y$bqsz at fd%ic(gmT)21yq)E?tq at pUY1rlLcI~P_wVf?rcy+$)yZE
z6ecMQOe;v_K#eOP)dJVxp$Kkw;YCEbH7WII3b9iE`AyLaroZ3oU80ycq*n&!o{c?p
z7Qtnw6!=yj?Kl{vu`-h9uX6<?&%vi_x<c{dujsF at xY!c>>r^zkVlOtx{;|?7*lOiA
zI*HtuakylT7#ITr-fJF=zIorh{zM-epEqVpm28kGtF~87jS937W at p|#m_}D|$D!*b
zr2{Fsp~5U9Rl-NkL4}Ipzj>Bcms?pbM1aV<m1daQ$pxIw$Z7sD2H<tbj}3zy)bHxX
za+U?tp2^Lqs5HOVMF);?=8)@AXGOawE2RZA=~P|Ivj at wW$##x(1NrDb!Cj205U~Q#
zdifnx2c?R+srgZ_?XoVeXB at nvoCy+2QS1?rr#YM)+8zg#5|{*RZ_0ND1V4821Z**B
zKEs!Lfx~Fr5!Ll>Zo;QLtH1v~i5;;}gS3sl)~aGiOI$ykjC&Uu3c0P>j9eI3$OByC
zOF^<D#X2KRrYw$c2f?QrSk52#D%PulB*{+rws at J-X5|N0wOl`Ifi!72a-HM+I%mLD
zQ;Bdv=h72Y4d~o*Z-X<sloP5h594`OlA^b}>;5`c$E&qV=e9nr8*c~ila{;6$XmKy
z#ajONt at v6msL>+5VDh`50zrb%?(7UAUqqH>tsbvI`XmKP$fXpVDaiKz2`?Iat=<Nj
zr0U(zo>UR%;Es4g%i7nD_8Z-$!yg&0l=m`jfm+<I&$d`1q|xaOHa~{MNRe-zUP1Bz
ze?vfLL#%Fy3**J?&_~j=-mMX`!mpZnn=ZdDJh{gBoo(xIbesFr)8MFg9g*-vdiT4M
zDexCE$_Z4tMF;CS{{u`}fbnqV#31*_!ex|huO#55;7F=nFIP<#%yZn}^ufYypy&cJ
z+j`f{CL4~_v)B8B*KRyH#SgH;inSf(79030zf8VU;Le37rQ?qh8(){6<tfM+^KnZH
z9l|-jAbR<_p}^$<zplHV<_EO))ZIE at P1#_}g`v(ET@!Oi at S}CLuNLkHxSw%$&^bRx
z?QJaoc?ld8ks2hGCiK^pf|oB?1*&s}c(4u!%Zb0OrLJGc*AP@%i}Xmjqos>QAL~xe
z4u|YnI`27u?~hDQM|-dlzd*3OU1nJInVKH8uFxaWwP5eJj at Wx#cHRC;(YJ0!Dl3Id
zvp%XWiQAjL;7 at ubbvNM(g;S}iM4rhjYrZuBCV9`2pF`pXAuB(kyMbmEF69KJVGBJs
zs0(?gM<VB7keq9}u9L*M)l5|9T{RQk+0gR&&CV&5BD;>G4XeSBzop5j2P^vAs|QIc
zFK4+yi0pRo>5^1c at c^D-&8RlF%t~Q4T?3Cj*A-b}i+u>#Z}E~&Q-^+(e}sM)eu)<>
zzJl6yhi<`aa2eK9^4WayoAP+Hkr1-sodw at V<sv`a>`UH8BKCa=Z0q1nd))jEX|VZt
z+%Y2mE~GNqWCg*ou<A89o**b$y*yCEpmZMW=};4+SCL0HZQ#M>JS^(6klc{%ZoYJO
z^$PHfSEp@#Q=)F2<tu<CDkVCjJqvMIjg=CyBY5dc#3pv9w?%NK9cF}^u;VUp(1;OU
zx!3C!=n+6!V(et!WHB(jP}dQchi}Dr)HK<&)4K6_M4*{CAh;(C{7c0X+w=A`zZ8M*
zO2NBj8n+F*qjW7G$h>uh0&BsvS&hHod^1H5nh5`7`?5XUQO>Ru*zuMs@$q@|88F>|
zX3zd+VG2qzjO}2jt~{n30H at vYNSV0(@mfV()BuC#=o3reE{9DkBENjm=yfwXTZw!^
z?VL2>#@C>Lp9C-6JqO5k?q2T!s99Ylyo)^F1=Wsv=@Z=n0k_LP8vSH>m!RXSuO;$E
zQJzRalgMT019_7f?7$&Ct$k;^s?OM1BhS at 5E7I|WN8Seh7*30h+ at 2Cl9|;WInd69h
z^1BTQ{62qgYkNCZXX1Vbd^a)C{`nH$(MAhGs(`S4s17?sRs}qwnJO8D(AMXn0POZd
z{*_ACZ{Ubph9phB0w21<*>aH&`%Waz?dyw46 at 0FLh{{ERH+quth%>FCM;@&Xe<v7#
z&qX)NJG_#>7!2Fzwpz-!=ys^G4LyKiOu<)FC+q#oahxvID}z};L<)*h7m+0D|FQR$
zQFSccx^N&!fB?aL;qGn=cXx*ng1c*wK#-sdcY*{<aCdhP?iSoVID8AT_kQ<#<QwPy
z*k_D8#{IEIudc4Dp7qRG)4IEgHnl9TywLB%mJ7>P%)1f!59N>fhBK5~5*u;fV`QS~
zjB<>^2 at qmDW{9Z!(>j?y_u8#bK|j2L<u$|Gl0F-UY1Dqz+9UYtyNDB2U{w0$yW7TI
zrxFEkn3cjNY8~TmZrtfiv4r(YFmy3z`n(c8G%I%zh`?H=e1de>{tv9H3SC*;(^Erv
zj}L2)yDzEnFJ)0WQ0^^}%5T=wO^72Z<}Sx<vZ5>xOZYP$vLB!kI`DeDUnOJlo(W#w
z)fUt6;&jyW>^hD%?=F}{9hor7`Ai4aPKoTszT;0?U}m3 at J|JdY#3PIPK;$8M231~o
zBb+YcE%I}x*JG(_2n0)vhIUZC>y at fY{*&EAkbtklHc~dWdrVrGUxExqo2 at L1ue5~p
ztjUtODp at fDuFdjrE6j!DJAo40gj|ie9sBgeZNw2jTppMBVKf$aeMO6PBx>S)@`_{z
ztHEZhAKzU9fuZu99*C-UqmPiE^Q$lZQ at Z;(js3iC7AGgyU+M0r55t}cfj~xCV;fT^
zGa^oo|4F*LNq631p&O-0_q%deNoz_OHGNp!`)=Fn!rIsw1|5Stl3N2g25K!-sn9IX
z>oBs%XxrYJb;_}qgB!1ZtVD+xHWd1OtUEt<O6OkW+dE2WMl0XxXxUj^pHy1*6n;!2
z^zvEf!Fa$CSUKw}*!;To^)mg+WIL5jnxn^5!fUH(+QotxSB4OF)O-=Yg~wygyB3G%
z+c*2$=r8^?pUf{)ir%DMSeW at NToos@54hAQrA at vK{Y4^>wwX7bQgj#wsQkLLV_bnk
z78qZDZ&Bf_cVrGoR_q^k%bXkG^xA?kY|M$*$>bqTwS0_j5ui!qpWlvvnXbRjYV at qc
zml~H`#(zvwWx`kVjZ=a?u;u7kjuKC^j1WNd`Eay7dNP_Y(F5f*p6)tcdk`o at o06we
zJh*vNeDr}^e%gO0JA>nsS+$o0pS(F?^Gsxa0NNLHf=b55UkBISF5(_XKjXs~Q^N6K
zlG_3T<g=D$nQX>G&PDR{Sg+(Iw9>0v5a(&)*o*H)Jn-k1t*fK=%ds%=?!3Q-A9xss
z>bAtfW8ef8iON{0re$=A(#tdY7{qZcYRbV7Y!MV~$OCc(6@`+cnsP_92AKjWDaV=o
z?Jhb+w$;V!ytEHq2TQ438sPOD+>-4_{K}o~^NO>5Waa1Pi|m1g9V@>|M_IRMca4Ce
zP$1A78jTR3L;7`u!c>VeI$62xL7Q^04qge++bK6N)%=dMLE}p1`qu-RP;?B}RAM=a
z*11^|f(JI%R}LK+Y4RZ321I%_8g{ti at cr$LwW0XJHfh^A4Je?=v2>M&(90%u at iB|D
zOD{W)Z$+W>i`(za&k-O~gk*#hIq at 76w|a9CsUzQXj44jeZz-WJzukO`WjstVw8P4@
zSiWb_Chszqxe7J at aK3#7QkY*WB^=XQ=KreZ+V9VYpD;Ec6j0ZzTbYqC(jilnlXYRu
zcWqkYq$sv~?yz7(4mTy^V^*J at 9E}mwmEFTBBxdtw!bPKr{8N+(6Vm}X?+dAXDb}&I
z%L;SV*$-DppZICzz7`_k at -wjT&ArLTHSGCBiVyq}f#61$CW<o_D^cU-R5?3;oZrVB
zs2-tB>d#`WKle+DN`u~M0iJfgWS*rJOG|i8S}H^A7bIimw1$|KR^YvdO1EFP##du0
zxv6yue|Owr!|2HiuOH6nOw<@})2ynYsM_Z&WZuHOywr|Lpe*{fR4D+p64?zuw>`G9
z(a-7x<v5>{B-DC#ZljO>TF-m#)x7&x0wnheCK)ldHTRthmU)uKJ8tAEQ%+0Ql-WS@
z%z2QQbJt<Xz(Cf)DGm&uX-|vE<yv7O6GL?ZYPBKi{I5 at dNnCoP^d(%jp96iGH?4V|
z<?*2`f;nzkPV0#_+4xTA=Pc*s26B1fw&LQbI|$mHw5YP!ti<v&3gPo*jEs&QUcR}Z
zr?s6avpT74DNarHaAFT_?!*~`YCF!!;y0$uqcD`lE@<RhC~vGRhm5mwkdep*#O?+_
zql)bwcF~R#!cIAS42aG#*+6ibqmZ0^HkK9&At;J8S)W+b>PpR<lHwCWwy>$&ZipBq
z4|su!z>ecS%9GWL#oje~UFjgA5Gok^onUDo{rJm9?}|uK)FJb*sfdjF6EDM*>D#Xr
zAE4hCyr*^$0=*}XkSGt_wj^R2X^Mpi$xz+K=2m(~QcroaIBLiwL!gReyQAo8VYjyF
zrhv|CrD><f_eO{t$v19%48$n%(ZHDrZ5&Y)?^BY$QWufztxK3<VYRvE>Y=nn=MO{-
zJOIwFo>uraqI-ahi-{~he{fxVf`M130W5r`{D5QwB<IR29CwD$bln#Xk}6By9-#59
zPdsbgZn9V_lEU=FW2m!@-`^Q at _b;SLY6*wzp_F`JU4efQ{sm)eiE%2Vxrs}(J4yby
zl~xAFw5qv=mFq3Hl!WyUix&cJUHg;^L^cX8k#;XTp}Tw=6XHywWAw`l^05^lo!^u#
z-8GKTxuz&pkQwFj+Ld;^23Dt+`ENqbcPlAqO?vBC-Q+cL9}}RTg<-sy;~)O~cH9#F
zCLn4jhsNWpq6#`Ms}g!htgibW#kJgMEO`ivLulo4FTK&p<`*x2n1J;TxIVPt&@g7!
z+RvbOY8hJ*2pX6 at Sr_aq>xk~4pJLRpKdAISqr#n3MPFeKqRlWIZ^rB&T`c94Suk#x
z at Gj#YXQq%`PlnugjhL(Ip+y^Cah*vJwKQzdn6Vwhd`5=9PKzL-_WEJj4!1NwX{S^S
zi46Gmj>a1f`WFpD3>F&F)Dp6E(XFQTs<NgWsH#*6Gh$;{G#q40^KDEOxT0F7T;-f3
z+-;szyFAkVkTX?`;-5IQ`92=C7#S_2tEK^pU^FAHYK9soo4q2Dd=~a+KRKTB#}9PF
z!@Y8~)utQx&3&!WL2BkPrglzOy at QmmVBp2pY(t=!Jrd2_Jly-ONIv(&uvUCD*j!;V
zb8cmdVYsM%f5*O}S7xo?fIdeaT0rrV#9rn*RpJ5;f?(H`zIqC?7`;CWwe at YK^v8^)
zEQVp^EQ29PWdy)w^M>ep at F0SV=J5r|VKJubjxUj{5D|8jyo at N+$0J&l_a?8C$Qk9^
zldg<iBCmNFeg=`AsH>(;v7MT)w)<k<j^H!?gw24 at mLPhBe0Bb=920&pwWd`~LaXym
zbZ|XS{`>1RoK>lMBkSyzHZIz}QnaiU$rscw6o!Dwgv-J at ZghdpD#_~K0_LNdR-^X)
zR$*!VOx-?lnFx>$CHi4A%S3<KV}0|6QW(}8u at INYwkAnvAWTqANfj48)qZ4H8=NxN
z^82A|4B>w$rCfePsY5ElOfiegsUoKSQ}$3}s4>4(shD{xpnZbn4At1=lws;t99bFU
zjy0O7&vKOI6vQGldVCuQNzYd`KE>Y5SqZ}oMJ at j}KzsFvrzZ~X10$r_n#hd^H{O_X
z;?+*(xcXc(TIH`7kWr;~)cDnNJIoXEzdZR-<+dP?Bo#_JylEXcSb3de>X7#5kwZE@
zCQV(By{goRp`uar7j=4ki?aZP!y_q&tlcrH?Q8lvIH%+An(7r at AosTg5=7O`d<VR`
zC6t^Tt=N74Ql53`wE+ST4ctw4t)Y=FB~vIai#=|ZP&Ppv8rtzvIM(D;blE~DS}M$H
zO%ncC1l4}m!)n!xJApjN4L%iL2-JOEQ^=Ui_BC{qg11*<`+Uv*GtI=^2L8D at jr6bx
z=oh&Dj^*KRB}WosiBvRT0^&pAdo at Sf;U(oHls#<Of4=toI_Ol(qh0xlyJ@?jZWT^n
zRP!6Xp!JKN$x<}tO!U=M0<iUJ*ADxRLHJAP3-fOvv%xRo%r>qk?#%*?zU8Q=-l*kh
z@^_>pLvpF6auok!nB`}%FUJIR8BC9PDYAqyU4`{bP8^32Pb3*(l|QB;CSDO|QXr8#
z=GUz!7Gs#_B2#X~`7`*?gXHX?D5y&y$K-N_^LW#ez3SDVU#dFI|9GXY=HIZ=(-Ad<
zDBLd3`~LcsVEaWH{(2jD9YZ||{LRT$npFA4kB!BhKrzhbSK1>2j+`9w{-rMQ--0X3
zqrb#PZj)nugIH;#0udBUUz at +#!yItl at t?}-H!WE(U6IJ&`_<<mB*n?yKHeTrXXwDW
z?bJb&l+N*;Bm4GUk9<@s9kh`lwD;Yuo(9(ok*zRIwP+GTpM?w5UZsf>Z(R5&7?y+`
z){kd+{)O<k3Ak=Lo2Z)3*aD&0)!YRdibXt9%6U+=&e8AUZNuVGk8`F&dAS27k9|4U
zu_2 at ZrLWC`NMmQ>$Pw@#VrI}B(ctq_a-!}OQ*O9UW%!M?>Ntc&803esIM3d!7$xAb
zE|L_ at X1k>A=;cDhaa at VLY1ohJz2^P(eeMpmU$$^BeCIZY&NnJ at ZAk``lsZwCuV4LS
zdiSkuH*GswwX8C4W6w(_$F73VpZHq?N}5 at jyYXlmQvP<H;O{EhK1aF~E4GMV$4`!M
z5{Vbvv*Lh~(BlYRFRlc7Cami)h&0Dj$$iu_6?MR6ohC{n=(eNN)p-@;8D9w1SVS}Y
z(<{KtP|cgct7Kc)8^{Q<{6<CL69+d>=QvEb;B3?~IJ?nJNcYa<yNf)p0h3IoZtWed
zq}rO8F}&_$RpWf&Cj;!Dz!~K7Jw%_442D(4#1XikPzu*7>B{9_^cn}a`{oM>nI at 2v
zP}Hjupv#35 at F>Fjkom!@TED4 at 5WLca_Dc#<B+_%!72~*4{5TE^iu`e)=#GL)?^fWL
zrT$V#h69z~4obsYJs-`-(lO^yxs_r}eok$vx<)xBYR;A8 at HI~9<JREA0p?x`k`e_c
z;_ECP`@{<VqYDK7WPMVX8Vs1#?0VhAq5Zq>@JQy=cX5H9ui`2otU_Fegs(Gq5Y}t=
zl@*fEzDc7Ef6~KFBcGq9D_YES$QziAq26%f;}n!1N>Ten2HwBMmA!X_HSmP}^^7D?
z+JITWSE=0BY*$~af2`i+^})(f?%_<0g0#6+O(~p~6r{ilF3ort#kHd1X4NvbuwAQ7
zfW}_;$?Z!Qzp4~gW(cQ*TV1yR3JHI-LyPuB&$eRmA*j*I`hhQFY(99LeZPdO2)8<3
z*&W at JOd1w)*umoEdJ+-m4Rq?;vKZKCI-l@*T)k2J;@Q(uFya+UEy<4rz+BrvBuGP6
z9Wb?3xB2pkc>P6QzpW|G_g2 at WU6_&~(UcNuf?;XKtKqJj&wEZk8Q at fYd9Pbj+%a5_
zRW(Fi=Sgg6169h4y#2}g6+V+t6+S|lCn*F-^b{pJuD=u_LgS4zr9Tpi?+5{QMT*E8
zprMyllZ#V*n;JQOaIUGfh1)?kJB}X$3D)okpfb!LOrCPm6y&!|_+(?P7GFsl_!S(N
zt_r$y$KVvhg4f4<_4)nnMw(0Lz{f!!y-({3{gsg$_e=*f1*xqf0HWI at nC}$OO7clj
zo37oTD^xY4EJ{+EVHeViKa9WEgBhFrIhc*k`~~{teH2uNRLrUC=0^&L4<y^`(mfdX
zvUfx9f`N_`(5C#jO}y|rJ3K=z(HjanN_Ig8!*PN#SCIBr?ga at tBn1oGY2JsNAqwMd
z&O;Y>W((W;C!9tDsk?<hmX^ZwLjtXv(26Te at c??i$W5|5K?n>ab(mtulH&7z)NJ0a
zRr9zlasv(LKD#huJ`YS6R+H+^j2)l}1C2u!mDdqr3=7u28+murBqsc#?ui56h6)z=
z#no2)qx)#9AwN-d5lUw$o<kjHER;i}W28Hq4|ZN=OS`U$C#((X)e%r>-|bR^KbXA5
z_P7>xl<4K^7(kvyke7LRwMwfSJF_8eXA4hWQqb~JE57IlEPmAl$13O93&KfT!S;{#
z*mnW|(eg9%2Rt at x#uP92bNxG7xw`+HD|pHQJf#KLnArYWSBC59J-C0*6>zcqk8%a-
zGB)T^Z!nk;RP%ZXl1SAe(|gl34UC9k at Y-Zq@j|<LtvSO>A;w9EUfDAYw`$cwMXAN5
zXW<!=A=E5{MD&J at gt{qRL`W$n_nr5(pJ-9eirnZsAJxC0xJL|B=jS*45PEo+9_9XO
z4tEGMFU=pDwEhIVlwa6e2ojxCXNkM!<BTYzvuUz#+l?EPSsL)VY+oKJNhFK1HXmQT
zL?Wf}ew%F^#UKiLH)@zMuhr+D#imI5sB;csl+A^%Xj!U+^B^kuYH4nJ+o<HVCI+OB
z?*8q?Sf(J+*5^BOwL{wv!l&o&Phf>sd|vpDqe0yTYBrLHio6T`biOBF)3W_#lpy6e
z_o#sQu=N95u|I33I09;RtjDV!Wv-&_Fpa_lLC*Mzpgcb!4Iwc|p3D*w at n%${!U$$r
zdYo8Uvm<I@$$*G&Z{3*~M8qVJu at gZbRf;9<Oe_jGhGD#UL%%GeHs#jE_E*7rqnQy%
z?peH at hR)1fwX2w534=k$V9n&BDZz>UI7p0mj_#TmPs=>W7EB{7E<@%h#d`f&l*LLn
zp9y(?ylQvjv=o~zM=I0UV(mwB&^KP}qE1<TwCe;R`0e16V8MLa$3hZtUfLIxA{R*&
zVd4Zf?p=CNfUmlostEL$`4s4N|74f1Y*;UwCx%B3;iL;S44ilXk;qF at rB?QuBCBFr
z<SE4j4`bqpvtx5Sv-X!&h-dQoNrWQIO?V3UCtGmPwx7m5={+QvsBFz(-}_N$HWP2Z
zfa4a3L=Vf^M02cYQW}dsfA=F}L$k^N{bh1_<DCBoL%4UzX}QHHudRe~gr<h*<v2Ek
z8Qzo9Awp|+JSwn at eyjXxTi~X=KcKGFIX-GL=1t#!8H0VlzI+vFG1%`@;HJBuBBjL_
zbuF^SPyA!+TxBS?Xcno$>j%BJfHczU10vi_RO!4tm6&(M7J7z7-`(Dp^L*<!j(NvX
zGS9n=zUU|1|B2>7?eIu#qp91A#+l|=Lu2Cn&=_NR-(t<A=c1#!pX3nZHiD*Xc(-s9
zR8_yFrkfy~vdgJf__DzwPOOp2rSLkS8u2W+pkTK_rwbA9KDoM|l*9|lddGTCH`21d
z3$dV6Fp@=?x^gf3*Je;kCt|{OT~fSsv+9!H`6Y+c!1swjnEBa|UM7wi2G^=pr_kvN
z`$g4lQ#=yn!IO=%U#q)k6Lw}zF(T7q^J$^ml)``qPsz2pWJ^fIAV3wIXXryJX at TBQ
zQ-0R`&o|1-^9ocRd|BU8n-<QsYA$}13$iAKV!xgI;;au5YLfo`KPif51 at NR3xH(z=
z%qepH=bR$f|0t)pp)KQxE`b`ccwz1>wp=}*Nxgf|1!)3j*otjZN97sGj%tF%CkWYi
zi+rzhN13Xor<Sy+$BJ4ucii at dD#XLc#Mt}#`_yd&D={zNF~03&HpCUQx|}k38)r)M
zAcf%M={dzWQ+U|)I?eDcE_uVyPDsTYzv?xlQ7ug;Kl4TFVfCQ<`!?op29A$N;v|};
zH!%kEP!&RT4++Ff3}TV at qZx^lLZJ_h5!KG;2AY;8mE!>Ugg%=$MBpr<4__hQ8pPEL
z5$Ys3;foR2;YL;p&6zsP5S&%7M?F0s|DZ33$q at EMw^&@)W>Qiwa|k&V1fdUMFf^0Y
zemok_+##^-deyk&K3-l<ejVC{<uc4_Ufnuivq^H)p6!=+U*F_jOC9Je4&BfG$S3b)
zIlR(|xqtlrMH~VnBo&W^J`}+X=O<(Fz?BNAF(XdNt&g8TSOj8C>`cGR#bA0Wk96Ph
z40y~uvL~z2Sb9FH;nY9ac4$K~)Rm4S&S`dsi`5-U2f())P-bDU(@?CiXoaq=6BKmr
z4O_f$+D!T3K#Qy*stA!BrQ6|v!8-P8#V9%Qjd%Y{m!YJwfX)HpXShOlHba)>H8TQ&
z*VA2IYTG_Cq$LDyo=!ycqmN-PG|+tA-(UmT>ylwZ1w>(Q*`Z%si at LDWW7m}HfB>pC
zSkbJ`E3!Kh&Lvlbx(%1gAJ>T@*kNqRlt3I;`9V%Ne1-H>82HVmFLl8=L-^uwBPKXs
z=-4$Pf8F+Mqql8Uj9+1D)y2iJXtqY|T|zr*I&fEvjG6B3&&9u4I{{yzV{)8pY&4b#
znSh|b9=~PT`F2ed-_yG5*Mrs{i54%By0z!m-?X`+^*T^n23q(PPXmky^_JR%@T(QR
zEFYY%jdxUU8V`Zt(J~vc)D=_UZ~uibHwr(_V(S)GE3pQ03x>TQc;SWd+TYe;m&O*O
zM$K;uC!z~2tAt<Ax|x3;S;((7+c%b!M2jJ9*C(vqgHpFXK9<Q=^^xAji%syD?U<6{
z70S?SAy$RC6U8^LqoLmfI2*U(eE1}?jBi6SlT!nM|3dLy*x^xlv?RbI81LFC|0CM=
zq{cO};uuv()thbON19-omy*umSU_RzElJDJ3JFrJ^st=7Euw+yL9x>}sFP-^oowe=
z;b>||X<Q~88 at 1tel(Zmx9%}y=uqztveip4Ft?k;rw-T<A_<p#pYt{?65bz5+SR{9Z
z;_SQw#O5ueoxOEyP_*WJWS+b*gB=gcR4+s1oNfs7k;4#y5ZKg{A_8kxeocXrTbpB>
z#7*lyQKIZ!&XX6zAGQnvvXZ{J^|idIG7}_ufZjdT+XxP+t-476`bz=Tc0doBETQ}n
z-|?lJ?{IlreI}|YFO?;=FFlf>J*3p90kckae={z#2F(V+UoyFchwP|K%qRq_#}?~v
zlDw>N51^dwIY`;R`<Ud8BD=cj0+bVIf&dh1T*R)}3AOg~+33rnesC7hGhJK?wsPym
z3~rbrWvcBGHMEk1Ye(8Ry)79F7uZ@~L!@-$4r(okrY#xrb)BN=z9OXzkPAk%uYXs7
zBE-*T?*$;ys7pdI5_0AVEEF>=B<Vv<Gk0hs1#@V?F)~qF392bJyTwq(4A-DtT4CqZ
zE8=<gn0<?(pe|qaBPm3J``E`^ZUBHVY5pPZg{xgh+7LNg6Iz;rf7u8bkbH{3Gwdby
z at q{Bjw@(l%)dERAsblgn`aHNu%7aoXW!)jr0>v!uHjd7>i*MmZE+os2QfdHRI8@=I
zk^`GM_5>98i^s7TbGGdiiZbzycSEli$S*yWzUXd((zH-HsBp;0`h?HB at srp+n;TUI
z-y8l6qEM)dzJhc7>3`wC!ds_ekvq9f+8-31n<2G5?Utu8*(pOA(m|_z7K>2HZ~@0D
z-vPl!_#+t}GFL{o#cIQm=Fz_4P*JheQZkwo1E5jl5Ug4F?Gg5?q0qF5nw<OwojD7c
z$_!WL%D})Gt{|%!i!vgSgP%vp^#f!O*}|mEk!02-%2>r%2Ig?l%w6NAQ+XWO!bnkS
z3!Tr;V+*F#M`CV9Doi8TpYk`~8^Wh41e0n%Pu~Q($w973ElNw{y<eej{~pYUfID#*
z8yCggWrtG6kd9PzdbHLqA-?eLTEhkPMJk7My0Jw$O<t~lzm!EgMZ)>lB;y7=a=$x6
zS-iKQdR-e4;ucU9WpTK*9BQo_etHXnW0X>{M)CWLVJJGhFpV;u-jcHnKQ}rpl#<32
zO})cAV7Y*T at selup8ozUf||R76Av4M)W0|nr1_>-=f(&4I+9bA9<<EfF|t*B5jTrw
zv&0iNA{|7=MNgkhb)Eo at +n5G}R~J<bMO2@&`hi~K?r6sB4=UqL;rN9)$C!-Zm~b(_
zPu{zHXC#uk?pz*-E}q4TW|^JaXTOF!Y5UcjQPtQnK30X$6j8hIXD`!B(rip^ZHh)`
zXe)J$H|q(-EOf2#>u at G0VXdMw=RPR;2;G`|^6|k6Rm_s-Y!EIXFDb5Nf?v{q<{T)-
z4`N%n!5xW2%)Y%hhxO6M!G!PhsNZ31AiSWNihij2{t#I*b69oo*g at dg)>_1q2>d{}
zxQaopUHzU`__WQL_4+_^0t%qnCJ&)!bF4S8oz8U25dAV1dHw2>N9w9kbDT0d%Rx=r
z9wa~AvJQJETTkrjS at IwQX8DMPw-vW{{~=M*kE*CTl;Td$fo-`J+<PO)X({3;+_aD>
zB|n1E`FrZGoB<D~Id)jb2*My5Ve`>Rq;-X4WFY~=YQ2ymGJ47#YbjG7vE_gi!M8BK
zj+jdcr)|<|hsBE5Upv0V==GK=En?EOxL41_G&H5>6W1bfOVj;0UDGD>w%E<4ZES at B
zfLnKXeiaP75PR%2*|Jbz3Qb7iUw!ow!rY1&UX>zT#8NWi;I#CH_<PshdG3t-uju)A
zMJ!$n(uCeuKkIsZ`;!wJJ9 at H|q`B#XDGr2|elV!L+7##+ at RixVA8M}RZ^EZ9IR4dB
zd^Q at V`VkwK>4s5Yccp4?x7vNu`ivr7TS2vFXhM{vTS!GC`(=lf{Hr_R^sm|muJaQs
z=Z1y`zYx9sziTEng|jCwT(6mVJ9((^V4iYYz*!Q6tgs&+dR0mjLxo((hhD93@)&3D
zTV1XiIeT#WcFy16X)2hVCkhzg%BjFh^=(;meoZ$;Q1h(fGV_}KM!5c~I?Y~lF)fuM
zif><kGhBKKG4QTgag0lY-vZdHi0(~UqOGXML>m0F$CQ-{NLR7&PD<dO9r_L<o@^Dl
zU>Ty5^#$gCb_M^88Clpkx&QP9|F#sVs=J*rqoRSSF{7NZk-34ets9Zf)5liq-0VbL
zY%F^4jLNo722RFAOyF7$#x_nwtWUuuxY*dy*4e?(*b)4UTG7GQP{r6uhfz^foCrJ{
z+?>Ge#BITqnV)W at 4A0LG&nN;u2e)@5dS3PPsR&H$latE)H_!B+Cg*8$s{cR^^RpNH
zcY6L at z7;&Of0!PY=ScnE;bD1#|Cz==@L_pE{ErC#AD~Cd8q9qWBF5*V)L-lqv9+>w
zP_Z*GG$vyCjT)n-!g~v2Lnk7Z=l#6?;4tf7<o=7pEPrwRk0G%BElKeY4B_t*$NJa3
z|3we$Uoihk&;OptvHlk#$NC>d4!rc}Kgr_1<HPnB7XQMB?Jpkwfe+h1D{}uv&tF*l
zjh?@F_$NJ_|7-ZLKYv&H?^BZPFCP9u5Bn2{|J;=PJ3j3H1wQP*b at 0DbDEmK)&z~yd
z54!%;1027h`mO0X{tLNc|BtwF{IhcP-_iMm4%lQpTO^LZX!@7DJ+ICEca_WWzc(K_
z|KjDp=z0F!`S0{_K23;!PTu|-+&F(j^&2<N-%9`AEIH?YH04}RisYZ8^Y8P5>%YM5
zIT!wS+_?T(-2RQ8-$M3p_&n#_{!Y)o#2;*D!JZe<v+LzZ#3*BK<Vd6g|6A`pJ4DLx
z%)b$THvh`-&n6Un_zn59!BB>O*3;m_?|VOMC1v<$WuOfIJlDX7- at QCbkTU!;gTRO1
zH_{^l%bfF*o5t~X|L5O%2(Dw`WME}$`X{l!fd~7GP6kHcD at 5FXapDi{`0VukAqC3J
zOz_Ol=7mw2nHiq>S!IC#vcNMxD-7^oR(R&;9vGE5;hzV>sLT!jO!dDM9P^VX{{MIR
z-#32tvi_^a|A$1dJgMTResmb6Y>bTEjE&$KMU7p|4UHun4BX*a*xBK!beWi#bXhq$
z!9NaNRwfo*W=>8~8r}D at x-4ugBp$jf?995X%&g!uCOcgwR&X6wHUSG=Ru)!QT_$D@
zUT_;GT^m+#0lS(mD+l;G2fHo{2Pc<_E(;U5m;>CDiBp$~PnVUAO$pqZ6Wj|kxCVzV
z3k&!<D+~BPvzwl-F1VacmzA9z`~zQSWzuC~W(QLTuFnGQk(mul!c#d5sSdaVe8vo>
znVl2dnhAUkCXyXIj;9)&;2zo8<d|*5!T+De#4cfI4<-WKF}Rbb;r_qh|HI(_G8v%O
z`$Jbf$2NcJ1{MzZ=Q*zYFU9j5>-=?{6Q1Q+L;dag)A=9QRh-{DJ%!WH=JYAx{Z|C_
z+lgb;FgF6f%gw>g0naFDY;J1iM8wI(0?#OH?&PQl-hsi^+RoPIDaPgkJ1gc^PR0(5
z;#T0`Th!Rl*2wtJSHao96Puf#-_gmz*uWazEp<N)L1zw#*K-y)MpF4o8%o%i_7xS4
zN+zHht{A>Q*YWm2e{(3UlFBqY$Jg{;KjGX{@`kmC*Y)ADMKM!203L&8LVygY1|h%@
z>!aP5haj%ffgY7r?speU(vvOuQGa7O&MY5jCjGhkr=)y-a*MRh09D-U^MSh*RKz$~
zxz`FF60?&Us45;Nj19 at WK^`$rwUzB0+?6%8baR5Eb!LY|n1ciRKT-ILi*-N1lNQt#
zczVpwDSU5U#4%6w%#|TfCmW)!eh;M(Ia6X at inb%{fP`Aj<n<w_D)Ol>AQQClq<)Vo
zOdlhCo^<mf7LzD>LQi=v0g)W79bhev#(TstJl<5z>2oj~`8?_Jk^wy#E)6m+f=n5z
zp1Y?poc<<}^Jzkl2mitGXW|^w25L~UL)h4nyHSY}_1W2j<<mIA12#mt!L at h#;qI;X
zOB=`Z0WC(Z6en&j5gUx8z+^1;u)bpX$@2q(DdDWl(~Ppx&YG8>OAokgR8vpWc2q2t
zLr<s(sB`QADfzxdH%uT*zi!31-i_xw1cA#EiJ~GKLSKc<_o1WI*-BWjY*??W_Ccz{
zq|Hjr5hquOUI}YX*AWs2{eEg+^!NCiI(QG at 3Q3<p6aVC328C)kI{(ucuZXB6*Ol~w
z8Bh`V3UBiAB8g+m*my&Nbln~&942QGj;tF=89O28PTGx*>MO8%8);^2h-y8%)v%Dj
zcao$;za*oMechfq+<zU(D;Zn3<>I9fnyE^;Mk{H&xO=RT=Ha})?9w)PT`_nB9!3FF
zFpwFMJvnTss07gCDO>zOxlxZ at Fj)Afwb|mrsB9p%@H>!;X%LVhCn^$ZWX(s77$jp(
zMn((@rJ82mqIsL-Gu#$JC-XFPQersw)Pmc=Dj$x1tfU}}#;hmCKGi*QvbvQZ?dAXV
zNFerSMN*qQmkkBNVT^{PNAN2`V6>wm{ei<%5HHY|X>J^AeyV$t3x+&_BTN#~W1hB|
zEH?SEO1L`~zLI7%XU_ukAQ~}_sZ!B}iQCdHU9qelb8tReTK0l;!Nw~KMYS=9Q}yl;
z at XU38Q7BN?p^cX|D)DTf!G4sNryn3kLaZHD;oOf;wZlz9ISRt#$Ghv#H~a7=UZGLB
zU(*AHV)dJwUE;?o_H6R4O0%W)p#}19X%HfFS;lYXg3pVBk5n1z*Xr7kSj>?<-Fptp
zL+_6MxEq-p05o#ST22SYuLk9e at G9gU(*j))hcKo(pe*a;Q1B*gmZhy`$<3d#vGdMz
zulIus+eDy$^g)KL12&@y(pll8(m;>xo;$sqBs2;nWoH|1t*GQYMRs<(yztl^{}032
zi!*u=fB?fo>QCfxbKa!BHNKmuS%Z|><*O{;Ar02RhH1CNp0IX~KzHE$$ypIG{rE!o
zbUq?TcsuZ9rZIyGmt|mJWXGq?n}f%V{ia+CAZ+q(y}9Q0*U^}+&-1i`lZphM#@Zu+
zsI^~(ZKn8Nx+FrSliM|nigfw=ra1+AD~+>NFgd`qY$=r&DnGQ2)Z|Ld4!rxiG#}WB
zmDG<yDl4}5rG22l)IUBbX*9B9Ewc_|3j%u!rG<#>_Bx&zM2JPdh*-5I$J{?j9`Num
zWgnkMv}x)o?Bt}O>TRsB?Yhy$iYTS*G}3p54H8>9X`qup_V&!U-Y{Cy(I;kG(P_%{
zm3(<EP$4Fsc%s^3FO1a^;%xv&y>$X{PjguIh!!M4^~+>|S14@}#O3v at uE@yRPVy;~
zkKDqv&?ci(e`=EqV6r7mj6%qLMPfY{3p%yLPKEB*u~L|YlkzwXr?jy7u at kC&coUjw
ze#&v2I7~T{61!)65#8;AZ{1K5$ZIVZ+IA(LAhAtHSGT2246?<AlgilW`D{KiRFbAJ
zxW}sVSzTrR1hqTif&;LrB;vWEa1r*PJ4XxG#|yCBuQCr0#H5mud~eIblv(<2Ulx*G
z4^Xjhl;0xTUm^ln2G25x>5|KahKm5VPReavmKk#xU2PFln`ToW?*-2J>(E0r)e!1s
zb=2sl8mxPj<si>Q#JRT70s}xvTW_;303V>$+&nT at NpT{1wnyu;tiq-WU_*^AE79y<
zVHjP+ at Eovsll^Y7y^V(LI>)^On(nF%pwr}pe3A7^tZHW at IPqWyXu;=-IM<<No!J_l
zZ)6aOm8Sz7Eq8-sNn@&U#O9=bgv~WxPW~VX>8+m?))UV+xCX&fkqs0 at S+h^U;6QZ-
z#_Ffoq`q0kBl<n>an?rtE%r at ap<UIIslHjn<mi0&Qp3}QM99~})<Dq|>(_5SN+A&=
zDd|5e+`8NKIlKH+D5-BIJ<JnoH~#L?ALS+94?uCb{d>-r%hutJ9g at Q13b;s#g)}Ok
zWa#owfOtT?t*yWVz9V+j=sw^ad4dE at d^|Itz=VytlXQ}~$d?76qE%gDRnfUe&gwZo
zwBG{bM8p3J(=R0MGCcV~@;5?#K$p`UIN;O|`#ipOL;A;d4JC!LcR4*e-v-XURN#D9
zcDZ?2Dp#=8y10ygbO&5k2v8nhNd$h0AqIq|OO387H7GgNzZX&|A(NtX1UHZ#$3<j0
zyl+_L3v+8c<z at J-ERzOF`IQUp0UIIa)rKO#r74}64V0J=ad&@jIAP_})Ge#e(S!cg
zvA?!#s!%PDPrZ5 at e(O^ew at yxJmsp8|5Q-1?1#`TB7JEnBpq@%=BE2nR(mjE8(nGuS
z-D<+k+QtjDlPZ}Qso%pg2fp_ccW(s0s_`?J6Toh4X@}NY8nhh}m1ViJ>k}YR*d at S(
zZ2-~bRXp(V4-)$gi3QpXqnrm<y>(1SEyif;E at 7+t-8z-(l|Kk!_|YtKkIdp at FnZw&
z8B%%{7qdPaGhvqQ;9p^cnlU>5#icHlFjqkn4Xz7c&V at Fna&DSEwq{axU3dA_Q}&58
z^B&YDnG0sx9PiGj at ne6Ha*o+{R1D{&^jhE>hCb_l)JneMl80mw?%$f6Oc8Jy!=l}F
zT<`Ptx6Od+BPG<uI^Ayh!vsb2yllB=f2_G6J{ozd!7T6h(|)Ar{mn+nwLlTb-3Rl@
z5wJ%^2PHo~BtLfCB-XHvD!TuWl=Pz$z*JCQOIvNpkL;Q3caLE2K<Av`4FDFaA3B=h
z;+)og6b5J%xQ={-K5a$R=v}j at YP+8nMnA394gh+~5`zlVde?gX`_su@$K-<am);c^
z;1*3j@^x4UyylzR`Wq*J>pi;<$=(^fTfS3YQC;4TOt$nJMxN=9$1-3036rLaosXQl
znO$K8v!XY2AzWJ`bzO{{CNAi7y%<ztFF_i{I+<>4EWBncle#f=@BEcx8^xtJZ(qpx
zv&7vT#kb<VSoo0v<P)nl$i-6#8&H>UL(5l0(}Kx;drotHbgC(P%@goLR18)g9h9xw
zy9RmV>fXs+xm!M+bGl?YB<nLYK(lTy!cNRaDXATfhb-aqV_TH*;not#eL+zUJ|ZBC
z5clwwm>oIp#cne(2=9CF>gX3MbM}r`7O>MNTi1E+>b+}$7Sf*?tkE07uJ#QxKO~7=
zzte6O0dT5T_+4Z$l3~!hd}+Q3%=Q%q7x8z-ZJt?~dQ*XKx*?(q#{VtY-Jo5!zkqkw
z)!pk|JA-fR3<VSVQ(FS?X!%t5aQcch{cSFDaT&Mb5UFnUdq!mhLj(%6(dvpCCQI^^
zta1}!Qgb-E&-C6dq&*3mpO%X_$Lmn%=~rP=$QqykH>4DU7L{VIw&UQZr2<z|4STmu
zqgscZ)a&-l>@b*y-f}S?`SHYEN_?Hq(I|)|*R1YB&+EK<_=BB{rIRc_eW^-t0l(Ee
z3dlL_X4<bc$e#kF)PGb6CrE%*L5e|`xep<SLMue&2XUlCCIdd}k(YHnev@@s;%-@@
z;2(ayB}WWG2;t&VZ(Qodc-gSb&_LSk6x)GK(U6%=)?;!zsR%_Hzy-94G4pGI^JH-a
zFmb5KbMs$?bTWTPh;-<5r=v|^iX%vgtk)+vHfwC*ui~i6itK^T;Q?|yb-AV_ftb+q
z0h%I2{-}d8*nW=W7W-bw1{-wivXwG4DA4WRNm>128UnO#>XLpk<4*Io0KfI;SMDeY
zPrFr({6;YQmF#QIABGYKSiOyQBq>xYjWmR=H6y%G1&hC#YlnX^rk1dRzw)f>V_XH&
z*eUg`N)g0b1tZ?l_Z#vsG6Pg#XR>XFHqk3|UIJ#_aklyw<|ZbD0a%lmYs_2M{KZRd
zvF}zOtVB^+cI$!P2R#zL#Cbsg!b6PJ5}}#5%8;-&y4}f2^*^~|$@HUaD2d<<h(cSn
zLtN9hm~1(x(Sk$(pW-j`{kX$j8kf5<)=qH57Vd7a27Q_d`GOQ2)VH`<*G@`KL(Vtf
zzTPEO9QhKd at AIv})oY-H`5`rq{0=t=egJ1ABRz_rxh4L~9YO+U0Fa?DJ3U8Y*pOQt
z$F0<?w6eycoszng#-6^*t&`l}Nu9FIK7Taq%<Y_m<A4vuLssf6=`NGs_o7nK8Wj}K
z_^1NG_${(0po&nU^HvW=7^~dMXPTOQ*<j&&y=_K%pss*%x at c3pxkVy)+u$3Vol~sZ
zO at s+EE|7**WkE5?O#u)Im#7agKjPL2D``<`(8Rvc?f+5(RSY%ak>%Rm+=Y)v=UdvH
z1X$hFR(sO%DQ9V3gQj$@S|=g9=X{gs3>S<))ZFB}N`<zbHK!eluQeHc-2mHzaQ9t3
z`GE|ybU3$mTQ+5ATe#sCvb4AP&ULPdDJ{jJ$t(c)(?o#<E2ezKUAct&-agZ0M}Mo#
zEn4h?;K&h(Y at QcU4(rPAnbt*aZ;Mgl%kKnz)8no>-~WpS|Hypa{Z?SJj|}^M9LO<J
zd{eMnXYH*N_5uji^w5K^y6CyxqWNTth}o)C`G2ag$t#05qP$0!d}RNy0D87k#B8$@
z7PRp at FM#D%OdwA>igjY#nTD!FWBa0yAo$X;^`LQ9TjHm{4_S88W0E1IYA<dp7jKZX
zMqxBl4p5)#CekEY+NbSKa2q%KmgmXqFf<6xScfNZ*932eVCN^dIa)<lWOCNU4EQGN
zzC{0IH4F`AgKsb^#>LbR0xktw*Qh<JTOQ7UfJ{G-J+B=1`nS(>GsWFelu>5y<D5Ad
zg(;3QvtHRqfK5MZTViGJKmt)pbQ4xw^S3WXUVU%76#VSu?hV>LB6hT2eSiV_-0cC?
zS@<Q6j4l#5Rt~BUWc0Z#Y-r=0 at iF$+r at 7-JKCSZjPq|%7xSw0=PFayBR5?|}iY)WA
zjgt||nhB6?AuI at goe^rzFRRygx?NsZlJKGQSD0E9v*(QX!lSvh*xpU8|2>a|O>Ubl
z%LoH+Ykyq<TL2HMe=8Vm4)}E=RVJ<U#Mk>9m0l5HWNHs at z?AXv%bUR9RMlGzT#h}|
z$@`Y>wd`(*Joi8b8!7n;#d2dUW+N>=Q^mHZ2*m{CPH{|%!By69bvb5IZAt;SUh=J+
zv&DqU%G3Bf<WQ&88X--NQtO}PNy^`y^x?#RMY8E7&2v5T2(AwZSX;5+ekopx>fu at +
z(R=yxV-Lbc>`AYVbM_gWt8PS(iPu<DqnSWGhX|@?>~H_Zaa at 9T6?-}2I59NkC4d|i
z8i0_`%ibNoc2a%N0AWlUdF|q<kzgmsPxB-PPxlxw0nE-Tt~t6HWy#2iML~d|b0pj+
zR^IZNhOG#XBz?v87>VeXc>UhwY>_T$e()U6w#)~T3udDf;Nxg>k+x>?vttTB>DT<t
z&6PwFWggxKX#<hpK9f68*_ at MZ+R5z>BEr^J!U`y+3n()ZcmWzYcf=c&QrpRK1C}jQ
zf}*Xl1>*FkIHqI!u*P8uboA;1A2HF{!w}jf9(YrJ<VzTJL|z8&GS%~bJ at 8h3+fcq9
z{oA22LIUjovE%XQ-RN3!rA+UQTMf%lzi#w!SPrCa+gMNo3{ZzT%QCgNlTpXx=&?-_
zMcBMz-mcScg^yP at uAFX1c5|>^XU>wQVon!vX}8o5{n;-(h(-?TXh&nQL}2PzZ=Vt%
zPngn5n)sy_`2IfRG_kS#u+-ayeKkVn<jSje?9<8TaO^^-%L!hOagx-IUmdr1VtHup
zjUpoK4-cfTN8a6ovfO1|y)Oku9pHS-<=*?L?V#P}+>Fok at Cam75cI*tkN at _^WLKk7
zxJHlEJ|1<~vo9qO%5{_Sd#*(Ra_h37(cm>GoY5J()|ePmC2kOCz5KuDP{`CfixPgO
z at y~z)9BcZv`?Sp#zQ)nfWT~D5;`_NzoMN1*&$>9GAKpR>;|N(La`UhaVYBQA at bkVg
zXtS8^k%RctoenmHg#VS<mD*)F#D3)__VznJ4a at i_%VJKIU1oQLpjOF36@}v#C+KQ>
zg<PTkisQTaH-0{L2>NW>YiGak_s$$ai}6}6T~+1zk!rhEZ?AqT0?HKUHtc^KE(%mS
z+FUUnaB?!0e#IwnrKkKYdFu~2k5#IVgJb|7+|u_#Y2fe48a0{+98#yu+$iU-JMV71
zYn2x3d-u74$z$;BH99$=EBbEwO?Yc3_4YQknUaTyXP<q-A1$AJ-R|aV+)I605pP&G
zFPIIOYQ_zZ5lDoo^7ki<VrLuByQ~RNvb5^y`j%&I_{UqPW*r$9sNGFG##P;CdljDP
zr at d)cr|;N%1d^U+5gQd*VrGBCfaX0vV%@m!uP+37O?fUT%LhFISOOxPJp2h-FF{t1
z#)!b~@bRUXq=}`DhIvAkUpFUih<!A20~Nfx8e;E@)S(J5)z(#=W)PO|^W5o%4hk>D
zF2+!?k{FL_0_Qsz<;H2=hsGU at t0P2^>qMl-7LNm#3{!MSy at 1wl=NV+&+kgPlDFFDp
z^wT{yw|3bsZtqc5d$N{@yS%eKQvI|OSrsIDR^P(iq3 at pM^`Ay&S-MU?AE16nR{|hs
zz``{ntmKiJ`%9d)=%#1RCZrGd=bHJ{ov^z8f*0?CLV25e4fGZ(6G&=eP1YCL`8j(s
zl<xiBc!l888l_%NQiWH?Ra*%0y2oA1b6f|o_D*q<i>ykm!f8GikQjoFCj7X0_8DP*
zwgW~ELCaapag$+GHlEgLh(&)#Cz-VlE*;O<FF5!KP9Ref+ at sxt^t_JCx$Jr07BS=H
z#qz_FT|MT?DC6(6Cq=wAeWbSc&S>$5$D{_)02#(iVsT`T7}(f>SHOu2j||_NIpx1R
zzli`1`>>IeEXDm;F#x-LX7Allx9zGwC2mZzMCM8NMWtR2`E)~&w5 at K3_sH&mH$Df;
z1_f;XsJi;t6$<m&ulO1rHd_>_n|ECEyV<d#kubGi-0t%31(Y#bjHB*nO&@F1t<ewM
zE<pt;A0)lcf6<g3ZGSMSBaV3T;!)&~RnKZGGW3tdj0tYp at 5CxtDhT7elDvRXuiNPd
zBembcZxDl0iq4w1Igd5K|L7a|z}ugSqRm at llXT;iWlp5A$sKPi`}sNe=C1u%SrX{B
zgV+7xD`cK<x3Bw&an^G_2d6?h0BBPgfdb#}g;OzF?WnmXKF`-^z5JyLE<L?ZSj%KI
z93^bR_<qb7TDHdqu5Ws0R{6{PM^+s>)imuEc=rXfG~#pe3<)7DlM`U`5t7aaK_*C(
z5(r>!Nrv at z-!II{-n>BV^bLW1QU1b?9X``)w43vX>&PBCb#9QNV$n#UjGlyELJx)C
zNr(r6+rn_UUK52lCeJ2N9f{$(ApGnz+O at d?ocn?iIRWy5(B~AlRx`(`NdL0>ynRWY
zfs(e`x}DcUo<++qK|Z7AXCZOqTp$kJSbMk1A3L#m{y>}Zw;y{%nMOR;MjbZy7yy^^
zA>D3+FAS}kZHf|Ee0%suE9oj|H-WjenWY13l{RIOJ&5Wth=EejXH4COH}ox28QOXj
z^Q~+2gmDmO&X$!C(8V@;`->J_gC&%E9VaaiH~kU_Fs#baCsz&p`6Hh1p##$RkMqki
z>4&)1P#)Fa9JT{{TsbDpy4^ir?Xf;e0T71`nN+$nMb0>K1V12KN-eajZA=x?NE9|n
z2rHe at Eg!M~Qmp-vlfEr-{OEDFswl8via!>EoB!|-3dBrXooI7&@sILOgSB&LpJ?r}
zH5Ct>lA$wOnj%}Pnanm7LL!JNJ%2r&O<o at wj`P at 6Qg7O<<Skp%_1b+w_d?ed&5|v>
zUIWbZVkA9)OxE)H8jmBsao8cU`-0{oH3O&($EZa2NvsgRFxq5+V?3RwFi}k`<6ZFT
z$>%qyi-9mw4*mn=!tV>uHWLMh;5YYb7u&h|n(nj*OMBi8h?p<621oV`#bX29Q?9sO
zt2!EwyB`I&h6=ob%HZTcrh)TNht>WS8%=z9Wo+yA739$pcE_UbR%ks?jt$EG%u%g|
zb#v*xr1FC&bEv1av#5PvBUD$ELiMP5V>`SWJ`JVE_V8WvmOmo7slLl?LNgst9>;w1
zRB^y?`UHKBR|Hm5EMba+r`<$#Jfh(AV-LJreQ^K~$uh}o`9<*h2`%J at o-OPY_FXsu
zKbZ-Hm;^%hY=yy+ at Sa&$aFlEXw_ZlHT>j1D2S<08$k(n$S=Up!?zJ4{FP(-CUsCp;
zhDHT|3no=Ix!99Ir at Ux%(F!6pk7ctKR+en{e5KJw&<8!hQOc|GG$WaT9BMS`1?nlW
zZD>2$!K8y(u$&JTc8YmjGMeAznia1%D-GaZg1J%M4qkV4@;ixQ=~Gx;5F*$Ho?D7V
zG?37ii0-fNQ|L*M?JI|$%G^JHb$$xc`$h1JDxN9>eww9$(-fJfZzT2#!QvYi#HDS@
zSK=X`2LcD;DB${vgvz~ZB;e}>irq|M6}H162AbH*MjPo;$t%9jveTZ?K6_4tXGk`J
zBgdRy;b4>Aj)RvYTAjXQueHRg&QYm94Y9wep*vy`c<(d3Ad!5o&gr=%?j4Hf`7+d&
ze|QOe_s_u2Q5w<r at y0Y1`elXq@)L}XII%{K`z}AM*SO*p3_l at Wm~v>_Ys%?NSud8W
zB9gCsXA{qz<QDyuWJBk{r1Xu at ch<6u;NT)_g}<(F{li^U8rDYd{d#_X-XO=e!+h}#
zRwY<NuM<b86K@(xiQ($oyDs3i8YT at PhtuL%ty9N_^`xe)cwZ#?R&5SH_A4ILEKY<I
z&<)g-q6*0!_fCIo3SvA&`#JSdVr!sc=vDHn;ln3zIzkkT9>!1X?#I_|Y(`bVDXmoM
zm)QDMyjH2kJ`UT%!<H*0A4otv4vtp*xGSB3aj?c%p5bMtZN0R=vd`Zdk$@8SZ!rI&
zEP|Ms$pE0hrW-*_rjO~{19hJvmYl$`J<Y95_pvy*J_3O^NP;3r0?1Uq2^`aDm1c-I
zWn$3&$~0CPFF?(4wSI^By~3P<591&a9C1g185w{NaHWnOw1~5rz%BX3-RdMLu>Fm0
z+W3lxv!6O(6Ur;<y at p+U#~MV1{RY{R+$M_2<&6~DBx5z1G2 at ECP06~LPpcui&B=-t
z?vfkuAa!7QwBLzdLcig54TJ&CuDOTUJa)O!ZSvZ9r%@=P at vFbcHJtF%!(2m&q3WX7
zVV8 at +pru@&Ui9U(i7*g=ro~(<wD`#*OPzNP<848?IeqXF*?07NzL$wu<)nE*tk|y&
zP128GGu$iUR3iLB!g0FU)qSDHXMAS3x6u1O>Br9{k+8GGC6VvLpKq0y(>o1s9H*LS
zd1q#1+C#qIWpD;BvuT>2ryffjic+3v-eu7V$cT(LhInb;>;|aCSh+hLQa)t>cS-j}
z-Urlq3~LGbt7n{Lf3al(SgN6yWQ3?(Y!k(e96_!f1@(ha8KF#I<?ZRJ7q?84sKjC|
zP69SM5wlMu0NiiCl<j>7S<ltwh-Br=>4BokGWuc411~>=O}Jlhd4RL*9xX1JE?=mE
zzi*0@(Af@~rR)UVCUhM_- at 8%iOSkq?F;0X?k%U>X_`sc!TR2?=r(0}K9)dP46c at D6
z98Wtmv#o5<{^>PO^^CA_O5H|E2WXd!>#}j4^!ZU4WGE|2{dgS|Q0rmeaC%txUdg*@
z86C_KXl(Pg0-0JrpYqva=$poln-3tbCwE=Yke-VVDzqcNvfyKXL~#h#5#`-eeiRzd
z+P6>Rwg*(xp(<tYVC|)b+VtV+aURu*8G^L;X+Hg=`oZ!UmNN6BO|v){JjK>g%-Fk)
zldl at 7GiW?=>R`TD>w!Zkbhk!m(lX!|f}6{8G3PrQ8_lQ6$0%5zJ5R6Ha}w;O*hh6t
zpPm3yU42FneeWJybiH*|?8-p)6uLu_FuV%hP(oo&rUR>kX4C;v)HYH$cU}GKzO~Vy
zBXC(Il&J0-)IDUPb$fHT&$UFAWFSN|sIN#eObwXvFX)o_r({jwtsI69LSMV<YCrWy
z-$&}oqTgN%-fd0s9fq;C_XM|re{a69q{k+oUGG=e=U(VQ#ne9tPobzHjz2G}n}8js
z=ib-jB;HG5;GbeQKHXM3UIK5SSP|+nIgiPMcb(bnkEYZd>JTYfas{aYKo~{{6fFP7
zjcM0 at SFMO6c5YBqWX}p2(NDifPCyaxwRah#Jv#oKRgBv4j8S024=1P2?o0I60*I;p
zL`2gL9?pIG<1&Fc$jw7ykR_chu-j&Jx^M+1BpGH`@insC_~3Ql*Zx8mgCEvvzPaT5
zE2(e$4oHk_CR5(k>}Ej+3itzy%i`Bhus8>=JCw=wz8n01Onqfo)Xmqhl1hk3gDlb|
z-LQawl9JL5OQ-bGN=h!RbT>*$cP-uB4bm*#@2=ke=X$^G_1lRvGv}N+J+{*`wuWA8
zU%554ObmyNf$cp|bcVFPkqeRb^}=7-WO{6*Cv>bnN5hQ-a054~_5CfT+O%v(iEFFr
z$}`9e$D86!`|`Rd`l2W(&y=jYS4?6a&Ypg_)Zxvpf{e%)#shr7ANLqoV~-BpY+KGt
z>AgYS at sqcErNNXGGGj9IWgC8bMErgZgqgloln$M;$b;1uT$>_h0)b2Pw7BP+4_DrT
z*Cv%&Hcna!a8AH-(o-nDY6}ga6HW<GFRj+v6Y(C?Q2~Xqnt<e|raMQo0p*+&E9D#Y
zd5x;oRl4zJD?9JTxsN{(z^;}@gTXXHF%udVHvw1{Q_4ItbA{u%fv!dkK{yvQ=Q at k6
zF;!t7W$J at 5X$rq$Q^}NGtj}N*t)AeOa!V<$T$bL2KTbrGXS>j-YxoW5Y$2naquv99
z;mZ=~ruOp2-dw^S1!1ofuBq*C&zFAn)L#OKe6Ge(WX#~8k2dmM9eefCL2PFoXdLsS
zJ3CO52;dCQwZS#7k+LWL4{x>oq&%M2e`$PGArlNeaz4eCvlqzgSm&|#b at p0<2*bDP
zA5Nn{IAS$uxuwy`0c!PyAVwbk$<@(-(>_YbgWS7L)J({!W at B~U=5+c9V at erbVv2!*
zv*iCx0*_KK9cNWs$IWlWLRa|F3L34_$>#eXHF$?Z1t}SD#u>UY^>A05z;Q+m!A`Eb
z>aJTE<0+WW>bfM8U(%LFF0%gz%NZr0p{U3;bE1G#%v%!Ozk_>O0pw*b!)Ft>N1n`C
z(tqK0!gYT?`ntuaXZ`OESin)p8-Yvk<w(%NF<%KTXUHPJ9$8-c_X8nceEEhc?g!>(
zo8{Iz0X?QQm%%6 at d(2`$(w>B*%tv^kU-)K96GNVD<~rI8teKxPH8RuuWvh_dcQjE_
z0sBXoNh3Ff$eXq==>ErI?lwntRUO=_`3r%M%@3iCjXx2(lwV}^myKf4+qYV()&1G8
z7fkkbNq3uU5rZK`R%g%@2&ZgM_w?+`?@a0+#4ansSm4S&2LGHNJOdPj8ovH?*boey
z*kD9_<()%{<p*G+b+b9+Z8*7jUf$;+SXqxIrcxHT(Z&2jjnWs}D5-O+llyAKAGOk$
zs(xTE4XZdM;~Vz}S-I%d-A2S81^k$r#s{81-rg4Cb~*uWG|VqM8W4sJvCbb)em;fN
z5=g){sSf3u3LJ{7plBZv(5k=I%c^<-91B0=iL`MK49#xZnO>W5`pY<*RbFbtt8iIC
zr`7};?^}B_e)b00A~^mRQAH(fGJt4Kk>bw;_=h*XRE7st(ZDn+Pu~YEOoXPqA$K*c
zNY<Mjpw#)D458e5J%7Bhq;`+Pjq$3BhC|!){kSxpHyZj{oMEp&6#->)fbV@(2rwy5
zs*9*W2EPAM6m13)wr?PA7{ut;W4YAu&4N*1Sr=e$aP)qp_qII2h2JvQxOY4gXt1ju
z=+f2b+TS*mXL|*M>*br_&d1qZQV3uqcBYk+q3d6TUJV3${)Oh*y`a at 8*hUC=^Z#{<
zSm#)a{CM_XC*fo3!iaTc9>C_2bn`{-zmfiZ#GSY*qyEi^u)ba6Bk$!!Y$(HpVRv2X
zKwuE?SFAWd0sv2jgTS#bCkh$`by4X7^{flZ{Je6eMyyMbqs(qj8o<K&m=cJHlz=g!
zsTVaIrZ=B(=yi&MzfN59i2M>UgpWYh3OZC0`^w;}{^OCi81AmRIW0IqL8hK|*EZ3F
zPm%_WM3%KOB3hDTI?9RuT1%4*ciFgXJ0y$6{8m4p4c{&JWQ2pq5g$#xH<kbI974ZN
z?hrf)xmm9?dg>js>W(fZZ0CYlNl0Y9*6ve4>YMau5*f;TU(gA-U)GLK>YU{lh;24&
zwaRammGnef-Bh>{{atrU at d)Ej4TFg5IpC&9G=&9W^Rill+gX_7D!0zW=bJVGHgAY^
zepwpYV|kb2ZhoD-;H%9OsxLtl?R{w6pDpoolLe73R5zSQcN*Cn&fXdz5UGU<j4GJ?
z!(Grxjg*cR?>+3CdSBH&#ooqPh3)k8+j~6V53BfJhRpVO4Z?0zhG2^)l{l|znnc8u
zjKZa=o1J&zZ1GiY=}W5h?C521Tv}4?)m1{{OLGf=7 at E&|xzBLq-8=MK at S}}gjFUZe
z4)BUNyw?#f2e!miMnU=9BW5NU#Xc21 at 0}%s%(LUOVJA}qB{O68kBQ|H_od?md>G7U
zEDEWSCs}mg1Y4BwfBE^YuO!DxOC4hTolU!d1jZjl5#2zVYXRdox+H^JkZmtCwT4$Y
z&{)HD;zYO%oZPt$x0vcxe3v67Dzsk}k{0Pl`kpzGl~XNReV7Y~($NOic5OBi{2S+$
z at QCy7aKODia1Gs3YStfrXjM5Va)&#&lz|KxaB_4-uJ<)gDaMpUVPeI3d}O54Y*0kp
zlNmFfnZCvDaMD!kdLLz_`-S`2>&jvkWcdqnSU0sbYi~w&`JHx_jJbU)sB$r%86I0A
z&~Hrp1XHA*Rxxq<2J9%%W*`96c99>qz|em$Q$I;h2}uLFs at +JI*QS>}MnDCRFNvDx
zi6NR5rFsB|%f5tT>lE6c at TzBmxfG8M7!aO~*y#o>M{rzA>(MxgNP~0{x~EKNrtCRf
z8FuwcXqCC$)KSOF0BZNS*3e6koO)lEL&0y=N4`jD_a=O1nG_i*32ooX{nZ&c;pK<{
z;s2)4G5_jrH-NTNM&<40Ji~|a9 at f2&NA02L<1Y~8#8%#bA-_KQri3nv(a7svF%Zq!
zK4%u%KzldBGLjPwD2d~AZj#(dn{p+et+}J7FpW;lIN$44B%BX92u5(8tZciYypvjw
zpV$zb$e+i-KV=YKi@!-3;KO!YLDm!(?6e3n<$8#DAn6v=`yyV&3uI+M%$+`J$YT41
zUCijFwh__EM^d6^{WUEM^K+hE&UEa`kxjXut>4e29s1|2*65bi%bi|duC#Ym+Wza@
zL<(pqk>B`4TI_4|!PeO8d)&h at o%Q+bB9ur^O^wWrShRQuqr1Ld(RzjH(`6(Z1^b->
zho}xYgn_mHA~1+YGurfAEg{}oOQihQ%zhg;%5>)&qPfDi1{p^%>aA^=A0w4Ex8qsE
ze>o+|V)1G;A9+8a<Uk~r<yAo%Cum at _I&zIz=;wls*lxgv3<=qY=z<y1`%ISxDQQ{C
z4Rt9_`xOZS at wkc69Qx8IDX?O;-o%pN7ftGBGKWB*O;;<+6MG!DVUT&^MHXWYg00uo
zb{B7-V35{CS5t`QKi7dVMVx$}aUsGvkrvfG73{r#I<bTCOD|QpxheI~)w7lMa+bfw
ze12L>aYV#ERhI*jzcbDa;IgTPeA*#TY6ao?!Haz&Zig-&ePM|A3W}xPkZN|IL({Zx
zTQ+d4Dbg2%eOKl^M9KQtRRTEX6&oSnGi&HyOx->>tUf?3iB2lWU8uQ34B at r0Tiw;c
zZ0evEI_mj at 5y86ajPTEFm^+syNfGS#L?yhQaB_65!`zxY0 at LJa^^Sckx6(>25*PdE
z!p?}jq#%Vx-zJ9q#Ab_&Kc;0OIuW!v5`W4dzaBlX#X$(WqWqa6RYol_SMwCot*BQs
zNC|MG>7B7Qq_*q)m6cxw2=MvSaPAFUanQ&jT{}U~ItNhSCOH2}fO?*Rw0#!Mb60PN
z7CY}q4c-c#DI);WP`&-d2be7|amG5vj{|H>mmnWxjF?1hU2j?VEYiPiseZ#}MvX?|
ziU- at HPB7piV~a)US{G{;h=9K4dK&}l3ISe>bnZ^0ATGR}`DthSj)P@`(mi`y221al
zrkO0#<C)+Ic?nB973=YvaBOvLT>QGPYrgW=ynXBTL)4La_+g&Mf!RvgB-;+lWtks#
zh&(d#JN;Hh<C@=>s*;l8saS$}hSqs(WG^-6_I!B~L(1WsJ8azR^HB at D%L>QvmlVq1
zS@!JpoRt|K<-Ep6uEuz}{YPmB*!vt6*`PaA>Sl!>v*lxpUqCjfi4Wa${Q0|DW63m_
zT9k^v&)n}09lygnRYsELlDxshbaza&Og|a;t(o~|GmeNkR<uKl_vLZIvGG)bj*pj^
zP6{-^R+~4)5sT8pz{jRcZo?~5I8!!qu#xZy<ah_gyD!=jhD6;>`-Kx)V`;+kqL=b^
zd(05?^qyAN57P-#6d@=C$V&@}v~4?y^t25<Wwv*iZ^NY)ORG%j_kSxH*oj)qu~Ob^
zS2m9dXerB!J5wXd4g))VlWO(~!r#TeC*P3S>Gk>S$Y7>?OR=B+To!SmX`8v1gX=n1
zIVLR~ZY_eP%#7E9;y?viSp>n0mJG!XPt9kmJ0C(A6 at dqSbQmVTae~3lx@@XDkF8>X
zJ`XH*9eyzc)ElEjh%>Y}>8+JieD`$w1g{?2h&sDE`loNr(S5?5XquUK=@iW7E!k&>
z3-%yW|1!aPbCD)h8zqt<A?K?|=z7!X?2)DrrvAtY!3k14d_Yjt{yip#wu}py`!pa&
z6BBlJM>-}|Blq=06X<xl3R4$;3XDdX4>(iqdVUM{D|T--7K!1^ylGLl9E9x{Y$;z_
zzeae8p3U>kh(+JyU(%?lwfHAKIs+p1Gw!bZzf&kDIhuI~3zwGHz(BU8m?;G5YwN(Z
zhq`MroReBM^G8#_(Ve3yg0Z%T+^C;2B(%SPG>S9*4(r&2=HaO@@Jd!QayQ;dhj|l-
zGdH^6 at mn+2{DC>#zXDt3Wl8bJ^ueqgP$J?ec#@t_%(_oq5A;D$UG*s+IRMsllp%C_
zwJZwrE860_ZC3p at GX7TkQZ&ErcBcP|wzXIX%qL>k<$WF!&R;rK9Y!*+)((VsGn%CQ
z%axyz*dGWNh|Vn`PbXy`2){hd85BXnTiuQq>CXNrNZWUpQ7G9j at x0Z<^jl2$rWITh
z32FBJbjdzJB++nrdvUzgaNk at NJRB7i%C4_XaePIK#zQj|7qBU6mm69*qxQ#Igj;kF
z+omV`9Sd=L6%V_SzRFLWB_mt_L at 8udbftXBpAg<2U+niKa7x8CF|Sn;Lv4GJXG1xI
z5G#|_frUcqH at tb~-Hi@;*WmV3NQ8A6>xR5cm3H8!AXi6_#P%#z^+hOJo%K*7HDNFH
zpIavi#APFjTZY7<*)k<dkh=oAiuqkKL1Hc2eh%sbwHFEs*r`WV_cZDuxu038bNpZP
z_I1MbAh?h`WymfbIB7AkF4%o^x(By3{~N6xv`r<?%;X4Njh?;5zZB%2Y!pEz%WwmV
zjG1sbPQnFZr)#{Y%p>C0JYwbZyo{&H+O$jbCV4YY->51$KtzbO{q%Dht44syYhZ4i
z`#K2X0!e?S`IT_Zp at mXrihL_ulPCVSkZ^$;@uO+{4 at IviW+5K$H>2^}s2&?p{3QCu
z`||(kAE-Cza2*j5qbuz^ArB9tgZ=Z3Z-^28qF<WFgZ;aosVN!lw6Zp_JaF7~5y5|N
zMwR*C;pqKxaO$TK+|9EJklM-<P~K8V%oYg0u9~F1<TuK0qgqKMp?JyrM8Ycj!gW+&
zlvua*P$|{Ok{HIIgb*Gi!6<weiFYh++7yFC?8Zxb=%B-^*W}(?rm{mFE2(l_V<lc_
zItd{(a$P+eL}fy+nSK^3zHrB9K+K*EM56KzglIHpw`LgiW1XQ?KO<sCmo^#pbC^UU
zPS+X9+735$wc)R`r|S$WylIn9N_!c-q?H*6#E9D|NG_5ACfO={LO$;X?^kh%47IOH
zRHi73-)K4pNy)EpK&52PMr_|McQHIJsc^!P=)KgUY6MIgYn^_VeK_T$3Au*IbY#)*
zVGi5tBDmeP)T{kI-FcdVWOCT>!8%xK8>zM<u$_*o{aloLbg`{toHY2qd6fahd5i?D
z8}B}~<=DLSo*3*f*3SpJzN{@^XdOyYdx|4Z{U^JSeA=DQ^(lCW5e(Kffa5$|%P35W
zGXSN{TWO>|2TI13YVqF*(8-Ls;q|X9)jn^|hr6Q9QL>pFu!TVjO)uPaDeA;VGGK++
zK?Ean=40G7eeCGDP637H4$El+_E}x8GV at H6^lnB|F|SdDx7*#)ib93QO?<jjRP>5X
z>`v#mZp`vO9zCfEtNlK(5)BxK$Eg(xe#F%<)Nd8MHj5w8V3`FQUvsQ}d at aQ+77TZ@
zokx?QFe*x;+8SY(o7E%n#!FBeup~SypAV>|_?Gc1=!A*=rruwF=GVn6=A!jWzqK>*
z at hx6`n?x0iL*%~Mi`h)$7qufW#-yKipA-y~2C9lb1nDX`V7Hr{%-F=XmA#k3uS4;x
zE5)<Xz@~+)Ne^u=Ut>728oDt+XB)QhskVRrgW<Wz`u7{dyGP`kKnLxw!`JXfPSnwM
zjwn!lM3awG$)-Pqw=l9Z1ww3GZ1QF*uDFSDjYW9cey468&omC<w+V!WKF+N4&6}~R
ztD3qUi+2Fi1UvM{YzB<wm!}iaUPzvKF8~8O{-8JCf4OY1^4D|8_qRv7MDhtnUutzK
zeRwkbS^|$H0DjnHDBbH%Nscuo*ejZ!ByPWi(B;r-#lkd|v}ycL%47T!2WKYGLBQs^
zMmw0jm)FK|t(tiQPPQg%n=GrmyXxrXiVWrj(e7MS3z8^4oMP-=(#*4GdE)j_C$~p$
zb$eb6zmW3 at 0>@Q&m-wRFGu}(3G-fpDk9N)SCA5A9u&sDtCUgE&e}B1<@|m3mZne7W
z<=u%uQxM;nQM->QMs at ev{G=e_dzTHT`s{agVo at pyYB8TaV3&J5TO42DoO9<j>U#-M
zC!@c?&o<T{bHo7C2~@m at pU}CH2G^vJ at X0rD77T}<^u#K!j2 at iED$_r5f>kUO%m2#r
zb}u!3QYEqZTBg2Kaehr`HUj7{#O$hs*Dl909iD`6wKDc<`j8tXihPO%foGk-Z#kw9
zz(Qf`Zy_QI#sXhGE!ewc|Kmk<Vurw7b~T*khoW~)Z_vD3JAi95ik|5(izh{*QEBc>
z;=!Dsx%GYGexVUW5}S<FJFXmS&igYv-=eg1TT<1trB^aJKh8B7J_j6~2jl2-C{sd^
zHqpealA~J2?vk!i`U#jXT=PSzz~NAU?1)Wu?&}U$At%*RY3^*_{?he*v=`rD8Dd)n
z3WB*Fzr&T|1di25!{Wb~rD+#=e`d^oeMINjRk#ONGR<P&7L*Whw#`}h)kNt}FRW_L
zkW+TuMQ;mn9r9#FNqu$nKgRS*Ou^Typ(ABoB9=^#zn<vUk^X5=%aB{{n)PTjzsq;`
zhNPQ99mhzL6C27R>rd<NoBQcfg&!jFq_U&M0<g?C1Rhs}vNtQ$EBuJ%`NJma4S>K?
zs)W%2mwU@!@5ArI6)ic7_xPkIUBc}$ytki{q3Jn~tck!~i>R`27C#9D*k^OgR58av
zC7YjyY*O-v_KA5ER-BC;KvsJj{C}uLjQN$jzcYJD1jjkdAoh-*#at8+GJk{XRW=|5
z3d#-lRNk at Sz;S3mt_F>==%>1Yfh*Xtt)SDGVcNI?1KEPF(Z)kRk`PRj5NlQVXi+n(
z`M9<8u8g_I9cU#a^I9kqNO(!5>5<h?zwPqgLacT31WA(FXKhpJnpw9B=UzT>nD{>N
z++;_MyOZIpW?K>E^SPH}X)E`4=#U)VBm+G4E6r``TOq|9SlnlkN=;qPI3h#9{j)_y
zhPDZYa^QeRY1d!HlOCy7ohyCIq<4<7-YD&~x2~ut9eZF-ko>k{6t-Rn!zjivG%#an
z&1QHMh8uL>KhY=y8fTp`Km?}f;@|(CZ`4*;cD$Xhfn^WvaS`&Z7LwL#{efGdOYcIS
zuo{4!x7kyNK_OEX-#Ql(N1ac;{1#Pzf^Z1V78=B2G`Isx-{HaS>-Bx<&sJc8QWYJ^
zLf?1O1yVhSkl>LF7;&Pwt(!QIDp$<-Sl{wT!3_r%_xIoOqW`;AH{i>@hElUUlav5_
zPxyDmuT*rCH+#gUv(?nXj}tL2leI{}TA2k~JLtf+1Sx$AIsYe(jwS6Ze|NTXm`=eo
z#?U+g{qmpGf6b4Tfi1tb2$@7>_f3ugYVQV!yq^EI&5#*nww<vjW$DCP&zc|l7=C)-
z^}qJ5p#5pTbL%oH)}@RVb}!uAOk}jYn?|d(Fl6G!tLG)`MtL~85|*~3`v$KeQ?D+V
zV*gs^=#jke#nciySO=*0NZxKu_VHpxec3q52%K$AlfwpoY}Y*v+28rNG!B1tq6N|8
z6}bxt>66|FcGsHSVQ(CCivnX+!?PuOVbvSN=8AkDTxvm&{Gh at Bal6n6-&<yY7(Vl2
zKT4>Tp;-9gIrUMvgPR(g4fHu#LReam4f&`z1_haM`9uGPIl@{C?3}eiUGLgbAnfRa
zgrasy=nhE~O*Q?b{v4mo^{GGwlucKSy^w23bJ at t@=BkmRv(Z**TJ<LuRycRZm2b(<
z@%`!>E)qb*g7qcE>mSDIh~P(Ukv{de-qALnijHqw<esu;*P=Bo3!4_B&Q>Svasha9
z7h1^gJIB>vOCz220}$Bh_*!97C+LOO3$22X<w*GuO1QW<0#oMwm35fUU<^4;V-O%1
zLr&BmdG|-u;W)g4n&;vTwGh`;K}G(Ci-`lkG(d!n991_QYOLGtK{y(N^gu&>jdy!s
z=nbt6O at Db-7qscLDQKB-#a06Swa<2?Dl&uIQR{C_8*2E~XX-PHE5Wi6&`N_F-))z?
zDdK at SEberTxhNffIKHV8*V-H*yh49a#2g*0a;11V)dBN<RC3LJ+|Gv1Rjag~$Vv$H
z&<j9Pd7WPiq`ep$KkA;y9zJIu<cn4QU82BKx_-sWI#ZWW5y-aAqWz0M=;<NtU6zGu
zOlwaD>I+N?(jN<ikX;=?56dm3%;@R9nrHO$mR{;@Y&lw=7t~aoYuXagO?iAmAp3wE
z$b8P^>r_4+gfh>u6>Q>t$>nYHu46cbKJ9bZSqrDN8jw=cGj5aNCvIv|#hrl6S5%<|
zbdqkWU9b9F#mb{v3WI_O$LjD}+z+GNEKW3P=T4&6sd)**C47 at t*u7QfvALN$i3u at n
zP<L#tq8qL}f5vS$+vzVCjCaIplcGEL+GYBwxKLb1kN75J!1)>5-W(F(Ku_(!m%^tR
zL5H9LoL&u2-EdG`I7!DZ^=vHfouAXm?|c@;pb7*<o$FnGzO3VhK++Vc#ntF^w_*ul
z*)1n$YIDaq)_tcw$jIlYybzJ5Ev()emu_l)0h-y>ZG>~AQS#pNdH at uVr7W7{o0jg^
zcb+`lXWI3R>cH&jD3TQ4bg}=f944?{?KT!{^VRduVR;8=D^NY?WrNI|)fOAaAs3)V
zYnA2$Zcgx at M2Burvbtpz?yWK6V5bXw+0xxRy`R9ow_K*M5bXZpLZ at J*=>}=fcBNOg
z2zoBAeRzyLQEg-wHH~YD1$Uxd8y(P+16)4rz6$VV;9E at BN+~R!0<3d*lM9yv_Hc+@
zs+jlc6pc!tX+qPz6tKhNQ-tp$ZT%tjZVT6(@;6hwck`Yqk9@)VrU6lhw718<0^xyh
zmKBMuW5FBMXf45{B3U+ at Z(Sp`I!Kq9=%jf<tmy`^bEr!`^b#&EhrNmox{ql*zFn=7
zk#0f#T?>jgVPnCxSHt-n6V>@G`WxaHJU3cyv3yR!!{N+pG1E6);y28Ct>fX>Xz#4q
z5Q|P9T at ixRdUcce?6MUDZtljm6k#JlO(K8Y4<y(Rr_U;Ft``dg<j&I`DPO+%*QE>t
za3YZhz>iqQ*&_XH6ZFqG*&z$g38l%3zvg!TwD|#M at xEO0zfp?UPFkq8NGma$d9~=H
znHE7MO at jLDiQHR(csEOz*8;yqgHD2{$}WV0D22Z~4pwzC=|+ at B<`2n8zDKB0qU%B%
z4%MwLpRs7m12lbQQ-6z+sw3I-)V at tt|GBS<J8<xZGmOzuD^!D>R|Wlh&Wi{BK3?lP
zOSjjG-LZmLf1&Gk>sj}dDQUQSZ;H7!6iVXO=>rPccV<6Kr`B8a at 3d>#OVH86TFR!V
zt;<gxuYAlR`|<0~i=*?S*%YPs?V>OByd#DxH2$vV8dV&yy!Q>;yyW+cpB6zIzQ?&&
z5^RQ&y&dUKZZvV_Zi4BDQ~7UwthH8^_^Q2ChF+wyiKOYQI4Px5i$f<NtS=B);N$Z(
z3VJzaG~!p3KkFy(flst}_xZJqJMhB|j+T^%c^s5EDdAy?BY|SF6vJ6}iI1~F+znsU
zv`6jx3AoIkY`M+ng&lBI7m<~)u}$>M-K*4A-Tr&6GDEn-#`W(Yj8|<mMhOg%73ZW1
zb(i7`J1l_2-1pFRni*dr_)F1Yk;6sdP(rIF6SFxY1YzCXAyGmqyTDbylH#BW_gQF5
z9&9(a?81e6&VvzR$V|ZB#fKsCvE2<f at +coWj5b)4Q!b+AO-Nz<itv7 at n^Gh)hLcO;
z%3H$P{rzU=w)o+A{yfd}H!3AeY&GYqc<GrXiA59+05z2Cv6RG2`f~<^+37C)C<;2;
zcalgxtLr6<&Bvnc#wvYSa+ZN)ANo;eAWtS+l}?^lL-_D~@IC42>~$#NUG>Z{rgcm?
z!jIWAd<ECx at cs=H6a_Wyx_?QTzHk`;bcB2NL;j(qL{GL<;KLwqEVPHWT*LO*U!}a?
zy`$wG|7`8GUjsJ9Ksch^67pl7A^7b>NNLIk4N+migqttMhg{nc0tW98D0uYM_tP7o
z>-^WzXPkbd^6H<{cigLNd$O~ifETuTlxc66ywx-q<pghs00c8%bULolk5|<Z9Vx^`
zmEgG|kf~}vnW`$CZXedDU?XUH6@~ile}6}no;D~<m}Y1deB$r%JYK)TRipfTdjE;_
zFR at Xy;e1 at lK4Vfc==+_17M<=-67%yq9{4Z~-v2!VBwfOol7I&{rds?OymaG}7zAQ0
zbg1I$zS2cIE&0I~Z+&O5euCE)L)Blw;@MjelrmMgz$;tdXI47I?n9n~v^$7ycP%^0
zZl12vknZiJ`3F_kYx<WZE^L+BO;5Y!B`hC?kUNrIlE$q2i1foQ0<y!bkRX;5fx3m5
zv8Y0vqANF*Hq`hOY(j#+8Ni#mDTDm`6RK*lY~`JKJ-pap?1)6iwp;v~KldBg at dWGz
z=errsVrt%u!VggJ#O|-<*&i~Z+QO4<S3Nx%q_wWr8WyfH)w_j~?Z2juZSz!W1Z}Sh
zLOhavxW@{sZqEp3y(q$Ovlu*34lO^Khf9%zJ9C|73Z47S4;q!L=gwa(70uLy<#evp
zx%vsk@&S^sn+e at R)_<8!7FU at VzGCljgr3#`#m`Z|Kx=xQ!Jc6jQGCl(I-#Q+cdG<r
zlV+7dRE5y^ElGqRi3q87x^Y-9Dw5TZ at lr@5BkH}ss<YFZbT#%R^TZCX7~WEQWV?>&
zz3jpEn85P8JyFEfYL|}WzL*fY4fBa~mcPzEnmUS^Ui>z2YQ4^cG44LmT<lgZB at 7dG
zvfrO-p4&B8-$q)-_h6?pf!YH_I##o=0(zrLlNG!TiVP?kPZ-F4l)j^CVsi at XXn7Ux
zR_!B=*PZ<?Hou1HLhq5O*>gp)W2fzxm+6o at Ih-wzfDMbdQ)6K{eiotM2;{~GtcXVa
z)cxJ3_gf6(K-Dl><dXO{bISXAhD=KpG6`F1vqJYDT_2VeMrCoWRJn-h$>(nlS&{?&
zx2CnRtvdtm1U-r^p%!0%t=(-*MP2@(E>B_O8K5q=usVQi=N at Gh_acLPjPtZYUqfJ=
zluzNw#Xql0`SeU<G`7=!*ODGg>|53T+eX7zIM?SI$GQ<~RmPy>H43saF^Ccpd4AG+
zQ^`xFu~~P{fw(~IJv2wpoMepCX?eQ^SoVwA-+)IXV9~OzV;eYb at AI73caKZ_FqzZB
zCT7vyZ5JBfK$fBG at l`RL5285W&Y09Z<|oXxV8fh|9P;8ma(Nj29}k#6ky`9PDe<nL
zeI)-Rkprb_nEpUx_CKK1f~9QcSW4JWJrYFHZ0r84^FwT?@E0AyHXC7Yy-P-tHUHF{
zm5&EWw+Lqgc~_DA^C8cZ{N9cf at Y{+fxdByjYj9PSCC8)|D&&v>VFXA(Nz3vXZsz^h
zV0+S6oOMf7!#}5nCsr-|1-Y}Sl3q&?MC4x835v5Pf65f!gFOeYawZ?YxGb*!#7EXF
z;eOGm0Otl7C#@z}+v|iaQYUqHg$|UXu<WTiyTkYCu}VHEt)P|%YH^Yk;L at b8b;;dT
z(Ge3)#9q)RAFl%6>IscA%zpFXJ|b-@;f}4y3$;IYJZW3LQ`f)>XOg2Qs at cFbdkhEn
z&UzTh>LCF|_VAE$vme{H@%ZB_AO_wBeZ7yr&&cEW(Fj(#y|3lcC`=#{dT7b{vmG<N
za;?)vMT+SoFGrHqL((iKf*+9LU`>?9+Jt^Z>m{gp?v+FBugZh96l}R*hFSX#RQ$cI
zMo*X8a^a_+hLQNV*k^^5KW%@!>kYzU7+7{_{!l&~EpR>$8QbWmf>%{oaMibvzaz9)
z>!mv1D+G2Yj=s4M!lj8nDTgJ9Nb+31>}IoQrRH88G0dSlTb#2oIb)H3du!YH3UCXO
ztZqC*a at u<NCl}|ya0C$FYczGNZ`F#)b%YP)WlmRAahduXmp~T~^qLKKuXtJ&P)CCF
z4w~}4qVDTbJjFGoSDf!UuK6GP1JoBw6?7-r;3DH~^$rj(M}6?gGmy6BRz;VYUKyTJ
zRX5wU56)6>^Jm;Gd4BFcj5>I>&TQthzYv-eUBDKemK at RCIM}9QHa7G!>VC=1_c-nX
zpOEK%ky6*Q)lHDKB6M8%*4x5)@gn>F*_E at b;^E>+cQ(*$yqHS+>wNzb$Ki!V<fpg_
z4Y-$uNJyxUV6bwPf}zs?DBgU33SWw#F6l%XLFG9suhA{@TgnKN44r6U-PytkwT0|X
z<ogjd&sTX!br)2?Y;{S3AYippzwBz^_4&XoF+$qthdPbSsRA}RFkj~LQAk~={&>X-
z8w}EVhkc694O;Q1*I&7Jf1-b#cQR9(_HQQG$?GcKc<9W`nG-7{xWq19uw)YyXgs`R
zRV{-er9v_U;zS+FOM$Ixdb4p9WBx7PK&^@zX3i`hMo{fy#*~+26RLcPWfnPt3Z<xp
zUT$;d?bf1|m*^(3C9_P|NZE~CMOSA0m@|#1_ppu!Xqaow*)c&<?<@*l1Cke9S-&P3
zq9dJ`y{?t<Wpo52ySGkS`o8{YK`>wW5s0|5xx`HmNV+3o!IIDW$%djs&uddSE{D*}
z+4I|f at MH<U+1fk*D8OgxXin1oVS)WE0Z9frc7o_AU%n>^zWIS8nn!nuur}S<D^JOm
zxi3&5b&||UJbgu at _^A6H`m})Kq|i?s?ggW^dcKB40?cQ+<PY8{S|%rjLy&iy=CZ}`
z>xCi?m37L+?&czc&1}w<YaB%ZE{kzftCnQW`q7mM(irmw(#bx$JD}9 at NcNjWF5_Pb
zzo|g|Ni2LdjAl1O<6}3~8Mge~1By(4yGw-^u81APef(^CVJlyU<^_1;lG_Mo at dby}
zvf7ZJw=$9o?e>bf?8G3R^axMPmLZ&WHgU*8<V%DLQ$;tW``TG5;MTnQu7+|dZmgsO
z!A!)6UyDwACG1Gebb$B0^55)_;g_F$o8acNvZ)=UN_c(m at 9>E0D(dnua(aZ1M~D3d
zkM8(K^iyV^b9-uRVH$4bfyR at W9L61P`Lz?S?uFco=ibbL58S0aM8MAMl0DyEftH=P
zcdb6JZeOU-5Y&s5y%oCwLk839f03Na^hQbn%AF at q9ItC*`u3v{?#h1vnN}OlXkpYc
z at B7UfH-9sDn*)Gwb80$5;7_eH%?L7?n%P-zD${mI3i1Q}di4n^d!HB&c$E*rHY&Ws
zE9XBaghrwR6|AGt;NAz*?=!Tc5*NJOB=rLqHo#75{4QCoZK~txMe#shxw6`>#QiuK
zEvOO4aBrPSlgcEDk>xK9fCKvVEHo-?(DU!58Lsrz>D(tU&P8>=O{ai21X?i`nv;Pu
zPm`43XTell;|dU|>YyHwDIWye%<pG^R`;mRJD>l+zelU48zVK=yZ2efTWPpZdgc)O
z<VQt`LW2Zb)}z^?YI at uAYJ6k>-Z9RMvtUJWiOoVUz2`ZK<h4j8*XaQD5UvTEp;bfF
zE`tb;9fGXiU^UJUu4I3?4!E#EixXCps%@F?9eZB__g8!!WfER5uVhvRBwrw6bP3Wa
zy=RBh)}LRyC`XD5k(twa%7>xiOL(aNe5g0^k-w-+U-j|#xHNDM^)(L$qh#}KlCg;%
znp$XO`bgNvcOwH5>*nc$r5<kIOPr{+HSUe#0RGJT#(ItQ-cf!-927~WF)$$>u&G=e
z%hIFz+$4N1!%JbfO#1#C4E&rRy26&{M*~Dc+O)eLf{Rk6z9MB#hZ4-;I>q7W{&9f4
zGp!R|H$*nBiGOd7VPXdBkloCgrsXh`P}jLcQwbujuxKvs6Y*T)<%^Mbbw6&~Kp6<F
zYVa9B)g`e*owR%B@&VQHRmr||qBUq)SfcxK=ebDg0IY8L<h}r=<Dr^?4R;B`{g*=L
ziA~(ipahjyKX8?J$M#E>6+DN{O`|W;6rVAX&+2v0MHtInr6i6J3tiPq?9rV}D+8>h
zK`-U{47>oj`O-?+iSSJ78thvIU*C*-c+ at iwwW@$Ntlx&fA;Jgi2N-E<-BKp+ddeen
zbl7J$gWiI!|Gjf%kmnhddb&~UoVAy$<<Bk=i at N0wD~zcZwPY7QLw at meO({z=ABT4N
zDULO5q at 0r8Gb@Jk_|cSXW6QU|3l3q+57Uyx5?B3d*pv$EbkEW*G3i%o+<3804zSP6
zZT at O>JZ+ldMrW?_hj0)>9N at 39;^0!fzj9OaK(aIRMeB|j5(vD{?@>0H(@{Xz#XNt;
zh9YzT=?mL3fkAf?D8mf0kO8SkTvejg@=Nh5USbT|a7SQ9Zu_mwmiC&8x8*kV(JiJm
zsKeIk;l*r=uR$a{vQR-xO3nJ|_m{E13-70q{xdTos#zEz^eVVVZw;O?0)IddmNENu
z?;(Lg=+;?a=G1NHScz{pqOH5p`)jJaKTk at ITd%?vz#MC77gXNGtZroFGtvhSdjC6!
zSrBvZ$Gtb{Z4os-?U?`xYm0tEgM(+?hpTcZ{-wzW1(PzSC%Kk$YU2|F{M--Hjkw+K
zDJnTN!=XuC+EMkt(izBEdma39K=SKbKPWuxdO%T0B&@L!JEiN$JM<-O3^hn&eYN|M
zZe*(*s1XaaWUet-+<DwexU5g9S*P~zeBM=A1DeVR*EsTXp7kZTgMgll?692^OYc*e
z>GfH7NfhD~v!K0q*XkWz=w4sbj%R;6h+RC7Iaj=hyh~JzZRjayHd&+$awpHEIC@>~
zv$13UZbvTZp}oxk5LWWt(}ATEbccBlt=qu at +vs=gd<vab;=Qt3PLy-!$ARx~C31q2
z5^yx2dqZ3(qAkU*osi9FYXb$u5}7<_2o4rIH7?m~<CAR()iUT!h}fm8T-yS2Qh&#)
zEvCvzef(j+76`WS&n8dOY5?k+@#J<bp|8*I!t2dZ2de1Q68)Sx4f+%WM9UYTY_M*e
zO4r<$JWW390heSJ81=k`PMX}crnHvRjIfK&-rkF(c;Uv=pbh(M;MAGDI3|XquWp#X
zourkC^c at La6bBY*oGecX1CEJX+F(y>ZY}k5%iXaFIZL<`n=1fs&-%s3;67Ib)^K&W
zJkuLtF$-7fk;23720hi&ZXA2pf`7=F4D5bxnz3lx(JP!x7f%z&k`UUxx})i%*Y7(*
zvhR1E*iSq#vU&^-x6-r%nW?Jwi6dbXFqut&pCg~?bE<nk#KNm(j1PQPdtM4r>de at E
zmFa?CZ>R at dc-|A`r1e=tk}`+BGD#MN+^w}zqwdABZ{yw7s1a at 0@l1e~3-j$74BR7I
zqbEr*Q`gjAI@|E0o62b`0KPqmt7E%F-Py2E-%RMUx%f1E0)wt9yv at l45KBz&>do)x
zfK&#^<w|{6Uf5{EzWzYLV*tQ<XdQ5Lv2|{}z9!)v9Cln#IT~v4tz%sn>9(@u={c!d
z9mQJoSLtSW9>aGt^pX-gxu}h^w<V^m*236QB}hZ2vOuG^SSL?yebh1Ye{z#)MK~GW
zC-oes=&`J{71)%Ndz<b_aD~2&GiaHiEzZ%LDQNc>w6)lt^&^dl4t9tzOKp0hYN<
zT^(@-_uR(p6zJ$;o at mBZIG&N65qzbYTGI#Shg`EETb(xg_ZU9P4+q&eJ6yW_-NRT!
zYj~i4JA%Q&edAs5e3Y_(Mv)GyZ8z+HWc@}on{vZJ9zpmpE>dy8^go+hwh=bmfXat8
zmFCYP@>_X`n|5Z%HhG?u?A7e_Rbs9E3lD2}+6f2$H3_zLYc|@0-|jV%=64PuK1`Cl
zg<q>^MM-KO{M|N|d}L9mdG!n}4-uytn?71b2pmgqxFP8-x_{P8Cn86D1f{*h!}0}W
zA4eJx><|?#uY9O{gtOxfbM6vM$dAS^E?S%ho&2}rt3pMqT;iYlwVy7P|7bxOA2fpp
z?e`5^Xkr4?)b9OaYTa{}+e&miUPtSFhDt<XMdqxM-<}=u$&2HXI8P0z_JBUa7dYi7
zRkTkC_L!(5+By}EiJFy8ZEwSzGdt3JCs^$i2i2+J@~nolUIeJxfVK`tn<at6s<*>C
zaSwg>TLi?vWztW0-V`#q?UTnUau6Gp8~QBbPmNTS{Lk97p7b|H9S;2uD3kcL`;f0L
z;F=}@{kkH+IPWXz)f2Tr9op5#?7`PDV|uE&?!%~xKPf80 at ena>aC2;#F+gbj^c|#d
z>&jEa=%O7bCX%cHZNmIBALyO8q|jGnCa(AE4`$nR-YA)vY)9n(MTT~Sy4xrNpj(G}
zO{9k@<;j0z!T*j{F4lYOz7>AWBr=*GS$C;p|DU-`?X+i0A*|$r1XTYad1?+1wqr2~
zT(j+y^j^c)X2!dkPTgZST>K9$)Q3PE7|sL5<5vZ at TvhLAtq%V04{BB}=*lESKwIm<
zQk~pZcb at ux>ra#NqA;bttGq8wYSzBrA!e7MS5Z(0Ff9M|J2GQbKc{3w-G9D4PY;d>
zKvDx{RLyAo1uoPt<@TxQPW<-znDl3EruqIMG)h(Y4fJMt%@BGW at HB@$T2J5^@ue}j
zFFo$_kN^J3XcJxgY0sC!A}^;+5gj7pT<=nXI{#~<7;!KUACUSbOVxEfbhUS-Qn~Fb
z{`<%*mUitX>I)|f5*P=eG<z;|6JD?e at p}Sw-!u?jUWp5ihpJOEHt?F2TyWX`kK!)W
zq>rNU-M!HI$-}Nx&$^_Ac2wy1lQ(l$Ftc~nT!KvKrx`APONPpRi>%n$2cLW6c@(DE
zm)tS#%k1e)!Vtme8NE-(_R^89q#OHn$x%*H;9*ZAoyT!|lb4q3xF6d-oT`Zs5t)4=
zQNa$s8YfL=5HmMO8J-LrPmnETD8u`m;3;OiBlzn@(vi_X13Vp80<Qz)Ly;7MocHHj
zwuZJ;yy<MUuaV4GOCiAGI@*ndZ<wc&>9>sZvOOEN+Z^ecsLkT~x5bC3Htu>9bQPs2
zBl7Tinj}g1Ded1ra3>2g1Y?v8=IBNUo<gxqHrhC3AcM&sny7qk>`t$ij5{)BI=K!}
zy{P0l7s5W7#lHBZ3D5e?lxgdoxs{mr(q at U<umvaPa{wZQlVqT-F9+UDJmGU|nQ44a
znB%z)96Jq8cPsM{T7qZQjOMIF;Zl>Yl*cP+GtKnaY~z$opg~{w|5?HsXIQ24W84&E
zOc~&7`~IHRY2bw;AQf}w{r>%^SaG0lAq(9z1hUYf2HW7+pK1- at SX@V|D(${Hz4i;)
z0PuYbGdxF)8Mg8Bd!E5-w^tzegJG!Y3#%zUMRCiWoTbJ~1XGm97$URO-BQ$>4r?4f
zz#C|SKLDJ3DeW7u4;<{d4|(Lop}oL{7h`fO?M9Q1I3<oYn(3n_sqp?oklwQb*o_y_
z_EmkE300U)Q{+_NRCL}9pj-XDC>S~}B at 0us-6N>3FyQbpxi#RJ3Ku?``0Pfk>7B!u
zb+hmlgSdxLwAgF!eLgCNRx`+xX8SehEPh%Vff_PxiE#%0n%Ct4_3)U+ at 3sveaqK0!
z=z=3T8D|Wh{(|;H#tIsOpI at IF>6qcfU9Z#PZw`;1N at mvO(pb1FZQGk<f)f{OQ~IpB
zST<iJk7laVu1DF?GQc=XEG$bl?Z;U{&?FREUu<z{d~jy*Cq<Pn{!!w9IWbmc at nPI*
zB{vkulP4-9q?b*{;NSkzb8*t2y at Yh_f5P4Rl<x at nq!6<c4Ops~H3ZHA)m&o?hU02(
zemgx~cQAMWdOZ&PXvp)-IsCBll?ZMl at E$fE7NA#}ya^pU0^iwj0=O=OgF-OTOK&e+
zRhL<Aw7sCzWf8-6SH(sH?j~$6#7Kw8vB02i&_xQ`gP>icDa9w3xqA!W2dT*)`#m2J
z3N|A at Dd}NHgYz7y{%yPvl at 4KYfOnL%EUoS at -ksHFMiHy(1Qe;bjy;Y>$XkvsY|<5R
z8q{@}w;J6GJfl|8*GRR64DDc=R4>w=BCdeY9T+)8 at 1{NuDpoW!4o&i8>jvR$rks`c
zZXKDe{Q+6S%iUn>eHaJt&z|vetmQ#YZx+dd58hxsiR*a2gn?iqc<U8Osd%%^Mcn#c
zIg?pvMNetjw1z<C#!s}q#fA^K0oOXfiLf7TP5Z~Y(>l8cb}%u+T^-B#qcVO-p1aPl
z1pOaw0M9isg?4MU)9hOpX0g^DHIm9FFA)eDK6rL64v0&c^%Sl)^Hu4FwQz@$LUwRg
z)Eh($;gxb^bAg8^l620k?m#0TFWX0H3N7Yay=aY=GtW!2piDPYf3wqyr?*GP06FEP
zDB+Gsy6tE}dIinW2~|K#$|Cb+>pE-0zI#2}ohLOvsaSdR3&cG%vhmFJ4d^Rf8}*XV
z!B3Nx-0;d47M%o_9w%i?+-~Xwm=+#X|7y=tUOlhp<w|tgcUo`*_MjXglLCWi9_5L1
zKSa5jJ9~XoDz6YI2}>pVh_(qmQ#H7f8W_Z&8Y{0hCO2^OB|l%ae}nKYh|6qH`QUvC
z98Rt8xM>dVD*nJTVH)=yciX`r&t=|xQxVtAGzmW at m2|54k_5u7NXKvcXt&nmvMfWf
zygU6R-G#tm4EpH~+2zn8ekVCrWH$`<ZEsozC{2;y5HRsn%9QU7a^V~iD&X?YCmmt_
z)n*Ty=VeKZnhcf9;h=O;!z~VfuZ1b};On{F9?v`8c_Ivwei<T8hLS*x?Utp2Pc?@z
z!@%%!>f+Wn(ajw1CF*8O)XMFKZzPK2 at Yc4zxw*DpvQ&?eLXsGlk<H`8v0RnmsU~|t
zWg$nAXynt%`RVBQZNFGUv4-Ayeih2Tq1O5VB`<!mezV?iv4z^H>~*Rs<H_sxf~amW
zJZMTucZd^RV5oG=Px}>9W020ewMpgUQvB=5<Dl@~5<!as-JN^r1ZV$X2H#+0`9AJR
zBC0;^_S+<Z?WP%P`~@8{KHGyNaXBA>_gk at 7saBoV_(_hGjF1~zOxIJ-0CDDCUPx&g
zS<S$8h_#n*wbN4!40@(Z2bSEv9ck62lB!3>D6 at A&KIL;)>23NIyKI-7y+tn at 8wiAG
z%a+<6q4iEdrA(&P<Kvy78JUimkaTB(>y>4j=e at 2(i;WAWc`1<{YC#-09By7=>nM;D
zC&@?Qz)zd>b;h~PdX@!cx<m~hq=4FPDbP<LlVWy3+fHxKc>+Az8Q#FxWf6$)tC8Hb
z&I_Rfil#u{Zl0q`;3{Hb4qK*=u>rT-QJ=7&;ESwrS*eaPxKauL6J(jT9W*N-TeRYo
zAI5Enti}p&#TBRU%9Z?r`U2IZPuzZM0nxHqIL8;SFd at Bi>wBz6;f~-54tj+OE9;XA
z&b4T!1AZTYq<y(CfNhtS9Au>Uj~7|f%Ln2|3{oZ{7)&n!2<wjH&Q<{```T<#zi<CI
zx*CnX3NYkiy64`)4X_FHZX0lF<*T&fFl)Hxj#?-A&QdmIZgR&AUrtZI8!i^)XPtzb
zT*1~9?59v~SC~H7hKEF0f;-FilRoBKdmbpz>UpZSOl5~-Uaypl>FcM4GQfF$Mbk(0
zbqX;F_>;x4kMVvh${|tKun&MHjaP0SAjuB*qVe9-u!`MD#Xq?IEx3DRH8^YX^Ziti
z at SZR&Et>B!KF4jFTT3s5h9|?%*?@U1NbpDA+Ng7K`f(v;lZ0wl{kHG-y<;q#41gZN
zPzsZFca;&Px~Q<Kbd at JS!ti`T{0SS=0qVv^<2Ym_)e3S85~4c*<~&V at d*{7J3GwSD
znLcgYx{~B5Ipm<O62d0!?n9Oh2c5a-O^X`#t!Q-NrRoM at Mv~KK5?K}SryQbg$s4}0
zN at 4ce?UDmFLmu0rq;#Gh&#dcFq6e`<qzYn>EM1%-Spr)phVAWsWG|EL+C2>~gseA$
z$WX&FICp$Ts&m&w@)u%ecrnb9KYwJpe!0B`<zI7;^)ZW=7o*wUwfBh(wMxx@@h(Mx
zGbaG3(1P{o?o|OTg8Gk+17YFM-RCJo@|++7maXpZJ-LLFQ7S;NwjFYqsxu$yBLK1o
z&;E@|%GXb`r5v^Q$_zV#S)7od at 9U&zu$0ace(+D3J(rSLpl{JEj_>?T-#x0RN5jDf
z3<TDzzOmd+Evkn0MhX_4W={?e!0Hp!Dog$tciZS_=QmFERk5eFWUppz^<8mojONl&
zFyi%+dZ4};X8HNa{X#kaD)Qs}SrUum!VB?Dvfr4peG34N4(w)f>JXFQTsbiYnN3@?
zXI=06B0l!U4vB!h(qJI1kYuM9x~nWUr^?H8*8cBL05$r$FZoGN3PxiRzFVyeb`?C#
zR0x6q2iba)g5m%@tF at 5k*wT|D+;XIPVQY%z9IMneuppsMk3|hr%5g-?doFz&khSU}
z)GFnT7p(#6YS3?ddj`A8=8Ea&^r{0;tzD}3F*w!B$oc_yRG{!PCi4!_Pv(1S4`g~3
z#o3!z9WJHd at Z)HUmv4i5iB48nAjkf%kr-johsY-dzdE~0ix+~kqolqkfvK1w=~Y~9
zAURU$z-i#zZAFCZl<LQ_{SbWH;o9m^-u3dC51*__8q`K!eN1B?s<;#b;N=9WSFaTh
z_RG%q$CN~NcL?c;XDuXb6F?fRm1Eq$%Wx)>p8R-y@=TE<|FIm4rP{4}y?b!w2Ndh*
zJY?Ij6}_@<^U#6GUa^KaoEIi<LxIKK&f3pX6j>MVF=i5$079FrBI4|vHTSK~J$odF
zc()sLhH$rmj-GZd2>~UcW-}T=AWz|~lQ2D59}=dGeCP8QkTXo7^Nta62w5Hnq&ixV
zI!!;T9H$sp6<}<o+CFe8IcMK~wH&k}QW8zeD^{bb#=}~^219R{Bv1y at XGs${+ST;U
z=&#i|@bC3Kx&Z|Rq*55zfOH;jX!K8|!J);m<09kkweRQ|*?{n*-nA;`Pen}~AV{hr
z_lNDACGonZ`oKuC!IfwoI!-|UA0!1HbAg;hX at gc~00$$;ou2yJK^ac}gB=+LGc~#a
z_}f&-U_A`JwE2YCC-SMoTjo8lV+#P at iI2nX?{!qM`4XTk8Y+{;3o?C}5F*2Lr0JaW
zrB!9h_*Ba6QQnNfQVVikxkwHNvxl at aD>Ml1MnDfiav^`Q at _W~SS07KLrGR;tnjv;o
zG)p~6Te#X6H2tKTl7|6vuOF6AG~3j-sPAwyp(g_3!*5CwDbog7O9RpG9Hs4pYBg at 2
z(Sh$ea7I`?Yq!#KhPH4!@z!guRrHhn%9=ID+v6hk1-bpDC~5COKtSZYK)fpqADw_6
zjIYMyx5-m4Z&5WH`NzC=`xG94rPk19PT at Ex3NnWmCVScAo$7WApZ=LumI`|Tx1rZQ
ztXn$J^Vebm2A8{yj5}78{#5-;XF~Z0t}W&6>!H9R<rQn#207qV66vGwE`-3>OMP$+
z*COeJnBugSy3eujE>x;$kGvN}SdOvHr}Q<4=~qL7o>dONc*y9RCJUWnqdr%ojfT}n
z2vp~{d}*%S>63tJh8XkOin74$gUkIN<M!YHiGFYyfJrpO);~+}_}R(nO!Nl=b9wlz
z%vkf^Z?cme*6U!skLw=+v9}+n(qD;ScvrX>rerr5ZQ8ouPp at sBE5cIABeG>*4K=1H
z>sucP8o2p?_s89O^aWOYG-=R}N}p*lHT`m)lCww|JurLN;xMM#=3eX-qe=<d>aHyP
zY%VVe_bxE7REF&x at B}_5+~?W9(vDP&v0~(J!Z`1fs+xu+Lov_3PQ*kCSPNI7G8<j%
zR1OHH3(v|oZ7TQ7ol7lRS<V7i-a7y21ueCg5^xVs$~{g4e<{9jY;z{azS}CNjUNO=
z%;pBF8*2<pPcDX`Y(|D?W|AY94+uT!-aL;S1hwsUmYWkMyO;hy_TD-uuCHqog^=Lx
z+PDVyK;ywJxI=J<;4Tpe78(g|Ay{yC3qcwuxI=K4Zd?M>o&3Ic=GOhbTX*J8)!aYc
zu7Wy!PH#PHuf6xP)_&HIV<I)an~jUucuZP5s-Ll<;$r<^t$IpE at +$C$L>2(+1VJ;k
zo^{t?OCuyA-(?M_@|1CUldmuW&4F6;FnTn~K2bd-vNZOfsj`gxF=Fc>E6ZOm^dnHT
zwztl8&+KriWaByGCPZt%2k;xl8&w3Og4+3Mfn#)5bEby&BOnO=6~=HBfu~6xV{y4(
zGAY4VGvppT=q8TSk-mMFUlto41aMoL2r3EXCMP)bLQ|Y=3!(NPgkHa>`<@t_@)YJk
zNNowofL!xMg5|q!dDoasRPBr%<7(7y2(M21s59hcHP);7a4nj~)Z-Rgqwb at F-!xya
zY(hNj-ZF1(+|3;btmBz}6r#qdB(#_oS at 43G)?kcp$5TI#6<f%#!=y=>U6{SrZS`Jp
znm^m0EfE5uw^UeC0$i5gYI#5_%YmLQNI$1|x*9weyXOH%Q*Ia}5=emWIKv^YYwmN_
zcocov_^Tzi?S)4+`l;9H=fNF3n-H2?>r4rk$Ph_fw;WLg)56MzrQtWGQYd+^R?hBu
zn`bEivLz)~nn<3M>k<is;+mhbfU&65M|V=(iaN&@hW0`q%v(`x_qm|TIyYpe2)EBZ
zJ>^_Qfl735u-x5+f~%YL6 at R4Dwjl5*gcyemd&^zOd)sL?Mr_rRr7D_=qUoO`G5Kw7
zu;vd;VlHoWMvh3vu-(OY&Tqf-d at BnEsG+iXoAXzR>~SJc`aJCF<L<bUFo}8G+xmX+
z$R=zLI*J2nmkPf(xAEg>+yH6rGh)eadG)kVq|9RRD~Rcz=wH-5&3Ti#9zUJ09>c%8
z!=6ZB*{Jp)d`T+VVI*OG1#lyJCLGOeH`Z@`j<UUh4Vn at Ur6wit6Wh_#7X3*rsj{l-
z8F268dzjk{O5>0wU`EDlD%fxY+~~h9P5XKDM$y%86X2qJNqQaD1MQ=O45UF6Mg|^2
zz4kdkyX+1{2>vRrRy;d-8g+w|V-O~Y7*+N<d#+Uiub22i9#N%|HR;)0HO_Zh*<y!T
zp?pk3g!c4gh}k-x|Bj|;I8pblfFluR^`%f=o8XuQ at UG3JV=F%BGV1|A)?rWv*zvfu
zyL)rF at wIl)vFbqBh?M!n*^f$(^Z5kU;ytD1P0n;Ep&{b$zR}P<=xx!4CQhU7LpM<E
z{NpTecUa+e^$wLI^7DPuC<^cgcSa^m+qx48QK+e&p|$5-n8hfXO;cYv=>_wT86Nh9
zq=C~kpRVc5HGP`NNpr~t&dLK<NUs97XI`?a^=$~v^vq1(iWBz|?J2zwn%Wh2wO<61
zUm|A8AEn<x?>Ggr3#ehSCO&)GShfiF4koCA3!q=4JX03UgiuOj;yc#H(<b(ZgOf&n
zgci*;QB0}SX!neeEe;0!L~#yC-7&dRO<epaih_A95?1-%*tEYra|t$rKf8<TVDNon
zr**`ZmB#Xpyq{RUT)U1 at _Df@UxE4z6j0>3jc-edzK)Oick}PT`1)yRMU at l#rR|x~E
z4Uhl=7>>+#?fIT_&R(OR$)`Yh6$5`kBwbtG%v$T%I?K{lXjZiP^WeFOpQaApPN)RO
z7b*{J#?gW}x2zM&a=?}nefKL7$;nkeQoHH55DyaD&X{EU+Ve~ccg3s^%i_48aTYoD
zL1+}sabVW+kKpCL>t{k(LLH~NQ^gm~kgT0k1yn)v2ls&NU2(P0We1WC=DxFjgEg2a
z<Lxi0|5B`L_icr(qLmKT;@j6_&kWBiD!mi)C{l at j?Y0N)oTh%qKH`^qUy6Gi`tItp
zYo7I3G~ct&NAdzT^fR0B4IV^i at 9%kG$sIT1>-+$I&u<oz$&^(_R>9*yK$tfH+brsu
z8RUDDMe}3uI1frzSKhFgMra_JPJs;N;>{OdRKLuLTVDns=+OFoDjG74C&`Kz0A#c9
zA&VBO!1Wr%`PAQp4syS`HqIle(K^RCW}<{h<xUW=aJ>s-`O)GnX9RB&YtHi&CS88j
z;X3hlbLMaX2&I762^el;z?1_SqL-Q3F+a0dD!urJp1;maz{uO6B-)WHJeN_1`N6U}
z7aznCAjHP)T66abP=1Ln>V=(S(Q>z=dd;xrcxktT^{P6c_Qt|Y(7;qeBzFe}c`B;;
znIx}9e%^jB&Sb9X;rEn$(ehKO^vq^}P!9Ksf}F!hnZ)s2P!1`Pw>*pm64MQkXIFR|
z(q##9p4CZI|J at W{!G at E-qBMuGdAduQ?*<Uy`Q%J>!wbb&<b%PPG>JQ(zKxCLEv<K;
zI1Z$XA?GN6wcRsW$=MWszBwu49y{(16VGk$wykTPKs{QJ_yGG0!0SgGU*kM!K+uFT
z$u%92&FxtAE<VEouz+%ZkmXd}78x)e at q82?<_G?UHg7Rbew9#Rq5PYyFCi-R9QPZ5
z!ALe=tvm}OmUuz5;+te#`9xF4&EPwkFyL*W<b;bXM^Bz7+MHzs5WDkNewrvW@$M%=
z(Rhhy`(@xheam<%j6~sYj$yv}eLR-2_~$6qMjUyaB;w|ji<pzZ1xtoA80kdD6UA1d
zdQy-X#m~A2-nsP&Rq8d|g=z_=9G}-`zLCWIZhiDdKxI7)<2g&fcx23ZXx7~VBaREZ
z1TOyFXfmEOXY^t0<VcVy;)5Jft2a-Gjb?w|iD3*lSeK90CU`%Q-DjNeYXS&&gE3??
zFP*7U!lpdziLc*i9{Z4NnX;4R5>9eF4#vW>VyV4iG_SsF$@!6u?7jx+29hYTaZJB+
zlW52{>pOM7K1-<|F^tGWnI?uCYDh(c2kG^V{b6D|coGDM`Rc5|`lAe95d<}D at Jqmk
zT}*N{dh~6%Hmvt&Er8N8No<EnR~7SPHmwgbKXT*DWKwlPdUj{e>V-i@(Vxrj2a7gQ
z?(S^YO(qW3T at SC-j8^v>FJ&n8B at oDMN&WlWY_6-Z(fsz<P+>rTA%Cur6b95+rQ~{0
zS%K!|(8$ZfABPIUG`t$72LhrgY-5<ow{p>Ea~ArTJ4ZbM>G>bRg^c3#FHK096q2cQ
zn{d(gPA{qm%&p&0XWnisVdRD6Ltf^<0TfvRe}XzxkejYKaPgh|h3;0`V6MN`4Y8jc
za2 at pArRQ}97NegPi9!j63a(M6e4SZab^xf1QRjYu_a#qZ$=p|SGxg{@F=O4I;{woB
zC!vrY;w!!p!<RdM at dFIzbL3ru7!oM at CeXE%e&-F at 0RISKysFDd>Atr<=cJnp)qp%F
z at ixyHn;AB|rNX&Fk~wd|#E!Bf8F9kv8sGtbLp}8bb42$(S-zo#S;dk?VER;pw9#(v
zxWYMO9?suHNQsK_SRcl|(+BqQ41j#xeB0ui(p;5VKIPQDf2f+lwIFJ*_iIZ!MwGh%
zzp%X@@=n{G`;!!y`yT%}A{9oCdY2`mvhFEnR^m~y6p<fMr2|<qt1WX||5L0ajPlcS
z@#w{kLWxK*PA#evpLh-Gv2z*!GcH_yZO%>GmhwV%0W-v26D_^=n5wJXbe*<r at n$wS
zq{N}<Ssi;5mT!4|!rnWRPyrV8ZJdFTyw0)tML#xnI7mN(XU`b#R74G6-T&2;b6YUl
zc`kC^R5lR1(e?;zNk&T{h!Tm<Ug;r%R~UL+u4>l|b&gfBz4Rg&9=SsKr2vq9dL~=F
zA(Zmx2fIn_$veQ~qGN;r$tRGk$}Ex;@$J`F{aobK-bFu7;N|&2`&I at m0X(jo3q#0j
z>ia(oS&b^354ISCl6`8G0k3983|3g*!!Ru4j?#H(GI(^(NU>COR<|oarzW~J(VcZU
zPk0y(53MkKr|=ux$lnh}5w|M1AaPU7xh#FY2}y+X8&-%T6(?am{)mLdeh~yn*^&H?
z$<DgBxIa5_patc`%FndrK447})=rJRj)vU;$t#p>eq*kbNGzk=?`y71bL-!*-Nd6Q
z@)>AB>SLTE8r;T at Cu)O{qZMt&vo<=aJx@=qc875^ug;P|uMPY%S-bLP$zTkgu%Ib0
za;bwgo~?jN0X>+DJWR}KUGZP@!Bh_m9KFl~mtaat?eJ8z{7SDJ*7O){-nR*ae9?<$
z+Z at +`0Z~8YL0&fR>>|FhG*`d>)Tv}Sc(m`jS8z+yn+xS$!G(NRHu;-tE>1QZ2@<Yb
zlw^1lcY98+C0A}PgcMnaYyhJ&k`@#TM*+os*)B4=>BA2qqhH=*ziMN!f7Ve;{qfC2
z1TN$y!#L8G^5 at Li&bU0LetV1iP%Vf`3;&wrYU44h-EBv{uB14fJkRJxlH1 at Yd1oXZ
z<a3t3$ol~Ia+dLif>tsdG(S&5m;j!4X;~k{fMe~6m3b5rFocsGAZ3_Yp0SjcKegUs
z)+NL%C$y+Qza#`pwb|KR(b!rOCcLA2YbrGVmGBaOK)Mc{k!t-C*klv=S(6t>M2FU`
zUy*kkRF^l6Ka^q7$?{LH+~M?YoJF9e)A)xaRh600r~4CqVcZ!XlOHBj@@`ssNjYS4
zU$F0$5Wm%*EuDUNo!Rp7nDI+1^N?6V&t3_mt?9PQw1r*G20xiQ7#TW82d)`5i6|?H
zlb44}(uu_95&GwqV=<z*444!;b$PsJsUufR9l?NNe$Hq?%w2r6D+0nFVq?J6wg-hO
z4F-UP0$b~YZv^pb>t-Cp_e*2%E+drXOy<O-E&N}3YP<L3&3vl4k1wAe!7_MAjouvJ
ztFIP|28*=p?%PrVD5yk3hHK4i!kj)q!Klcb9Is!DDV?H+TJM0$rav1n-1h~b|ErG;
zR<kZua(YfMFRJF$#Fg}ZpBtR<dVR|gvW55(C_f$_T~PsW3KFjc!m>RlB%<E=Ylk!(
zfIhwyQ16-hB<=484N_)omF&>bzMZUlXz|6$$p*K`W8?glk`l4L*zdpFuP(pL<f;e7
zAV^;EzbdIq{NN9Gol)@Gv;ysWNZR7h3HxRoB5ym{$}Soc!TDRMbZm at Q3K~J+%TFw&
zpN|O<!s`->wxU-MICrZXx;t9 at U}eZx{v#q=?41*1^v_1}Vr(2*EBdmA2lBSwwo_tW
zHMxRG9EV at b!e^S>-Rh2(y+AZcsWTS+l9H&2ylAa9_p~kGSUaT1ZFFCDuR1BJqEqa+
z{!g|2*+)n~3M;H{05=zYi5&K=xK$`w?hV$iVdX;*V1-U~NVU3Xkt5VRsJze5Gof9x
zYD`Z?;)2%^6Iz@^w3K+IN!T|zQ3S8y<`D&&ZbldVZzQF|1rf^;!Gi at 6^PKA6eTZ at W
z0yo6bM^|r`D4g5fRD(mM$36vMy}U5K{ra|wTA2?JrVMGrZroGeNYAO9KVC*~TK={P
zd=T|^HfC05I4}^c9kryqA~<9sP+t!;7fpsk>*$AD<x+3Y=BB-}Y^)=WB&FI|^cUI9
zN)4E_ at nvcJ`uP^fb!gb8AStNh*pgN_JtZ?W$h?dGQk%<8_};bU+KSKfk9Jt`HHi(E
z4$S;fPC;8BQ47RN->PPG&@WSaNLZFFGF-989Wq$R7gr&jo=0FWRL-3msZdTg5>52s
zvkkVJM&rv-mYkGIZ)*B!Pt$)b8M~(}8H7gBH&g{zXLYs~m?QaG&895MBGZZ+D{a4(
z at aDNsxIy?Jul7BNRByMylsHQpL8C8LAoyywhJ1sSKAm3cvv}cfIfMjs#}qU!OXZhO
zCqXDT`upi3R;F_#5v-T%q-zM~V^@{&buOcoL=LM%bXiX0oLIuDfQae&L%yFm+Pid<
z#!;|sP-6M6G}^OfBi*mPi6fQy<zcZghDJ7 at h%X;|V}p@<5QB9o`26 at A4E$jj(ntj|
z2VK#eV!D5ME-2eJqqX)2r6BfZ>J}&y%uKao(BVN3ALAYbX?n^pZQY_Ax$8p`MQPr7
zA{EWFV^5$}JPNsMl*RES2L-Ed at Td2swT<yAUknl7F at q<pkU at b~Q2)10FFoq;fPe`3
zQ>RQq16fFRFi5;g{Vc7%*%op=+q7?TNeOVZxN5EA$RT&Q$Cw5bxb`YP02ksf*eS_;
za9dNxC?Il1A0tRlPKttiMpv5S;me=q$l+4Pn%ZBCysR2Z-f_h)luu2jlZ4c~=3F%q
zy)qyXv|~L$M+Qv`ofoiJkBpg{G1i?GdbRq4-!Nnw?q5lrMaVo!bsTuTFCzGv>pY7o
z7~3E^ZOfV$o5J{eHYQQ~GvSCgWd~)W^i$X0WoQd<=4K6*xN0ahX08s;-u8`#U&tFs
z=z0dV=6qbp9thqeIZ?%dPvYqr{chUJrajGGbgX?FW(BJ;;59O!Q}OLGAt=gs0ys+w
zlE^=S1Rza1AFwU-J*a#H(`CnU=t$KgDWkct2RJ<&+315$c6P_L5C``eQ{0un4Qmc-
z0Xl;Q2V~^pLklOj-S#Q!q`)3+w*^O$t{SNtlksZy6WXZBtqZ{88X_!O+n at PM*Vk~x
zHiLA1g5{t;Z!()KsR`y3sVztb;>a*5vF33+%%wiI2VgKBHvSZ?FoT_SgHN})MA^gv
ztS>hR({7c5=|LkXhbMl%dnX#dqCJOvRUl|at(!UXv4L7w<AJ?v at mHRy-T`thwf7g%
z0gK81=<er0jz)5p)9T0k$eCF0q`Tf5%}FPBqtLg&xf;;xqg<*W&`zYHhbQ*raOi*|
z!iBS);_*&rI8fxz=oZ<N!|(8a6Z>f!5934blhZPb%Cy2l1eXu=ki*`0>w>bfm{Vfj
zCmQ$N;@+K*lMpNBH&;(m{@C332eMEJpZh8odx}L=Hp~O at m)VnR6>-rT9*{pszfJb$
z`{ZO(C;vJ9BK9*ytAvA1^`nto0J?B8y2w`8;NLt9l_ya0v3Bq>Tn=SaUXSTce^sNw
z6X|e#XBBnB18OzNlmOqk=H at L-CKLZLe5J68C;QhVPmWG*pEbz>q}Y#za#^!9zATX!
z at mBbl$=?aRIa>!D2S;?jKGo~dpO=sU=;`jdx`zAe6h_g%$0HbKrW6iLRB?}ta}UZ4
z<j{S^{4`U)vA0GEAuGo`mX%WvZeF|E!=?rC$yR(`6*g1lSD%pdnIAFK8u)xaZkLg_
zNpSh>?blDQHYt{Bb#C!u?K#$mCEzy1jhBru#zvX8v?8rO%dqoWyL*$ZXK#W%XWz2~
z7w)p$wBVV{!jSBU`kx9c?3H>jZX!&`rHVl>Ymau>`UpUo?9hM|OJy2$bX7AabMxSA
zd>*yIj{Ud+7Oqz(3^<DWf<(64|0N(Gf6Ns|GNj2hxshGr%1|D)gZ@)lgK%16>Gn=^
zVO at -%ZeaS#%;`Z~7rsC<Oc3963$HrCeI9Z_NvLAW>p05KJR<H`*gRsmh_vMOdX`Xt
z at 1v#R9P-%Qdqa#K8Z_9LGL#>LMd>Zl3L9#@rh}`a?1HX*X+TPX!^hX;zTQ9J%fzvX
z*P67TuD>hyJv?Yry7Z$BY&ZUg=~VR((<u|oCrcF=<$#MX191Mcm`+4+^*pp*T4nTX
zOD=-IPNP2RT{h*wLh}9J67p=iCBl4tp-qbDFh<oS2H48j9ng4bmt3Z(O-&dW40FR8
z8_x44a+L4ron>50u?o8Q?%0Qq?swk<bYU#RI8iCFEv}*(@+Z`I!vjlf3P91k`5h{0
zsyG=LAT$AUnuhQ}^tG8_8M$^C^K+_-m^wb_AM{ECBnUvCJ^K(3c at s(%s+3vix8%(*
zq;-uC>S2#aKp(y}w8lk2Ouu2Acu5~3q^Wk%F>JbxK$#E&@wS$^HHPU$m!H|t1{5&o
zf7trudjaFN9?E!Hr4u(>k}%2htudiz%*Hj=sxG=B=xEL`r-~f9tNLV)cXnFfx7 at nH
zF!&Jfm$?7-WFp65?vCX at ebz91k!<7br`l3@#4{t&tH`J2{Xi7hep>Qp6!MbY|Kjof
zDiy|Zc6IrxaTE8K5C7&kieDP$!A|wi!6!2N^4T=qcVOzoAsF#@O3FZ$3!v9z1avUm
zRKpi?1^Y3q`n+T_!vn&ca<73;%0eP0F#eRBh8gUJPXo%4fs24C)B8iMR8SoF#Xo98
zR|Cdy3IAPt5aCVfEqizIDNYEX57AmIM~G$Z<<%=yRs^q=9<lf?!c_mJn}#Amf1$&<
z&bgI{S)ROmjyKCS_0j}iEFOV~XF`ix0WuslNUk?Jp9{>q|07?&1aXZsjhc~#n$Qgn
z_e>nJFL{Mj#ghRER<<!j at 1MRhnQfIrbdXb=V<i)4kdU*|^Lko1D!5<m^z2upF~XFm
zn6#(KcfHc2@^%Y@!O$PA>W2tDL$FAYf3)=qP!XwH8JI-Foc1c>h+Cf<0eyo<#v74d
zT<tW1tC$h%OJ<+lVO`Wl*kFXbc6ioPwwD|bGeMujZx+O at wbM^G;x}Q$yI<fcTu2?N
zV<l^s0HISTx{dF9?I-9G54OV;T6Jz;t8S+5>>kGm&Dk6!q?acEvLS~LwMQ+-i#$-C
z`F{SvRm4ycXS<gp3UH<{$#sAJD+PUJ!bF|Ly|fN#&8;rhB$h)zY^@~tJNCMn%e4u~
zy!>i~$`nyi?08R9|5^{OUw at vf{~1FP{$5F29!PWeaQ&t7w|;sUBiv-|Zh#4S(rg_r
zbKpdx^gfIm3JSA=phZ=F-d-z?5smR<XxQ+sDZ<@iJ&KhVZQzL~dN*!p^)Aibfa`1;
z0ukBSXOOc`E~mHF4d{nHmXgNvNE6q$eBR#paF?V$*z_5n%cB{F;7m@{-g7>zd-XAN
zmA!B8EBC%90`X)9`i%PC)9pH6gde#_9}v0s3*XQ>fAVc3c^+6#Mh%5*lK}j@(HMGt
z%Z!P|)~ii650?!?=A%}E3HbIKpy^;?@#H5<hFG27XRN2PW54X5YI^+oxD>_JCrAtG
z*c(aQCA|yyPM>5x+GRG*Jv6wBUHR0-t4?pLeqc at sA23m*#@0WeMdxtVWI26}#*sX0
zrN!Mv->Zuw>h#^$$`-e6&8`19<*gOI>F8y(LxyD|Z0Am1JP&L*y(zin0ZECymnUtD
zN%#mu^Must);(J`W`TmO_zkuVb7OLQs=up;OOmWHD^TKjck+`NGr{7nkD-0cG251>
zC1UT%cB+gd0dJL7PQU&9&?o5+nIFF7tqs!y%AMP#{C13swJ%g6OgnsE|0#nCP^~5D
zgB?HM+o1OMO|HVCUY1UMue1b^84>2KVu$S(+=qiVgVEXOh&1V*XQ{T{g`Zv1xRDI%
zwQ*P0Z4dwf-*#AON7Q=#RE`6BEGPVfd-JY!{H(lU6#+U at uec2SRHsqxt1eZ>Z;QU&
zBYc!!$tF7!Zg8MDW4Yri9xsP)PFBQ~I13IM)_&j)8Kvn_i~#R;4{ouCgc>2DsAT<%
z<u>hT3t<(O`t6uA!nsXYWQb3*r*~xo$Z3prGTA_30VcC&bBYy~a%5)y;b%`lXe)a?
zepb_%9>%6c?nVue<3%Vrod{Q@*4&<zy7c$Q*08|?WBxiJqsDh?EjlnG{$_U2;qjtD
z`C!z85!xbO>(OBCa?`_2GB}TR2O<f4CzFj7HTPU#WCJT`GCO|yC&1 at 7y2#$E?F0`*
zCg0BmH7hjlMqrTCsg+Lc;0$S?ac$XPn`WxpKFD8saQ-Nbve^!}#z`{wHLXly&{tvF
ze|a at AI{b|2{?$*##waGRZ9 at saI}h6}ih at qNQt9HWNu#D4U;T|3>#BxOGjIJpCp`uK
z09F7fx1iQLI<lO6zQVHqKF;)Di>_|mjmelSyYIdJY*`$ljc8*V-NuHD!@d2Mw5
z%G=Tj3$88=wW7*&5M#`CL>yDiNTI^z)6^}lKD$+}A%2gq(6z4ARNoE$juz&roiYDK
z9U&GrVFzPnGAos+8#vGm3VrvvSB-K7RVU(*fp1aHbJ8g&Fk!laGA2m92F7RTUH<UX
zlI>enSa&bXczsde6MAWrt#*&?mpQYK<9szSw?SGR{fG+6hDDw*gk|X$8!nEp$}Z&_
z0Sio883(q9rKe|$VK#A%Lwv=*9xnqRfjQaK>4w;SjJg%S+%II!uXv6ymfR2w at Tr{j
zSn(l(;nMJ at U}o{*R at KcXjCIeg^?5-+aQXB0tpbjB#%!&v$&(WghH7`zTRgUtHc%$p
zE|qtJ+^UBzn=<Bh9QG9htqO0gwof*56P7N|2v}jOCzjp(jG49jSF*D*W+%HnBB}L;
zQqAP=I{YqhGTQ1>iXb5_YUnTmY6nE28qJwm1(%IxnOc{+-Sr-b-b31vVaa8Wnqfk2
z9C`WdxpjcpA4D#H8}2HU8P at ON)10K~aRpJkxIFYn5z6C_j8+<82dI~6HAVJF0(|IL
zlv~ZP;O|L{r)W!6V)zMAdPN?m+0erK(k??|{b*4AJaT!AM)l%}li0We{xDUNcY?Yn
ziu*qHr%lfi;xC00P+}*qr{v~Dznl&GnyH`2Vq;*HBnyM2-v#vtyh>o{$4g%cXTkGW
z0C1c|ipT8;x+OVxd>iSgg)gPJRx?9@#C8kO${hLTmsyl?hy686 at rEN#wxyPYR&-Xo
zrpgP8C4G`K-A83!8Vpe?B}hu=Z|ln$zot<Y6ETlV at goLVxKI#s#&lIzBdPP}5sc;~
z5S`5hF7OQ7c(WYj&(KRNtDke#1in)MXVYizL at ynRV!)aUg^s`&xwJW_)0{C+80!vB
zoYtnn=dMk>`mNi%50r at 4jn65oDKBa`dl1M&s)m%A$bWI^z#@bZZ7D<~Qe3P4irH!j
zVby>46?KNn?*mfz at s@Unrp0+xTgMTVWab!_L&HYYM3dQhHvDz8`C8N8^wamxM{qf(
zuia&RD{JHY$(#4-y*-o_=Zht{)NY+$-sBfBszHKNr}1jiZTo|CXnNa&olq3vImCRH
zI)me~s5X#jr;4Iy9XQC<mty|VRCT`@zeU~W!^L@(8(hS-dNK*V-6+05 at puv1y-h8*
zE<t+{{aY9`24h<GrVwL-IiSoi+(}7U>~tRFB!t9RjM87Z<j3b6Hl0kEYpnrw<MhH7
zlP>*0v!8}ZPI`>sI6D-2bTdeA7m#mN!3UZ`wuYWd9i%nds?ZH>K8aPV$X;f9S^B~q
z)2~<{8muJvtfubhv?)W!@N>;!g-%3rZa%U1(XAzjaI3t?Nh^8Nv_bP{q%m#lv)id*
zM&e^_IFjW4dxmL~3&b8>R&$%t!I{J;y;>@=AT+o*yN91 at l5mv!QAOJE=TL$Rl??=y
zEIN)>82AIw>W3bN+Lh<PgReYy0j6po8Ih!wYfy4}bO8)*lD^*(<5;=^z+sGCdQQ4d
zzo$)~LbPTJra*)D6mP72+5im9!NXIfYneu^b<PVZY5fuMNCIUHA at h&NSv7vi at L7R4
zr2ht*1AfH^M?*HPd11loBH;JiRABrqSaA3^E at c45<xcz0GPG at NJPS>)s-;U!>KHS%
zaX7>6el=mFRPk;Ayn=RRCG3!MKF*dx{nYS&=GfHpfpM#|q4(I{^~KOOTysuO1{-#a
zInE2S22xt%fbpxRo$#r?NU>z0)>SAU?wl5oev4zLuN at 3(Nzb=OxoO}KzRiF8Tj6Jd
zNS~$}+8+V)!)Yk=1v2h5?Lx9wB%X>&*YQOvnAPXdU(sJqJdF%8l;@fcy_}|-NJEy{
zQ57#lYgVZ)JEQSZTo_01cW$_R1!aA?{O-qI5-zik<n^b5MG at 2;#pLm$j8TBkCi8b<
z#aO?4<xkX3suZ=#3%&w6@)45g)YQLgD2A5g;JrcOYM##uAOf at _vq1H$n#88F^^xZ1
z><6LP at z&4!8-}OWJ)GS|$XaWCzrN at nXAS_8eqr)Te>Emvea}cMQ!^aIw}|K>sF*2F
zW`FSVr#5p5(%9M;337WiDCBhcN2<JkJQyMe`62F~05-9J_AkD0m+HQE^BegEQGOp}
zD9qdX9BTj8KF0QxO1?uy{JG<~;kBb?pL3ds(3^Xzb~j(`&R;vkHzsSX4D{$5Zj<iv
zfuRv4*_QT8tFAv*!%JXWq6^L^c#l1<eK?$r8A*b7Y*lr4$;dH=T~<Evs|Fu%@B~5q
zIR!5C at ZIkF6z&;JK!5?(Q>w?k;#XC%ZgdvYb>#|F0KW(QH&E~)OUM*K9+x1<n-dAG
zi;~EgcR<09>tqFkHNWJbF1o<2=fgP<^tz!{azI|8OkiC at eTesHZcW|-sVeVw&bvV%
zPs!fBa$4nOwnf+)es<g+$@3 at pMg|<57Z2c=@_t?VRbpY8m)2gz^`ksEOvtRK?+p1O
zp0Tq#&+<Yx3?g8aP_L9RL%4HpkM?$aCcweq%YHhQt%c*c6%i(~4j&X7WJsJtVZ?7O
zB`4ob0K;-*H<p}oXQ$DT8DW%#Fc&(d4aY4?0t1?o+fa^lq^3xTxp5J<6gR)10!nf-
zusrsW6Zebt at fzjr)T)3zr?`RR;(Gfv?87DQa9}npR1p8ySK|n(1oC`z>g<%64+-vo
z`_`VQV~R4LL-AdG7WLC_tCmDHUFCk%11XGuJ2j7_3#LUZ=_Ptz#_ko`dxsI at WcN^k
z)n}=#d0nz8&n}FthSsXP?__xMSJyo%je2 at 6heL%@1QpuaVqbfY88RJekbwm9oQ^du
zKeq}HtcT4 at F>gDm2Z=^Olylz~yiMvgRcdV;-B_ at 6*Wfa^&hOq(NMRrM{5uyo53l2m
zmap!{gRX4zD`?qfh;B$mgWY^%HrThLOlTyOH|!1V-7p$@_;)+s+(|(-C8mSPV+Q#)
za33 at _!bOXPYmeIk5oTLty1&?7R^~I&Rsg;er62C^<*h}JsBal<N`-jd*QeXwuWINT
z>}Q^;fj>JN_4xb-yii(3Wja6E^VqVAjI95&+3fg~2E=aFN7eBYoTYpJq>nYVL}FhM
zG;-|wgk_b>5MjAt2PF)j?x+eq8AfL_B(P|&R7(MYB0iY?0m!X+*#*TX6IRm-UevE1
zkOyjd8*-`oxpnu9;MLc2rilFk`e7=~m)lgoTILnkvAT-EW$2guxfaNV;Xk`w6ktPU
zKRX6>obhGo<GX(p-_L9dnSV<701VDAHsk|o-pYAr^7OZDeBF9qtNI5IS=%(0jE3sR
zSs(r$SW}>NT-rRWs}EVPW5U8>*AECUU1xVYddHFbNjs;*Bu}lJ>@Jpf$In>6mh$c^
zTop2EdR7o`^0=5p9}%Yio-+}P6&2-$mKrUAA`^mNU!CK2MK++h-7n1sxhFz%KO==%
zdXz5xc<YWpem6~d at j=&xEeaX^#7&zJ at L**AXsx%-8r<3|_B?P^2)M%LgAE$o!vGZ-
zCRF;pSfsj}>WFgM$^Y?Cp3yi^EI#*VJz_Tb4oFTS44t6H`Si)xGx&HNS1lYz{tXF3
zo{#0n at Uh!H`x7y|bVf!jl}LOo%SKTefjJ7IYX2Aow0h;sFA3cE6M~y)jNO#v0mSn{
z1S$<k$HMt)CnZ!8N^FV&{g!XN%X?Dgt22`Z9g+_v3x`ITx&f`b4JxQ%$au9(yU5im
zD`tPDmr8pnz at nY`&$}xCNIvk}>^)ko?}wEF+s4+GezHspt0Ng-hR3gCUw8*&az^D$
zrjGlm+m<`yk*?E`?MWJfb1MOUW_?J*m&9Y^Pl=o=nXeB%YZU9t_HCRhh<@P$%GHOk
zTdT3ZqDZ9?I{dmyv1|S%*wp;7wdl5kS71#PuI7ej>u5kvazz8b2}V%~H1mM&ZSnhG
zw-t5MSAax;gH}lUajALn*QUC&7cOpb;T48S19svu+3TWTFrpTYV-?m|OR- at H%h)fJ
zm;}Y2xqYR5zE;2?aS`i2`iG2;&$C|#z}Fk2^PVhZ{#gNSKP?ZP7%Jq8PPqOl+b+Nc
zcO=36*|)by#P4}P;bOr&=G?od5eL&eAPVl^KeshbzV6Ai%f{CJb~&-H8~09p2keXd
zCeKR9NynfYA^LRhLWB2cpJ2<25=-1G3V2!Qt9`oWgF2ljQuhI$)dL_4C^OPqU4ffv
zdwrcOw0?14-kbUb4cs*(?oI~D`7JY3ygsu#vr{S)nDqU?#qUpJ0+M;xc6$B2<9BW&
z*0c1xpuRl6 at -@UftCoy&$X!O2rqL7`Xj}rJm<)*Dw->ab*8Wz8jt?+CX{9BG<rk<I
z4H1^C)v9w0{0NpKlNi#+9Qjqfkz=`ZWqqcv at QN?Nu10rI5ZInN)NxA6krtqdrsQN^
zzdo<s#j*h_#G%c>EJc<teI&P1-kwX=<}{ThBjAHWWCWQip}#?{wYF;dPZ;Olv<1+C
z?l)d%LhuuvYTmJhc{~7W{Atd|Q+1Jp$5<udqn@>KZ!c0?Wv!&(06*|^rB~NxXG*<k
zoJEIukGzRR4;ij)M86t*XYQCO`2%cOpJ7KmSt5cU%+JHZqsD28+E+O@{@{Rl_e_H~
zAi{bX<yub_3LjD*qh8&VnpXdgVtzBIw)){Vrg;9ZIxBp307{CL?dj^DbOalSAafsf
zijgpGM$Y2HP8aZ032ok9&(DP}Y;6BMjbe4}3L?plFvbwQG2L}4o#*(A1o}5TECOBr
zPKEGA*cQwrn3<e1 at Cys{gLh^X>SJ!gOTA;ZlJdlH7}-Cs7&_V|+4wGf}IQ7teU
z1gRoAP{F+HhK8(8Q@}{(5|=(%BS)>KEz9~l_r0*KAPh9 at fa>SP;v{)Flv<VXOu?|Y
zx;7PL3BNLimlBxI(Z>&OEvi5M%^@CKsNlMDvh@~wYN&o1O&U-Hv*uj)Z)goSMjVd!
z;K!RK!+d$+$sc_W+b>F*<d)T%nxVS1Ab8yZH>%UJL?1_GoRyWET8!lW-kllF6OU!G
zvzdL_ at 6@a0$${<%Z(o%SfHk-E^6-Zl78hq-)kA^+7Gim+BJtmKX<3%Mw8i4JDs=W}
zLvA=;U+y0TjswJ}I|lRzj~T?xP~R+T{ex)&AlECQp0nPVk``I1B(nz6o at dKN?SHKc
z at SfbeDtR>cD6a`!tl-!j7lL((V?#*Y(1EuMWO<bL at 15Y&IYs2xR}`?FFl;qkf5sv}
zNL|6VF=&=lKj_Fe^hb#%nlA(QIn3a<twx~N!GD&hD&YbiynsYVj=YI!Q4}r65|(|i
z8}v~(B<3S97S?|-c7nn<FbV87EvR(QEgS=&_s{Yg=1BMQZ_BzvK7*NVm5*A}M<#P9
zYhBYDr2L2Y4{CjKir^&+6<_N<Y5<7vHFV<d-?$-qO?1pfR^z4w7tgM99A<wKyw6xe
z_bG(WXvPfdpC4+07RL>iO-<j~0h6K#hK;CP*nkac<eSu2a2*`|GIJs%9gGpl!x~?N
zZbub~Id7Zn$riUz+i%MCJS}ckd2?@$hBeeV`qeWP6NWnEdYFBX%HcEVVxZR4c6_V-
zh!a+euDm64q%}>lPHqkjqiK_#Qy%jh;=hydmgC(R6h<G?Nfz>}5~8mW(xq3 at ztd_a
zYY_SEOCR81Gn!7}%gI$p;+$oUcCgaNq|d1D&b7qJpM1)mS-kxOsV7UhMZbv$*7^ZI
zk5VwJ+Ld%BNLW0w+1g{8Yt0Lt_^0T1w$-Z+cNw((eaDqY5<`}eHjK%Q<6;H?T<%!J
z1dapJyxq`rYCqRL_mp)m7!U<>)Xzny`J9;ADt93=pI}{brL+MhH*9BSH3Dy>*DDoD
zRkw9SRLguidT~Vo7sC;Z+KZemjqvoYC>5SAs+$Gm<7<Q0!1PMr&2drf00}8 at 6BW8Y
zK<`2rL^qCC71i&@{>3b2TZ<f|rSyU79dMucSn!u#y?lo)RwxSG at 91V>xM4RsW at G~o
z+u-gt;%El=_RJ)d;0%cU({*eUq9W_FrkVj3#gxBPmSAW^XoW`d3{pRT`u0%lmz(V#
zh430M9mi=4rw&D=O7(qa(?aD5+{g)=&jis;+g-c_<K%bs!%PJ^Qzqk-&OzlWY;J8y
zKW(9N%wo-!p(FG_#)gK-3dXNBipV2QO5O{6pUPen*NV;My+>B93CHaja2Z-iJoDIj
z|EIDSgZES6NwbRSmO16;&kps~?phX+R8N$sur!g5QD9C+W8^bb_et-a- at i%&QwWN_
z+QnVym*E&$oF$9LWNrL7b&2^d7qF$TQ~2T1uGB#Ai at KtP3zr{U{)D)O&74U=8F10g
z{gUWH1T%%f_nv>SJ88fUyj3vp$ViqJ<N;rq5qeIP!-C)a%Sti+6$r>!6qr#<l3&rW
zieaGhkS}~jcG&zJ<Ast at HW)oS!=#bXL$r>(ldUypU}$<o_->vuE#00JB_rgQ`9kTU
zsGzfl;Jk5vB78XftU+r>Gvs#J;W`jkRqN|^eWSpgyeMGn==+o*Jm>(Uxy>kBUJ75<
zvJ;1C?JAv#R~vwtq-0rIXXIZjuJDm7ktjIMHWcZAD;QKr<o??|U^RvhjoRW$bJdI?
z94?>#6i`vEA!+uN3%E~3nNGlc`oeRBaeb-m2Yu|_o;5c|n}1kUbka|kLyM)9W#y_v
z4-jmwEViK8riv+Af(2tR$$LqlM5^*N)89GmG8;b7lT~+Be_8-t=s~X=`dnv3)OOtX
ztugXigo1v>2c^<k)1>K8eqL*&jqzot195l^TSiY+NdV>b2b{duh(H?TK2Gd>eXrk^
z!rwMW#?Oo|&xQ_A_VnBidW~(a$kob_biIR-t|t*_M93XD2s2%t0kEV<vuEeaFFO$K
zO9*cEKaF&oI7#>yw5f0sc6=B5K#I~8QZQ!{5>!opr??QP!qlv;_8JkcqW@$syU2%B
zuRO&ADywq*`f>A!bcl_QzB3p at HNfC4xS~RjBWu|qy3UYzC?>2}vAk6$8r2eDfF;dD
z#tt^nowx8Qk3DkYOW-s)N1?HuE;D^T+UftV&V at NS+XR6pzA+_lcpH>q>s4(cio_X*
zz?M2jhq{x6#Wm5r&denj8h+)!QZO_<d=JkS58kw92R=1Tnq8(vH86Zuj|t9i``-Pp
z&gk`xf%-AewvJ{{kw<+^bw=Ks=Z*?2&jE*Q#x3u!dftk*&T;tEWH^Bqz-HVhlM(Bu
z*UUd$)M<PW_8aG}W`PORZ*Xb7d=;t-1#d(j_u*=Oi0Pckjt5{pQB1AJF_Vp_GO1nT
zXKa61RrXE!XK480m!k#gH8j0nJYAz4O#I%Z=mQR at kFlRq8VTaZ0d8NZXN&ujeJKPm
z7Folb at M_PU2>b~98uf_yAZ0ogX!Ez#sRG-2JpU4ncNbvaNZgiZipR!!U+x8nbLQlm
zqs*L(T5n`W>wqApY at _mOHumq603qJ@7AaPuT-6=Jq_paayJw5slWiHnID8|hi>u9!
zcbDiI7Do)qWSJ~8cH at 4vL@*1HuCS at _AICK*=4H0qDWJ`{0iSle4*Y$HBwCxm?rdi&
zlB{I8{n>dPs3d#sX`StUgx3vjA0UMOUs7{Ys@#nqQ-2!s at DAaoDr1KYf4kzkCEOqz
zfDegFzi=|p%2bt4WVw<WrVQ$|EnFhR`9SY>fUwCH(NV@%yT^8~2KBU?1_vquA^w0G
zD$d)~K3g8NW*}1ep3*tdjEKdwW_dkjhVNcL!4O%hJ{BQMAWzdLu<N5Rj%Er&ISmMk
zbi=!?fj8Fg^*bWaURs at DMyTmiL;3Efg;*+GZF{p>No}gLKOVzgor7L>GQOV&a8)!1
z^k1;pWGsukN??D(#mrJ|98pUZPN+o15+SPi^?Oy!&4!?;<cULzXUq$ugsTV4bgp*E
zUKjjx(hZFR^us6H*X~cgS(W1QjbpROIP@}?Pal~-=*JSXMT*hj%~v at 6aiO}?sgR-Q
z&U<Hr<Nq?bQK3`B$)I3a%BM~TxGjk1hVEXPmg^t(4NT5eSz5wQoyhT9%(XEd=>IiA
zCn%b~nQCFzUDc&9H(ix10g3LaMwSwZXp4!*+Ccq_8L<&uCbQf5sA1={CzLaH-StBv
zR*O11RG44SCwE(p3xkXges(rqM)&7=gl=42G7ItNHMH_ZQb#^D9ojv^bV`vJ-TJ-;
z5oC+RqN7H|VYtjN=}&VcfRe at uM$2z1wJN+jpAsdm1?QDp0-o!y%Ux_flLXaqhd5Q;
zpL|_EwNqypet}_3FyA$g at j@p>aZBexc)fbo*&KOcO!7c+>&B9brk3hYZs3?Y6J_fR
zTyrl`dt+mDXzFYOqnP5bZb>vYHK^doN?>tnHCm)LqkOi6uEAExfkV~?l3MB+k*U=G
zqU3#QrFL=K^bw4ODj#c&r+B4{>h3O^IHWpxK>Y%t<D$<fCoyot65YG_n_0n^V1|{U
z(^T<pByF<c(ksqcQGAM`H`da!L}TIfNGl8*LfKE6_E&e;H6YC)+=Q~fi7R?e7HY5K
zCM1JAz2OE2=wa5)H>@=`4>-p9XX^U2Jzh65uuU<-$qtrujto_Xcl-)tMhJSW{L#)*
z5cP&}wEAe;^Scj_J at b8^KiI5E%}vpFU;b9d=9<HLHy7B*f}PV?i+-|RZAxdwp at +SJ
z)d at S6b1M7xl(cBhoC?}x<Clrw!GZv2RQAqHfx9TcpFW(i#9s_<-2JJSFk at i+Bk)1d
zKu~yg&~Ov-8!ljfQbpWaO7&Niw_O^;YX%?F=!HSc6EI_VC472*HvO1-&j#c4Hu&?%
zC9rV%?oFrazou-f%Ei31DU(tmw3C0e*(Sj8K at G4Udw+T1U-2Jz^Uq@|Q}}9?=Xhbn
zFn at JH5`!m3uqmhJ#hC-(KjwW!pM)Ib8Yq5tme3zR1z4#L3qzx44jWl at 69EKTLc0G{
z(}9iQ-ZEdI4KQld`cHmDj3-5bEt+k=2SfsBHhB<w6}wXoQa2Mvw*L5<Kg;rb?nsj~
zYxZ%<vj!&GzLo#1LA-t|pBy~S_&+P~g~b5^Knnk{V0q;EM>3F;Y^Udg74%N)b0+!V
z6B;a=<GT99(TV{pqp!Dgxj{mjQZhtr+TUK~N{6BUxyX at yMC$ye@*UmU#md9e-P+t4
ze&uT6fX>HF!%YLf00Ox*dAQMe;NLZ2m%Qjaynio+(1re9yg+{ezYrJyS7LtH_y6U@
z!v7x<^TX@*pOpMx*Og!Je>Q~x&wrjmfE!)lpI-W}ixc=SiW3k(7Z8Bu3+v+lAs-$d
zbRKwV at Q&w0=i&Q%`CsQQ_&-)nkQ-g_PksLTi}~~YpYGA}vhehKW6h=LY;J3f&L!jO
z<m#^V#@y1H>!r1~y`{CLyfiwOp1qZ)9Sslv3juU41#5d-J5L%RUT$<QX?stPSJv(_
zuFh{<U94R^X at t?a<m{b1t=+j^%4ol|wsf_!=2EeCv4y1;5aQ+M5f}eQz8>cuj}fq?
zU_xe%V1)qtAU-Z4$RJ>&V_;#RVPj!p;o at N95i^hw6A}`$)6kML at N)_Y@^SL=ib!e6
ziHNC*^YSWKD5~k|o0ysi%i1_w8#-wjnHaz`LBzquB_<$dBOzfk5aks$_ at Dkfen-GV
zee(0!ED|Ce!V^41Bs|2&9t3Jw9iJio`M~Q7{~$g=dio3*1r-e)1NMV at T!bfxNJvkf
zB0YQd^eOCV5bQa^Q at m&Rv^-MC1RCZjbZ&&aZ{u at O>7}c?h&0Df8Tc&R-=U!slaP{;
zGcqx=u(I(B2nq?m5RrK)D<`j at sHCN>qpPQHU}$M&ZDVU^@8IF-<?Z9^=l}jgSop_?
z$f$(G&q>KCscGq7^YRM{i at p_?)YR71H#9aixBTet>Fope4-8IBPEF6u&do2Zt#52@
zZSU;v?L(kv=NFe(*EhHDb|E4l{cRTP|9{#pJXpJ)JbjAv6b0Tc#3w%RhT}bbM$3bY
zFQtKE?nXez`xcc at IzG3$3yq#n^OVTKeH@*bfq#t=0&m)%mi at nL*t`Fwmi?<?|Gixc
z2$)ESu$PB~haiD)p3d?$6#d_S|L%i-%fP>7;NLRvZyETv4E&#$0alhT&%Q>nApDnq
z3CaKGEBU`uP*_>*{_Dp6KXt_a#%K8j`A3ofkG1s~aki$`oq at K_><Otk?rz!#OGx<`
zrOke?=Q%TWg0XV!0Qt^^^VeFZBhv#J2;zs3e5v9ApnS(U?Z=&%U;$5K&eFmI{~8a1
zg;d%{1g}@g645-=r{sX{yO%AdWi75@=XMiloE68kI^n30=2KoZe+1F=_M!tHd{eM(
ziG7XzEZ-B0;Zi!q*e|?SRBpzJi!^no)6>&iTS;kkJOaIS8JJFQ<cv at d&Te#*3K~9p
z9XxHE6)-VeBkeD}EJznV<yNM2n+3E at 1|<HR2*Ec|W*;lEn>3x5JWMb~i^tZhm<BfP
z=~e{FxTKq8ZPoEEi|t{!qW|Knro#3m1v53zs4`YmRIqyuoKxgJ_iLEw&-IDfYO8tU
zap*Q#pmNo4n-cEC06|wJO?Ibm`{0Bs=iTofYz%S=ut<@*5IjF}Uw6Eyo~oPYzbnRV
zbFelbnz9r&U$QNb;)>uZOxKz4G;sYDHtfoiWue(!u>_q?D>7BLt}rmrcg$JTojk^u
z8)op;#ynmT>!*A~xXo*rUer*m;A&H7aiNMckp5P~ght9(Gwil2a{dLc9Ve25;b(@n
zbrPO_pT+Z(6>PzcsdU&uPo3i|2iwk{g^k#27Q=&GH6an9-kDavV&^LSdyO9OteVxu
z)@r8PZa##bu;4I${MOIB>uA*Hzmnni#g91U&C&pSd=(zI#TV`uPC=_`ZZlc+c&)_;
znnotk&oRgZFxKD6;0w#jUf2LJ&p-tdiZfW}(1m3~Mav*m%+prn!JKtnO1x}7<C!A+
zr@@>p&N4dflXNXng5mnMc=Si4);9EHl(=GJ<tC>W9a)bEXisYl91N3-*tw7Fx3jFn
zq-c&Xkv;m}2<L~7ro3wVs9uwPycX4so@{}v_U!f8dh~(9!)(wOW2qvywEJ_LR|*K9
zDPue83)WSoow;5%w>G|-_(IB#hLM20j$)oUOSaCMp!(#^152WI))Y0<9F5jHYy&+8
z7AklD2kmgj1Cj8r9bA*L71=Iyc+C+QVH$$k*3Em3Vz=9^fa!sir7q`nE*w=iHW|Ez
zIM?`hu{v*feJkpw#Uft_*JlLrb>Alat`F}Ghm3EWfkqd<b0i3ldPHUK^i0mTsC(p(
z0a6c3IESO-6GN4}kPdG2B>ee)IlD at n<AwYtukYN52ca0p&VwEiWDH597B4vWTNVwC
z!M5f$w_)dz+o<tBo~dP|fshtT=X~Rj>k|uAi-sIUB(38VZWg4FkSMuncHT&Y>D+0H
z=tb{jyo!JQ4!XdDSwl(lJhLLeYfQitOM3AY<V?|SU4K&D>P#=mf+7>}0rT!E|Jf0B
zkB>-GV=@)%#7LvxEC)9R8QCuCvsMqVnS<22(ZKAHpa|8-OeWt$9HWj6Qn5C$>U=M~
za?K8~vv&H9zCqC<P5UnS^>Y%Xk4h$V^j_;hx_!P{Rt<gUa+c;@Ix4o5ZmK7*MTR=@
zB$IWu45Y6TRztvbEYius0|R0R$M&8hE{++K=HK?9_E*^kNw4~Ao>#baV{gPhfA(wK
z^7J9;+3Mw|_PQF;E+0O&HcWRZ8<ZHRJjzPJgexb9*MSj+s+FU+dmeup+X({FJ7ceb
zjhu&e;wg-xo-55rPA5-;<GS5w<Yt3-#RWgMkI9sJQnnsMTlqrVRO2eSQ}MA_Zw6a5
ze}6qtnce=mgZ*Hg59K84+TlqY_1jf9`_WQ~wPNBxPE<Z}Zq6Ks|Aw2?HXQ5}L*17m
z8IXRiA&Wrx|I<H4k6>|xYq?Xvw#08N$NUOca3_>?`4OR=`XRi|aKk9}^sOCxg=e!O
zSJAMgrvGON-kko2q_^S+om6`Zk<6A$1Fj at Mr%!Lx*LyqJre-RPUQU17w0w{#SD_7Q
z=l$JveN2D)t at 7wuY~`A$&I at R|EW;xL-U4q3L3)r2gT1limAVf<xm+UCR~q+(fgN^*
zGLcZD6Fg07e8Y-(rZ3|vg+fr%rMKH?e4kP;2B^c_TY{24^O-K}o6<~qwx;fQ^KkqM
zqf+d*erlO{JH6;RuGM`TQ<J6k at WV08z&10SAqIy>9mP&~r;@M*MayQ<tkr0aqh6KC
z2+ at 6!CUMk&D1)pEybRRvsWhRuIpQy8Gm$EfFjyVcPf{fNzI^q`oMXsdPf$7UI%2<=
ze~7F$SvD;yJD+<?l3MD%q%RK;&)MvIP_kra7~}Lb##(>E4U|-+MjC8#CZ9 at S+2h?g
z2P#h8){*0>W0X&27!|af6;N$c46x<5)gEW8t&Q(-6mC*PWq>rxtM3`OU4PX4*nKwW
zVv)+Yz9|1K^)w8vEAHE`Uy!|vlbrPz79xQ!ucJ(K&DovCyK4t2q53qx{Ga%*XVqhW
zG%Im*Uo<U`%`s=n4X7QqoBqMZ>0b*#_WS;=ZWWJW*}_ at eh}GHZ9gd*7K)Ch}>Bc*r
z_nB8Eam!u#zTKM6a#oG8w=CIcjIT?)V|X`DQJICWO2h*V$D%H*tKx)%xPK&6F58T~
z3W&+Ha_h=R{sQ6H-1s}-)A=6aw at Ydfr%C|&{;)x+zasO^px4V~F*iwuwGUfNy#BlW
zt3BoXmo9DUC39owPRX#tbHh%;Y{fCKv0HW8hAWk8#8K6X+pwcvZ#hhp;O+(%ldtE&
zD0RfLbR#M#&rF$}Wc3Uqr9l at -w){>eT+(3pyJwb9f7xEsa9<fMT^Mt4h~*qx7)8Ly
zbZSS{n?v+YlY?%ld%M0TDt}-U{oNAwi;J>lCWRa at cWOPwCj|;UGjVBjd>mR-1YFS#
z{<!&&E+L<@-_0&g>B0|_&&kr?>;(Tpx2y!T|2OvDIxLPZO&@L~KnM~D?!kiucZcA?
z-Q8*23Bdvcw?KkJaA^qclHl%5aQEOA2=c4Ev$Ok>ncb0{op1NM=8tp1>8h?ib<U~B
z?)!O)yEH_(HSlG5-2PHc$1OI#Ipq>aK)OT$QTKDLa?MHh3H02Y#g5Q#eu^ahw1ubE
z^HeiOg*j?rnfl%5_BX^bsSXm(8P>dHvg$mq#698P&g4aMM5abz(6G_(^|mc!Ck$iT
zs6y9knstZ;zsQN6=6 at J1exs`Uo2K;FZC)=8Wh~0Srkokd+DuTZ2|F`&r;K=&<I8V?
z4Y7Adi(HwP{2}2Qz}NH9k}9dD5DCd_&Z7rdxy(oE`jj@!${U}*QPn0d%ng#o28&q`
zI&f3q@%eA0WPguZRg<#QkoBm#5FOLw?}KF~@?%NFU*&f%nf2cF5$p1bs41Zgb(s$k
zxzG*XL3f;`h@$I?22)M7tJe4#OM~@Pz2X#3vUA%Zp?9EV>gG71>RC$-6{`=7=g}-`
z(~?IF7IElGgE}oo at D86%Z4JG5!`B(@F`4z+P~tQX5oOkGRU`>`Wrf0AZL}z&F?9$>
zN+J-D-*kEK!-O?Eg0m&FEum8uiWL#THa5LlO3O4fHq1+=BnZ6dO2ULQ`n6P(bBWqP
z;oBGG-5Zx+@`efvd=%avH#S1Im??!%ZDzqZE8u5bPh6;kJD!n!66+=M<%MMH&$XB4
z2DOxMG~xuM4fhSA at tSp`#_!&Nc83+d)j`%9N3X>=qsbVw`#iDU7<!T@@jLNZl##rH
zS=1RO9&A$Sn9YtAD?EcYRjp3T)YDzqs(H^9d+CmuX`$3T^25~dmLpqiPD-2hqkj;V
zlJJ9nQhO{0eo*I2jkk));{Mg&on=QJk at 9L>d2E5t1WbfMRsY~st}rfQK^IG)x at 9KP
zgQ`cij2$d*VfT(}Q0q>+U#fr^APh0NX;qv%bFo-PFxL;XKKebIvx0J|Fx29>oyfGh
zu7zrajKr#wuzL*Hh4n}v$Hw>~`(VLnk9`^I)zq5#Y)4oS#v$zcROC}7m+01vPD<>U
z3c&&CA%i(YNf=){Ev at hCfQvo(%-$L%r;=NrckpT<kwuxz=i9*vSGk(8t;A%3 at +9-R
zI3A at LTcV{0#7>CRjdZlaV$L=X=JeGDFoyBha^B)AELnl~maE^0+oL#iHDhuhBQDWd
z)57E+DC5DRTNmOa8Nn-yqefOa_94Y$Qo-D26HU(0!sbA6+#gdunGs=l78%=Q-PiH~
z(O=!Kt&<T8MyZ5h2A_;G2G7*^06)^L(t?KqkA{CECw+mi;3e1NZU*25!&t1TPTU*<
zSTIAoF+0B2sP-n{Vyito^@<N4u^l`NH6OnY8H<+~e;$=jo$m`qO=5oJ0VW53VMnB)
zG&IeSr_cWAM^;-K48;y!pk0o1eiJELa0R2(pRYOWRLiC{+sk{Ju%g_)B{iu1(T^gL
zb|W-9ks}t*u((v at 8dcKD%*r%~VK1vzeeG>F68aL|AQp@~n6qnT*&@AfM(ce}G-f5b
zywso$99ESgj5yZ+-lnLl)9(66%n{33;wu%UVxMEr(vixj at MBqfly&|sllXqa?Pigg
z_ALA@%ea|`W>H#(k?TNeRz-l~50Xh>_SFv*Dw}7|8)dyCTipjE4M<;xWf;HtiUsas
ziDEg;9~H)CtlFiQCXm(P^Pu!?8to`jnDCc%&!1j=fa{icLHPP0g`h<+3EMgh6yE!`
zeI0M4LxK}b=(rtLMv`QNT{$ahVjClUv7-5o-baGjdJhX`&_>ZZV5>^or=~o=!T(%Z
z=4oWe5o|lWk1z6jvGLL=#kz at u<uItUiIj0oXf0B#_meit_}Q at rWMIoRxJF#iJ9DHp
z#g|FzQ!xm>iI{Al%|xD3SFbI=+ at B$QLll(AyhNIn{Vj2M#nB$^y9h?j=XZz&Dp5kn
zmEs_E9X{0to1B^;J0hbzB)Ctfj1y4_CiuA@{pdvUwRGgiMJ(*G$}ZBm6Ett6 at Jql>
zjEZ`R(F!$C_6tS9UX~Xub+=@ihS4U2F7Ff(1wLB6H?XgtsK$F*>$#$7QWIIrpcIk%
zWF$3xPJQihCm6XCUK-1qxgZQ%as)Xs3|_^Dw{t(Je|omLZTnfMY;-q%;bt)X+vH~}
z(k4d1l8SA&2)Q|#rZ_cjP{$8 at jLWrRLtkTPuY)9?r<ibjnUgiMJt>pcreY8xGB5pa
z3PEG-{$l-{icVJ(%!eD1Y7pX{hZJPJCy0723AI|u=+_>ycOV0IgDXK95Pzh_t?%g_
z=o-fNmQOOa<PJ1^n{fy7W4Z%%+sKE3297y0F7H6&cc8-uccAZkBodW(AfYSYTTtsA
zh#1043&++2_1(M!DcphDaPL5?)#^KX<_q53Ez^dJRv$Vu%Ijv|xDQ_~yxtHZm at gSr
zxfMFKzq+=+!QcShzgG4y*U1+LnR^lpU*CbYfggqihUgYep>+q!xDmPofeh|IQP4Cj
zn4~oup_4mM{vBxP(H-b$=HC5iyC@=9+`lKwU+%|hR2V<~-aY+m9RFwMqiBcKhLaaO
zzc|Z4`GcS7`EYsNeASbP%a+MTt#rV9inUR at 1C>d)p27(u{vX*E$|L`*9UNryywXu%
z&A|4PLFG&G;jeT1eH5EF_N2a~LE67OGz13MzPmza-&?Q93C1%~hVMU?qrW_o;~)Ll
z?md?O2r*Oji)g6Y`X`M8b4O3RmpoCY7+-N1#kSaTOUe@)4gG_c-6^DDV5)WX3)9t)
z>O0Wr5j5>@j$B}e+B}DN#r0!PMo7^lbEsFjlTk(l(hLy0U8&ETTM8X5Gu`;i`)*y*
zf__<%`=a#Eh~>Xxz9w}FT~j>2t^6<x^vN^9dEK>Yhp{NM>-pouwT)XPhr)N at u3C4G
zT2Eh0fPR_%e+lM)DXM=r0RBG3U0l#x@^!n`9Uy$he>-ey6zp1c8<A(*_ at TaM?h$`0
z4$-)6roID>p#IBj_?<Zhm at j}&wafKnLHxY`!4LK?3^$QO-#!ZZLXR$vzN?k}BAIgm
ztL_#s;0(g=Kp&q%c>i|u?yry||BK~6%k$jHlDKDP4dn?ahh2R&QEx(G6_+kU@(yHX
zO9s7f`N;wMpwZxll-O$IPyE9Bmh*VYAmS}Q)z9<GZb+#>R- at urASIxwn-|`JNUykV
zUUS{PO|IHyQ at sO4-w@um65fGc$O#j;0O4%shMGoitCH?~O)Fpfq~(*=)n((hA-)JN
z_ndI`Be;QZn&76oX+Mh}uPiKwY4A2G&wTfozRPFb_Wq!m5o~ilhs<&wZ0?_=C*)fJ
zu#U#e#~=3ROw+ZmPkK+>S7-Sxc101hTE(5u_ioZw8Vd;Dav<G~Bz at b0t-F>O|4z_e
zH}U-G2nVq_4Eb=OZi&M?-HI->NiwgPha_uC8;G9bo;k(#VA2`($R`~!*5Sdpn4)Vk
zl8SdA7I>0t^^Ws)m7^*vmPFdMaqL`iD!j-$kV|@Ydfk=R2%?!PyN|T1tVEsEfHjrJ
zVRz|x#1xNNo{r}C<O8uFgCDFL-Ger6v2WpP#sgC$A~y04sgta1@&{ev`KUc}B2%1+
zit0gzscs+JfO1YutGO0q5DcR1>NOEfSC#Z?NsZC0v1(LKFsDtpcX)s7gN{6N^H`bG
z_&86z)hJrH>fBPEs$Cz%PHn02rmxWNeU-&BLzGXQ at Yd3uwb=Bucdml2lWXEjc9gmu
zMuihc%Y!-eg at Qr`<eig2r(X2Im0<nV_N(gLcf1h~@Z}1dl;hqBd5V$b82UNSsp)ab
z;moR3ajx8fL|d&SKfJeIx07_`v2{{_UrnE<#n^Jucu*?xc*-v42rbRm)3j5rq~==}
zw<?+wRs_*ne6K)|wU!)8%j1l1Gk2hU)laykt^D*E4NlH=aDE>A0eng$KJs~C$2Ur+
zvV5eqTP!=|XL at -0;P*DD?Ns50kG))W#9eA*Xx{QmbEbKXaYW`I?ZMV7QPlDn^R%_C
zLc?JuM|Jr*3qO7zO{7WKTOHS1l{D@|*jIfL9}#uKro|bjZH?3GUAT4ow7K>D^XG=4
zHF;j19)=)N8PMqtJ?@taTz3KniCexmY4|ahNAKJeUn@%%;tjXhFm-T3yjS+9=j149
z4_{KU)IiOa5)cc90B4WbmOjlDy9W{>NN|Lff|k<4$9HoiYp0ha1{2fHvpF?Wpx?f{
zo3fibvDpA+QkUTNry>W*!-7KK at VciUaD09A{^K(n2cPdiHuHRQ+wO7<f>wg&6a2;A
zdz2=JkY;&HpneO<5Jk&F-*MA!7e(a2Gh-n-cuOm<{PUpB>(>hJ&v^!T=F_g>(o<b8
zATmq(=iZM<G5f7&X?%oVMBj|fO8A1T_j8xS<+x<)fOn<UN2}Dn?LW^;3zt$g-S7R3
zNX#NB8x4u-3R`v6f$-jr%Vuac$MM;`h4Tp}`;q4cTKmRB=F%<iH7SQ+W5a~S(UO3=
z-jaSENg>8tr at Zs}ICyh*SKRM_o=bG%4pe=5<ht%D)Z{YT=sr*B+E#*!D;}sfz6xbp
zs{@NVjY+b;>|aBPf%WeK!4(uQJdlWPFN&H?vUtbl(u?L~i54RJi=U$HnzW*DiBoNG
zb((Sq5 at v_V2clJQUdZd$^egUAn!1{%$fxy at AG)g^S7dVc^a`MB at 1T=SA!cvK>3bLx
zHj+P4s;o<tbvxr5QoIV_&axv$OFU3cCdK%=y;FY4g(0=yLEKi#66Lr!R;Igy;}%uu
z>JE<vunA0KRcps-lGnj!Qq}fPw%#ihs{i2MrIak<?o#~$HIHGk&rCcwKyEMUb*7JZ
z6c{i(ltKnxKnG&^t~4TPup(f at u1BtuYZ*~@GbXiUPZ^aq1T&27Hm+FNv@)~vaMo^x
zJXR+vY3V9J2ziC6#LnpOc@{~oVa at G}&k$v492HC)eK?fUUg9YFj}6r!vd!a6j7CvN
zBAjC-*4U!;@I+|buY4CJJ`A>PnVO>IPQYsL`+50N$jQD1pm8k|Cfut|6xJkseB!#Z
zQe)`a=Q8Si3Jh=odkjiCD8dYV1$%gfUeH5(oxNw at 9qB9un!fC1CtUwR at av;aN<=H0
z0<8rc9i2wcX~DxVapP890 at H?sh^G-#HAin8B3sn(Y%Q#MclUG;LaSy4$e~k8<6z^e
zhJY1j6r?8uC~er>J4gcC4H8LK-YY(@b^1N_QJl#{=z<?6toeELwr$S5p|E&4Vct0D
zDr<r9c8^kAG;=LjH#HUd2A5g);FvnffH6$48?`#bBr0U4j_*f*KUhZ(Ly}Ib>hPv?
zgPQPTv?ZeDZX7zzC&X6yO at -Q;n(|<J4)IzwLRc5FlZ;naqa%+19RtV4-U?XkP~Y7n
z-_sWy`C7^MoHp1n+RXo~q1 at MmQ1j+H5DA9w)O^`(bX3*W-%O#ouPLpd=7GTVFz-N9
z^HaCcaenMuAHzVjK%>8}DuKOU<lB7<Xcq&ZeWcDr<psz<nDvbiUu8Yx{dW1$*YoFw
zL5q7fqzm{&%vZN+V;Z>UgEHe?1XJg32{&1FBCR)1ji-aSf6P0_Un&x#>gc^*)BkK$
zysf-FrW^OEHMt_2$c3T#;U-+O+C-BW#A;Mmfhk02)UcQJrk+Rltcy*CvD(K*vHZPq
zdANK{<@+Iw(2ml51l-s*q8pFm78W3+prU*SDy6v$BWbIp7<UDmAFq&BS(2Jp$CwVE
zcf3L|eU)i?UX-{%0ky|it2o at ufbHp_{NeO`B`5GR{;nPZz{()owZB?x?a?4zo|n|B
z`4q>;h`1LM9xC;IFO)?7B;#E~l%;pT%%)hbjhweDRl<5|BJNRUY?eD3@@CPerHCUP
zj^S-Lt_y<xU4k4I9qF~V<N;g at O3raw<-NoCSO<i at J-G$IWCMfELdIhp+ at XI1t@1dX
zCs<UkNp>vCdP|s?5cMqvi9XdN-48iMb8PQar<qHs(zA1}Hnx*Jl8q%vhi48=(%KDU
zW>gog(j6yjg??90Qeo2m8$^^`#DWlOPC`}?iDx_7I^#v$DQB)(p4N_=+n}xXgh5vy
zc?=3zo$_eJKfNm<*zHu~!x#2h1)9C^!mWlLI)W>o{R-wT5uVQ~(MC}>HujwkO%0Jv
zQ&4xW#PGG%zABTBq at fK^fCK=!qHk!Tjm6a)JQ_4MoS+yxqh_Y&)ER;_jKd-FMaisL
z5OecS3Ay5itbI!N$eG{}P-eG3-BMS6)rL;CX$e<07L1syq^mGvF}ZgwkT=X<^gaf^
zRFBX`3R|*2_G|pmGY(YDMX4!v>`zt{)i#Nw7FPHPI#!p#(iHrg(6o~Ftl3e0M~=zT
zvI2wNVk_RETq1W=tcPGiJQKCm at 0zbVHhD@zzC4+$(ErTXl7(HR3}<VfO;7!WM>oBW
z=U^mE3V&Qlw=7~c&N~u^HrGS%0QNF8vOj@`ViCXSDk{l-IVGHMk7 at 92ze~&<h0Fl8
zIwhu)0@*Pt5?Wu_N_Ec2Ce)4sTAJS)k8E5KWJ>;^n+M73BsIetlC4vdpJUTti?B`}
zpk~8f*W(ezMY|cpiilVZa<5JS7kOT-ZzLPdo?DJy4^GcF@|zI3lz=4_Z>JA$Q@|wZ
za?0Xi;zZbL8wNekNFya^ZikL=-<DG7hI`XaYt;7T!<}Xu&V8dT<&!_`%*)T!*})x~
z>n~Jz{C-c=92MUvwh9&mCzya~{m6TnPs`Ousbq{N!X3>oxDHur208V_Kh+{D6Ky8!
zq(D9?e|JV{gz*4^>3Lm05Ki7r9@};~Oj8H3(c at k0Xo$*nEW8}+20IxBJf$G-u0r{Q
zEJ+zZ2m)`<<j+<UOHB1npFhLqvKzXN06_RNh|KycDqApzj~7D1#x$?d+WOnsE*~j8
z{DZKa0!*Are(5_9Ikfl=q#y6Qg#ei09T~@r)~#pTT(^Ff_<xTYl_H!t;1SzFS_!Ii
z;niuDv3w<Il26{fHYZNe_}Cq`u8XJJSEkw3*TSqyZ~n8L!G5Tz+Wcv6srsx+xW<u0
zs4E`r!i!x>ixN9#<m938+i4%Fy|otH5DiZ|;#b at ICWNAKutA1y{=!l1`jgpX?4D&@
z9lA{W35z5~rzEXG3Py$629&SGC!5At?dh at n-x7aEoC;elWq59UXjev(od>CMWQiN2
zi8VX+- at y)fNwk`;t3CmZ*)Gmm)NUtw_6TWRbA)!98is}>vg+0aW$RVP35rqhA*%-e
z(!~ubv#!{t^y6eUN^AUahrYm(4Ef;tclDOnk|YHKuZNxKzn(48MZa6US%2+=RI>Xv
z@>?RptG67*8*xfKjF`_U#J&k5lAOZKXfIALurWgBo4#duh?AEal%i5zMZSYo5IkOB
zdKS=fspj^i2T_2PSA%B-%sDJoRXyP{s|k6YSr>u+6xP%K18hFA2x2p9w|rIWQF_x<
z;*l;=Q(7opu%e8UgU2vzI81f!j`G4za&G0 at 4)r+lE>)sWX2Uex2ilKyKRdVfB=fwg
zq5VO`2_=r=C0IHr-QRPSb2W-mtzi4|y!0uBi_$mH9!b;LH<;4jG-t&<%{e+E-T+|G
zN%u5m$sp6M&{@XSwZRPr>Ywone}^6-+*g#lZ2Exbht#^eKXnI^?ACVq%{~0-BL1<d
z-`9k|CKUm`@z8g7e;4>h?;m`{pY$i}`<f8KJ1{A<dIypKyw5HmD)&SYAq at b{iH0C~
zHi#W8*2%J(X5glPT5*7kl<Jqgy^xx5_`zQ94)j%rm92JYh;O9<0B+DbuEe__CU_M|
zWKE&RaV%sY5p^QC3DObO3oiy--p6C@?2aGly0$-y*3C$+Jg^=WChGlk#L*Wn3y~S3
zw}aEwp(xpBW&4(M71q$8+P{*q$G8Q)?PB~AC%ccDdfLUZ$WbYdgkt}#w{@AGH*f7&
zuy+mIJKyN$9AScYQE)Akw?m*ZH#<mGRYNr+rN#9S#=IAW*Cc(iQ5>c|dWD!1EI%{D
zjgQsoNR;nM at l;+x1dzj4l?J+2WlzX(YRN6;r270M5Q#k@*0gY>D&sVb(9 at Kj85ZIc
zDN__FV|)je?vqZH_cD{GKW3Av=)>7i$1f{HJP56McKS5ZeNX|E50_a(qSc3W=h>On
z;92`+fJd*|+Xy2y=L?TD_oDc1f{jv~Db2d^&bXdZxhne&>8drjx at G4*Jvl~@^OQR8
zS at pLQE3U*!F+MxyMSF6JMI-&d*=h|z+}P9Nf}BPbA7_VJCfaC>5M?A7=L_%G)f2!X
zMdg3|9;h>xSKZq5>CK at MW5Rho9)&j-nd=g|LawwAN^xnnl}6%`jgYw#WAm5tC{x71
z7(}wo{B1oo3+rT(loIkt(NP-u3(a-S*AxhTJcF!{lK7q37UsmAS-<Y3^yy0Cn=d0$
zp2D9DbJ(HM>yM3SZEGaT89)4L7dsI9OaOt~)@{(vYQ`)v0WsFZE=EFLN-6Gj&ap2+
zqht(LRiiY6tx14Ql3lKDVxqFNvAn{Fw+7z|-gBCx*D(y6k98T?Soxl>_jJR1kw_Aq
z==rF|b<OB}w<qDB6U{a8=`bW|a$$F^Yrt10(9h+}$H|5a+nxwV#I##O${#uCF!oY9
zs+F)uy1zs<Gjlg&zxmcr-_hkdC%v7Pb22fdCJx8k#FssoqcyLiD^pn#-xPL9Sy3Z+
z*u}ORMbG!y&D*r4;RNMVNicNs?KUP$mq3NoHGS4Z)vTQQ$jn0Lrm&Q8W7sjT6A}kU
zbCdcf%EuDr;&@*^Mu2EB0 at 8`tbavG*B3krrJ|}ZV#PyCN#!g@)2ZL!kG|mU}7Z;}=
zQ4{K0n%MJ-?;{D(4i^l%sw%Rjw2qc7O&GO~tW{}Y>OUC4|``k_9vc?uplg7er&|
z?7Vk(lz+j5RZyg)NaBxxCgBV`ZlzISpbf*(vt~Zm^duk41{*eP)@*!3_{~RwfdTSt
z8rLkja at kwkR*vM-<UQZCq{#w0qB5OzuoPjT7NJ6G+LOM!WA-;7XxcBq&)>pK2`O1t
zzS9H_)hDkG7WnpOYY#9TBa#7}g=hV60K+5GMp1UX at +qb$Zm=;+9nsOH3f1A+oCa%e
zj`i{O(aQ~Kqhob72CszS`9T)|Atm&%`?~dH`?YQU9(+ at Ld4J^^arab=5OO(58sAo#
ziLaMkuB#bai80%-C}>NPG{IWcT(jdN`3|L_CbcJ-gTjswFQ~5tQ0y*J>|MsO?Ugn#
z!~GGrCS)02<;7Oa)^4G2l^vtZ9yt-q*+wIQ+U&}3X at Tvv%5&|{%ZmF8AdMkmOjO2-
z?OBF`N7%j!V}nsy1 at oS>x3lCe^L5ifU2N&TGUFf9I?QIQ=<U4=hbcmpZ{AgzF=IWL
zjo3`R#oEJ_1JjK{#6Ks`>wbWHj$)~REbrM}hV3(NBH?aWlo==81{;TOl1`O~k0~{0
zrib^q{c9M4EFicM%-{bE)eYagX1aYF42WOZpP at R7+g1R`ej)Z#V5^muK^PLWk9Quq
zn)sQ;P%8k1vwcinHoOzJFPI*Dx)nQA;!9=;dvlMnq>7;{#va#AG}_qVNL~!9_#<ip
zMgSXYT+;rLEPE)CCF{^PY~c%<Tsq#Ku&vJteq at 0$;vnhY#&gfp`pXLBkBa0!`}+ at 8
zFb}mj30$C8`}wcF1=E*57S7ox)Ij9-x*~uA*`0$^-^?aXbsA}r8R8|YW#h0&UsvZ#
zOCf<Nm}kVE7*>!Mb{AlawO_f+=%+(ZD%g^8;%S|j2c&iccGbg-k^7HuGB}H~=Cy=-
zDqd!();|^L;a?({_~`$kVc|VzRu<$PBS8-V+j~l;zP9v*iH6%G&V1sywxhy;eV$oQ
z3c8$4JU|m9(_#!SZfi&I<}^fwI@%MB7ieYGE1SG>Af)dhb5z#0@`{L^?=9SUZc(qm
zs6J642|9b38lUcD;}nMA6mKO+0aU|f-M^6crcq$rhqtpZ+n1uT^SyC(I*^^%%MH-O
zJm$a<eabmymP`$o?chX6+^xO%G2DMpWcf!*wy$Hc!o#MfTF&loMWPZP_R=-iHZx?x
z7->2%xA*dN^}n{8QyeGCddv=*!NKwHX;9#|2|*i*SUN$Dl~#FO-C{M~U%{>^KD}US
zs7KD~hB81qBncVkKwaZ~mrga(TD1>OUz0jG_g3S>fK4r*@1^nIn`s!7l&hR%9KeNc
z^Xo_Ok}7%y?$e<%iHx)Rbt_a#Y2BWfR+8Gr$qN+h5Tk6nKnxU`>r+JQDw*enQYdl#
zYqHZzay>Ukpoy_&u2w|DK3*5 at PXy2T-$X}Y<9;G7^BT{|L{sUCX*$`8?R*w2=Ed2A
z;1-huc<|5zb&9cS at 7Y-^*Wwh3;4uYOZirIIhqhNGozZy1B09M0oKtbLy^bUoHSq&)
z_bF^l%7}dlYDF;yN)sTO)?7|j?l`38O&<nnW|aE7IzWz9C*v)2U4hy2R>{G+9GyM&
zIyI7L9^f{jH5a&aW-s+K5l<V`y^_S*XD#@u#idd`S5cPu1D1(To3sjLiTw8%@e{E@
zMwBrKG3Z&Og%Iv4cixSBcgJ;s>TeGp3rpL<Ao81}LL-BMO4BS8p$-hpW#R at qUh(_Z
zlOU&0+xp}cwFEwiS;n?%GLr1&<>?%oAeiG+G*(}_IA7F$uu0zYcy?Lmd^T3XASy}*
zw3V-GnBHOA1(|5-o8r*2efHhP-F^ekv1QZar|O#L7d$*vEp^1NtNE|gAr=L5x?8n4
z0em&+>|9bE^d=A9l{x!={>=(QY at ZQWNO3Kc^K^R8=uTeefJ6g_NKyL<9(smVg+%{*
zx5Y;O7q7-Y`6rk_Wxl0ta4wO`_-c{wBI-xY-j9w{6Y8pMTDS}Yl+U<f61DIxI;<F!
zPd&^j2>pji6R5gFN=+KmS0yJ+$6t at kZbSn_(UeBe>urJEtdD*yPugpCv*)+>*vAMf
z2HE#T+)Nhp$D^&&$K}#o+{-7JP<L71oxBibtBNoIodbTDiA;caL_;Vc<um?_FMh1g
zU6Abfg-GJY>Qo<vN-oTy$@)kSZ&-KbX3y^6uxQ5}UQ%h}Q*c`*Nx&df733iDA%@uH
z)(7huXuMa_aYx6}m+8m~5pDBX5!g9POgNV_QEEM at y{XFYtt8}(a2 at 8krdLk<fUlk`
zGrO?k+_R$EYQ~8F@&Sz7D$k}+5TE$Cj!cz}l7o4z1~sTZkl at Le_;jF3m4UnFXxdl0
z0?IfCP)@MsW~f>4i#RTP4GDPWb8<f_AOjBW7vb-k^e12$ZPAnlG*JfP>H46dyp6Tz
z3C%1AYD;;Zo(y^IgzZMmsnn*rPz*cF>wB>^I=s at SNjaK%Q4*mwbrSG}r`%hXu-0_=
zn_)9JiJr=|6K24`q6<05W7 at FE0vG&<{~kr~_Y3;&De!Vb-PqH*V4jaQmGwV4hA26v
zBu!zUuOBOgmPtojw=jOPt5|)5NDrg87Eh6Wa>4M0z5sE%lHu=?0GkUQz at WaPWGqWw
zQvk3REIUBeLQpsa!gcg)w-^BO<xk*y3rF)^=?;YSGnywH2 at tqo;;(7p<iiH;KyQ2j
zHzrde10bFC-eUQF5z6)*q4)_Vo8EztLF2y;!a^=<o5G+n!pcohM|-IA_0R`OeogLf
zrTEJXZO}IDFGJEI$Sr+u?};50?4Lis*q3|GPFqM|LHJ>h-`hPCttf1nx;kD;#JS|V
zw`g*|13J|^Qg-2^x?&~@!Gw|iTn{foUqP^tY#0&T20up8t*NmeXF-W2dj`7h7wX#a
z)B2A>-9#)E86C($woT8wsBmUjtVAr^qL@<MDWW71^K(J=qngi*mXpk)X!l&)<J1-j
zN*pnFcMy0;RuW41nco{v?g6&4069x%6L7<h$)Nd<6&a!<tD*<FR(;uVgLZ!CW5psy
z%PDV3JQ&$MjiL`4Bnjz`(xr%Av%@rsJ2ohdoXHA^tb7`Y^B_?osIpWgznQWVJSZeG
zu7<dPiA$cQugZ-{vjlsR`=eMy#62W+9~nW|19&ER-|fz1wWh6bk(-`iDb<ncBAX_X
z;Dzy!$m$VeQcqK`JAxv&e9+4?QFrL68J~zIbHp(+$;INCvd(6~`#1vYbAR0cV8S^W
zEi`8^)2gCBPqUX|rek2x?oBx at t29OO$e%ge{Fjx!ztsRZRV(A(c8k8Z)d1I6-QwPM
z%l`dV1N`CpcJuwMTY0wkL^?!xA~*bHdDM&QgoMO-80E)H0f<;IqoaU-p3pP1tG%Dk
zamovZ#MtP(e>>=)`Bc#Z51*<rzOA%w>S2}*)a%977i$t*7Qw7%QA&iYm8NE|6&*Yf
zGqmBdvRzj^vk&*y52h38XP>=c6JBISbU;Lm2tX5;eO_L;eXhJ<tzI*0Mq873$mbcc
zH>#V?HlE*XxbsP70e8yvSymi+t!~#*H(5Kpy5d9P(6!ZVJ at hb$d5T<RU2SNdH~rgv
z6e-pr+yb~M#M4Y9>dCYDXW|RxR>roEf}Cx?=|7f-0nd?S=c>Nj>0Uwp at cQ$onSk8d
zJ5b>Q+xYjBRJUBH>#o6LE2nL3(P2k-2X#)BN3?HDP3y^EfUK*$#&lwuE_DT$K_X18
zN0w`JQWgu$P3{MS9Ca=X(#1Phk&_eJ*v%R7Hcvx08_nD6cCeW8yprS4F^jg7-(0;x
zSXfoyvk2AXqS;Rn4B=O|%P7+E(6`xTG#IFrtoO`J={J{Fi*i5?>^UtO-*)#}$XI#w
zH8T;X*>bQ+8MS1Wgjq_kZdGss$T^a8RlpD>Z)zB4Oz#t|5B}OX_X6FCCAiyxu5%w!
zFlQZaHAB_h*l$-fF6S~pAvzcI<yfAlXm^!R>!}nYV=2u<kD_gaZN!^Zz7#x=1>^;j
z$>Bn4p<EGVtk$A!!gFl=NzLcTsV;)4itwvy_36W^<JH_00k at CLo!41RW07O)oqO=V
zSp$3b4z#8*F0>nP2cm1AbG5lq(G2EM?qxr(4O$Zowp|YgdA2Q?LN|YZ9?s&_2R3s+
z5un_QA3^VX99wV+NAA<URqR5wmqO-0#5~j8I8BUp(|K1RDpS78Pj0*GaTce|SB)Ou
zyrW5KC4D~Mw8;%Yf6|WKRh%WxZ^fYCupB$Vnh|q`P`!7W#tBtotVy#%$ykiSRu+2v
zj4@>5ONRQS%h|}c7Z}5y?l2mdm at MQ4JTw9b()>iLVda8_#lnnLK%jKU$K2IjP>1z1
z0bD!L^+GDzWuA?}F^`A$w$QB~U3576EGHCFO{_d53t<;Ede^1H9OHx+v`OszQUVg3
zUno{6ZUp&kTcJzO&Zb?7H&;gGFc%o*L&sy!FX_(SEmu)lTN^qJ)AF1|*B8@&gk*YG
zOB*#mBo7MS7-8?FlnP|i$EQ+_SOtL=JyK8i)Sfn0HKc2v%{FUQOSr*)VPa85Nwq}=
zkwvYYh{5TvcCtl35-cRH$n8+M#C$Prh-63zkC9>Rx=>rP?fzKK*TmMg)D%OK2(8Q1
zSWvNJSzfA}m~Cn*CFoTd{JgLcGhQ`wiwdXAs~rc0v(mE8MQ5oy(5DEYf$_(oMLXxo
zosb#`*u3T<LYcZYHe9030yTy$)fKjqA#YM>nS5$_jI7p5+tfU9{ewg+?J6>>^bNg2
zN9!^E;lYhh?QMXK;IMY>7{|P48B}Udd)fE(<BH&~g31U|dw~magp?DmTJ}vGrP>Q^
zH5ccx*a=ws%1F!azlJr;<Uo^OCzu(*4*NPIUs~@~V~a*qlGNc7q1lk^>0i7&;w)S1
zN8#dJ;eHWbou($QLo7^rv?mR*su}D4Xs|dV?V8_hOP*%J_~95v<4bDn6#K)JM6bj`
zaDj+LsfZ<NFFF1mehm&BpOl?Qm*=+L1BYbBvfxc9qbaZkK;_rU`OCN>QQ at UmKi8mi
zKN!L8zxGB^eqwfiOQXLZ-URTyOn{H{ruFQ?^$wK1_6L0Lw<<xugg4dGOm=Gy=XI?f
z&xsL at a!k*~(VB4w18-o~7YErY{Wlc_cG9nQ$v;3_RpyPH#+FTYu{V-3LM)71EnZ at h
zFc6s(^j;4`KrZio(Oo5A`NfxEm+UuO?N_doyEGqWm&K!~wjL4JubsjrgLY|4@%N%)
zaKO&OBRQ28$m7$(LeA+hW=7+4>Ni~p%e$JdcEeHW at aittf;kKZ!ay at 0%U8zuh4{%W
zU2dLBN%h2%MHF5O7YKGQgL^*CN-%71&>pY!KNmb)js3jg$F)^LdwK^#>E{(A&Wky2
zuvgX0X6V0JVoCKLfs)|KmRp}E^UjgP;;k(BE^!Zn>_)X%ErItcR>f_5S(8U3+PHvx
zJc^DqJ<M$^>cYk)<KTxbF}eH7Cj7$yd#0|Ld8;8Ec+Wg)a1il;gj_MDRXG+Wb_P+9
zfU}O{)(mZBl;`O@?JNH at DE#zz+(TkoIP0*E>T!M^ub at 2!my|a|IAOGNaNm)mWSJgG
zKIx~biHPmG)oGM`jJ!BcQ+}R_dPV<?T>$Nch8(@TnNf`TJd%+7v&DP117NC_I*W|5
zCsm<xYPs~|Q+jJAB|%0KsSu<oowe`V2LB|pwqzL7x2L)Yz-4n5uVC&OFY2$GKUy(w
z5xgt6N at VFttf;7%&7>10dMv!+$NL8aoKVk3Z(^e*M6S95)yC-vzw{A29XcEwWv!D3
z_7uS~JEPaIREnmY*kCZt1Qkmkg}J(IL|GpiI_P9q*o7U)CS}(rZffT2$6kN;1wYgN
z16&<kV3!CY*~DJ)4zEv8=WVgQ?WdZ>N)b4gm`b7t#va2Iu?M=h#qf}2u)A_HKG_e~
z0ed1R6lQTVQ0&)qdBKD+kB>v%%{ZLZ5&eu+<wNDst20i*7o8xr4E3qgp2+ijGgDg_
z!m$Mj){dMrp5^jAHKr8x_- at W<OIsAMT^IR}%eM*hslGT~Cehk%kQF{X3z at ocm%4qL
z);a^)FGgl876F>E{{G26YcX5t<f9AQNsQ1z$eUa(|5v4maLtb#T^Rphn+*J&wuwK<
z`bK~~V-l4*Pa7RNMcsdCo463njq9m&D84OwUe#|=5mRbZ^ZWt>ZZ5*8d=9zXyV#nG
zAZNxc)ZXrRwy0`cj}2+0_NhZte3W&=_b#ZE@*`QB&@)ynLJC5Pr`$suT_F5jrAJ?{
zxyp7_tV1c)-Djy}gCtzl9|hzSqvvD6a8KG<R8F$*$s;Yb227`3zSN==&9T!xM(`$R
zT^72;J{`VVYVG at UL9BfTQiVPRz%Drf$g=0-tP~#OHWCMJe2)76I4!95Yy)Nbuk&};
zU+JA2?4hFX-6~ACj4W8BMSl3|wHZ~nt9rKh{ly)qd|$lwT&wQu3n8<I8}{A(we~zi
z^kN-{osLCYN(uzBPv*EyT~YGa1VkX-J~VRG?_|OG+F~>pt3j&K9mujqXUt}k)izwC
zRTBaDWp)s0lV at ZPu}~7pPT?q*Op_}C<3!?o#>GKvutb_rmL9<fUN#PnbcKg|wM<)1
z@>Vj9^tO5PR<x2|Deo)OTZ#$Sa2`AMslYzZC%*ecm|~Cfxye?NsE1Mtcb*lLrj>lQ
zC+>(%B2S$D&`pLI8`wiZ0TI4vp=`9G8vj<3StCJL?4>JBjm8QheeT$0!_|J?@MLFO
zDCD&MrDNCNIW$g9xH~eFYvCY6Cfml@!h=M>d{ZMs!`S`n(^xfmg}y9 at EU9kF9~=JJ
z#;ctwzF#<OigafD8QwoaiD?~-{tygWbMD5!F1+A{R_5sob37tc57 at h|Q=XA{UMdL_
zipL|+R_Zl at bqIdLkgg+MOvi4zR=6OKUN`6}NDBm|0j_B)fFg=m3zhY}l=ab2uYp2J
zTs;_|d~F+uHEvWg5eeY-biN36^#o4HO<`$!&g?AC4<qyB?GI#2sF}3A{9b<CXAlAJ
zTZvZGJ5VbCg41)!PBFUpNb25>V7A*$m8d%s^6s}ikcauaJymM;F}?jPP3?>nahF9;
znDGGYNHmHthag23xFsnw*);ha4DD-|58yf24DmS8d=n{5%4QPBDhXS^Cqa`TeDu8N
zuoqeX9R_8XS{OCR82x2xMy?!y=nX7xUnLe=P(hO!o049dwK_>YeXH*{#r_8T)!OSF
z?>lvZIh=l~FO^~(#VnD1#~P?T+?(SN`ziFy84oY8`2rYDHtt0Po4|6Mn$!?82=yX#
zSbpB7E)!+`9(VUIGM4_KjHG*Y4F at uAC3HxyPU`@w`Z9pi{=S=uO1B9FN8-C?8u}xT
zMIMQ#HzJ7K*kqS9>qtf`dE{&5RX2v*=ylMbWS$WnikxOXs&!|c(e8J2Sm8F&8|c6U
z5$`5zLaK*x<!j&8a at N$0HevLzzG;yL=N}SH08yF91%1}i+LFNnoos|-9PA*xDBvx2
zXbJYJqp0%}?ulG+ng$Z-nhB4Y&33DSg1;d16it^5Je_tU%+C+jtAZ^}2o<rnW+g!<
zQFLX2RkI4;#WBwyt8w2-a$fd5DtO=rN)IFsrgxSbnHk&AneFdoDqD;0XX6b*ZVAiw
zV;hvt-X@*)!rX50O{wBO;RFw at D7?%t(B_31vQ+<MbqjYp5xhX%s$)f|HO;!T<Ugzk
zS|tf6)cUF?o!yOpXv-y`*-n_(96jjN&Ub-P_ayafrBRMXR*&yRh at 9txFA_ocVS95A
zc~)tl74O@*K6RhV%6aXea>cG8C8VjIYoU`lcjuz+bkwj}L>+J5Vx-Y%Nx+KkIYlgm
zBBK;g0+%CicyPFpf_*hV+g5W2YP4Jnqf(1f6G(F$q*@FCQ;$QyWLAuFNvQS=78?;!
z*Js1N+ELvibpW{OswXce;lcWpM7bk0Ho$>5!wbRKiZgN4!^R+v-a}c0YQj4K^i;y3
zzFWZ>hiU>n$YnN0dq`G=txFz0qJT4S2f;aPDx`f}<R%}*E1ZyT`RQYJu?xE!9S5 at 5
zsVF+FCIO-uy`a_^%CL=2RWVn*_Gchi$DYsov<7RWhcEY>zKlywRCy0_F3?-$H0ZcX
zVUfiW^7P;#;vpv61?FijS~u)2=yW^boCV|*;j1p0E*-^pb7Wg&$}<@?O;+=`;zN3w
zqn{bSplKPM_%S)iBzrqlH$ipMG+aGo-Zf|214rz+EsXyfEfTy21sh))2AsXIN`L$q
z?Sac#QZ4{BPT?M3E?CTLvYFU%n!-diodT%?gNKXP;o2HEUSdG_4tcy}7?3MH)w#Y?
zIYL(;;P~%7ga0~=4VYYjCn2V}Kss(d?pp;vNa&NdxBtY5pWXzthGt{_i9OPjraXOB
zIUPDdH4#J42Cs~@<g*Hm%CsMqQ%A{4 at K<I<0t)a*^lexKFYGU9sT@!`6|>$5^D90m
z(hk_zSr}sC-0;YZ&f6xUy&lB5VAkv0m{lMe%~*|WXWF2JwWb%mP^k^zdl$AbU%DKc
zbNs4J$3I<zr;|GJ^~DTT?q1sqPh(8M1uRqVbi`F*0#!8~*&lpMU1R%?B3r%tVhu=j
z32gryM^qS>w&*=UP(oE9ap!sytI(V at l^P>}%&YsuI|@Jgs}RWF(M8 at VkKQYefS-JE
z?E!E+4BT3;1bKmkgnL5pPZJn`-}{rKhTnho4 at _|QNm}`1!%h;qj;Ikj+jayPi0y#a
z_xr9ef!dJesztc#`a5HdW4(tvDt6rG at B~}CH0q-&is|$PrBs#8Z?UD9cdPC|kKo$T
zO at wjnpJ$d=$8GG05V1rKbUcaft8eu72HSVeyq{f>{ZO(JY8WN*4#wYjRso;iIjka`
zsG%W_4Pt$~!}4Moih<mTPm#AIc&0VUF8$nI{5->#I>Y;u#27iEhVJHu%I9utP~Q)J
zZH+LC>T(h6>dEka%4c7S3)>8NQKINb(Y6y~zmw_vq;odfSfQt@;(kT}p?Ja(sV`(?
z-X!eXJ-1tLYN?Oz_YTDZ;f|_0Ab57l8s+#zw- at Jn{y6_K>e<MWx|wQvbv1H++tgkd
zl&d|v-GWGvR$b+5j{_!4EntdkUD);C-qT8S!&Tlo>Uu?pI-1DbwOwX>qWfv+ro#??
zQ^2EiR(?FLUoud^?Fprl=gUq9A``@jRPzd4KrYfuGji+}urEl)kgL0 at Oq=riZ%<)y
z6+b9XTAN?c`|d6SU44a=6K9zg_)v>mH#Wv&ljM3zj5LJ3$3Al6F$3KZUQPgq5>}CK
zQ{Csh`R`6p9YStKeOYN+&zf{WEk2TFk$rS+^NcJ7;wu0?i%5=~kWnW$)`T*9ElWD@
z3P+#{LKjG3t&f59c#20s&kJD4*bJXU1(Y;BE3{5VOLOCzhP+QV5pt6R!Ne|-q at QKu
z25*G*E0^m at C-?2?N)n1b4Ehi!zvMKpK%^Ev_`nVfgz8Ld8!LoA&2=G2;_)CBaIYyM
zxD}d#vv(Ufx>DfX1|4~76S-B%d)`=3qH%XwqoY`<YmD2l>g$L*No27W^`#_8NRkn)
z3bsrZn`FrmUu(?dWm9LnB9W7uXpXW?1QGYFa at a_WwN|vu{y5;;(_nJE;M|*!;!Qo>
z6&*3R2v8NbDb#sNPUwAU66zdNghYluFr}d7#hGyP{(N!Kv(>8JJG#Es?wjMpCf=_0
zJD8N^)=QkKva8b&0PVoe*GdOce`0N90JfHt?<veLL@@Qgg6|Qze8UOm`XT&sw06dB
zVVU#Eml|d?;f)#vnYK~a at B8d;8Yl9rt1S{%^@cFyp%fA6O-d7J0ddLrGW+o0F6VT*
z=9(-sSBh at 1r#)XjN_kI-#tzJiALE!Rt_Dzz;PSl&hHyyaSe?>kvTJh(mtrV*Bg%3%
zyX&+6wKzyt++tzJ?NHJwR(G~~j)gs4;lm>bw!Tg7yse8p at 2nXE-Al)+@`prGKc!<v
zBoMh*gV;lu)RKpG9tG(?3={q?_&ra$P_siS?tB`_muVwGa`<MU*{1+ at 9a|YJ+PB~u
z;N9<$=i5lwS2TDEzXAD8aQ$U!#y?On{(mF*eqy&jWh9k)LvwY7xJ}~+ at MIKBqOf0v
zNW8j@|0KAY?|`TqCB{kvx#ruEq(emi`hJ&G_u>uht|8xBJHynvV9T}Rp{cK*Yf>!`
ziRr92896k;8v1qVNJe3OP7i}PtNdumYWd5D*iH*{RE{V)Hx*M9$~k3POmuQ0EYx;9
zyT*h!0m!4d;~B5;xI`s46$JxOw9SEUVzOUXfp{vy<=F~fb{tegWd{jfHBcSO at hE9l
zqPo(C&x(NB`@P{f^KK;^2(C*GR^k-*@tpm?<t}sqxeJPpBR^En`o0rLB+jbEu39El
zKg7#6p_AtplG!OR0U|oTB_E*tVlxcfH!1EXDExwb|H9}6uteHS_)@kn4dRh!x!1Kd
zqk3Gk)M}UohJo&eUQgzt4 at -xu&9)k}#vq&FLnlqs3$}Z+k=@)oUO>_s_NK1L(zu0f
z8M$B{G%P at _KHBk;izKOw)yKLMno-z<aTD_S at T=~ZzENE<Pn>nKi?#>;M33cM6u(=;
z<mg#luvU%E$)D`2eeIF%>86R at 1v2HnaCtiP;}k-My^TxobS~*qK6#+EC*H7$8fr|=
z%E#*(R9Q`FSMPi&J~xXr4Fh at h4udk36=Zs<>)y*xoLw?jo#Yv}56L?8`?Yi1GJ(!k
zuWfQ$MoJBo<9 at uHq#R%WKsUgj5Nlu$S~?Xx)$*|818eJSHpHzB!vwp2Fm0tsjRLiG
zu?S;5X`j(L{NN16ogtVV<kBQDKdzr|Ei>@yt&dxggZpx>q)phfkLwci at P)BnaZGMB
z-X`}!*qk7CJ<gbBX|)r3MUe8>uABk^7~dw1+3|?Un!|6(mWkT0;(I77k&Wx3*Uikf
z+I(eTEDdwh^KWuZ94(eDt7?LkHe^vJG%bfwN{SGCuO{uK#u=GjDV6nm+7o|{GDV_N
z{OpuX3D+f$C^-3gvHil{8Gfb(@;v+89aEe%uAI!S!+#>IG2hD9Xo7>VjL&!0RAaW5
zkh!kQd3amAW5{^(W!<ON*0_`QBY at AxTJ>?A6JQcJWXUTKb9cJv<0Oyip*<*%a}Q?d
z4v+ABE3&RLr>4a(;Q64VEN7SXs|bIv^iyWaY{wndjK<`NF_#{fVix;{?o{DTQBFhM
z#ZPuDbx`*$0a6vrpO63s#()|pcOak+Nm(brynXx^vi!|$7LS&3Z+w7^zpd}P@|7G1
z-w(rYTlRcUU%BSrGv at xPs#m|S0YEQb$xizH at cnJee(UM0cfVzZ{bt^2CFfuJZY`dD
zU%Vo>2CTy0mt6VxEY&|U#?(#kK*y|KfjDGt_iYHO74&}pKhjzgw0F*-z1XGuMz!kP
z2wJePJ_vGJrHE=Z69-MsM64pYx^%OZoy1?*nBF(yQ;_<eW<&h;pVkLBt>9<J_wyN;
zAj~+X61fx>r&diGRqJ`JcsvcJf7U~dH8Tdv446Ca<{lo)0xNXH%%+Kg1$(CHE+g&Y
zSkg2KHwmTTQWX&;E*|G>!^sg0EG0yInrm}y+p)G at FQt*qz}wQmARg_vp$7_#!9F|M
znHkEajNj%=1RG&RB&AEB3obl-X$q4XWr at kPR2RIim6^<=rdde)+0SUgaUrkNqAkj7
z?76As4C_=}xx`Mr0IJIb>`x8`^KU`2cHd)0D}%FbbRb{?&~LbYCKV(ANd+;$g2Mii
z3w1xH3poErG=PUxcH<=nct`{Ql_884CO_9P9b at zxQx#X at F_iwjRj#6$;w-}Im(6_V
zF5W3i8#Ym3gH^B}R9v>W4;?hxEjCGS$4jA+$vFckg?^eDvcQ+YSKxuyc3-ye-^z98
zIY#GW+pTn~nAn{QJ`Rzu4-j`rC#>@ak%+(%`r{L&pS2<e8xcIQa(}`fzEM?7hZ+%~
z`H3RFr(mkR_B|%qWOd;I#MogQF|Is7WYv?X)?WVIRUo}}WL`@v^$q8)W)nuyhZojI
zI#E)+whw5^UeD9hE2M)3H#-jOja~K<i|KR_lrd^jIdT2d>##@lwzwo!l22Yv=6<pX
zXz}u-Cv!r8A#B<1<Wo3YbK{MiAFYpD()&V at WKI}fBd^2Lg~Z)O3n#!Ykel%^gmcs<
z3(_?1?7v}8pEi4Sc2Q!LQ<oxp*c{;|afSbswYmJA1Qi_u3{3!nbJTap>atEOm?~gb
zgF=>8jWNPnCU6AzN2gCS_n@{tnk03!k@*yB&N-PU7Yk}hj$^9xXW{~kaC?iw(n=#|
zo8`AvSwt!39(E>ASNR{SIc6AqDe>@qS=k7!?hh~a*mP;8Cr!=@aCk@|faZO+q at Iu$
z=wwgUFnOlsiqH79>e)JjR8E9x!H%Sf20Mgd`w#x{EX_~<*a3m}WE=u=irbFg9$!S2
zZ0{`*;V+|^C7&68AW##V8pUgfyu;7KQ8hzV`!z3F6T=OTYY}f1b)PO2;0#oivO}zO
z2(^eBMb;?&Fg<4!=&ztH3Hf5Vnz<zp!U`!XQ!C5Y$_4X+J}s>80QqqLyfnXGgJ#$f
zQ;E!ja8_J#cc2{kouei`Ep2r}2~)&uw2yvVmL<b`^sQI0)}$A6q}NW-zwi8pvHbpn
zUylBgGcS3buy_N*f&OD3{gy}d;5k$2b6ZL|g71&42BMS5*6`*%ZZ>s4U9$-jo~m2?
z{@i<C{Eag5KXmlIPkZlYRoCiUe6(l2Sx&X+D#{X~v!bjgv$Fgc%6NK13->x~;C8GL
zkPg&YcOV}i3%Z;3*ZxnE)UTiY+J}><FBDq)2q+}O4Iu8gtMEhS;MVW`9q7+YJx}eY
zc$M-ay&V3&3oWHo(;#KURiK=BBH5wmn->iN<2xyi=S0J;ZoE%YO$2UuN>}e)>tlvi
z@;uyN_F~a{RZ at 1uM8hH>s5>PxsQ2+Bd#vxTu|s+uTF|`>d_V2SJ?FKWXO*m93|<}<
zlATEOSUXwsb|SZUQcM(?0~bHUva53kn$a*gg`W^Qs1y3WPCBFE`|llCR(=dNU6Ep$
zqpFuzZz7Z$%H+|TT{%*2asjAVSWfKr2f#p$Qo6}?|6Ny+?XQ`uzv3LweLJ)Sm3rf<
zU#g-iUZR;*1XiNc)Gt6yn0p{Fif;4w8|r^{huzBq_q77Dc}AW9hGxqWV2~szsMohN
zk1>HMM$2Br1EgpxG7ye3g5%TGWY8kOpsOkc)YZcR-$e)CqrUsS-xzlPe?ERc4O$b&
z9T$4Rbu1Y9N_=4DMMsPSyk*nvNvY~BUUA0nci?XfwBLVlZ-4%uJW6msTu8rN#*`KO
zV9XvUdLrdNz2 at OoR8rKDDy1MU@WAfBA3dbUFJmxDMi{fF0xq96x$5CoQB~3w(St5Q
zEdG;2aBsH%rpblgtFUrUcN8w at 8tCeNl}hF~#2HT_WfkdRX*l{ZRB!a%{3p-h?^j9x
zo$v0l`d;GO0tr2U;~*A%f4sfUudb at AzmAr@s>@8|Br(PBXE!yuND~yhQbf4HPP#AP
zewB9!X at BvINM~i=Uh(kT&x1hl6`JvzxMI2k{i(zSIolgi=bNlFN_i&iS7dYdBIlrY
z<6YY at h%B8Tg}Qj>Pjaq)<H>ma&4q@$%lpb%ulwG$#GN6sHn=H*u|5QLYP}x3aUjxN
z0jW%scu?(;P{@+zs~}k(jXpGk{v`*zW5->kt(ofD5U_9g8GN1Wha5*FO|9FJ+|UP7
z at FYze4d{FHW&ergz<P~JA(yMybM#^Qz}#GHQd$6gtik%<G$gRoZY~h-Kt$QUDXrv?
zKlCysa{tmi{WY57Us~ZmbjbgCt?@6d at NZ@p=zmr#{2pKW`M=hGg<1VawBe88iT|sA
zHPWwDCI&cbyk6OgNp)FtE8+&-NM9F^6u0x+0L3`0JU$)^I}^#R<&S#YdJ08?ywUs%
zP&LFVn|l};Ox%Q;xQ#9YwbeEkk<XY|`*!8sHgHZ<gy!v#u8~MDCcRMq9b5Y&#`eGQ
zFLu)ZWACd2;@GnEo8T_No!}naJ$P`p;7;T25(usdBtUR?cS6v{-Q696L$LfhZ+CWI
z-p;<6`OUmpOaB2?b*rlD-a7a8z2}_o`^x=5uk|mlt-;xy8(??Huh}t2e?hGORZg!T
zu$6x&GxGO)Vae}b(VM9PN7w?DaeH>`IwuxSzbHXXGBw7FS4DyoA>BQMu!U%@0VJgW
z3+JLQUY6*cOQQK>IgqzgPB~i71-&H>D4t%uVMV%w;U?mPP#>R%>md)}AP)Cegv7X-
zRtY`MaN4bce6Rv(haF|a8c7)&?)D<9;)^E(NJHmW;{}JeS<>wrVA~WeASJ>_XbW^4
z;~Cdc*An2VS3d)VUZOfFgQ3ihS}-@;3T;S1C~ZoMUXekw$$<Lb_NKpaAKLnh&>`DH
zoS3c$a4XQB%Q)J%c4I~mdip3<mfe2s<I`t~@;e6EmxiyVe(a}!wR`F{Iky)xtIdft
zErAHKb;9!FLLk_aa at oHz2l&@n0sqWx`rnW-xcp$8ze+v+N9v9JrF#uEG$wlt<%SWA
zu3Nr0HTvQ&6xP}dnJ|l^uRt!7=sKwGA+hak-v9D!<<)1;^In(|?FEqalTM5y at q6iG
zX at NeTKK=w@<l5?xF`WS<z2R39JZ^$H-vIJK8J~+)f7Me|o2$Z)Bpwc)rRIdXHjya$
z<%^95(k)>^d<42MsFdbj at lSsPXwaQh8z)>C at Q_bXm_SD1m%G>zO(G*@Z*h}GBg8tw
z354<oDb6m+$S&xvE_O!7eU4L*B+*5aG(xq(fbjg+h|ZJ$pZ`C4#eZXY- at T@PGvm~M
z#WlZ4zxXi+?O#${{+R|(N1H2U7_Ws(Cz1Oh#(;OSHoXv)dqErJ#aZCZ<N7^Fu*<*g
zgbpCbM9ZJo)Sz6CL+wSoA8}86gVOLNVY$ZQ&*=4zOm3K{wgyWl&oBl|jXrGMhX`Lj
zsiDb$AukCni^S73K_89L_A%e{Yl^mhX`mye#E1MEMp3|@)E6uBZ;y5SY1sdr5Q_7^
z2R;7Ak??o(VE%}E{b<z_MfkNfGwRRy3<+ea#p%Lui$2EF$4bHCA%d}?;AP*=fzbKd
zchn`=>wj9r52hsW1}6^Qqxd~?{NiLv|IX7N0)|WmjC=$16r5kCe?9pQ%j5wAF{i%!
zCenPXzX1fnxJy_t))GH^1WeK2`5jZa19ldvf?LhZ<96i!<~wc^EPi)y6(jVWSRWaD
zE+qJ-QDFGo&u94A4*%!uK~n3~EIrGra&`q?WiV8RQf)DtB9izUpmWz+L1C(dgMG=^
z1L>hxg5b at h3)WUT2JzOt2t-Q=Zq6Oun&I1CxaIrJqU#a6$9i&t8e1aV;&QULtKooK
z!RmG`j&-*4g$2}p388!2mX%KJwn$S1hK<Clm1tXyt$V`N$J$+d-6ZA$J)b%nqoy+>
zhBzL32<B)hqV~%S<Z$pr0;A;)lpK7NAIjcv1Qzv-`n(+1-%qe}46&Uv#V@;F43ys{
zki!|SkCxEfyrU>>6j|LQ)F4|I(9mp~EZLqs6dZ8CeHiy(O8Gh>L6K(3I00KNS?8M7
zvhs+rf(P~`w|xV|tN5L&Em+{VB{jXk!H;U2J5J}@m{qZbhYT8dNz(H^3XDGaH-#ol
zEbmrZv4p!D%%ejDmcDzpnEvu<s*D-w^4S!r%tVE%MN`waJuIJy9kCj(K%SL94r$;0
z2R{sJlD`?!zK1=29M*_`7}9=0m;X4d{Wzrk5 at Y&tSX27>48IKTe?U9 at u(lifv7O6Z
zuuDDT#2HJ$q6Q%nrp;hcgYX^50kEjSD9qBIe&v7u|6ztim>+siKoaWmzMP3^na23_
z3uO+1XuN1gcM`Aha?3P~G3P;cY^k%Dua?ZibNB8f{#ec808(5+HPl1_r at Fn;ri}tH
ztHM!wsfD_4)=L#F-AL$YV7x;@ZM*&hT;?sLF#a!@XCC=oc3<(FW577f(Sz~*+~IUn
zH#%u2b36X9YkaA9;n%BaZ(UyUCGKWbc<YzwT6mJR)Jml(J<FqnDnoG^4Hvdk(n=#~
zOHv(UWHyJZ8ccalN+vR5lc4R~#Rz_*9pKdf(l<b&oc#%+2ylkb{|FrN`v#C1EqP%7
zI^^OqDN73vo{MImP_W!vi~jG at n*K{`zkg<V$Tn1CeLOs?vN+u=H#a#J2PTB9+{f*@
zIZnX|__J-2BZ!$@3An3gWz)bzN1b8j(#b}pI`^59u-TZaq<8 at l@(G;GWS%u?Bm2_^
zq!Jz|MX0xLYZPJm%5UmGo!3eWOWlrNp4q`rDzl_IB@(~uxoPh4bZyQe;M25?S76a+
z%?jQSjI<s*t{Fg_L3J|)+V?-l*L>GY5?Us<j`A*F>&7y-wxYH^X-MP~jhZ~q2B~Uf
zZif4{?D&POEbVHGC7E1_5 at w*Ir)jXuC*o6%^w8k;>hLxGsOFX_r5WxX_zRBoH30(I
zhLfS!)q8c0y{B6rSJllnFDW#{Ec54PkMD?bTkGy^&$3mQVsu at Kz2fT_$HXy1u*6h8
zOk>pthc+|_>YDhW7W+b`m%(~g^PetsoWEQ!7KxWrsR-Ai^t!7#+lmE7v-O4ZpP=4J
z2Kqq^vkBucu0_u;(~v2~^<<(;8o&Sw3;xWS>7V(U|F?hFU_88ysmI}HAL7UHg-COO
zIRVsyUC$GzkGwQbT1y8t?BNaU2S%?*uc2H8drH&@QlJx6_*n&7+ds2P#=J)vL`C9r
z>OB+}ziMvQeW;r34SLkkd9oU#obFCy*n9v+B77*bxVqg=zv9b(N6tXPf`%eFiC+zq
zX=&g6tRX6gNW~S`NA*kyzg8IYVgT-9NRWUAZ=PGjDyiMZYgMgZyHGIG()NzBA<9Gm
zjj-~TW^?q}l15Ww`1DhXks4>nLgQHg5B02?dQ(dyZgo8i<hXULF!35I?gPqv{BrxV
zq>*>?>l-8oCOsN+lHw!^Aa<l^T*~HmOw3P>$OhFncPtNw_LqY)uKB0m{RJe)AF+b{
zKYCB7r}OR3!AxY)CgcPr!kw2T3h23sF>7}N4OyCc<j;f{-a(8Yeg-)T(d`xw`_k?6
z8CFjyH5!@6n#aN-e%)%L1MIVNm$>*~44-C5^Y6CXZ#FD)8#sEj4_PlPh}Z;xM-fa&
z0%PIqi!y?>SJ~Tp*V<00HK^NEeoaflx9DK(r$)x_!*>3UzL-DMh4Tt)gXqP+FI8(Q
zr?KQ&_QiaQiH{WZX}L2wdGglf3W~+31%Bk{UR at uBYZWVNMn9Ml?`t^VJO<KAXl|LN
z#%MO$(ZDa;8Jy4+n0$=@(nqogZ%>k~MM4pQgSPqkI{(=w``4&y{Gs;r&#v%)_wSP)
zuTFc|iOZbWc*>f}Um_KSVlUwrdIuKe&Awx#Ca8tiK8XvAI_r+!Fj$SbuJYN1_)@Gz
z&kNsGmdYxcCjq;gk(^&VeBH!&+1i>rA<d5=5s*cnNGCjeXk2RZjKTt&10g#@$)END
z;>A9$ZfoiM?c;%~(3jLP;$pMIA~W=6pUL;Qvv)uP)IE(N9&%~LoY1oTqKaO22uiH?
z&TXXTavw_iFCvl0?4Lu3jrujIo=zw7U5AUmQ4H1T2O;dwMa#QGRJIGgnTZ7ZXXyU7
z&%FK{)DJBc#hTtbxfv}udt!3Kp4&PmlkoNR{!+vKU6tK&R5+oqKgR&d>V?Q9VY*1b
zy>JwkViA at w00lrq15qgvCsElI9-0BIh70gQi2qeYsUR<eFZ2~&wida)8d&NbRg_Mf
z29j3mA`hNDiZYIrA=+{DjzvIYrQl0iwtBX+7CM-2tAaGBmvjp8*|ZzA!n+R}q^&hk
zlLh>l0{y2kijvCk|3$&X{|4{q2PTC7?0INuE&i@}UeR?DqO~Q%AtID+Fp=gJ<YFpn
zPl>wA$$5E6sWk55L{r)tOHAJfg*9P}z3Cxqi(-1%k6|AAc{>6M69NJNEXRL;`w%o>
zFQk|sQ&N`R@@MqiU at jVanS`{PXR%;*LDbhS5m8PraFV1kWK2xEgo8hW3OQO|icSeg
zER``<V+)Qwg(suF>&&<%elwfgC=N+KuS>a9qJbWWp at syBgnsC=-)(N$+1J=wyJ6M0
zopGVD368xi97z0iZVp<S4Cw!gDCR%yJF6~T(}yN90=H%DYIHH(B58qHv;-v2$%~>V
z+ve*1E(pN+NufeFOTT2={`vsPuXp~TrJ6N$iHmJ1UJfqJxaWy1^?Q{Xmm at NOjtORP
zV&UuJE_jBj7Bq^Nafx6KYUz3S4fgyVb*f?df+H7rL&GMCXygv`$2nNY8AuN8&PR!I
zEWa5Vu+*@+H(mHA=sJMCfuqk?p6ra}?cC{VW9vMSAH`gKl3k)AhHjcaO3t*mFJqNu
zX-4Aw?ehA%5`HH?AFlaLv^?hn+d}xx#SYq>JFjK0j<_XOgfT=fnWJxGx3zndrciB*
z0QMFa2~Zuf;nmK0H=mV)=n*KK(WoVDjM1*lmNb?eWQTnQ1F_V8*RqlJPz03hQm^9@
zGL!WhSa=7KMj-Vv(*J^SR!s;6oR{=ErVAMJegyYE0gP|c;sxdv3O1HXT9+ZhKThgp
zaTb!9k^*fihgo{~jF)4F(^hmV8Yav>X@!fyc#l1MXVJTl=NY7W<gI2dFsL{H^s}Hc
zs2FgtRpyc2#k{+l=67|qglT;!EhX4=iDF9QQ%=)CZSvI(l*IeIK{)2?0>tRyONkf|
zu$Ge)E#wz5*?)3K9=LSoagl+%1b*siE;(zW+M~;Uzz9(ejHv&`(yUqeUQ<+Ez6BFd
zx5}gtDOaWfti6ToG@&Ih-0UG3T%5wUR<X1oi?U at as1h_0&C2$5sts{@h|dapMt!<c
zcR?;Jw-z_rjGW^3_;Lsi4=1wLvSz^AG?2b(d at v;qsuuy*RQgaS<FZ)vkswM(hKU2w
zBR^b1iUh6!U_Pk3V}Eor>C3wDvQ_|g!2s++?Z!{Kk4|QOuuuQWh<JPVx~DCoB%o0W
z$b4*C_bw}*n{>I;4muj;Nt#1EFEQ=-O}&DS`%ZoM*%_ze at boASI;w_1We`aFO?wf<
z^el>eHjUr|%}F?&yW at sMk9lhP-EPb#G*hs^kflomZcXlFnmr?t82##2^(e%Q;FL0`
zt8PZa63AG3`8GaP{Hg)TNM$!a&!;#Rk at h4h#6+jOswWAGeTyq}fw3mm9b_kxJPp=O
zxWqPAIhE;+;>ArkL_lodH?{;fkAj%F;wfeAyN!7e(8q^e^IUx#lKNd-T*Fn7FX`zM
zFuawX@#@1>82_k}X*k<m_F|pyE-txXLqSeuBGBy75A}e=tPw?U3dq10mkJRyLW&+O
z;dG#y*BXD&K7Xj9qa!9z%_=k9wx44QZ}??I at EhPA<i4Fq`9gP=n2!h1P=yh>=5}Nl
zQR`ci)Cn7J1#1^CqMYfZE4G0+jHS=U=WzQFeFZMoGl7iH5Outy%WY8 at HyiKbVjCI3
zOpW#|L&gWlKE!RARJjH7V0!TYO|mzZx{<aN at t&(*kC-kvVvhP)b2&4Ll0Y8^ftnDm
za5%Th9znw(|CUScJ-LMLL=w|zFAF<6$S1>fC)gHm#M-JZ1oqOf!1>qfFrY!ZQ0iF}
zp%1wyo^*3oFPISZN7DULFrD#SH}ormBqLOpM=WU- at g(5-Ks at BQUoMJ9Z_0Xf8rCh!
z$->PRkS8m!LX<s$c76`JCBd+raD%BCO<x0cVz^KBs3T45Q##@)Y@%&B87q`gt{6Cv
zK-z}WAUSd6#OZa5Ohg}n;%93uad?aF2dz36mj!LrwqRrj7Z}-r=6Bj8{GB!T*TduA
zGFCeIGM)y{ym6TY#xK(zEiajv8mU!jy at 9O<xEABvbur?fTP?CDDtpSXHhu%R`zg{#
z-NYd!K9bgJ*_t&OTjhJVLqK|+o)yJRv^sI}`YdNNzcU3g9J9pXC&ZNJZwQkJFX*<r
zp(QR`Y)4*L%s#*GR*P{usNVFhT<F1SolCa|FP7C?&P7g$8!;o;lUC;5Jbq|2zYhQ%
z=q<lPT+r8TXIxBjEK$NZgGTQ2I}QNB_;Y?wsF9(6Gvh73Zq4{=eMMpHam?zqO~PT-
zr(+2Ipy+&(U`2gw_udNF&KtbVhj6<`y5IuhIC0WVq;PFO*C}e4%{^}g$EHfd`wD$p
z)r%gdcmZ)oLDJLXq#}zXS49!`s+TFMO*D#*ylYWzGoC&luf{wqp?hB*oo8z-T=bTC
zbtiKsfyKMN8cf)%#~JrG2^S6Y9z#=COz$wSk>Ksk6NlHiDK7D6N!WCSOQ22#yW*op
z9CF-&>Z(JwC|)qVEXqKS<zu-l at q06`{?TKtHgUgW+5 at SKOBSILL}(IzPzv^$(;_7%
za!wyhjE}u2aM+c?dX<xPiK!%I`b3RmiV!AwE at qM(^^y6q7)<7HW&go8KN=UL9v2uh
zUFD*qRd!h{wc3<66%5W8X$@12ONxkjk3K9AgVkyM32H`L;-j5VEoyP27Vj+!`DF%5
zlFChR_S~_o9qScXiip2w3JfrcxqwM%e`IJnCC+sttPqDnm%zhvly{1jKKP1o7BN6j
z4Da3C>;YW4t0GhO{ZQ7nmWrWkr7?iDIGrO3ZxM*G!X?(IbCH3n4F)3pV6H#z{gP7t
zH<c%kk6TbOQ)N3j<`_^E;{r}c`u%rKLF^F2R at l{6zHIRWce08#0Swc6b=7YNY(eC4
z8m0GaS6-ocj|<bAVA=#}w?-YJP|&k#7a}T<=ILgIe*2C at BEp(m)p<*Fm*;eC(rae{
z!85*MSrR7qXr-1JZte at laO}^09L#9id)K0qVs0KzQA1cIR1)Hk=bi}RE8W;Q(gpm)
zUZt-^wR~Ti7^DK^9iKa*@D%X#uW>wh;`QPv at HW5(jvYZb9P*TCB*K at sSX>R3q?;*f
z$%`07q6#TVij)q=gSuSn&q$~UGHomI<R-f7&|sCCxqC+ at f`&Boab|H)rmy!bq!!$-
z21)r>iI&rYmD|E4Zj1_T;SA03NS)4Bu^-qJWlamDGd^sc+UU=fqToIR3N*v*wU5ZN
zOs6b`*?btYN?xjaZzK`z^<fJ-J8+(jyn;xII9*$hO{Zou-k2l>D%N;7WDUs<2k&dZ
zRS6#ZO>UBGG)OakK|OpBF84X191f9M!@)3HT$(uV at Ui=)fSCNuvziZ4nenp4s(i^T
zakZcEhFOJ4g6Sb#ybTR>*+hI3BZYN(>J1_KK+Csx8ZY3;1&V{MeZ=HPqq;-#EekCQ
z(G2ap;JyLWVTG4QUP6W+vM<EB<?HCe4T@?`b(uIeg1JFI%6S at ZXaSM)vJB7}3}5Ke
z)vY7FNpG?jv at 6xApE|NB;qV&2o&Z at pACSHcp_5>Jnue1GJ00q`>D#nZ8tAucR$<$=
zsdj4q;3s`Q9sl_0^zyNp2=<<`$vutIq8wvc7pmDp%0FQgMrZ{0I=^#0g4PAD6wHzE
zVyF&p>vf3=al7W_h%H at G_=dub*hY-SEIt6%##TxBW?-v+-1Rg{g7{-2hn_UERq|z7
z3h%=EGTX8bZlR1utb&Uh6s{!NzUifND+ at z(HL)qaX3@?xrw-}wiBo;Ch9dMt_3^Au
z7px)WKXlm^0H;;xpg)s496C)*=L8PZToS3(0&T32819>MWc5;>Qf$8M?Zw-`YtJ1u
zi+sgY>7nA%daquEM&6t_Tf!ub+v&B_fTf{0vlG<wTpLkNd{y&3Ry0F#B{>SumN0+X
zl*Brbtjy at +1WdMR@osN;2V$@Ppm8iDN`C&Uhi3JR_qy(FGAV{JLlaANTuSlzuzPa0
zOu<JAUD)$JeRVp&%pH^I!E?uq3+h_z<!4om6SiN_&GoBjE!UhEo*9@!fzxK96z|j?
zwrdN-a`MtGDp+&7&dDq(fEhsn(E#o~A(@V<xK#In!RF{=1yb8Yj`@7J&;C4n6Xo2w
zqh<MEQZe}GRCRdoh?S~-94<kTvux^Wu9rdWJ>oKtbQQ8)!CB2{z3mu>9o``}tAM4)
zR)GWFOI2@{c^KGP#f=rfHvn%^cM<uC_G<#s_W+RH2g|uvT`OXu-vC{S>NsawvP4};
z^!QDZm|pX`JHdd>G`>}@sVHjgx+(9jFzOKZ%54!|n>e<KXO-)Tm^V?Z{i-roH00EQ
z^6OG0Z_Wx;OIFbEGRRs|7?YqC0EzuUU)qIaq)^hrM;fXclFi};l#ihW$fJzt+OEf+
ze(3{C8JJ%IR~}$4QSa7KAGw5wz~TsZoL?S;Jl{N=f+0%;*+1<4lHT&ek-xV*XB^*;
zODWLn1C#HFJvy;)X5IU28}AI15-&xV-2Y+O7kK|U!^MA8=!3?+7?3I}D^n+wbA2+n
z5Vi>TyunTAsd8k)SaOIlyeyEon=5Kd4Y=ytE(J^6ZI_$but#{w5;5@|@v_xft0ek-
z6cJURASpH8YtqHhxFVTr!WvF8x8_P`PqM6C at +VV$B27+RW>4Rg6 at UdiWGuC1G7l7}
zTCA|tao`KZC43riqo$`KGJY8t<!6?_dO|>{iTbh-7OPMkl6HPCQ9aRYe0iScD3mN4
zu at ka=r2LaraCxw^tZciRU4B<JWURCqf{!pY<Ox>2IKb2 at jbyym16%fvB3b0#gKl;Q
zEGocLoT$#(<kQq{{zL at hLKXKL;4-;d9H7=#SvLu!sA~>33kK0S-GfN)!UI<%Dr>DJ
zLY|bGMaJGAy29-9 at IP{7gHNuisH=R6XmRCn#$M&!b?h{G=AJd)Gv7Z~dbBb36x*md
ztj{>!L-5iz9FPf<WD1kaeseT&Xn=K=&hT8`VMM>DXm1&RtY#oau)l+Qi~pGNeN5iE
z!;N}3CPYG!yOZ?PCdSGfMU_9y$zX|Z4w{^Hgj`kl=UEUCV?I{33ulY;a!3!XJp&4<
z at jDzj{8CA;4a0!`A;nTaiel3jCX}BeM at I}(aTbK%P4fYv+7^WuW3KODxpp at Lp{|Z|
z+EG{9j<}~GVd(8KwG;9E#XU`RWj$Ehp+3fFfLvz7fya!8IzfFm14W}tTSn)`)TU-Z
z3lDn at S0W&F^^^3mkDR6Ujh$5WC+h((IOWyUV?b>FL)@4jfc{lDK>JsYELP at wzC0`K
zNVodwj&+MsiOjwQf9bncREo<|T+;A)%5b=Nno0Z6OyPTIIk0MDswk*z2bK%_W|;dc
zK>(No3`GcktmH_m<$YEw^^<7 at mJx;6z!HTDsQP;te|C$U1KEE|0)kh)l!d#L6SStC
zo2E^&#w6uhi?!OqEP*i#%U;;^bg!=Uc=f!ynigvF0Zg_?lSgKel`N$w%?()!Ns|p`
z{Qab#h>HeuX=X;@3!(bRk*RenZ&L47_1t!G04%<;$~>`!2r2{T+>kZYhwM{@uhVQt
zHDwnV_(5vj`Ud3fxjE at +>cA1J&F|+Nk(Wbe+|z6S$o1Yc#m|;wbuGG~2xclmK;wFU
z at cF2nIImcIwH&pbRTewFPHL7lomVUnMe<ZbOdR0wi8P98G6x^@CVvvB0W<GKGr+y^
z&K{Sz@#?Br8zrj6Dq(33QP%6ElCm67BGc@#Fu}tQ+3kX<=pVSeOmD4uw5C_llL?-9
zVj at 8dnU=r4plrEQoG?6hO1pD<Ja&D22kst<lNS-l+LY0cQ`BmAjlJ|2<Z%)oax8CO
z%3%c;iI!eh#_Q}WP=Pbnk(h5 at IFr=*6yLFuaPffn{Jx|fJ<!`Aq0mk$!T^_}@>leA
zmUS2C7Z*}Nq?A(ay~A)L0x_7J5e8RDu02SduG`W{Ufzce38|aTMD!BjfI_$Y`Q^C?
zozVIYgxKcLyL|Fkr}@OIj`;^eo%b#6ND*}2U50PO+Ka(@d$xsHmh&BS``CqoD^2oh
zZ$<-=rStb(c}*+ptLmcRVwTnLmM*CiZl?BS0G%-%+L#*YR&t!^Opd%qBqxMN4|Lnv
z*_OB29oKao>i)-3^;q;bfdahhQJ4 at herv7d%1t^c1Gro7{fK1}Z&&YEqGZ_RxDh`l
z76opTAr-wYFah{KM}d8nojji9Px~pwlIp`l>Z#ep0J?`)%n}idQ`%&GlS}Dk(j1e~
zIq6qlxh|iHxL%&a?{JZR-Q6*(S4=H9S7z2+)#51oqQ^+?$g`_gJf%lmFRRB_&lniT
z;cjEI<9($0(|gLsgiHRE2KMAnz>w-6k(WQnl7p83|L&aqyPU-Tr29brzGC at rSWy2F
z at DJL^qJ1 at gx=twKi(9Z})l0J=pVv7_b)TbRDAymKuEbqSU*a9p%NMN7*JM)FlQFIJ
zMa78imp=A=z7IH3*3!$})R|;ut&}FW#lv+K+JQoogz;zmEy_~eEJtoIZm%zO5b>qt
zNp~u-kIr#SA$hK5YqO5jRNV(k*EvG#so4vX$+S+Y6ho`&-81=xV>7;7-?_u=hqzH+
zAL$H40|0;!RR-LF|KWkvA8ZCpaP=#JUa&v-SG<6~t=E6UeTcuH?5qCq55an3i5~Z*
znKsurV2wvL^3Xrq;nVc=B+gECAzjJxr&-W18TVi<tZyQGpLz+|lolxf+6Zq|I|dpy
z%O!M`Wo()k2xeTuE~?p4J<g_A57x6>MXw$iUf6>Zrk;Kg#%EV|n2hv0JvVnl<?in>
zCDBjd_ll(j=YG}+J2{3rj6JG+#Rs&CD?LtD3c0Qk-co5kf(53?X-Lefo09lVvsoa%
zoUd*yg0obyB)0D*$?EIH9NZ#9Cs%0>HNT5>_yiP0wO;lKSv2Ul$J|%f_ZeS0Lfrcc
zUOFz%6Iiq~kZaEHkh%%<T?$@6yc+KXvFR=frMPZbdK-YLE?guHZxd$bAN~Nt{2q1x
zX`FwE!TwPIXRN=- at b_PcRQ{WnW$KvNN-I{p*$Nyx$7O2h#?6TwYG=kafD%}B>eCG5
zdGMMOW9HFwpXBB_80Qs{{li`cr0RG1TY|?nFkS5nIc=BNTjYoun<4KW&c&P+g?HTK
z<U6aYaW}~cKVJ4-=kJFjzq#$t@}KSV(?0(5Zuhf){Olk9v-`(yNpZ&>!oZv#0xqcc
zxM0w6_oQXUY_?SYOuq?mQ2gSnu|KP?uRMUnKLgqGFTQyU0 at JFQqwEmtY{JB9wvu{Q
z8F-BdSQHTKw%u^u?tjG^5rU?K1ZQP&uZcicYhddoWt1Bbx!nL2%BXzFs!m-61U=Q!
zP2V#P_#9B{nR<bHoq>3vZuYL+GP#p??>dZ)dRZ5Xd<E)>WGm}=m#09~{=Du=H&HwH
zyrb(kK#L^e&ALCwZb^IDUf$HPc<*yCorAgg>?~@kaeg7;MNZ93P4iTvmv!$lpZ?{r
zV^OO|-)HjZEPoD~5+9+iCc#{1V9A&4c-s^>I-R{(wi}gN1++cw7ZF4c`(oq(QG;sZ
zH2aKL?hIsYx)RUeoI0v2zW2$Kw8k`sH4rn&+4(m~l9Od}wYe9LaubHovOcUZeh7f?
za#8=Wr5k;Wd(=Itsu-9J-qVWvaq>Uk5s=jHuXP^sdO0P0hkU49`G~Xzx`#R!#{Z9E
z%D*+4f7~n!UL0%0z3ljQ$$8fKuB}Se#kJxCf35cuRO1^(STS)YN!e2dgQlAgl=Qul
z72 at hw6olc8SqIWyYd4n!)kF;huS?|7u(&CnE_~O~t72clu*A<ff_``5C?n(_nDg+x
zML2}e_jq3pAYbl_)BC|7q6tU=Ho(&>D|&jBwun0f*#RGE#w;?KB?@1AMNQP^6#*gq
zbfqbVnqt$m$1SqR6GW3l!OxBsPcE^Bv=534+>^)Y5o?k=bR=Vgl?FnH99dEL at SA<s
zfg}pecZ^f_N+YRQ?ZEtpELbA1QJ8KlnH`nty1I~Uo!~PwQ>i7#_6X-50UxW{w$O`+
zQjrqUwy@)Xp7vPy3s_vCfbX4*OLh9<h?oN?x<|TnEi%Az3xF at QaUl>89o3y%lw=oL
zQJFYc1hd`fW>o_3RkC!KAL5B5sNbhT^r{O6R0hpdmKFiD>EBhKs+By_HB3Z}viMha
zeA0O`{=^MwqmFOY$LgW3)DD=p^y-zBzC|GY8;OKo+O7Lpcz;%hRP$B<a7+vBCP6Ai
z=?=>6lw(9Lh<haO&{%LTYoyCl{PIpqdqteFBNd3(eoJWZ2sH{*i<MMlefug}$?gjV
zj;-iwHTpp?)OzXMtA!Pv((ar-cDb?1ZGVO_O<#sjjT1F~g46nR3cMUk{_YGA2S>^a
z;O}Z`<9x2)bxpcq&<k}7IhvjcMp4s)SASjF>K`fkJX|YU_{0j;ski%ZKko#jv3&`5
z$(>)cx`s<ywt_Qu0B*iJYab?X^S*WM23RR+;(VeRpE**myNwhAMsv}%)&tyO38Y7C
zCX>xd;%<yal%|-JxVlOyV*KF+JHH|T7P2aj_+ at imy@#$nlQQ>X#H2T=r;e47ge?4=
zzn?}nUK57RcQ+X#v15kF2{xW&z;sRlqisfOz1C5#be3)O8`7yC-K~F*>*xm!s^3Ub
z{f>u9{z8MXSfN<=CXa|qVe+vZuZE4~4C7_&?Cugbd7D)lBXt)%D|;H{Ik`G?!XbK8
zCx7HlIAFJ+<VZz#!r4WvKraXSakt$}$RBDwp2$hA!SV%;G;;z at +7<*UHUAE-0Iv%;
zbDExRq6=T|a$j3VucW%g*FaqTu3=aj1WOddfqtqU!t4ur$VE;6*m|||a0q;`fV2(I
zEVQcQRclCo+#-!kMcPcbfK0ei%Cnlruc6XG%=w6U9vLW at aVCT=jI&!JwqA;)?-O$;
zLn6fIbu at A}yKp-_y7!Cn&W~OxFjv)n`fL;bZ9v2 at +EG7DJC8xD2RO5vmp*ZV-z`hu
zpCWax*Du5Y?8Ns$iWv;wGmfJaK5c43 at 1*S&5);PwbDkx2oA~_>33M&ZDi}UlJ+K5#
zk8tSfBUMd72GYpy0 at EkJtj@m&jxUxE2mCHL9vl#WWcxjEe9i7Z;u*H?{lIkSVBV1#
zi?$0gxhcbFKm6SYx?@D75(qpzfU(eUor at 1E(W~xKCgK;7RdvRsgh6Sm<3o93oW;Nb
zHYut)32{JF4kue0jsDCcmGM5pQ;@cC2E=CFdlr%|7&uIKI_~Y0b6IwaiA7nm#zj`G
zdcu%<KJ~6!6E>^5`5^W{YiAQhxOp*`-X5o$83G4~tSHfqJMmgZs-sOi)^XSb(;0&m
zy7P1!C)r+8W;&7^Q5RezP~1atrNe~nB_v6(^DEA1wTX)ZFVlL><zsf!lxNRiWI{kE
zn+UdHYnSPrX7gvXjLPeqizTwb7CozK@{X_o2R)*PsJd)R%F;zqO%uYxN8{0nv&SaH
z!hWkOU-E{!h@)|L-+69R$!NuKG6^VSs7R!Xd#lMS&q(~EqsL$eL+7(YofOO2<H&%|
zP|m$Ou$RS&%UIPR>pb~QZLMe(?%7QZOmT4&kVLHybb7L#i8C`m7owq~cLV<Al&L+e
ztLA`9*iy*n>)6_9K}CDj9qh8fsd!UcNP!Zhg;1$z_S_`Fj9bIm28<+K+NQbPr7iu+
z`{SZ{RI9Xf_6pSXBr_Qi!V?=4R23|EMyUyGQd?=Fexdcc7u!V(H|pcG<I=~Mh+R=Z
zlE_2t?xWGKnd?7UUZR(}QSKjdD(VcS9-rq~oKE0&aC+UU`Tc0e>K`#fexF$ed<Mkt
zvD;{a<0Iz3p at R642EuYo81MJkZPfC`0q!GRoDFsukZzbtvYXTcn87`)9(`&%3t0V6
z)B==6hz{mC%3-t*l1CQGnJCXX=-{#e=6L%cey0h at 1MOtHHy5S!FvC5#i+%&5TtW{Q
z9vLW*oyzXBZ||PJD?R at PVD8+!U)TnXH<!X55!)7DwWBrMDStVu(hz*1$2J61|3TyX
zAIJP8v*is$ofoRiJg0~l{3O1DK7SDF0b5yCRDH?FN+h8p`5{Ne`uRsrG}g{MM_wmk
z!o1duMa#MYR}O(!w>@l+ at 9ZeY7W)<h1^8m**K<!X$SrT7 at P%u!9pyi1zORp$A6Mn%
z;Su^!HWX)~91Tg2gyuCZ&=zv|Y&>UzP?{r~$3<H6DY3kL4nP<--xJU?X>%{er`3Aj
zgxu80%kD_t8wn at mj7<c>{i4!mSvY at ybKQhFjZ?@MQ`64oz55xW=J_7Tcw)SK-u>11
z@;=W_Q`;vC9cR(w)!xc<HA1J$AtA++rP{59C2<!YLkB8;P<1M3+rYqfH3*!S=6JOY
z&yQZ*P83#=4DX+|;(Jo2C*8gmlLsgbR$`8hm4`ShffPm+mNg8$?t#|xL(%+*4`k$@
z3^7|F4;iR7=V5S4h^5oh5U-uWCTEkw9Np<}2<Vsku(}!!nHlGVspNjk6`-msZAi{d
zXtD`S{nZyMX)W~r#VuWH_-+*8 at DW+6l!+OGn;i(um2#jVZMas<hb+#VowzOgF0yBY
zJ!S9%e<F3f`%%D=(&CN!u+9hS!2R2SA)|4SHBS^Jb=g|X)`2kL(o$-y`>W;YZENW_
zS6PwTJSM_AZ;9XYY}8}bZ{Qk7gI>~v=h5$q0&(Zr7Qlf;ebm{yk3d8G7VN9>l7h;(
z_<pB8(jAKP<r1r88{QnnZa&@;<&q*re>d6<eq!2ys5~%d3`hP)$rL2}5`v~8G^d!t
zqGkG!@d<HoCSP?j&2c_UO0E`sM<OIc#(hShlRqoHwdyF)g=cy#x3RWKe3psPl;zWM
z!$>YKNVKxDHVqKva20b24(}}63WcWC1Qm|x&$@^BpdZ=7zIj%Oe)73Li)Aigmt}TF
z%W&H#aAi1-B4I#p_zfKhl7e{kvc%-XC$K^iEH+r93#^XcO2%r3LT+^ciOM+(LjxfE
zX+w7o{WjgE{0`<xwx@=c{k;*(AV>F^gwE(@3<blWnz}rb6`Y_u{L3n(U%y2O=;6VF
z`b1chB-1o8FMXd<7<nwB*B$}0x{)X8oh;=xnYli|=a8u>Os*EiWGD_WwxGr(PQ_2?
zhKtj?@yS+GuJ85thrtYl!>TOUtF+=gZD->xais9iJkBTQfr*lQ`9>*zY}*oZevn%3
zHB!Z1YIN$@qtQ12`P+$*W~#>0q5LTNK^$?yrLSoQiZEkm-vB7QUfZ?&9F|_zg;r)~
zdVJowL>X1CWCXAjg_|bE2jPtcH;&`CyGs<;t{M!_&$R}P&4~i`k#)u|iuLHQWlbGT
zfaB8S*lu>})P<SI9|ZX{7dpGmNF^q^2<i!XEcs3C>_~zs&c at J7uLywI(74Yb?eg=n
zN{cOHt)}%&8yUB7)cG9xlu=Mb)(bk<tZ~c6%@fa@!-6{P*tgbSM+d83fEw9JY4;pp
zPlxjvijexO;YJ+dhK1<L3u-F&A1ST9zZU0b>bk%p>k;CcJh!Un4P+0oeyeIPHUDG>
zcRi7j6}Ofz<*- at S0l#Oz3#(%%1JqVZ%Bnp6`3B2~mSj#Y?fz<b@%VNp^>$h?4cpS2
z47wwv{*JG=XbCAmLWm)r-rz<^UeE}>Tzv|xZ}8%2I;HdunUCZr$y$vWU`fpqwL(=>
zU1N&En;_>%?*XIiEx&huuUNjQ`H0SI>SKu6 at +++Ei54_k&N=eFLwrCzFFC?<xo(@<
zndphvD<?AHdS(8er6VTk#$n<#&oXgZJFziuC|Ay-fU>wIVMPzecr;j86J>`R@<5L&
zbFEotp{^->{j6hKB#fduWq{Q<?>J+87GW8?rn#nZvU{`1!-G=e`5YfsSm+L&?;bzN
zbdz>=?w)ItT4Tr<LWt4vVE2dFYa`F(R^dQ!MBDHmfU|_5%9 at d0M})LQV(q)q at mJS+
zX at Gok9$N}d)x4Sd4KbV2ucZ^Uo&O0xrT_4s^TMk$D?8SR6j`Y?A&=}Pb3;&OB;+t(
z_-K|t_mIBA<Jjrs^M|tI32f+=0WD6RueVR5Q>Q=XhkP1v!TbhT(4Jh at u|3;vT8uiB
z;K6)Q>5Sd?41ATca3?)3M8i>%$=XPr27jO^xuzFg`p{ZdH<8M*EppE}S|fakmkpiN
z-L#odz=e*R9hp{)r+a#+!0~aj9X9m-WnF&}a;Ul5E^gt-g-{e<SHj-d0fUQ}S1xN-
z32$(A1sm;VCS5)*dA?xHHvsZph3=A766dKLcDf5jCt>^(V#A=u;bVE}eM$CukqY5b
z>yu8Gtiyyu)4o at z<P5$IBOI96uSgR*_iVz}Mh>rD8w=WU#?4Hua7 at i<ikaqh8l~if
z&~zCOwo{<KVgbiEPu^&nWHe4<4}}dSFIK$110KZmq)b)pMxN)t;^$?b){ahRDz>y0
z&Hi+=E#|M~N!%Gr2jtz2vRX&hSCA$6dKAX>eDI}X)}?^2G7tZGOeY_2bg;P}A%gdU
zTRpU4;@qGe?w-G7#h!GY1!t3U4A%!IH^+m@{HCNq$5z*!JRt-qCwj}Gy?JtNL~CU0
z7qc?&bIvwR_;<)-UP)4ySr=6{->FB~IpuZX@>>~2TOuIYKi5HWCkEwjXbr925p`pH
zQHma_PT at Y*6cyQLnSnjfIq4)s%TdhAv3OEcE-XOvB<3$o^9KLvt0c9Nn`J$@7HXfX
z at 8&7^YCC%G<C`xym67JDZY1N)U<Tuhwxd#FAO3;Ou)3{|&B*7rsh*0k{^~uiUE0Cw
zrWgvfUq+rGfft&3ZM>JrT%SEDL-1Cu`(RIXi8>#@?tKm4%L?Z<M(>KiM+wgW2q6GE
zPUcRj6NT!rN13?i{XEir+L*WWkpH02^q&b&(=Z!XPP&Mv5sWM^7H3OZGK3Fo_B2AP
zMi)u02@}+S<#d+H at 6RTMPV|Mo0njCX*!vf`lb_{3+vlf!{15FP?@}J>!I2vNTGV?S
zFq3ST(vQ2p at WB3f=;s+f+vDHN<-d)6K>RQ-b_ql7Up}}R7egM6=F at sNuVeWPP~9Dn
zq?B-0zop|0_;b$MpT{tNw#)yl)X|^s>*xFWX#+oP;HM4zw1J;C at V~<bAim8ZnA(~+
zIXjvf0>5wB8CfB4vXVY0{l3A^&#cVy9D(IIcwZU3$%?@8y$HO?g~0VM8$1X+-!}vW
z|Lx-M1%JAjlM{iH^Ec(+_y4tA#l^_k!`_rx8fa*4ioh&pXJhB6VsB_{%KXCA)za8h
zSyB{%S;NxA*@BdXotqVbS<2MX+`^fZlY<+9S=7?mNzv3%%noR8XKQNfObR|GVQJ%R
z>d5>;O!bASv7L!2^Gj1(bMVz1Jlt$7f`b2A&2O{cjsYkl;JTUuOaR~y#J2^27ytzU
z5g8F41sMq$4HX3)n*s+L6BC<(lnkGOor#N+jfs_&S42gES3q8nl~u|}T3%gK at 0A|6
zxEat?+eSr4PwV?lAW+fJu%BYn;o#6|@v-u0{mq|mp8 at D_PvW8bAt9ato}fcOqC<S^
z0+4{85gOu`&-ds3{(*P`2?Y%U3kQ#Y2tJ_}4e$g467mTYBs4S>6u8s}Tn>OjhsGdd
z5rKKCWC;7r0h2W#E)$Miw6YybdF+IO&B!qj9swH%7Z0D3ikgO&j-7*(i<^g6?1i|5
zq?ELbimIBrhNhObv5BdfxrL>ble3Gfo4bc+(CgrkH=$wS at d<C=B__T9kerpBlbe at c
zP*_w|T~k|E-_Y39(b?7A1M2PTAD at _<nx2`Rn_pYs*xcIQ+1=YeJv+a+yt=;mdi%Xz
z5CF)3sTTO}pRE@<xL!}7pdg`Ozt;=miQD&zqeDTHvA|%6D8U*!JblI*0Ea0Wms!~k
zPtK-%f at S15hJa1MzD9Zay=uSI?60d>;Qv?6{;gvFUaxro5+nrp<w2qYga8-GzryqW
zRfKgv&;RQv>wdP=&vyEsU;}?I0p*{~hW>LI(tq7C2>ST0+pEfg`H^BhP0Eri?xQ0U
z&BaK_mO6OSoqT!v(lY(tv3ofeH at m{w7fE$h at B4AOf<22(3|50uoiicq6ly0gmP9hV
z!fKJZyJY%UR2Xbof~lkCh)%WfZ8K4F^FbbPE>Y)a4V|>GP$Z&oWxitsQm1ltAI*IP
zhH6fm2cX71LXa~If+nB1w-?<84<@r6;5$pAdaKK_JWjr`hPW_J at A2n^4Lf|nnb0&G
zr`NkjP~|VEd%xsK=n$=t%lIaYk(v3iYx+7dp at I=(N!0jU__*=0R#Le-d_wW0HjBwP
zL|yG0z`w`dk42s1L}-odWbgAxIw_d%3mNChaQ-^Ip5Vb at G4#?=FTpRIG7+=&h8pax
zG7D-ocjDQJa^0G&?@SFR(LC|HPc|4lui7wYapW9H(%>Tt7H(WhfxH{?X*!kp9y;;L
z)GVMHoP9@!5W!Xpv?UF#+Hs0HovKw`HoB at ZVN&xnrxB6J%{bM}Q at w~j1{E<6YgVSM
zXm^B`Y;I^+l at Z%dlOt$FpIX;OJ4tnU?nN<#-tj3%5%hSnm_mh`lOF_s13VQaV6^1D
ztSjcPJAj at l9hAY#Y>F^Gdb|>#YaIzMS!;~P1=@XqVo=rsmJZ~+a9Nx?I!b$o^i at zm
z at ePu559C3MI&<(Lm}b!QjyZIC^u=J+XPXV<r%xPl6H>4HF0%(wih!;iL`;k?Mn26e
zLz!Y$^H4(w6YlWf7SybuEm+}*P92rolxfLQXxP7>`@An{tb5O??PY;3&Qv!%uAfxb
z8V!l7RXyw6<1{SHbD-X`q#--)JnN#xP|KbyhN%J<sDMtox#|#UFLm!T8%5BDIj^%M
z3*}mWZG^qRo;Zpl6{FH{hx&4AXPV)uH2#MM5)%VFUC48jRRHe}zft3jjcwG_<w!_v
z=or$mc}+v;Ta0f29O`XFg^YsiX*YMr8QT;CChhaXF)Bd476kPmp*<@+?vB77(scc)
zT27LK&Z4#zwxAPKxNNaw7&);&$!RffUK=oPk+Cvl|M)1$J#d<trDKO6CZo)}E2{8?
zbL|ud{j(ttFgXbsxeylx3&pArtoH2K68WiZBjG0E2P6ete(Xpl?kiZH>+B4iiy_|;
zwU*@hg+XQn`bN`*$kUpjUUI at fy3|jMm0J1(b!}<$tYS- at w%S-A-Fkb0fRZWa##8Dr
zm^8KAJN&#Y0TV#<r|f_mRea>MLasNZd9|l6T(1hzV1z1qUT6{)Qe5$DULHu{Er_tY
zJ*OCL>cb4j(*&zyA}dXCqyQOu;wjEYXasf9Bwk5^lmU-s87Da`B5QXhfd at x%$lX+D
zk%j?A?kc06Ud~1HAGZeFL+FWE2`F38JYt+2;py>gfQmfS<lMc4DqaCbpsvLFQhw3-
zGsFgC))cMG1$yQ?a&DpYT;-GKYR;UQxDh+glIq?L>Ff^ZCI1g2%S52*LQMhCJ at sx<
z(x~2WooxwOgh9+UERno|ueC-81%aM0l7k;NxaSqX=$;~t=~&dWjxbKL(CCd=eU=h5
z5LEWlw~D^*!gkg81~7U at ZP}8}BTBV?zhM>7XJ_O5Me%0gQC#**mfx<m+LV~LsInw|
zo{8T|KiaMC=s3p#P+Qtis*JJB5YU3da%hh#79&&gNME6oA7#`W4>vr=H2!3iY#5I5
z=pa%}w%hfzw)ah#j<i9J%kX*3bl%C(P^vFVow>7A=IfHXXwHhe_Ueh$%k13l>8{Rh
ze7`HTvaCl(9o<1gbpzHM+s)OBuL>{IQdKM611I+;^!3DC%#q}(AE=&1?nGh{$vf!=
zhf0EpFF)&MPQgH2EZ>x_5HPfz(z-NZ$CZu_9^pC=V3A3y^sp^n<b7^4Ue~cM2J^p8
z-}Xz(KfBX~VWGWHnGl at RzlopkKRq(8{q!lB!K2s`BB8&xchO6oGdFLge72`>{arj)
zgBXTSfQTC`DRREh&Ui_mG5h?~{UgaVbZxK0Rk4=QVz56h{}}{PkJCXnDJM*G{zuY%
zUtdO8=(^M$?w;@6s#x<Tar*d(F{k!ZRif@!eQW<7N|0WD+Ef(LQ0ZfUAFvpX0ZGG%
zx}h4 at Fp2aH6bgz%ydS>H#QrV+G;+b>oYmEceIWtA2$ecNj4(F^ijU`mMMd;<xQsT;
z9|fHLbN9uHS-1XrafD$V;fO7)2KCxR0?(g8vd-G5%H>GJc>ycg+EAccs&XWu%K`))
z+TKf?cHuuH2eY-#rS3~HY1&}g-z-y(NW4PD5<VKCnmE58r>71RvVZhit|Bmu3Kc47
zbR)VpMJS|31ad`l at YZ!}=_*32p9ywdgCv-9QSV?G4wc at v1<&0S(so;dl7Of>;Ohi>
zbAc7QW^0jDJl|qJS(Gsz4-d~y1z>a9`le{qC)23*Q)A+?S=f3Y(U-GJ8%Y0_Q!qCx
zU0IUnC(;?<@yz>@QV~#26^i641WA<a8Z<<09%#^oRz$Fsrea7|&aCH)EKq_caDkL>
zu4n1}K3vJ29k%9(6Q%<6QH|4cO<QXJ9OZ;jdat|n#^M_4j at v8D_cQ8dal(&)eTB8<
zX8OBUTK3tz*5|F+SS%q5&sjzWz+7Yx&=8we>R4aGju>shj0Ro{dq(R at -%U^G2!HWF
zcO}Rqih-Hb?8!cT<I8<_9tJ7u6EN$c$GwOFyHiniO=3~IW<e({0}d3SG1Pf-!po`k
zDa!XEK-W0Qz-n3SuD66|ug at fj)82LOi&Kr>tFRN!^|=vu at UlJ0?7dC9+>P_EEPw<c
zA-ImOIxRDwYdIG at Yig*&!6_zaZ3!l at RR8i-*{=gkiL3$x77Yw&dx&D|NgQ29CZ1p8
z>SVm7FLQ`htM26~=1fBwEji9=tgo13mun;#j$Ms at 16?T%Nj-TvAV<4VZ`#n*WV%?P
z(_<2sly~TQZ)sE83{;-q6xr@{KzKZZkIjioH8H`@%x~)6 at X?|sm19?|!F!|t?2tr>
zJMM8snR0fqyiss&AU$DqEb_Tc&!w)oqLuV=_2ydpAUC;eG80EpzD+a?ZxbTW{&~HL
zNJYPP!d%CgXlQAH>D0~|J??4;kG$e`hK1xl{L at n~;qh!+ih57<SoivoD{ZLGhnK(n
zurS*hIH at YU(2nVvX0^P=v8s-#Mxzc+u!-FvtV$R)=l3M8&GakKnH3?dM|~atB1&q9
zG8FRO8rnDntjs!d3VP$O31?g3h={#P;%!z8g4`cNrg9sY)#)S5FFZ$4=Dea5vFlPE
zHJA19BTAjF<dr(plite8=o^NoxC8JM*ABY-Zq*j|+HH}X?%0x%kDIk8oIyygTjys;
zkVH@=uDq}$i(8MCLtBhgU2O_v6%ct=&xil{G^_DssWS`vESU!vVtC44VHdvCz3gba
zM(1rZXF!sD?DK^f-O)O{p*>H~tn-oC*{yV5_=-|I%egIGqavX1Ga$XAj5&e<xu0Cz
zHSvsdvc&=}ppP5Cb-AZIHBIojX{&deAZ0s|RVWN89D+m_f0tj*!JInCeD<_Or2~7B
z31ypm9_O^12zKTRuS{ptDaXgwqp74gH;$>gl_fFkEAPlHVFAFtJa71PwT3J=#w!z5
z#T8vAO>MfEo0t5evjlDSY~KI?c6gwp_^D}RVgyGEPCe`9o4SxE<2{mkomm@;4|ES@
z)ut}FAH}H_tTGbK31o~51VgItxkH=MTKY4ddVpE?{AnLeW;mS7mY+`5 at kBjUcXeH8
zwhDuhUw#ca%<CXb#7<0KmFO%DEI-PO3j{d{G)kqWy|?r8Rx8!etDB&b!lzFu&WD~x
zw1|vWb>Su|PMdPRyB_;^iHajJ?;Xx69%vZQ!&0-E_K|MuA$-y5YQ0m#i_wfv5uU|X
z%Xmbuw8trJhiJC~f13B`-B#)~Y0Hy^2T%E6Vg{s-oqKAG_gbdxr`Zeo3!U`P;oDx0
zaZ(Dx1S^XO1JS!*lZVsS;f}cLQ8|{f2&)JK8UXUEJXa(iLQXjZNThJ69tSkXjpsY)
z3;cnNUqvf=87H8wA)_bZ_G;y97hXDtZ8S7A#@b3TZm>#ycHzFbonZgyxuBXXg~1nL
z{XS$TaGNz6ho4a0XX<EaxP`H%fFZL9d;e at vkCq9&I#}_!JlqI1*2lco_S30(U41b(
zNA|OvPU3E{2DDarht$oqn at ps+lx}qvy*>mn4|WI6zA{7q8YT2eHdKY4{Q6Vm*Yhuu
zGh at 3g30RXHCWE2@)I0)oNkKdHF&z%@MC8^!>zh37l1Q?)7nfyPzJ73fXA3VGfk~?7
z=Q1$pW!v-J3Q$PVsI|l*+pgH3sB0UVqL-~@@5tGJVaFLPIr$F>={w=eW#k$Y%-uO+
zs;AmD7&2+v4*{%4u^P4$2 at AQdow-k*fAN+B0-Y$(=Js*MU$_oj$vTlnUbgciO$pd_
z#kB>Sf0_^<NL)g5&*sZC;9)|ggDoHbVD3_=?^6YQzLyM)f4M(+ni!W#$o3oxBCX~8
zKy%(X%{<^<%KOw_t}Ca%2vZ309Ka2 at 1hp~+yYK;@U99Yl$peppeYkV>Df-bj*LnNN
z-DOGm?2pEMv<sLlWF8a(_QmQ)aGY^yPH##28->usT_0I0(5%GdSFH9hOA&ezZ3*7O
ztYWZ?tSzT3zN>7^J(!+<yS*spV(DDm((ZyDb#>88)UDBYD*hHHUMGhu=^2K)Wrlx?
zecW8kjahYqLerbBWRFSy7fi>oeByvARZ5xFt+yw<E*Bfg)vJwJF`a|)O{HBJ{Y+wL
z2zBeycAc2c4FmM+Q34lz9O&CViF`5DNnagO0~(y7T7uH$CwILiEh%${CQtRWbwq-;
z7hfpKpo)GRcGz3gc$SCPg6C}nSm-O-f;Gw$2kq%R@@PRvr|ogqMKPnbbaRD<z#*xg
z2vaCh7CO#!UDe|#wR7Y?#C at pix>m#;S9$CkIqw!yI>oAun$QLTy}TU#+2iJgO=wU>
zmqM&jH74x-{pD)jJuzJ%EnCW`u9vBIFs8VDIGiW0ypr9TyK43zTZ{qq+xs*}KEec~
zpdTcAC%;UNO$U}}p=e2&lCB2w?g$i>riUHb+HTY*PCpLi0V`Uk8pYp!1628 at HI%p?
z1HZ8A8I9=5dMQwuY8^AYJv664dtgfqi1#B@@}B<`K~yp`qwYEN|JZx$xVV}vUAS=w
z76|Tv4iF@`dkF6C7M#Xif(Jry2 at u>}f<th3cXxLufk5ugd*;r0$;>_XjGXztncw}R
z|5&@KyLQ*E+EumI^E at j)+4oIZz-LTB#m}ozGko4z`>#IOh)`K*3jZTKiI|maGB8q8
zy&TtegB#zzs{&fgfv5og35#`zRBqhxr2&Kpsy=FfV=`67FlcjzSIo+eGR at A)DmT7k
zkxYs11ITLUg)P#^c)dF{bk#Nc4F}G|U{X%a+cEZa_Gm`eUDvBj4_c3;5c6%B at dYNc
zBs)S0JYGj$_o+f^7+U2~)+YTdy}9h+M)*Eem<bx8F0G1++VWa04{=4j_)O~7B35}g
z^TU#3Yx=wd4k^p7Osh^Su>RTT*}<DQTPY9Jg`R{pl=9|v_P}n}VRRwY{PZV*-HZFk
zUWPylQ1u__cM(g&cHt=&VK;$}R<BQvIGi#1*zMIZ8=BLfqHkzn$-_yo9&lNTe2Ob)
z%LgF_qo$*M#+H*l-HyucH!iOb&*rtE8(i^yYX?+?XU|v*BUG%r6WMxiFOwVx_o;Tq
z93P<F-KxC5b(uA)`d9)2iPSz}n!ww;!uARVb$w{RrNb$)LG=S$_ICzab^1pEa#aY+
z74IYn2J)Hr>rv`sgH-N#Ys at rN1UgFs5%5o1Z)fyGKn}VDWzjqf4c8TSu$Y-5aOEK8
z%$+!x4DjWT{iPW|xU*rUdL-}7g(>(PhEy?q^?|&mzzl=Pu2`W^G($n~Ng*S`nlhUX
zvdlEJx0xomi+`_Am1GW<$U`;d1q at 6280&$C)n;v#xbBjMA+_WxhxY2lL=$~Ym;t0f
zCSPlT&P53t?DQ7K$bW+KVAxI*=p~N>PKG0r`90oVqwcLmQCoX1qO06{zgY`X8-SoP
z-uQ*eYvYCm5D?I_oT{gWPAIRvb87}L=Hr99;*sUui#vG3+;=8*?LjuY2Pa6{QpJAW
zZm at 2(&V3z5H3;iSrMJPLwsBUx)zQm4sY}cu%XPczAmmpPBY4Bi5R>&~!h90KpmFtN
zgQ#vHlJ(r|jD2{~l(iB<>AH)<Vv2C3-nSaT_Z-h6+T)c^w_}R>U$j(67=uvP64dX-
z$r5J(5kj%tIWc|9=;;^LWd2Wus{D_X0gOG0*}T=vJUtzSME!>sNj4MUjnk4uYA7>6
z@%=wShW at vHmj*}<g%K at +5{hVS5GmFqAp+7zM{s?o?Y1Eu;+;WX`|y2uW&F#&n!j`<
z=8Ke}VMLMOX}&=|&dc>LV}f|8xD$GE6FPZ8A`VWp+LHd6IK0%iC{M-!b<&Dwo{0<$
zBauT?gR<IJylEEIQi50|RR>yc>ckD8tik5Sic;_<`?3x*6FG|2r_CXz9 at h<?{NOY$
z)ach~@<^>eXiWfd(w at n89N8A}$b3hk_R<2OnA!J0A}xYhK*UWZdv+ujP>*P;K;{>4
zOb}}8-3zs6QS)hCj|227oeXVUPuV&>h8q3UAKtQ;?YiEkR$B*C!|*7NRo&~d$-JJT
zLcWpcHNM3d;_i?vpRkgtjx7{^hW`1L{i0#aWLiPdr98aZSTS5qPHj|>6|qr(OKool
zC)co?(sY=T at PyNQg?vr$me&DM5qhWo19pxGND*M#$}leq8(K$-Es`A^%PD#e$zV4!
z$+3=g7lAwR at tiDCP$q$&b!--mFkmgylpzmfW>wv5TKar0IKq3O;e8tS6ExYhj{XDs
zC+M&!C)0<yszljhfYFNkVU!XIH_~z?`Eqk7Pe8M98SV6L(QQXMh~L3So#Fl*O+a!v
z(}xT2RcZ33V%ODF)|yfbAYn<w_#Cvtl);>1zg_8Y=WDuI$edWyigsv7hS|qDMIQz9
zYqg;Z+YD{smbxbGj#77Q5*I!$Rq5Nx!XxBpW%|%8%(X#YbVl}6ACnG70^+m!C>Ikx
z+)7qecUtPX!H9l6JXO&zU-+|lt~(s;jh8A2;yEv41DRR)XW&zw3?=*7zfLp$CGKnU
ztJ`(3osgB&Y?i2=#{>CzOP)|e2c~7Kn*-VkrVt|emSuUChJ87zvqE`SCE?nJJ7rBr
zDMl`$a)nDpTLGFypf|2`V5Fz<3FRv#QaDkgixF~#5J)D!v0Cv at yXMtYiyH^BU}E+A
z<V7=$UL0t$$|E4q*#TN#9LlFTUBiW?&O;CkMv1;BopG(Z{MUBV_4F{3?DL3~E#X`w
ziYKhDC|^y$Uz%Bh=K-PKLtdsV^BX<$s<Lpd%E->RdCv-2fhT}c2&k*>v^&JX^SZC<
zTk5;bn8 at tg*kSZ_nn`z+P`jn>ntsfq5b5{PL{a`dLOnTV3W8WwL(CHrRXEd3Zt{NW
zuePn_zJkYB_NEhKE45a%vd>7 at 3?_EnBl7={PL0nnPvo>YykmV|tJ-J6zwnU?dtg}v
zd0+OnC&2pENu2Q3h&j&ND2D+03q#JH<zd<e;Gqu at 3X{&7tSI}sjPPjpVV1WuS+yD&
za<*QV?5%~b;|7{di!WLk0n at v#fI3~2??WKaVFyR-aTq=9_IS|?ay1e?YjRWX1ad6S
z>Ef>?Sbd;cUT6`W#gau0K@{bgKM@#*Rr^*1{(64}hFGik9;9jJ8LxTbv>zO)*1t37
zx5taK%A3U${L75}dqX4z`MSu2N<1Rq7H%@}v#$xCsk_CA)t$&orcIiDf=DpDXO_M^
zM1=dGAJMklLR*mDERz1PkNC6k2h0D at 7ygZ<PSYlk_*xxOdFD*oierDDE+2sdrXTd*
zJ!$m*{Hs9%o5sz+ at mb3I9?fC`5El4z;lHuT|M%Vh{UraR5AXl0o;1HoC>iCQ=T9%x
zk$*SpxvgI)Fzo_<l5{6|^5B#B;r<2?yC=%h1(XI6TMmwAeuBh$ei0e(b6kFc1Z9LD
z?)-m(!hu9Pe>VS64S0O%e^L!)Cde}>@E%D-KFjMhFk$+4Ei+5+;!AyRg1N`ywxsiF
z-j}dIVzfGeZ(EOyU#bXc|J_T49g2sntmr#|!4vf+^mrki6J(SM9+8|Y9>MhPtu6$F
zMzU9Kq5ggO6<Ms=D1)y`49e^49m9+aF+$-`)X|#{?z^)e#v6Wu(kU|lX%~Q at +(YgE
zAgXn8dsz!4^x6jWaRz at c|IMcTrN;8xHR|8&f2|G;p_K_F$*lDG*cC#svYO};sF#B5
zg3?`e$i&s=L8OHCq3Jnr3vi%E5BibuKjNwXzkl at i&)YZkgG2V$8)B0v`N4+`>8nio
z=(mI3$X<Gqv&FrCUWP#eRi!{;FeD%`n639&|6}WyzR=?<kFEa!<xC~j38hw<bsxkg
zNnR at G5IVw;h58RB>?wJh>j0DLnLOZ2Sq1WOetI<3%tzr(uVYv4q1e#xj?o{u#Qtpj
zAx-n=3;#_ at t(vpPmrIJLIaEu}s)CJ~BHJYWZ-`r(t8+ABeu8=j0B?+LJn1bFTi!n>
zpMW!gtemak-h0jiW9Zv~Y$e;tx6_b^<|FURcaBhbe-b at _HzGY}Uh$sEX?gg-W<LGM
z8iiKlVF+s!KpDY`ztcfdC6P|XnE3|DEXC~re)rll`T8elj=OsLRW84Ab<HK0L$|}U
z54R3&C%0}TUDV!;u19yqXE)0_(o-^8)p9_iq}qe0x~8&aBJWM7jSEP+90ArS(k|Sq
z8Z;Ah>N|z^cFS;9KK$LP^HI;;B-H4bc5Xqj&YZXDURb{7{IU#s0^io~XhArVN-x~U
zU2a=*))W)7>LAv;l(z$FQjrUk{ucF&_#HISjH0$?WyCr{PEM$?m5bWF6oE`}hO=r+
zc`{!h>@&KeImDj>_tmGBtV at SsZ0lA*e6|t~j!q#1M{2DXL*N70n)A=4*w1V~6$PxO
zCa;bno*+=LR?A5Tt|L=#G}hl~Z}8-tcY1u?*djuseWuVfrPpOBT~-%PtvfPEPY!=R
zyp9YtnXGTfxx)%QySG~^IqvBtb=O3EvdY|EM71yo!=sfx*E%e8vd|=xcc7-~T;Fa{
zK|ffpixm5mD)GrNkt0GamO7A$Xic;-ACG?pNTl>CyaLEQly+{lm(p-!Jz`U$J;f_V
zS8gs~TpuDGA|Kjoy(75J;(H#tY8o>WPF<#bkY6l=sUyu>zAT59VO7S<Q5K-Hd8L>h
zwJSuV3K#yRXut4AwZ7|<nxvNXr_)kdBq)uKDCn|cK)d_F-7PWdiOAxInNk{*_;rag
zrS#v1p*O9$R-K3(G~zS^vkcb>W!Nv2{guOyh->K}gq_N!nW-NTt}fHjDuTDhH`J#~
zkJDtEg4>6u4WAtQNKRDTA8v-ea@|O+)|}~7tEu;@bt-soZn=XYhPg@&27x$-k+}MH
z;C*;#Xy3<cHdpw5S?H{nS)fL{Mk<5md9UB~@p8zy=Hf+Hl~T8tggkfJTNXjgG3_Ls
z1GQ!HDaQrcx+8u60RxsdL_M*?Q$)}beSN%Wu at 2&s4Ac8J(v$4kOY at lnL{UDe?nE1*
zt3;tBlj>-G!5b%9o$S|g`PvX1XM3=T at Y~T0h0ryrm}n0pmm)g|uIw%GL1diES<qGl
z=zUb6vj0pp;=!T<#CIGM#C4={$WU^H{HDIjn;D6vx(0|ckf8HHxVMkj0o4;@8_He-
z-`qMvp`}y|dpBaFjqx%^D at jj*Hf~J~K=@=ydh|6Zk1(!bOn56aFj%|j?bRF(Qc2%5
zH%}T&(hUYzie1~`2QhUkhu2Xx=}mT5Iyj$cEQ#dKWxm9fA?P3OJco{HCCPSK_u+UM
z-CNdq-s_wRJ?QyCbDOoqejQ=z=!!7kMl=QQlXiQClphkAZzLiGHrC2%l(yOUK7)(3
zqB`=ue;){rf^#MLxMwX<PG3yP+yQ&5SdIOp8DE*19kG`W4V{h8$W`q at B#h;uY2v$T
z6Gs*%i_>4<Bu;r6i8nu7MSg<xoLg at BxhK-!Dyv}@<D3tErzMEzJn&&roRU#{5K{BL
z>iP+~d?s;-Y)?$6UOjyXRH<o~jC}{M$nz9wJ2ml>T+u)toE at tjT~`pHtprSqE^Wj(
z1(43xTW-QBNC=06Q8a!me@&J%{+6@^(!)&^da{GS6voZbd(6Q!#9rL#c%`J!{6VIQ
zgM+XJUgf)}1lUJFN!J^v#lRb{vE at wUDs};L0Iq^Fm_dZX06jwcLiLI#@C?CkwaKGk
zT0BoZsn6=;%L{B5?4D8IZluh%@?9)70Yj8}(Q53Xr6ui!C82Y+!JJv~4^%NdG~Q?P
z5fW3zF{;hD%-&hjfgYAS?_VpFe}@Nx>5BN{3fjspnkl{I)xrDfFQ&4iw>GI9gb}-^
z1l^4ccO}?N)Jq#VJoN|imv!fKWXWP*tqdBOkkZoXKV{~2+)jD#6e~F*AAapom#Kyd
zVfeIF2E~aG?ru+;>^Fa-I^z^%G<jlGVwLwg#D;PjoZj0vjAfX<8f{SsA at 5O{vZP;T
z@^*QLNx`rv7bUta at B!xQ4>Mt&Le0FqdA8Wh&Y5T0EvnI`bm}*^=mxDtpFoeb&Z-Vr
zq<%@rn?AJQ0$M-9fI at _V$p0}N0CvK<+;^O6z1zzq;ONJer;!Q<QY1kDsNfd~AaC;D
z{_?*DKlyddL<mr8qQAL!*8-L<2i26{?SH--?1Z(J?>JRoZZAIotz!eN1D>!bh$PUu
zxc4R0Us^Y{m~V=UfR at Xb+w49nWAC=MOKrVXbtGXuy}Y24C?GRgwFw*bvIONf8aSbT
z96K)6Gh))C<uT+W)GC9W#C at O}y&^<9qED6jo)Z?UAHLN0Run;{NB7caW{OuN$Lzd^
zB*SI1s_3h?^H9{mxxcc&8u#hWJ*@XRY}yYQ3p3RVO|v-F8!8r&Cq);@Pr1FYlhy>T
zZcpyt8$C$LmH6ija6C|tU2Y&OFz~kUGtqh1JA9QBZrwP`o&aQA_tnUec`n`Lpfog)
zJvpG#+AG<tGzY}bvK{uHeP?3x6<oltaM>y2kA*KUHh%yIBJ+_BHq>8i7$+F$)1S7+
z(X|@-m}&Zt9+_nyX{R*a9v65PX=zzt%yn2fRB%ZnQ#g`R6QS|R%CY(!*ooM)<C2g~
z7aN^_NiU0=Rt;DbY8f%+q(V3jPl4JIkhD8{Gq5Hlwe$4F)?k{c((?t(=5=IDIb<Ih
zM(%#vpohh}32?YuqgbBBXk&1N5Z)U{*o~LfA2~<7rXcE5r7L=#?0lN21e_ac8pa>B
z2W4FegFxsbtWeWq+6ViZB_BR~IsRf03`x at Ieo4{Y7az(G)q4_k)TKDVz~+`pm}F%#
zpdKuy$3I6QK1kyp%QY;P<pcxlHg0L3sSpfx%>{O^({qm*qRd!_P_URD2T*|w3-}vV
zP^mP&`rRp1Oxox-Yf;1g1et$)jmg!kj|PR8ji4f8?_BXM{J7U*&&B#9xm(n%8yB1$
z1w(RRPYug_dPrxy#99ia#w+^i+xvufccEZ2C@#ZLG<ZhNKGs77QxUtk`NeD8>8M4>
zH9;pXb5K%0V)Pri*U&MsBYGvyp823G?Cw1l*AUGL^Q=jOO7$E^RrdWOcD$=yEJq at g
zQyDW2i`&=-U%I&I@|psa+-32L_iC`vi>aTvdUPkD#uQyjxgRdnN0$kcQY at ywlNg5`
zp&;W!MevAofjy>&nzjbLC>qMX#yHxGR?h!Oe~N(e6(*U8RM)I5A<M%BE+9LeL#_g`
zTc=DGVbnp17D0ZXmx8=*$@zHwPU4IxQ0|f%<OhDjW!&aGqSV*33K6}%y_Fi^qmXi4
ztBYP4j&{R!`IG_$1jcU9UgM7|s&PPE3eDZSO4AA#dpuEB4N&{LwkqmFV~vUQ!#~U?
zIi`|*jW*X(_7 at G%iK8BT`O=9P|8p|RRqHPID1wZNjg8gioj3o!+$+8|hpJSG*1cm$
zQq39tWGX(c*7LagW3N7(j=Ng>S%kfC_bCBPtpn$IP0yMK+Ffv6i0e}~QPO0LP}0w)
z;cnX@$Qesx5>kTgl>h`eD(>!~i5^&9xQb*S+xalEfWj9_3Ph`Fbi+?-g;9*vkFgi(
ziL4v%<$9I>OPPK9Y$J^2uOqk%T`qQ+LflB?(U-I at iJmItk5R@*)_X#q4Ol8(E2e!n
zC%ut)IKRAA7)4E?Quv at ck=azMrk*nl>jS?U>EP$#uLc=8VsjpU2bKFN!D at NkBuKVj
zs~>JQZXRA1#H-@{O`P?fUA>eQ5E#;1=`~WIZyK+p3zkJ&%%2x^n5lVLUE-Uv*7DxM
zuVb6#v-A=tawPY?#g<*=o~}!Tzi0D3RQ1Jaj3J&aI~xDm<yFyi?Zn(}?Whc`l#3~?
zVCbQQeJ9>WzH at xBRdYv#ufY2A<vL)(RqJqFl{+oblEY8fEvY7c<gSH{na88a`r&-$
zPWF7fyDbjxjyK6Tw%t!7IrN4|9zoNh<bGaVeZ+;PQVbebzm5uIw81vWw{-Phb2*-G
zF{Y_0s;=g%;YapJF|Z4)Q~rGa-j5d!g}N3T4nzFOo$Y!zG*334*6kzMJ50w*g;A8%
z##+;FBrxNMps>uqn&gxUNC95v_r4hw6%A%|hZ~{8W91T*mbQgr&xH`cMA3X)e6DzS
zg+>{Z3L?)F9>2{soql`U|MjTzujE+4|Lt7#k3T0p%RG2yc3n9?k_ln1oqu6_I*+4>
zjL9g0y#NFeZ*^<$^K8ewO)Q*~)ST~%yy at jAd>m#HyJDp4`KfVU<8mtvv%$xh&l90o
zj>u}EhRzs@;KIb6k$m>9KB~4koSa+>rwsJ44?e2pLMe0w+cV81%8+m#SBsleo-S%y
z6${2EZciD0u3>}*+4F3sX4V%{!|Q)jT!whiRwuqBH)%}paiWdXB8#wb?|B$*jA@(i
zs?d_y>2spZWR88s+iK6UkAS at ZL~<MlIy{Yuw&&MpYlOnr*g4yP)XMLldAB=S^A_+%
z9NwpzOw>C6*x+Lcx6`^*nB-RPsPa=e)6HTLU8kxIQ0b2>7CrRw*kDU#P54sUs9?Q&
z_F^TjTzvm^Int!NkNb?8l%Nx*c&D8feR3)ariEmZONja)N=Hk=tjj&En!Hq!6d|YS
zSjm?|-bU?(`Htv_Ni0QMHNN_r(!^lUMk9~DXzh#Rl-$KVRa7Lk;8kp9iBz<~WT*)1
zY9)E)CbinB#+P>N*>j|3RY8ce=UCr;D|l=j!mpMU1c~S$Qan)uBXCN9x7fT81}@`5
zxyc1Hdo&cB&)zqa5C|m+#n_2u73IXCZ<PAy)|hQs<bdI^S)(he92ZulI-;E7nN;HX
zh8ip8^V|7#=R_ncd3NOFSTE5<lB+b+yd?;?chJ$cdRBu+wef2gYU3wu5+~hI8`Cwp
zr&pV5 at z2b;I}_ctdyV2Kpe1v{nbkOZB*(`cd#j3+P-M?}zD7$BZDl)5g^_Vt_R#5>
z__v8ChJz>};$8Kr%q};ivXH9Ui2mAezWgMtSF<w~p>{qzPk=D+dR9x_M5A75e;h7u
z+)f`RveObdak7DHFYgX9XHndAeeA~md|u?V3kS|RviK$$w+|9r{c5zka~EW8&)azF
zitvQTdqLWpa?x_W?jY6Sn`$gE{0;B<f^47;zm(ZKdV444_Y!vQ>AB~O>O9XHQUzhY
z<Y_L{Ol&ot5;qlPM{4&#e?rT_2g|B)?e$_$s+v^hEm_DHv)}s9y|Jf6A}EiN)0bP~
z-<Xc6i%MFfjJ_Q{P)}hW at cOQDDMG01_5Oh^$v8*vC#d~-b|d>CLopRgh?~&sRTk*R
zOE8fiq`bN|e0-YLRn1a?T7<+0<w;`Es1BNdVtvyYeg4Graht=9Xpy@&GvXL3LVd`6
zi?Te*1SR=;7&5Plvh*x!I?3n0BX5I+@$?5G5qLKN*vc_=HcQ}yqg(bQTar2JTAr5f
zJWV%+cHtFK8Q#*cIKst&DtunitH^ncvC at a-NW33M>8*lzi+Cj24pk`ErR~8mgZ-zA
zio$D4+*}G-QDlO>`$9_L?+K<dg9+>K>mMD5iibQNcmc`!FAaM4q{QZ<&?;81ITG8~
zsk?Vv6O`SfnN-cPuVARe%FKRnnHzdO?XAU-!fzvH6e9w7GB+Pi9G5KaUr8j%nZdFj
z53*EpA&2hxh;P^5YEO79>d at IcORW>*EU_+laudHsd{DQRX^pVh2|Eb4J5sIY<4~?Y
zt<;iYb<qqmj!cGCecoY3gvQ8-^aGNa84}vj41imeM&tA<>2ve&FDO1{qhSP!a-g9p
zrwteF-=b7%nn!9d>Qz3hzjmd>(fJbL1?Tvw3d+C4JYJ43>>+mRK6k*rSdqAhoUu(i
z{DW_BKE0Et^=-e5Y&Sjk_^=+Q$j(PyF_<kb0EL|9z7GUtUqu<)SrtK8WJr3CDaC>|
ziCf8F&nD6|3rGU4tEN|*B8*&Ipw?}Lcxik(5d0ANAUT=73H8rg2;6%L#cH<6<?Hm{
zq}vkdCmXr1UX{>Lr at x+lq|*E^PA)^-f5d6C)8QV|y(IsV4|ed3>PLv?mwR^^Aap=5
z at t+c)_?x?tBH6!^3kOeEfX3DC-Gzb1p8$=c0)D*e9PgQIfm_gDq9cse-(Dmhh%&u7
zM?6tkER4o>F6+C*C$G619}`4$pGFVVPei&(<9<njsn?L<L}4GvDC!=(k{O<EfCm7(
z-%pvIXP<kuDh<v%AFm{v=GqO at IyBK^fno#Jb}0jIG|k^eGria|qRWQYNjGv2r*#h&
zG;p;*(ywmTc<VZ#E8R~l-b*J=!6$T8Oz4Soywf%<Y1y%)sa>@`zvlc=^twN?qfVf|
zq5h?B7kP&VovpenKaaU^1iRJ(SbWSQQjE at h1KUF<YW!?MaWqMJqQ4-ZBpp*cY>|=b
z3hzLN4-2Y$*O?$7EU<t*$;`?i9-=X)6Wyu*ZjP{7ac)-*p+vs9B0 at Rpne6G-aZ+Is
zJgz7cid}M8k1eM)%X|}jFvBDot!7P65Q(RnWb>5ek1Cw at C8=U7{|0*_dr!IA)g=Lm
z*y^HhNm38P6^&&~>0v_1!~Aa>;deMMdvcsF`zU~axoAv;O))zT6H__qBt_5Um^c0^
z at f@$QIt}J<6UwLSqK7CdblNE{{>4fFD{}AQMl<Af*HJd;BgRKqkZbR1=ybd5+gnbr
zF0>Z=M3W~$u+-WwA26u&Ci1XH=e0ykA=p*OHPoNT(FJKrU**x8l<6)npbE4{&5?@}
zn4egVGp9S8gY30NFR^DyJ}HQsBCVCYJ&_xqXUSgH>Au1I&a6ePFX0gR!>^8O^DH7q
zjz6YAcr5`GFu%Qha-?P}Oo^Apu7Tyh3;J=8ImWtjn1o*Oz1d~>q``Wnv2z2TzdA>d
z#4cZ5g#Qzv=xJA1M&KKh=yf7VL4)?tI2(t(10AXm{KF&qtIxPB>ip~38}^*@9ajja
zrQzjQs3cax@*j7a9Src*?g+ZmSqgez)JG-T9SP#QxEr0m6G$ARaI;P&KF-fcNC`4o
zwU?DBa6+_Uy;q%3uc?YV!kIa|dD`<(h7yR9e#O<><v3ZTjiyQA_T|7C*UG at b(7^a>
z&%BRVRB!8dds_tUo8l0u7g7mXQ at mUr`TNm4CR>xdbR3v<Z)X+4XGhMz2m!*fw5SyF
z{KSdREy$ZICQ%0!tY{+qlV6gdW at AE;OtF8>-zVr!F4NkLKMhD_VM^pBLLef;r at z_-
zmCgslVpS>poks;3`~LOg3-M^go1CWEXr(&TqN>nyx6SZR&E(@+%4MXc-ZZyMjRS5%
zZEBYz8M_dZU_rZUO}L++ALIm{<#SdAbCRYJZYUZ4OrTiKHiTN~sj0d%`pfdxX~sRX
zQj~ZS_dy+ at P4bP<yKDaHu`!tj-;sTs`S&E0)svP-IQuyMhvyNj6r#wcE_`BT+quo-
zTWMNVUqkb2*Gi%GyO@<oLx}F-tN;iShS~X-V3WqTxxK#34Bnij?cQ*t->!t%YDxeo
zS-hrBvU@<&1`-Jj<HxLb`jO(@Xb4S?lkx~OtYM<t3+hcx;$2rq<$lx at wr?Q*0gJXs
z;B$ACIXY{tHj`G7hfkJ`X$-b^cTAx>#2%L=^VEC#Tt!VZ`*@c}!g4_X=25g<;;DYK
zqbp$yA2WAEj-!Qyyf}^B=yjfgU;yy4#s6)q{+nY)^Iy?#0}gX=|Fn<)$M4;-YQ`5`
zqfh#q>Z%va;orrE9)I6HXwtgu(X75ta`_1oY7&0<27nmCM&VPi6CPh0du;yK)$ld7
z at w9XBCOqJ1)sm%<7DFKG4jVbX-HKQX{`&hp#bWnc5Cm1!L5!xVlNPc;RqS!2;{AL-
zK`y?Rl-c7a at Oc!k6NgJ|9DP_87TaL$sju21zRM#hh?sbrECInan_s~-U at _Zp{M`mZ
zNB=o>azu=@HAbv#=*}VTKH=b3Jp=yf_yXs{07M|0J<Z0AB(GSCxO=FGi2bmYol)-i
z8w?j!H5oSzkZz3q<iX$l!~GcmLm|w1Waa){&&#(+OHacbx7O}}BUhZv4U5(Gsbm2K
zXQ*A5_QTLGAi;9RPtZHS3)#I3I1mPZzxKQRUs(q<)3VXdaC=w%6Et=<o#HTG65Gjs
zDnVJ`!X46J*+brm-QtzvoX-D)VAl+kos-S#v;85IPRFGU;_Xl!6s7O)76dJ-g#g1z
zRcaSbbNTRmVr_hNGBoLM+eswK9VOXlhnQ(3$HbMu!yt2!rXC at DTZvs9@B2sC2fXca
zXu4FA<#a-gtFqDXE at a=Ha<U1S!iY}iz-~yxXY0e&Fu#JY?=xcf*wG|L#*>WKJ*0~2
zj<#x>%A<7wIwmC(kUrRGbIKcwS;RYI$j;X?-kXaLJ2)l#fi;yinq#7b>^16QtpcGf
zn(S(8v;fM5=sHEkj7fYquCtYS{&yZ#)<I73Uix_6(-L3*6>RI1E_PcT30U at -nOsU*
z7R^>8^`L{g%O3^w0-N?z at NVp78y6NI+Aup)Nf{fU>`*F at BgOk`Hj*FkvNA*qAvl=k
zR7z6s*$rIqxeaH-zg{0G)BR8~b#TJM)Y8*TSg>T)ZdG_8aF}*hs=1k+_$tc*^LRqG
zw1UV!2MX%5$Fq?f=lR&>c;f2D_-)*en|f6CHUnw(${@EJEJS~WGvb`sx$1D_hJ#P|
zuElH2%rK+11`11%rxqmmlh(zQPOce|M1B#^0(dbdF;srM+D#1q3Y(4GQd1S0cY;M`
zB_OS8c|vsK^(t`^km9EnGv-o_q%b8}we{TN3#{e#L|v9aYnLESSzf|V2$y&vL|Fyl
z==cdbXV`1GIwF1Wqm?Yws!(nJ+dE`&lURplS`DLzf!g5X?OAPII_v9mEUN~s&<E&x
zn(amdo9nEsw(TMG1oy1LfSURrCB2;Mkr>+FoIPkYtgRY_iautFI^Bu1MlmBeQA8<q
zteZWbaQyluS|suM8F?`zxvU3d3XXQgLPym4y5Ll3aTA%=gmPn)<tYfAHI)&@tF~4*
zg++sOoxh4g*3BO)sB807OC-0;%?@tqYizKX6<9qX39L^xlI!)U-lWAUf6?=En~SMK
zt!?1`GeYLu at brl8@*gt91FmTjOcuy_$Kh{+o!^VYoSm<QLs7KoF70xAE^0o+-{-9}
zIdC`dG9tfFwfPjdB?m)1f)&Xkd((M6J;SWkVVbWefw90aN5+geqW!FOi0_A(emzlQ
z1-?;Dm|uP2T89zJQ}MP<Rjb(HoL`{Wj$HE^36>(3ZcR~JFB5%47(Tb1>93P^48Ck9
zE6AZM(Z(uINa at lTVSH2TNUFg6dD^@hc_DQhQLi%ngoBPZ-xs5K!jPW!G)Y*kLwLeO
zdDv^4d~?CV;8QG#x{-c4fzQ|oArxU%A)NiFe#cN%w>B`}6~7gQq#y#&X%T at -ob=Ry
z7c-)1>pB+r&2x at IA9|~OHAU|d-WCDr-oU3c_rsO&S!+$Q9#nDo=>Z*Ce$lV*BsQI3
zJybkNw4Barnh3FR>7LW3=DbGP*cBgHQ~nYxlkb`fc}wtOqSK!F9K~1bJBp$QRxLYy
zwHe^DL^YpLJ8xhu>qdZE7Ng;WDWFWt{T4iZyQ|sMv=uf0-G{wM$!^OXyN^bj*evBw
z`aY3ot4zsS!J6PjluG|Ac%&W*GJ*z&yk)T>b3^4b>Qo_4R|6cH;!T)T<xLb*@R`bF
z>Ic)`Vn>=PNq>$lu?GHo(!xU}Ap8ieUAfw``3cGx0$hlX%)f3!R84`?@TTT?%fUG5
zJxDdh!5uYVFqL#d!^vZIa=!C5&Fj};{=T34HdYcrd(_1vhYKyYsQaWhouogkBmQgv
zse=B2rKp<ow*%r$4G|hm%tA1|8CgDlDiZjehT$|j*8n;I{tEqW9sPkrkU&{gdfGt<
zd4Rp?T@=~k;D9v<X6DApZ+<_TgmwjE#KP at P*_rXX`c+>jkrx)!yn=d-eA8923r_w0
zWw|(Walx;ZwTN`C*87qWfGP$Fd|LvUk7=a+`NDssRJl%q{=|wNJblH$zFQWGxJt2w
z&vn0&1F}(2|7hcU6TRSVVKJ~(SerQ7icy)0_5EE`xLA|=zgT!B<0=Izt$`>Z2LO_K
z>U~!K*aA&t{#Ks%qWauodfH#3>3qvi1Mj=7Db1K~#M0&Wkv&1eOF%0BPrWGiXX7tI
z`Tv;G$D{I5vnrRnV1K<9iC5cS4I$RT{ZF5A4E6LE^0>RCqt*l74VEh%NmR;-BQ>7J
z^!@jtc6Gj~a<UM9Qf&1B7a|$dO(w|&gZen=vVJ+4l9OW^Qj$+31d#)SZ_PqH!Zui4
z+ac!fUXoK#B!z`oVnrl`pf-4Vx?Tc;H1cQIB>@+6Rk%uvg|tEG5gwdecj-iD;SS-M
z(KnrOHK~$>9(~6-p_23lAKn$_HK6%l9I4PvJkTZudKu~PGs^uy-1)AMRb}VepcDKS
zXItr6H#%Gv^1>|9DEXu=dG*%`Yiq0Zdbg5Oa5V-gAJUcFIVYQ3`|U}}TANe8XC?-;
zb=>+IdvQWvf9gi<?#uG}k&Td at 8hrpP5EIL5vnc6*5<0E*)Qf1(dW99E at 0m0Lg?>!(
zD2p>G&TM0E1(0J>m;x^R7aNz0k8x!qxTOffUN6~Ag*m{Q9h4q`-1i)&3|qKs2IoV2
zyzvq-WrQUs*@@we^X-Lym%<09me8t|+Nb!JV?@Rcy at +%O&D1auDIkR#3k{8&4KEpM
z<YKp>BSHq*bs_2oF)x#2ZFBUHL;V^3Y-=5-07qUO@!mUO`s6YT)#(F5iy@<*AZOm@
zK}R|SP-^E?82fR at 1BN&yrYPO`Ec~WEsE+LJI%^QoNvqpRtK-Ou2k+I3ci*j at dmETp
z!qH0{*;>tC(rA3+m5P+E#74+hEUCTha=FxOr3X$+_`iinRFWPA#B&6RJe%WRl^5<0
zQGO}XD(2wV6tl8d5S-7;K=@j)CRKv2H*qMM<>Co(2lp at W--LZKwY7xtpTJfedGjza
zbyXlxbw$fptK~c|w^i^RYcteoyVh|CdPb&>gIueqA+v#h%;{xR`z^ZA>s>gfGm+w2
zlX+yVg^!93uzq;8h`pSPokVb#Cr45)z*f+sfGlwO;VRRrqB<L1B<KdkyMqnkJr?>=
z4GfCY%gS3-hQQ)#w?j%Py{9EfUA%r~{Jr4{wV3vun>(d)6V)(g2NP=LF_PSgi|FM6
zi$-mDx8Yw$@QS<6oYInu_)@bVFg253Q)jbfQj at QC7FCTKYi><H*la>RJ7v&sBSk|I
z>6()fUEz&z5vl0mv6qm3nB7kd^P{bJyS)4?{<&|f33%0zk4IrrOwN0qmQO(84yC~M
z;`6&r^{i#RX!d*r at Tr_^%6_9RZFw9LzSW7>6#o3Q3Czw8{#}dg_xw+^?}S8qWEiak
zPPyay*+|%@NX?e6%|yL&T3Y3y<x08=Ty2!<Dp}g-p7!DsOG-Qoph^=~zWfQonHSET
z^UPq#34QGcX+!3f{DzNmFEL_^&W6%*z-#^!r2Pq4nFvP9>L+Ge&m?sPD(G`(@bM6=
zpWHjlhgDyqe*-5rO at tW=ol_d1oHwtA0|N&MzXxGDXOdR}V(fTbzM#9JEf&Gd!zmre
zd(PnqU3O#`jn_yJW9mvD>48qSaSq55dGINE3abUr=xX7|oz07*W{P({JFijS^kWM4
z5*ptz$lYh3&&f|68=!t$j7ZbpkxzEXX8*MXSu4o8T8z7Fa?4FwdH at ur(%Q|*Nd<5b
zT}q=PL+5}SH!lChDv7v9101XXm;cZ9Uo$WLh9P?FGzS)*zv_biz3cfuNx?%U<N#>!
zhe`mR6!H_4NnV14gxHA^$xJKoPZ*cx835xVqN)6u=*BNg at 0(agJ(%^z`a=5K*5~qJ
z at Ax&%O7rW96(J9>Tb&OEVIY~0yMK7r>@>ON0Ri%Gy>T&j*!yUFOu)&q4&L+;Z_~aw
zP}H2;PmC+F at h<EkPG<jVW at Q-TVM76zQtt0NivAO%1)URHz4H<FreeRgM)iQD2Ua!w
z*_Kjhc^jPsLU5jU;r`Qa4(fhdfsiF`*D+J+3 at 5rAiEc*mUd|Fc{pl)vs at k%$5QU03
z)o5~43|6ghajt!qy&<*6(}(m2qRA;gxdOexiu&$Zm5x_fI6WYk=>YwG%33SWY_sU4
zo<dzYH`cPBpyHSt3zu+gQARXdF<#}w#u!&au{ei9?GZaCa<T2jnydH8Td{(xP%*x#
zepNr at jO{$L5b3inUSM`ExIne)%jsDp=}nL-O?29dQ+y2+NyMcf<4z8}bN*qbU(1<y
z1{atNxAfuTmoKF-{x6#PzlN)f4C2zTJAoY*u*Y;I97+z(_sW(>jLmJHNYE~@&k-j@
z!WjC1j^uDjD4UFH*`J!%88n}aXxOt1ceVi$n`+rfx at tmV^D7;iB?jKz_YMnHe$0|-
zvHlq02?v7MU#d!86Qe8F)KBgtPEjyx^@GXZ!EI at 8z?xp*@2R{E*5PR9HWc=7KH|L|
zl#anvW|CL13uUlWtzy3&&Vb|-i}hDa1xee^zeJ<hfJ<oI+$menFNrI1SX<!X3F8%J
z%RWV;R9y}d)poZ<CpOc%Z~X}xOx;~NaKB$pA-?JgOG%{qY}Km)cE-nh%L#IK%AJ>T
z%h|s$XIYkUgN-X;Yn&kJ&2gnsce)zUjJq9CoVY5u;-i`8nXDJlIGUHE4`G0cjUcA3
zn5af>67^J53Z|xH-$ta5x;aFM)~J7$%DEmhYhn^_kiXxISKSO?BLmmS>7RZ)&4XQS
z7J4Pi=lx`lm&=VZBYjS1B!XSN?H!8IWj-q*ZHOH5g2tj)#oBY~zDQfQV>BKBXi=5g
z(=n}D`*Ldt%R|Q=4NWeiOU+*GwcMY{89;vF$DxsI#>W8^AT#Y_@{d!(7w?|;dRI3<
zB9g2PfN&v+iZw?hM#x1(-It9(k at 4+q`-N24l at 2T6`_$LGT}E#?qM3OjN+H>4GF5do
z!OP;feF3o>BK-lhH+B(nlB@&YuK4%~d$Fgv;_W}QT6(Nb^}k(Fq)l?QPB*hAmw0Oz
zD25NKYMr#!L?QWk$k>p`K5YYBPAbz05RBca at l)&oUZZ9x?uAxhga5H4o}U24i0&2O
zqz!cR6g>b)>P!GhT^k^&*HH+BS*k!5vMVZ5_eBhesEzdNhCzK at WJEP_duH^ih+TC$
zsj6eOB%g~H7yvf2=gNGECAq{!<Q#p~9ki_gzyhw98sM}xRK*zdon&Y6V`d31m&*?=
zQR=wWE#1GlHfQ-F23uqZFz<V+WfP4*X?o0xSVzs*$C`~=!IGd;q;-E$5bU)|51(JA
zey&q4nt@?23&MlW1vr?Rzo%E9PBAg}J_BYiz at b?O_Pjx6FtUY<b8RkQbY747WjSVn
z&tb*zEIqn;C7PK&PYT$=8gJrm?8WLmtd^CmDXiE0TRFK%0D>^6$ionluf^81h{P&0
zrEU5XG^N7Y1S2&zm)Jh^+!>D{dMHA=oC>=DNWQ4;YAEux`OawmogBZtM6aT!<PO3a
zHp{NNW895ATiJPOvIGr+K7JshC^|8Mv?GwU7`l2X8Mhql^0EnIS_Gd0C6DavMB at KI
zRR;AoonFU!nL>(KWAlv7kxcq%7!kQPBBV`<g|lbda+}_>_YoHrsD?3fKr`CA?-1Wk
z-BLq;N;FQcw^x_4j at B%Ek5ShPLP2X8E;9Eb0djI9&uV}bNjNquK^{iYf+d>NP!N{R
zv~zlkma9FE2Oo%RC9edK at OZiiK+MN@@52B4Qo9a5<`?~D{xi9qmZih{oZejTY7hOv
zhZbm><5$zMK+}=H^Mn4EZT~j!9)~1>X|=<v&AaQkAc00!-xSUzb`u}te?_JyScnBm
z2l1$S)#jlpww3(o!1DPJEYGYkxk)gDyG#}Glsyc>`?+Uc^^neVv4AjNXws|jS;y_0
zJjsu5Q}@?YgfUf&oMD{NVfMxgEC{T#GG~VhwGKFH)t)jW(5ayiyu|Wc#U*9%3e4pv
ztc!nB7YNDWPQD1qO+t9vi%14FV+KOM2o68v6)D!_Pq52tGsH>*&%9pcG(M>o?Crfu
zM?B81pK6rB&S|<I%1OR;AAXQBXg=#6cSVTm#c%DRnAeqC=VJXdWs_MI-p}raqqkfR
z>l?_2v at ed&iJga*#L7=6l^ud(u1`O8&Z+;sn<X{nRylG`n<2#7M%sIK#3HI_)4uH^
z1+mA(vxD<{G#k}v`WV%Cspc65);lfgF&N>7;gnM(6WcuBo*Bv#SC>x4q>wxHYJ4_Y
zEi?WxtnmU35D{}Znf0N=i6*XdeQrY0;y!^+mCKk;;rHnwhPbv#edh8FynULX_uM5X
z^J!`r3vIk!0E%1q5Y}Gftna7?s~)Ade4<*%P+CU)fW!HwG9wx<%a%;gp$fZg1h4Pl
zD*vkFl}CxU at SV!lB<?ec;xywVi|+n}7tEX^-U_Pp&pH!jqW7F at p6Y=0vpK>z<%?p%
z`a+`-a^wo;EXIgQ&arf4j|W_1rRYRo5BLe~GF|4SlZ&=Qk7!S8N`Rx0wK%ub2Z&JM
zsbKOriKe9JX%(kfzX5EEfn5g5PzfU2GIb>iGOV6Kc(3a8ddtcKg5-D%?3#wEaJpjz
zs%;cbz6!2F_Pow|t3>lC^XnOM at cDWZ2ca@ldz`c>nW3-vlNgKJfQOXy0jxmeuME<_
z?$w;Oj>tz;48KdGuC{UXs)TJqxv?Sm+MCj>RGSgjPe>kwJdvIk2E9H!^SbuEXVzh@
zDC8qEg8ZDd9B-QY=nP%ys}si$etZWu&frhSFcwD?yjMV&t&jbH-TOPfrT4-3#!rxN
zU{%p0GfFh&cd*y*=-|h;Jp8XO;E^!Y#vO>9uYKHd;1#oYY3Q?>icqayeYzZB`feuf
z_T5WcSr?GF(nBvV!z!FyfrWy3_oQ?*$GoZfGkpY<xo&%Ry9LTG<Ish(3nh2U#jamn
zJEiX;XwU?EWOLJ}Sot{AMRZ<|QPJM0Z>f7xAe@%ay!4gqkg1%Ye~+U&)`sh(FthI-
zbCqvd#m6CcCpSlr$@Y~i)?Tt!Z;c9M at A^ww;qAPu)|-lkJC)`%Oqt}sEx$B<S%g)Z
z<D6WL{kR8_`+7MNhd?6#L-&aG=pDtt1;xs8r^o at 9mH`_0u_~jT<f(no$ONmWfOuGR
z`xF`KO;Df7ZA at QbG@vsOeYw<}g!DN%wL#)W`$!<xYD^H at q^u^@!#q2JS+m?MTzp=<
zZIy_G&tmB>X$#?hbB8@<Gn$XWX?bwc8`VYQT{m at nk0$OswuDM`w?&Yw7Qup29PVE3
zmAN)+Y~dGXO=IFo{w0i&mH!PQHhBzt6UQ{b_&}y1al0D&z*a5-Qs%ZwD7!<d4mteX
z6H{f??J#kmG3qKOTw9xnSoLj+l28xe16-oWijg_8eFdt^@XzyXHL8!y<2;AI?>UA^
zHsm~5DdFbfq-cygqUb!&7qjY#{My<hyIlf++0bH*x-UQ5+xXy;^1NF`&1xaC>!!&#
zZ4RDM(5^{AmLdpHv=;@n_6{R$6!euOYJv**!kPxJFNR3=tUg!C)P-e-lJJ}zsIbYQ
zn`<_Pytt2+AwL6z19rv9rmL}_G>3<myO;E<0-k?#Cw~Q}ks0m70<5M{aXk-BuLLeR
zfz^iUk at +Y5$KyE8BeK at 7pc8dE^gn%29+3hk9&-dI{<+#eS07?{5V%Ua{h at b{folGn
z|9=+*Y~-M*Gqd7U0;%GJtDoW+9&(Sueo{lt`4)8*=DgE49fxcd@?CYnysp>R>tUUl
zi>s$rNQW=y62mM~S9=9q?q(1+ at g<J*zSwyr2V}0Gp{W;CRidke9k4G&eo+ah9-%MT
zo9{Sq at bQO8@%#YgE`4L#OI!G*ftXRFivvMIM>UT8Z$;AVwWo6yOrG_4+UZ1LZ4!83
zE5jb?#Obk}=pC_&RFfsTGKvOUW=TI+EGV!4-4Bll#Q$QC`mc1vBk0v*#f7R}C>p|+
zZ<k5;-47~kBDOh4n<_-HWM0att#+D}@opY#v^=p>rAH*x5`jF<PyAt5>)Pl?>PV*8
zc0QVhwAZT))*?gL&X8Vow=b*XFSZFDv~Ca|K!hq%C)%f{9eVz{>852yeex6ZfRXZo
zVh%npFO3Q3^^=%h8u3G+{g+L%@xmnNPu0zD;)e2E1Md_rUMMFbmkmCN at l%c^6d$%r
zg}7+z%#3 at R9{@BvX9Km~@cC6Sik~3Y8d3K%-!2)f2a4vpkKpnU$v_TNx at PymaRw)O
z1pF&DSr1gv0o}%i4`MZw(|c_asm7u{`lv08RKyRO at T%i%H#HSA!MZg$R(Sysx9qJX
zvQs(yO{bH-JPQK;%x2PtCM%O;t*`V6IRUeHh&1+vb>Cfgg$fQYY4 at zFJ15`2r5D9Q
z1`9YL!diin&w9WP5$#F#sg!AAX;SR$YmG6a3$UvnMVZbs+ECn$A`vu6Bd0?PJW5Ie
zW7B7wF3e9yEWXgUKR11XWuOeQm36=FqKqh=TR2d4PV}mEDb!kG5|b+H+ORZYe47={
z6!C64X^%S;Cy{R}y65_i&<-V1d{zT1AEn{LYsA&dB4@>encA{G7YI(gIf+ZhcJT<)
zAu~J{TBCzp0j(j6NKNC^7lxd=DTcR!6Y^1S!(XxocEAMkZrKXL_|S4|T_q_uM5xVt
zxh%Sj4QP~f7AkvIm5OMvFM+?O!x4B`N*9>>%JBTh!Q6|8NSEi`=)^o%g&YD4`Nmas
z<5o_ei5HAUXhgB)1pC#JJ=~RJ7njH9T_&vN_tj03(^tS$)H{>5oNV?L?>KV%vUkTc
z2lvOJhuc`HrHGESuklMPawTypSzEsS1WkJ%oOxfq0XQ^?dOy4m1g^fd+>#Rrv+n*4
zwItgAL3>+C&A+_!U^B}a)k<>%viqoQYZXz;XkR{0ijWf;VH0~Z2~I`E*XtCpyx1|u
ztC$716XnS=x-G5XWk5}hY1EfC<nFG?W;jR2?bgxtoZ_>gP8Xp(5H+?;yiyd5<N<#i
zo_-=nc5}_ at UlJBfOF5w&j*hNrK at iq1N&xEyeg{&|rvkZ*7-*ENi!)RcKX*);j7Hja
zOn7}Rs3<Co5;2}qT~@EXZ<ZPShafL@!sCR6kIa8be8PSldV1{h|4ViI2UcdhOJ6kD
z5Aq at A29fA|B0}D9#~%vJXI>UA>y#{wx3Mafy$l?o0YTY?Pvxxh5Qa=opjLUAMtKlb
zylb@*N=|jZB0 at 8NCt_&l?$}Z(w(W*HHb6B9KZ)PBc`<3rmEo1~pttQ%-`!+xSF)_J
zPcStcZ)NeCToWFh!tg~194F{3xM{7#{EL=+c|{AshbTM`$j7P3<V`CtC{E5zG@&9j
zochw3LY<{O=kk4=oA<nNN7NsqT4A0CwZWp7 at D*Q`NJ&q|U5l3()rT12oYkzMlXiaV
z1E1RU<PgIYjO;6Q7 at N{hxHhlLsIrd4V(pK{KkSC081gz?v3=pYIxf18GC=LnWZSBp
zx26pFfb848!wF7p5~Zn7hdeW9*(=+v;nruu8(deh`tj7cFm&@aW~E7kz$DMcFl%|f
z^MibO?YVJHR4e975H<=Y0tMM+`je5o#l44DqtQ}(C-i3=aWk;2LC*HbAZB#D5m^AR
z{Z!q8!-YQoTi}f>hIAhajzRpBJi-LOl+LRji#aV%>$^@()eH|~;*u(8`7yfwBYfJ>
z@(nJ|R+vx+xnNX?P~18_e><OKzqzJ*Pii^q3&0_VN5RK4iZU$Y4jDnPI6>Wzp--|5
z--8NEaeZFihJcT-rv|V_`8b5 at CZ9JqH8dqW?fr at pg%Z}ydIiJw_Mt<)>9jiobwZ5q
zs9V~$Q&7EI6pN|bl~~3?PuOc-u{4EQ*x6Tn8A`QBg_j6sf;<p`BSS}s{=UJSADnun
z(_2?F34RgOZSRv$7GaIfd!|!uH^c3fSF)^`)P%Dj!zf$9wvt-G1IN{VgbzRjXGfs{
z_bSxC9k2CgpX}e52!DclsXo#?syd+RWCm-#?V~8R%bBWVVFFP<%K-1>fFKGQX{V89
zE0?$vKo0d60^Jb+cgE%xdG9Ca8)n+ZAAz_ZU;>VNIKX at U14-^I>j4v$@WJoa`vErZ
z+aGW&fG!Om2B5e+frQu4N<Yj1G_1cU0PAT^1L&DAA6P-Z5eyw{4-&;^PQctb5pmOH
z)HYR=Na(Lb9VU&u45zES*T}|Nb{X=1f>tHH!~O_^{)2Dt4~SK}P?q<op4?ARE8zbp
z5O{nO1b<o~CQ2P-r!PM>O=(n1z0i0S8=3U}`-d33-kqpOu}M<N(cl04|NYm^$5n0q
zRu8iW0NPa#F965>T+1bf1}^!&3~4*#0(~U{$k!>2x=!xXQ?9F?W?HZDosR2}cjX7{
z?{)jf`|9`kkx`!V6|&o)^kuc>N)>8I*+3U&K&sH5T$1 at QdUlbX0!qU)x^o8?x_cA*
zio5cN)>v8H=d{VG3Q|tJy*=)J_ghjC_>JHXPdh at LU<SDESyT at VPPcL<U6{WG*ldST
zQZ`QF$kE;A_d=IyEq(JsJ~+rIn&Vjxz|30gR@=f`_2S)Ob;Nw8R0TvO-jkd8iCr62
zEq>nR*Y at MP2p&5H##6|=&Ao>hFjj)M7i3N=!BNC+GDXlhv-?lphNkXkZ%}aI_m&LM
zYrUzg^qc2<+nG&Gp*Zt08Ok at +eQZ%Dte%$T&!chd_mPPI3qAKYA1w(OI5GS)fmYLL
z7&5;c!|q!kf|F+mj)2e<mb7DbX0(DxAi{rc>F*l%f5)Q-g8cu1(GB(34ZOpaVj}@W
z!y=T+?O&BFAltt8p?oc;>jMo00sw^F<4O7pMfdwy{jID1i040=38as1_7CvIqql|F
zqj3!2>i;j#=fCDAe)sJDK1?72e``jAb^(aVe}jMdpV8HiJ)n=?^wVBVC3`Gp1fLWv
zoSDV1D+&u2pVLVafuOd1xc@@@0G!}|uTbAFD8+Bwjk26cy2E!8F4qGM9#+?pRu|dI
zOCORr`*!k+^#0RF-S4j9$7`O(En01MN4j~rRKaWyxn^f^iLFloA~PaY at c%!?0{Y|V
z(GJ^@jYN{*WM+s`d9+w{tcvOy(p&je5IWmGWYSZA+jtP67rvhYaLWOdK&<z<92yWJ
z>G0mI$+M`PM=Q#cF}5q711tL0<xCpXk)1_*Qh#k8wKlCtxJqK2w$^2=W%7~GX}s9=
zkmnC%tDDX~<F9^|$l5_8p6 at l+;+{KJYrP!oCZIiP#c*`=L+r(mdb4N<^9Wp0ZU2HS
z{-<+ho|`V+$)3O~!YF&TUdv;jdDE(9 at um3D<nxzSJ%NCZXwjRgqmK=WmC`ps<NTt`
z>Zx@<UOJ-Xh^rWXJq=ZZVgCs-c~!WC;)`pu4h2>jawoJ~|AIaU!leg?L`~+u_7;v9
zj$3PFbIQ@$yRewHBCb=w@;kyF&2QBgI(V>Cu_|d~vvGqs&);h6Xq6kVb)EAtl7(V=
zpq#=B5R_(@gm(itslV~w{9o*S1z1(t`u?F)5F{m)E~UGX66xjuD&5@(hY%E`5s>a~
z=}=Hwy7kcA-TmLp+_`t~*4*FB+~?l^9i9iy-ki19UTf{OSG?c*y<b at OKOYxwAX(q9
zs5lZiYcdi&dqnayPRz}Dqt8U2VedRM7Ldc7_n?K#09aW6;O_sXQTrxvcC$dnVnwDD
zPwT^0qXofQjj12|-cj<`5cVpDpM+}t!1wzf8E*f{gTnl|lUgC~5<7{t^ef>(y%^=v
zkVh2(np!h3O1BMe%l{8g?yxtE7i*|YqCIEL(aHLCRW;vC1-GchJ!)KR6G(aQPW%;M
z`4fWsOFq%R<I?;Ux8|2*^52jKkuUF_6FGfQggff|3$)@yBTG$6Jq>L_eWY^BMl%1Y
zks_Bp#mk=Uijk4#DgtkzX&uN;pLusZN)8~K?UFV8ZkA^c4%kH1Lgqf1z>Ven12B#D
zKjgcvWI()<>~y~xn9pVaocreKpN-4^_TuI#uQH20j6jnWI^PYDDVYnJ^II93R?)M3
zY`Y5<Gs}QQ at BQGt6{RagopS3gy;s!-LsH92qJ~Ih1^NCrw#Ne(5zBTQDAM~TgJdaW
zJzftLHOCyKs<5as+l~~{AGtYOR9*T?Idb?qkEKKmt6yqy-WAGyek56^=f$3r#lVnb
zc<*hH4~D(`uX=H=`#dJYadyrLG_f-2)q*$IN1^%&q-ns1@};D~voBK5?2xB at pFXOn
zf6x*~`z=&S<!<*NQwrB<BXg^8or0i9+va_t5idrQn6*5U!<hV_ZUUoE?YH`0P1^~f
z_GR5?G{eQA4{6j({<AmRPhM^RX`jE`%kF!N>+g21qTEeV|B$@^kDch<DJsaac?bI0
zF!nR~)}{;Gs;m-<BP3GrOh$Qt-9l>aB293s&wf?%e)Is!gKY5F5Pj3TdqTU-SV2iG
zy*vVC-s6e9AfL%eB9r>+`%;lS&~!M#3V|QZn=pX-A)@)pBH>*ffy>@|JSHbBLZ0fS
zJVMM%qXP6`zJ8l-oYHc9x}#bB$j~jiODe&JBn)w#yqM at Wc6oM6CO!I5p1Wk$){1?7
z2(|Q4!GrQ235TeLG_c8gc$Pbp@@#C6LsEvJzv at +GxGBR0xGvnFGT!^YDd_$y8=~JL
zE!$1|=Z!_A`On<p|Al?puPRoz?cs)`zP+VMV^w<R5auy5`GwSezIqIqVR^JiW{{Z%
z?P^=exh&`Bap<?UJ7EL*4naZz9i3#&k-~U?YwqYBx(A8aoUpe=^i;e8G2J;1yKr~+
z-cp;6rSYZ?_N5FYl+WQ{GWr^curQI#r}}bd+XlCz{piG(?3p-VJ^c%a3i?-%)&8;e
z!9P at OyB1mdPxv(g;7tmq&<i(h$g<5yqMn(0^oZ8Q%NXa|W384}IP68Y?|i%yl$^by
zc5}J at p8>*NJH*@o0&Z^Fe+!`ccL(gQ-DSTA;=Tvr{wTyxc7y+wmSC=Zg{oooJd7_v
z4RQlOEkc?#$~Jd6RbDyj>)CP5D984vOMEv^%Bz5s)2~aD<ua!|cvuYmIC%RK&AaYk
z4T!ds^+Kj39wi4XV>UG<_5sqi59s}^KCO at bGG(<8ZPUAc8z1TNRFy2)M2(~<+dOY|
zr1-f#Nizd{W*{-ZQ}SzHWkpk407fg72OJ)pBl@>`QcdC0BO<!;nzpIE=v{o9)sjg4
z2_I4<j;PV|XCfi(+^=9-(A at Zo=ve*QtWmn5hvBnI=jbCU1HI3Ls at u%G)1x}s=CzO9
z9P<S8^$JSK$;Zjbr``L!<I7de!JXz`yDDRt$0aZZ`nhc|vn1Wh{<54i*700ZEs0sY
zSUl!l`LYFU#6BYTe&iboHa5Q_ZfSk1d4N(HXIZ8W0Tl%*Ctu1Alm5>j|33zj|0v_y
zuYMfg)3m=EP&x4YY;O&>7IW11ci+(oE+#@$eyxWD?>Mi3*z&tAtrh>$f&ZKC;d<1(
zhW0^JhnkPi)r#z>1}oLV*jU@)S`damRQA|9Z{m3|m4Al-X3rHUZ0?|_NoE`4%`-8X
zJChx2JYTLrQxu`S93z0EoDqE02xEPeMk?MObu*DVRXT-toA7Y1CNz07sb|R^2KTdn
zV~^MZv&Z|!r7<9`5-6(6To8G%BJr`zTI}Y4ItYs>N737RSk_@*0V(Ww?DL2qX+=du
z5BQa7LOzP76ah&If&*tVhQpgT+|=gr(*qI(!r{y)gmVjp@<c9fTg{v!rBG>jOSEWx
zhT9mD7H~SZ5+u;L at 7Zxl-py@$;)Q at rC?nFhQBFi%QSPeCX at 8G3Oil`}gfuE&)>%cu
z`JSqTGz~}vFJ>eruiMxWp<NdxP#B0tt8jl9c$}ZFDRMch{BI04k5EoYrp}mj3(iug
zhAq<Q<=>KeOuU_YcHUGqZvrR!CobK81M>6VR7T_n)8Kf`6g}ba$McSaB;w-P+5+7X
z{3(o`Z=*zJbmRyyEqp^QRKIk_U%)Bsoq~06H9fH90^7cBXD@%;hsnKG<Cm7vNTMkj
zawK6E$<(=snCn6}*dr9wwPQ_Z_i6w{QQGPEE~^`MIa<zXfxhkum{N4qPwuAM&sgKN
zn6pQk*UdgMay&C=qd=k{D0qr$a=Msp&9{6S^R=G<iPXKVdd}AmiG5_9wEZxU;Wc3D
zepN4{Um#|iYfffbAv`vBrczu-?)`~i%~z)a77yqr&oygp_m*lt2sx=?HG3$s_0G10
zcpzsEQMu|^JfkvvhrP+$LMA$;+~=Nf$l*jAK at dSZ#ApJW7vhEC<3*VkNPEpS{p0sH
zZiAeK0P29bD6!T#b2>tJ(zFa9V%&7pIyG1(O$%GtXD0ONBmIjlQMU`XRTX7%;_Z56
za+mSvQT`&N2a=YP$=(F+b&BUaT{!{)XH9FpH2{i0UzoY^VYbvqxW&bg0{vys%-h}U
zuU=-uAmyR(WVsq~rLKN%5PF1-dHAuhp|igJi>m0du`;R5N2PCRd<XB>#nEbk$`zkD
zsVMb`<`7aQ=y{Q;aG(uZVAE2tO>~Ci2+s*^uw3Gj^!A}gAV^qe)t#`<qh}X(`5x&{
zNUEQ6ALZjk+d?-j$|I#iS=AR34tuLoh*3c at M9LOHI_F~naw`Ta)n{%neVFnuBJF<T
z=KQ0J{?#iATi6|3F^CvVZlLHFF0KAF9UbfVt`~GtEV*ndnsCO70t2*<@Hn|S5(qws
zXy1{S3d6LXNz#$ncL^ps88#P?J5umAE~_Xvaej~Io`po;A`gS%Mmn3Z!<l{&HFMTk
z^3^e at 0J4>Ye{df_xc#(3maW-GtyBW$2<Ls9JT;#7c=73zW>TP!gBue+OXxHpQevJv
z^u=i-3~%a!JMan2eXC*l{0J008gWzqGhmGW1SsX-Q3luE79B|oPAxm;&PYBl*TxwL
zth1+T`!SayuXR8{CgtmS2=x<ZwYlVHHW1X+bLE*^*5J|X`k=AbHrV6?&-~z4)*_Eb
zaI3s3;;ZANf-8r^=Xk=FXj|m1RoP+Ihg88ZHjI`ni&N#Vhh)mP!NhYr`6~Z1F7~hD
zXn&-~`Ewhq8TkUtK5T$IF)?}>8UYBZ0z}DB6v)~p;C~?5 at z<p0e at CzTpS_2gyBY1K
zetGcbIPo2_apeT!(z%dngEIw+A!EV_m%_K9b*Nj<0fMWQ&<+&TSI~`g$$!nstSi1C
zqk7EvG<fvLa_vjd<zspV4@$6v5>aOfT7B$;>}b8VApKl{vTJ<O`J1)=BGK-A!bWv~
z2Bre)hENhDlV;578O=teMScfs<BMSs=^RX1hzq>!2w;ZGd6vZR`em3@#Jx)@9$b$9
z2oU-I-~OSx2|WHP(<}@69q$f3IS^?^66hcWzNR*0L|`{EVoX)IlV=~UCeLTUeWyX;
z^CSB=Dptr>Ad4#y*5S&*nv$kg&D)7dkkz{v`9q=&6Hg(^<7dxqp at Q`C()<)~g&RTo
zIH!N{GegC{>(+5EX;RG6&Nh|;Zn4dXguugfemW2gZe#O^+kjW+Zf8i0fSHirLki#I
z$sg9*9O0{eQ6oaY7xfA~h?iL)7^In&qcQ%tm#VN2cJbjX38ik2P&mnJffCu%Uu6sX
zx0|~ELzn=+q}~jiLi-!ynfzl~rH}SadD$S1p36r}%Bx&V=K757IPHjEheP1_ZsXk9
zY{qO&Hnys)EEUcu5&xt=9BSv$v9yzd_R|YQ#YAPeRy$nj`|JS#cm!2Hc?82lo;2^K
z1iP at Fg+{k1%q2<0jFctZZ9j>!b8G6grB!BK2r`rgHI0g#IOK%-{gxnwDL$U^=jfwP
z8Np$9?mok2>Fw6O0{Og9)t^}d=M>+sc8YDH3sCX)o}yIN5lN~Ow}T3WuuteK1-3%b
zDx$Tg+XL~vq~}@I8FJGam4=uyqSVY^pygmJ at 7fRz%a at YHAXUhl^Xh=x_+6oM<fO<4
zNaGH<YrK6P$qKcVgviI$p at WN*%;yBgmq@~Q+5bbVTtBt%zyDM9!2uc{NwP&<RpnIT
zM~A at p(osVGx%*yB{|S~WA+ at 2@0gJfR8A_KX`dMa_4sH^ugZ$Z=1`+`4sJsFlKp$S9
zD?7$~fZSPeimp-uXY_S0;_@^SEFt-Lpb#5>K$eshx2v?BFqx#TD&_SX-Y6V;a9r!L
z9#p;T!Ly=h8a?`v{N-x4T79{C_+>XbIxM}9 at MH!_^Bn(aA6s!+HB63~q6>psb at ZZ`
z`>h>?nXmd7>7b>1Zb5Fg>IbIA+mlgD^KqHYqg)~3_z9aK*!Izn;Jgs!5beE%DN54n
zhtEZ_R~mJ`y2PEHPII$@$MfGUXrF(wOQBJcz_d_He(eb2XrNIUD&GIVlP*yeG03pU
z(&Nkwzq4}Ohh!~o*QdRzc&v8`=KES9t?c~GD9GQg-lh>9kNCodfkC<+h=b%jSTAGG
zG$xfcJF4&kk*qnd&PDKDH{KA&X&^mOj^Lmrqo6^!YoPiI*@{ttPwO7buoWLOZw&{H
zI;}7UU^znb_gIufK+f~Ts_~{T&RjBg=ij*be<O!>CN(>8w7Ix-7rc0wrz7d^viWE=
zZ+Af<pQ|gqjMyOIAX{EAl|KpBfAZ8sYjGl|IHIM&DI)?CK17O!n})v+$z0sF(=<nO
zZ6#BrKv&w*5$*AS+^2=0T+lk at Gh=dx#(XX5>Op=n$1E8SdsmCfPMcu6u|&C9WTr_k
zUN?qry`5YBneiVao2y3HVyOAdRUM^b_z^#cEqTe0loLJuP`~}wSwU`HzjoKkDkXFg
z3$80L!!}HEFb=e7R#<V)#pSHlzd2?0<-LJz1S`w2UQIH_xHxqx{^8TtR6~|J8r=x%
zby>=-gdz&It;j1q?)@{}57#fLhUf8*22QYZ#wfpOa*+!O5r-#W%F&jDv<A);KQUdD
z$#;1VHs5D_ at rKWfA{6PK6HN3Ri7R`Dqt4^K&$S3$&eVK$Z-#;s?cmT$PP|%08Y!Jq
zocO*lgnTF;DlgP(VRCe at X-(NcZ!o?0nIvOV#0#d?Wo1oyOx!X3IId^#Ok#6)@|1@#
zdZp>D;ioTQh)4po6^*MoMXWs|S0MEv$Jd#|)=UM{we&udvPE)8L?JfmAxOD}@)#33
z9 at z?m)<#^|U)xiSp20XgkLuwIF at a0@Cv44YCCY!yTlJB7try3g5zQVU{Fs6np>!0k
zK~d!jbc3sFLw4r?jbUrOp>$$we0|T^m6c(mr^^`W>!k|Js*1XwX%gc|d3PGx3MtDK
zh;i{`lr&)%sV{JCzc-+?RArbkkaQQE5PCMhf8Gsp8lCQNevV9G`nG-gNL5MlQlPS&
z0kKWFe1t<rCZ8~<gsHyKC(~1}f6j;R*`?zLVFY_Y#^HlPrb3C(?28g9f{wFSkyb3B
zwGwQRC+f1&Qka0C%FC)&$o at k>TI}uAxvCQsVPau36rnZU`p`YQ=lBq3%tv^pX2th-
zBnqdQNxinbvi{ANx~jlg{w0eX0^?D{?93D+y!2F4td&u at IMu-S>4RUcP|5aQiv?
zU_o(#b=+>x<U+JKs!_mGV$xXL<ti-3!`;s(X!qZ8ulcZOdXjD*6u(N1W8k52jPm7*
z6Cu9 at DU?^U4u1^If_|Z at 94x#S<;HK5S8{rrK@>o*3eI{bR(W64Ni+7sP?Q)lo%eY<
zrmAq5;t|}7YKP_Ni*Pse5=vzU+}>vogkIOGrW%xmn0Fvb2!E|PO at ch0t9bbO?!@MT
z&!e)S!vMRlNBX at BfuMsEnm}st2o;I24dE^r{bhQy?PLty$k{@{)zkT~_ddJbG$N}S
zqoqKQl^bqlzuuvSzH6kyq}*L&DUNOtlRQ8P0lg(u7=~gX=I72yBl01nk4gt83C_Ra
zUEVL<M5~{lHrVeiBnNz*URGTK#HQPjY5XgYA-FQ~B5Q!|Joot?4p$8QhT|bcX~Dyd
zP;5vaAlMT{0F*yt^x?z1vTSmd_Qv*}Z7+P$^;EShF-6_so?M6X<Uo_TAU%@|A<;UV
z9dK_^ztd5(=VM!~PcPxZvrI3=RLo)Ntl=*!cL*JUI?z_`TIxMU4@#Jk;Ke|p_9pSb
zU5bgj2tLeoB~34$G%XdO3nZ|FW4K+9XM}1WnIw=moYdg%IJ2Q`TEt at -|S5xj=8
zL>r&fS)<iP4=qW^V&IH}d9~5UDvyWFRrpHvUfNo#*!p&RiGVj|&=OpzCmr-)o0L3m
zOnsQ+7JV<up;`qM-jmRGWgvO at IV50SsmiPM+vk13z<5?!{)Pa`Dy{E*zMB0iBT1G!
zwm5YNN9r at f>vz~4NnFohs-0U5k0;wU)ST=*@>WTzShBtvK(p<=t3wsP9;W9BJFx1w
zI){z9-l>W!m^8~$WC<ZSq~Z7EFsf<A{&wsu{vMnktG7#yVHj2#31b5u+I4L)Hg$4_
z4z6}`7JFF3a}8vOqA)jxs`2ni8WND0z?G*}e_$O|9x1oxUDK$mibiyq<LtW4BPy{p
zx7b0#ia^yH at ZjFIZlWvY+O6(6MkX6&ryjW_C=x%c4Fb&2hDq`D%jzpXXc#yrze9C#
z?k~P)xfvC!BmHUYTI>K&0WdMIYIN~2w|uHP#GcQ1ufkkAC6Z#1vGL9)?oZQ-&-jQs
zbJh!~Yu<(9(CV}klEXyGRYe(Bg&4ML;xPB75*9taq}$@Z<7qVPIaG>5%J4=L(NGuz
zcbnw^Zm<yS-?P}+gtuaz$1ZZnU!@*_$RZ)!t|TGowaq3R66dRpbolbFRdF_T>?}i}
zbdi-BU24*E)(skXA4Ht_(BeL|z$TB}V;~p<5EdSIHXXD0O|a_RN_IGoG;m2gX-&MV
zT5}Ga0+NdOP>-Hsx3I6h3zQ|*ng#^$Ru*>}DYA9<n{srM;mx{^lsLK*-itnffv)3v
z0orJvQZPn<IJAr$U>&-H at B$r<eMQ%9M9FXMa8yqEgA_d74k&qA!JN?3MVG>~MBJ!M
z8(&IeLO{y at v-e$gHRBunj2jM8eQf_8k2(&HZfZ?D?g~Dd at UQgOPFV87h^~e7|1~>4
z2G*OL{Tru$t<m*)$1=DzHI3s#hOR}H)Yk{o^@#Aha=fUH6H(1cw1dUi{9{e%d0XMT
zkwLaYm#P)wXd(nv(dyX8>2h@%d)DZz8KL2yES~8X8k+j54$PVhP(w!*2fIS&wb8=T
z$mJfFeCk1=2py<BUCQ20Oo(BXL6}T6iYRrxS6M?x6gd>W1q|^-OoF>-pR_XTxofJU
zPuib}zG^Cx>L6H_b>!WQrOwP|ycTBk8!^!i@>;cH+Zfx)uT-<q&}y?ZzuSVYkD6Yg
znd;q*7J6qg>U*`t!wKb8%fd0;$A^{StCdQ5-d4MevUkwueI&`ZU3q#RPo;VB2s{)U
zVk)eVd1H$h<LqAE0<U2*L(h#AgzegcC_rDC`8?lzxiTh>Dl;8Xd+KC$9whM?#06_R
z9_qG1n>KwvI1gAt4-Mu3Imw_g$=!OWI!OH<hTut`Tp8V-OoZ^*u-gH at a@xR_+E&wp
z=X at 3^4hfr2!`_0Nm%`lC6~$4!Lpd at wX5+!1$WHm41|)jJoA8tn*^*k{RpOIv$H=<$
zL()Eky%d}|s=}7w-RG#1bOS9g&W)B6G?Z<yFFO*6>(9#`dJ_gqx1%Fd&I#nLiOj+e
zq&n95=!j75QHpx&J8Thfw}Tu6T-LyVv7a=~^0Kf%Q&(sC at KpYr!QNf*Sc3>%419WC
zxB20AOGgIl+pBz*Vwz?|;0*>^MC?0Os7Z2CBn;_5%$jcMCXLd{yD!{0g5(g16!%nK
z2~9!~XQ!0tDr>1T at qE+1bs!p*4Z%meSPNxjBWX$&<zCy+Da09sBU39&no}8|3KZxc
z>*=LyClV2bSt_QfU{iQs9n%a|9k<=|fsZwK5&E_SWSfxuxpFxJ2`j5NUu(5?zSbkb
zdAnjp?Pb?}!)NJ>@IhEX?%NFRB7;SiTD!aFZHHqWaP6f_W+3J~lr``3ZgA;fT1oV#
zRP48+Og3&kWONDc#+AnWA!IGuG;hCax`+(~YyZuKmd*nK!4Vn%r(CwWZxY`D at wHzS
zI=@vwie70z-^_%bM$Owia1Fuw5Lzh*x-c&p<R?rsP1R%^ag{tTPhJ9C!bOfzmvd6k
zSJF`mzDaQ_qW6P%4c=N`gaO<sv-D1|c^JV(lB2%wIn!IpQ!j$#CTO|IPX=UILIVvw
zSM7~Q2myz<^3o#~9B;$-&q8_h?`f<XegfS3VU*1pc2VoEKt;e>FePsGBMzipNC7y_
zKMzJz`;_D>PQ~;CUWqHF_)IpzV7kL51yd=dYF7z at +Mqdjd8SbwYQg}^-<*`!HvhB>
z|2zk#Hrh9HHRYo63!w%8i;r_%>@!2O<{gFn)!tiqaBfUnebjDpyM%uhIs(L_0_|E7
zedcWqpY;#~zp-Y8e}@-Mjh;R=gqef8#{L)f4T=USV|jh;`*KdaDbqN#>c!oom+jg|
zG-SPn*8Nf#)@S<7$@}zIpalt`?Tq2H+FDuq6E!peU3?~zf<ag^3iMbKIljOZGylR&
zu-V8OZo7 at 5ZyTj$MgJ?&?9wcxhk3I3x at KBLPoa`}J_k-?1(W8bponHKL=cI<nzPjV
z>dN7Y*eoE at +8v7KZ%8)CFe^X+HufzHt*;YILoxU<qqNA<R*S6c&koS~FqcLubFeyJ
za|B~-Zk=(q0xB1a`mAkBual^)>jkZlyR|epP8{m8-K)tP!jDAm<`}$2fAKOrGJ!bR
zlfg3((KZI^nOiK4!{x?Q=p7=i??$6Z1`1jS85onSNh(^}L3iyD-tvA3s{V#170xb6
z>?73#r-O?F(|c?z9V3Sy*^R^5?M!ZK4)aA6)C$wMG)k;8NnKV`Kk)gR#QR>H{;tw8
z%c%<QR(ZIc!i4 at fE;tZ)N at q<={Cvb{ToOH>)irPu%sGzk+bh<NOY at 5H&q6*c)S-(=
zcEr!NUCvz#YiZ%|n-00~;vR(oQmJC>1&AW;#+2?39I=)uQ)DQnBaN9KGYw`<n!>&i
zvyc-T`(Q8>0s~je-wo<iz9D|lY5uK?lX>-!b<Tk4esf=feS`!7Sx+rJEnGzD_Q4mE
z(=H=gCh8&8O7J6lVj((^F<im{xr(#e-HOYAXEcNN_6jl)))!(mAK*OT-6u|h*$09S
zg61Ata1 at F-tVqOKKPM?1vWquaP<H}NM~u^g9N+}Z^<_${eVetqhe|hmmO6Ih$v2 at H
zr04c)U<U<6!C+H?!o@<${&n!1WLCB?=Lf at t(>v_~=7^VHX4^hZl7ru!moYWJ&+fud
zeRtcXcVVQ?EIaqBWbVqIW=~J)lRAwoIGBwvQ`qq(gV1L6Q<H(Dfl2O+*OW1rPGa|_
zXIK)O-F9?j%jqnwaj?)SrLZJZ;9wK)!UrojD0<6h-81=<WX?|r*bTL_HMug5p9hrx
z?;)yvM_<2j3g|KX4m>+7ZP6IKT at msxQkev9KFns7F?`)7cy_nCC|kG)PF}rmLh>hV
zR$c9?r*2)=4L!hs)ehbCK6<*n>`}wd1hD3%FeGuxK~ZmJJ<x#t8H>eb|KSTMGSBV{
zm^nhyiYj13&02G8d}ywjeK|6<aL=Q`sMUroXbYedxyW2%%a{jvP8?cOeV+R=l at J7|
zkf|4&VbxdSvq-RR-JuUUpwz2 at yn=G3rfca+=73L_C<DK6<r_RZy(8<0C_Zxqf`RCn
z-<|>jaJybAJTGe>OYq^{6s#Ci6#s#5q<6==+j!+wFWXbH6PM=;$)`p&Xvf6Sz|gL2
z{dJ=IA{mRE8FA*-cJ`5H;owT-N+Pa89;_E=8OaRf_&YQ;Vcb6q<azBv^k4D&ec<Dp
z=j;Hb{dcH{EIHtR?h8#62N_a83`#<jWPQmd0H!oQ<eT-O2d34l(n4W7?u;K(m5CR&
zcz63#R$YOTjV&y%KsJYD>q!F4v~Ws++15Fq_<fkVah5JMzHl~7V=8X4(qGfTubCgJ
zn-88>h)^}>Vn=`;*haX(tqJ2X2ySjPHyD<qQH`PR9qB8d%ZBzj3dLfTkE%}>=D2HM
zBD&&ZKqf at FPpjt5777d0!runiMn>z|b$LkhM{Wt<DyslXInfy#@nwY97+cLJcP at kA
zyw!lk>aX%U`aAwjY&S-}?_}_QwVxyGzl-hpom<z9Q$VEpcPs`nO{M>$&;yqUl^&;B
zA0e&6iv)d&_0brGpe3V=RgaH0Qe>h4>;~$^GBaDccrZLzV(xV7IPCZWL1nPmngdK>
zYie(V at 18=9_k6dMge#=FS9dmXwovlyVN+EV`K+)Xh0q2U=#sE^MjP at uQS!|M$Jl^N
z+dL6~Ri$9ef1Bv2>KQUn;4Q$)Lp)rvg<G~)63HGDp@}QCiYM4J(Ax@??UHV*G+X!!
z$KrS3o4|ahzTS0HTlBxiO8+k^>ix8u_^m!&i(7PL;sscUvWz1S(5^sbo>A?`@`{(8
zJ0ww`EF`oklj6a?C&og4&YtAkR3lXd+D|#tiS?c!h!OG$6Ode#9gi@@GzDytn at VKf
zMTnLv(W80mB<gm=klgAepc`0dNL->Rg0qWa^hnhij_5UUf@<9FijjQR4Zd4+{VwC#
z?=eQAe&+yx<McQ1f~6;<@u#B*kya!X6=h&X=(Pa?D#UrA(i3C{>dn$Ai3TYiGV^IT
zB9UNL$O<FiqRC at F2`euRFn~|PBYKBXO|lcN46FojarVTm(Pa1oaLibH)^UZ8sJ*Dt
z;*g(**<rdlf^bxrwZ-hoV&QY?4T9T0`HOs-R~h;X?aC^csKxMpdqI#@5dk7RHhPc9
z>1~nT7l6qEq^Qu#&)HXA_9a}Ry0Z>J at qjwB#{>pgQ2(8-leVYu`2J*?C>la9H}ReQ
zrGpfdzn}Q;WpeZXfAphY(ye)b_+#ymrm2HT`3umfn0D10Ys>u$Wzo7i$OD2UyKW!b
zw{0T5O7z3UK at JR^IfaE=N8Jnv;=2ZfG$|e0O2S_tW7yDT=)pYaVFy#@P++Qp*SzIP
zJW_;x$LyO8Yw-I at 9mRq|jA+=hHo!odecVvX8^Uu1LSIWh!F{0)1h{#B1>z_y#)Y&;
zxdK6hkoIs!rfeLma^l|ZWCuC~OniRCv`JiM1Nyvcwi)H&oYPI!7E!V?uLoLv6mbQr
z8b$r+ODNz{;9)FZHFPk60o_3RJhB_DAk_mq$DN-<S{>cGsDp9xM#ieHFvI%99MihR
zjSN7bYl~9xVzPQ&nHj?$I2EhWcp=qLs#DF%d~m<Ssv8|*EW{ge05R1E8N8zOI1j=a
z3sM)Gji)Ovr<<$P*-O<_6slMC)!%8czr432UX(slx8^RD?H)-=9h_#ki|9Ho)mEij
z9te(CuT31cgPKBBuw*?b1 at +mx9@&ylv*|}hwjw-;&t0X<DM~E(WEF!gVGsy5(kJ$E
zB;fbqUenMeAGAO$HqSA~v(q*$HoI?nyJ3^Sz(+J;+ILIY#cfn1QZvK@>B-xsyDVJY
zlzCoBoH^F at ZWr_c%knd4H5T=hdt=ZJN9O9cl!`twckYqY!X!kF;Zn607O%*X^yFG*
ze~A22T2kq}M*&1oX8P22(qaT1*8#iG>GpN`ZiJLcqi#Bcc3F0%lh{t}6m)B^u&tQe
zXG!O*N}%YQJ}9DmoAHc`a<j2xrExB at g({zjFCTo8tn*=TQx!)dfE~T2Gqs&(ewP43
z&&YvN?5HD3rO}d^&nQweKA_JuML-C*7MDJ2DpUsT)ee?H6ID}4-;$U^+U1kg-tNwa
zE{C~J?d%8|#|upK%$@HXD|mTU<&m%=KVl|&lOF)D&`e~t$iM3j6C!JL1p-u?44aX(
ztKOe6?jko=1~oUgc~^5O$lEHrB=Mw2$__pETLRN{$`I=_D!$Dncc$Pem{3-)+~H?l
zsg;bk(RDD**o{@gRYSMaz;KyX at Q&GZ^pKNvI+7`=s&c*WN6Pi~eO}~HC9Avz2tht|
zwJJv4Ir?q_mbj~Fd6}?u at bD{m<M6=`;(#My`$hp|6j>-z@%nh}$72Qbve`GurN^GD
z5Ng6hgp2W&8X`s{ik%z|65ZV?ovOhNru28cRs#=a)zOjRXx^XhB)$CzsA`3)oh>Jy
z-W{?7vN|vX;%;C9VIWcQ502oz>O52(@-Knvmfd>a^?YwGdZ at Bh!A7#XM_dH4w9t&N
z*rAg_axf}ZMV11^7&OP=?cL7Zn<?#5cW!N!$Yr{V+3ly;&r$6l%gaQj5a>8$c_a}|
z`DD_igFLkrnS#Vt^eYlgU{R?5?C`BxLejksW<T<%aS7gwb^WB+{DIZD%4E|zg{q3^
z-C0H40=%yA2sVGCebBsj;1W|;@C*ZI6n~YaHlFsrplSR0apBfo)6a(^J*G^$yOSf9
zd!GpTVv#>lAn_0rW4Z|yw<8s07n+XVPBfcrhxU>O$(U at sdzWzQQ>~9nI{*o<j=EJ$
zL<i$~oe?VDt8*GZAv{ra!*GlHTv5KAIJ{8r_8^L5p)Ibi-8K9fXub?@F34fiZ1UqN
znpq^o8Y-jDZ{Ug_Zhf)5icHTEzHA=|8m%5(tk~XL#aF{b_#(aF`oO7ZV at zaRa$+lv
z>0!lJYjWAg0|Vyf{?PYdW2_m1d`?I(iywCnF4ToKIcF^ntf{=ev*GsuGRjX3+l&?2
zBZUNiW~&)J^({9?tN5jr^~+1UfWk%yo$~RUSH#4&qX&y`%t$aLiXL}rj#h}+v5$eN
zI0x(HshXJ8KTXpT^0cx_w{eV&q5%lQZKx^plvobRbG47}TF3)p4XHv at Zew;I9N at r|
zBvA*MusxNvTCs&sdK5i9e^S%h=e|Ud{Mg3>{B-ah7c?;{yaXO!#+9HGJ922rzpX-n
z*YTj$*VVJKs0w?ka2G6ut|-+=QA%!%%b7nbqhtKIX3(KL$^Go*ZV)w|GM)=n00BV?
zTCIOs4#es~e3qCcgNzwXi1ZtTr|*2gBoZu*B(g?gfVVx;Id#cJ`q{&tGxDjjuCWDc
z!dBObq}bVF%}<BcL`{hH3#Rn32Tk6|1Bp30P1!CZQ1eQY+3Ip;i8B=Cdp4pwQ#clV
z0?F;yQq+HE8Su;g|HsV!OJ?>T(g5n at Kbaza`h-JMA9TE>w81nfV=%y4eBV!>-A~SG
zZ+C+d(`_6MF7U at IU}!T7Chkf`e~}1}{i>r)%P38bk^r+0f(iMWa*H0o1#XWr$3X%L
z(ZE`(c*<JQRK~R!gDEuJ(=(+>i?wdFu>PUh)KOVY#zf`uteRu_ER+$DJL<~-9s%g3
z!)F3L;BiME`?vBj at at@oZy7Za&r2e`y2CJp7b|j7V55O>4|FEK9=!MisQ4{Fj52nz
z<L#KskDCqqFQNdQf6Q|i8DQn+{Pa}tF+j!neSO>hY0Ng-UpbomTdpr?*r*d2pf4O-
zcM|RY_GGO_S_uYO<P*HD%te(s05Dcyp5ymkftnQ%6K~-0{Wrf>C3NHlgDa|UKYbT5
zX<&2)f6m7K2;HJr+`4l`nB*32?9JqgH&21g|44DOvHxg~|K;{DG7;LDd3fP50a-qw
zy&S8!0wrm^x<p&L0?pEgMCCSFo8FhP_AIcXdEwz>zgu4P#d9eYX(t84Kd|d&>hCV5
zz1HLYBgLN^^N;rUt~2~^^$*ZZVBa2Elzpou!xZ<!8FRJw&LK+Mm-p!`XGvbkpNk=u
z&?`_c6R_Pt*t-Jl;QSz6_1*veMjEv;Aco(#yp=|)X3V05C~Bz^@-t*|A0Awze=}a3
zBiN4#_3T}eT-umgup+8lD&n1+^rHC})<|$SomT+?#S|bG^vi~WBS#=e?K}`o^EX?t
z<Q;J-Xf_+xCecz5TZexxUNlP?-PaS=(Q1tBLwgIzg|aVa`4EaIAq%WrqrQ?98B)Cz
z^!J0J^}UdCQ&4DgX2Yq14FZPy{Z-%2W3Arel%5Hcnu}183=0eFg0UaReEQjo`exOG
zGsPVx95GIy8M~9!*I6jJBx&B4R?G+`>SJcCSqUHG_{lx<$&UguB9xI{v at rWBiZaAI
zj3w>wCFbat?T`=P9diMaRjTW3h{mj|qG2T8?JA}aIiweV!odBF!u6s^(k+W{z3C at k
z^$q<6wf>C^Suo!}maaa?+~=pKXSG+L{#~`izi+)#dp^4V%*N3iB&iRC|5an+AYz#P
z+_O75pVEn(sopk<2>3aPQ-PTNWpaQDKIis?aNoEXaX%+(1g#-A4<V9odkV_6KZWGJ
z^(nk55Q+vpc}DV_;*{$GmK_Kif+)ZJV{i$q3!+Q!yO2vbnqxo^68RDcLn9mvgrR|n
zJpW<v1}M;w>rgZjKqwloj!PWKXCWZY3xzx&QEYGpLj94DPX+{>!MfJOVukpTZHF#T
z?IRBYv2X~5KL4nd{;LY{xCU_YrXqo?--YZOT57GhdA#+Pks$)RB15%kqfm&6ZFp at M
za2w{+i!XfTFN0F8FPt!fqz*icPM4G_1`7+$JHeq$h6yFI)ktBiGLHLjfzgA_eh%hq
zA4I3VRF{M`G1?~hd$N?D5(g>Q13n>S(4z|o*QHNIFxA_m5mL&KZ(==hUQu_>T0|>_
zi1k8uuR!EXQ){{ugX}e1o9m4AiLsA at za?S2S@*!9hjHI8d}^*0Xr|`GiFddw>u6 at E
zOm|Q5J*$RqVs$UbIMWDFfIF-Wef7|CJuPuFNF$CjAf}z96Pu+?U`>?lgs8mX43X>d
zWjRyxAx+INhtz!KU`ka*ym|x$Wns-rEL!ey$m{ui5>tSxmLs0?Ah{4c)@Dm-NvSl(
zMlg?d<c~@2C2ru7En7lL{zhTMVxeYG3zIHG^a at n{Od8&A3I5KS<f{z_{1I#qIGOPD
z5Keu!fyX&SS$#xXq&54gw<5>PYd2HPph?Nb`b<6mnMHDsV4N9#zp;^q1i4#xn4>di
zp`^UThpPT#P<w*~6&^E)`xy^Sl21*1_V#&12W82W33LvxOYNi4p+$n_J*P0KwYGyo
zhEa)r_Msi!q`gP|cbEqJncxn`aSROfDM at Q)y;iMO at Z%C<u%Tv^wrz}N#PZhFP2 at Bw
zG+~mdg at Lo@j0$V$?BMvZVEm3wqTn&yJ+B-MWABk#&3zBfRin5ix$-!pvtCNnF5AZW
z(8us5ih#=H&%0pgjdwu!htL7Pd#_(>wXRn#FcqX28W=g~l5wWRlHM!gN&*Q8p{wTt
zRQMY&(N3bgY|>J5TvTPU$u4|txEeRbjKnSyT*?J4-1|XK8oJMBPE_))Og)kpyO7q}
z3%2GsiF|KU_d9?+CECKNVcxZvdr**gbyUN=EwDp?U<=Z2dpi0&>s+9};IQIzhdWI{
zHd^wL<N>u8*UcThE~$8^tb;h}8%Mjm6dO=tBX0h0MrR{#3xQJhg=d4Bt61>i4J^mm
z!lweedE30Tab?`;2)1$A_o|accGVgHX4d>2qzjKG0P79}Vx&mteSOzNP;_U}=lM~(
zXEWK7Oo}55yG%^qIRi&`*Bwe-cOo!g0nosTM}9R$bC8=k%&{SGYctze&jJxGvp&es
zPkKHfh*JPTp*DK1Iq6OkR0C~QO)00Q3duAqcPZP5u$6DWpvijr<gNJg^zjKZB)IkX
zYzgHsK_eOv#!{TeBZt$FQV4Ycdp8MV*QHcqMx7I?N)oK!e6m}VBU)SkfzSpTZs6<)
z7dkQ%dXtr1#vHwSVCN1&f3yoQk1hp5X=6I-MK^r=Qt4S)#&~*5bJ#1R%0D=-AK^Rm
zN`Ct~CheTqQW-HO4T?GqU6WpKt&%7+XA{db#rg(qJ{s+R7>RsBAySSlpXUA5UCnan
zG<65h7qJlG{g*PSPL48`;E!VA{)F(h2icrwL_)V#FI6JO#<X59VdWZ$8)$R#c}cRz
zXia=9p1lSCwIBk_*z;o7Pc{Dei8}e6trm4freKr`S>4OcMJ5b1A?gu%rlmR~=q3_<
zs_Ps at oOHdoEva9wHphFh9E}(6AGO&T`yjWXz!L{9Q4f~i;!|pPiVb~S$kZf*FuLXR
zxWK+ at evTqxM=h)3{i{#V`C88yCck<wcvGFuCop83BMXu=IJKC$*&c=FPEMV998sx*
zAT^hxswAAb#mnVY?xk~f4oh7Vn)3I9&!xADJ{e#~<OK4T=_j~jn2`at!AN(&Hi&-`
zn44Q>8rKtF&_Nt(P}z#|4X5-qsn~5q+g6km8NI{goYipcPRl|qankmnwAC@&m)=9z
zlJKbcc-gZ=%W9XOTiZ2CCWzUjKAYl~!r#gd|GJnVt0#o at CZ36tkg_ZSN&UmK_PplX
zIIyg3qgeuLQRQXZnS}*TwXN~Ccm;1Nrb%s0HJr7HZ at W9C7^j*#@2t8xaD_9_e_>Uv
z%_Y9=@u}P1ZS=71oZ8L#>t$0Yy*y3#RKj!T%{I?>ZF68xk}4UUyOZ4OvOd!#Rjl}Q
z5%QFKNf6ZT9fWya>(~2l#y<B$^JIx+N=nq8RTE<zvtRbr;$fdes*d`ISQVP%SW%+*
zN7Bj>FD2*)c0avs-kT;~z!V%-rcvJ!p7=5ZNh#55lT{v<H(~XX{!@?TEU*x<luF)x
zas}EXhU}y28aiSh=>qP?%4%o0#fKuOErese(B|j0mL!tIas9)N;PMZz<OiMwnt!8U
zjgEaCLfHey$-^VyXM7xaDZSosu!X;;l|C!W+i0$K>R)Qi)EamAnK~Tpa>kny8)pfg
z%niP>rYdXIEznFl+ngB;%tuC2nUzK??J)DtYv2tW3k8%fMYxg=S^xuAk$O{QEqYv+
z{W7n17D0c&M^h>l_?-$dkGatm9W`KRB#-Ys1!~Yf5ZzeCBY=1IR31Z9-tf7}h>b8C
z at c?t2Hb%a;cha{WbBUwoh<EG(`(N>FbD|YOKOtEYhrAPT+rFhRz%OfVI5WnenOc2j
zQ>BQ5hQ{*Fayl0d9?N%>B2J3YU=T8cUGYv&HClA8KM-4TT8=(xRwArs=-x29iK7(z
zQBCY(S6Dh3BSQOEaG1Q<#^ckXLWsL9=^JaKk;C_{cG7n1{H<iJ>Lzm}tfr at J=fQ$x
zS0FdY1-=TSqYUKM_~h4BACs3c4>{`J+~MsaUV;Oif14wFEJo3Hp!)v(4r019+#{v!
z=vO?JV=9)tFOm?XA%!lXPX?BT9{lxsqHNZGO}+nIxNq#e_C<}6)Keh9Z&>!Y_eorG
zGbv5Q&<ieiYeSqZ2fgP0 at +TjK1!S(*5~qEK7+J%I#SU3Lz4AVE2WUC<Q<E$jvkE*$
zZQLDjun|qlL#ab)B_q?7npU`RgoT<xR_YPeRw8i8kDEvL$0X_jQ-Rt5IFVcHgmr!5
zEP at AJG+{16s6E>RFNri>t14c2kTRSCz1PJcN{KB91m`hpKUl$0T}#r9qbT+KH{xg_
zey{J7G*Avq`cT&*hc%uo^SZ<zrhPW=4#E}nhMAwQLVr};{s+qdKupH?#&S6Q@@I>J
z9{|(um&4yLg at 066zA=*v{fmWAA at whV+_*-&s>*+4T+ABXrn%;`M5>Cx{4 at p<ds3t8
z)T;v_l(7{$yh4(6T_lf188HfvxzKpiUV@^0$svBhkr?M%QE(eNS}c^oo(yE1gy=p(
z=2x$pwh~9_6jPO?vb<~clx~dBPLdp}FYsHW>R`QcmKok%)vs|z-mh5Rdxv``HacgW
z`yVkBl@@cnF-!h-m at dx}i$}&%zg^v~e at +u(`zh at ymab&FNsOsAPT493RyrXZl^nBu
z8~6ND&=AXUG*()o(E1S>w14a!w6D3*2OoZO8ULgGK)$fhtnBpE>1OgNoZ?VSY4L;H
zd`%IA55pfaP78;EO4_WtN?(GoyTD02J-j=CU`rYGvX51jqtD8|IkenQ>W?>hVZea6
zM1vHUJ14+N^rn#aaCZRQNo2QTOYB}nU&-7^*8vJK=X+eS-O^fU;z05MoF&aA&~u_l
z-bNlVA^!yRHk*q1-JWz;Cw3QL`*Xj?$Zup(Vvmwx6vjn&+NePH*avKhJ+S_bUENuU
zkpX?_nIxreb$BCzzy}62bqHohZV$BkLr+vrfuDZdt&?|7j`Y<uNY&k7f3Ww=qq{9H
zPlNM&%L7FAqKVIozVI;eo*t#AoW|99ELM>0b3!XJ%QC*SJYcMPD~cpB&fTjYH(4w+
zaO?Gm#jBT_#RgTAp2KL?%=hi=_b`YAfJNQfp|h55vO9UiX6wkTFsFYsl^qfdD10zd
z49o3 at po9NtONRJ^9`Nto^|g_jyCwEy<AHFAi(qF#tH+!$XNi))>h$K86G%3}JJ4O8
zs&9x;6ZVBb<hD+;MXL?ZstGK~!&$j>D_FDJk}kf-G4qf?-xJie{V9 at V=XnNgB5BO+
z at muMarz@wU38o*>MmdOa%Q$zE=c-ST7Do4rnw}gRo8pZQ70p*a?`ijMIla>%lnHVU
zZ;wcEnb1L=c{@**noI8=pQ*9Je4>We0ZUrvmYmC<+*;iCi&*f|GU6aArC}z at 4}*(U
zfr~PLU#zq~B`fj4bm-$cagg1hsn%sS=2u5>9YQIkwYdX?Qx%DuubQyUY!z2DM}ZmX
zu_F#LfY<yGdHHV<K5_3#gfHp&jmr>YWE^l%0mQlMsJ7oBKED`R_&+GeP%(YSqra~5
zpG*3-Tme?8|7ZUI?x6U$o1-cI@#q1!XH(w9SEc|8cX0a2doO$6I`0H^WpU8`992|A
z(6n;SuQ%!b-1N<Ma~pHRFgf^ldFb#tR(fpU4!omKw<wHP=rwcXr$a+Za8Z}tJM7VI
zaxn at e=AucOHD4E}bEgyTR0$sRYihL_8X}aFzn&3(lPt`lMw at jVefZBr$nPN~6(bH~
zbwi~u7z2_Nqhy)VH{0*ejr)IiT7T<WeG&a!XhE)}hsk}@jHpM^L0w#RJn#V^{;HJR
z`e5Tr$UXq{z(6kfv;dbX<M09iU%k9WaR7hAHj`bB<dZAVHA;g3fI}Om{lbJv5 at lB)
zAs{R<sNo7klHNW70qh~yKo9;EXs#k#9AxBrxA*)Cv;tfJ3uNsSN}&n3KwJo2fk02M
zKp`WEKo6cb7Xt9xYb*$0B=11^|0u^F?eRzd_~U)~WBmAIJpDJ!F9=Mm{p$tiv6Pwy
ztkMj;S&cGvJdGV~Osi%a-z|53qb=$$T220OuiwP?6zS?4s-cyEy at Q?MbIa=^Ydtem
zPHw;}^ZJ0F|Dh5KGb#%+aIOR#vZAuE{&>iR%Jt&`4=T^~fuP_|7jv-z=YQj34rWx2
zKjp*4ipuq;e12J$KQ9j#aO)rL{j*zMpa1EW%8q&tt~Q1br7fQu8=^iGv$n9dQ?_}o
zZ}{+$p_8e;p^~I1>O*x?0|ygwPG)A*hf;>7#wHHrY}d!4rVjQBhIV4smNwQ_hE@*b
z+^7#FOf4J??H)c7Q+Z^lZ*5 at s@QI<7F>o;pHy0<Dpx~bx?&|B+CI~|WXgx!a0SNek
zxf%nBfiO_fZlR%I+(N%~8xsQyj}jjb2M3Rl{N7zk_6J;?Y!6sjc}0{Zcm?DHSy`p@
zq~+8!baZvN#f>ZtwJemuI-1v+z+m3Kjdur+9v`1xlaG~8^I!hCY6M{+!!W{Xz`@)H
z!D7L{VZmHAgUEm~BEbCcb6wu+9~f9TcmzZwWE501AVI}#5G)KF94tH>0s=fd at T~{%
zJqR8P0s9_{2;v>Z=ScT$aag^>Q;{h|OF!W%^>0(M>Dl?9pyJ^Z5Z<MtrlF;yXXoJL
z;^yHMdn7I)DJA__Sw&S%T|-k#- at wqw*u>P#-oeqy*~Qh(_obiztJeX65s`1BqGR60
z#-*iaWM*aO<mSCEE3c at ms;;U1+|=CC+ScCjWnge<cx3eJH|Wgl-2B4g((=mc&hFm+
z!Qs*I$?0{yU_fv`Rtxz1H|vE3)C(3K9u6Mqx?V7_&es*kf=9T=f`~1mi1gg{&V5#I
zWE|1()Y4BV6l_Y{xO#T|sCbm at GgLd*Rr{f4zpP?D|EHS$xnh5*7ZijJ2Ln7jI4qD5
zXg?014F7!V_P=zDT^gfpY|5X=dZ;2zDzK^t6IiMwp-pT+npSC^JkT%)g`A$%0BHS|
zGLKz;IPe<{sRS%rDS%kk#R1Y4h&cVs$odp+3Wy7&0NMEx5!eYWduY~v1^UOS1ky3h
z?+b0jHY{U8u0S3lz<!VR@>~3+*U=T|Xo&3GE+Z|}qzT#{dLwtCo0;a)rbC$z->*wv
zlyP0aY+Zr2a)gc-$i~<peWX{Q%TvH8AENSBaf85J6ARFA?gn at RfUV|i74wou@~~@a
zAE^NdGzQ=qb5T?{?h>TKep!Ww at e36ZUlyT~!I{c*K|VM+LXIJrUA#abMR6dmB31T<
z8?f6<Z2~(M=nQwKokX>y1=0up?f#MhZ1NY_<Ed%}7_;LRFXkhVC&o4g(MuJiXD-a9
z4h at 9%Ca*wOAUI$LJG;iENI|1U_jQ at J2y(_scCrODiZ`%H-En|iyEg~`b=ww%Ts{aj
z@!QaIz)=^vh{ZfV0*c=V?2wOZh05*$^gy8Lj>&|sK=_j7B7da%qb2 at F`vh+9%F~?<
zp$m`kE70;$`i<Agk at R&c`=PLgUv&a(b18T1Y2?sZLZmVe9TQ5zP_%zAj-n=k-?QVX
zOL4xfGh=|$hF6u()~xlSwkW{f818-t^*BByZ4<03p#aQ=9A8 at 6`k*maMrLM1fJL`o
z=3*2YMM3z)=2QOgT|fAQFxNF`#C*Iw!K&LkN~(g&j)(lNR`D8_NT!AMS>|{BCel~S
zG&1nY7-uZSBD!i?ogIkd$}qwc<u&7$C8;V?N@<1VY;sh~21#7VhbOfAJ9L+XtQPlL
zd3Cd->Fk-li1(6eQ4dqBI^S)u)+c*?Kq8?dJs$xbnJG-G#84H{HVna#=6Ak)*B>v{
znw{QlhRtUwV5symVJ3`zuE@<!I;V)eP;^isX-}fsTfE~IBR=Tu#Kp8E|0(34ME49W
zR^ScPDBL{LHLXjkv?Fw)+%HP1mz-3>zE{BSY2IBuGyQH$C39n=*5)cDT$Y4{Bsn|5
zcSrdL at l(oNA7_B=#QplGyR2G}(Tpq5DKSTtcFQ`NZQ(Zt_I8B&C6>oK$sh2QGOz{8
zLXv#4FS}k{x=#Us+zSA+(=jK^h%nm8NyoKMNdu;y^>)b82Vgiy>b(NB^xqu#icMbq
z-$%Ntnb|g(rVdI!<3yP7&PqIW5VkbnY^yu07!lplY6iYLq$sZVXFH*^jI=QErXOJa
zy~TC;Aq at y5j{r0S{<#<X)al_HU~0kjtTC)<J_0UKy8S)s`dgf<-Xgo?2O6_DKJ<e4
zz%$1Lm>w2rE?$g7mY1^oJkSn^mBj)7OnQ|_eH at P<j1dx%Hc>v`ZncqzqfZ8kti}j~
zQ54)ur#%e<7x^DD@;c?>rs=jg9v;JoC at GxL5He!01%+Jf$TH*yORSU?`0q}d=fBaI
zpAh&s#bF~c(ZtX}z(e|SIB?#0h)8DWT-7CyFdb}}vfuFpvngD at HeP3tOy*226=%)S
z8OLrGS1Gnf#m%>upA at N3P`7D*-O)i^$k9G`m3N}sKhYstQ8SIukOhS>)?XNOn1}B+
zl5Lv4h9yJq(I98ym3aI4 at YiP>6ojo1-p5QnzQ&4<t_aDIhp)aZJuYec#Ftp+-6&w=
zDwHTd6DVyEKqV2u==!<<jaouNQZc-}S8J)*Xcz#NOFC>{gjj8*2Bb-;Fq$}9Dac9p
z5+`7Vx)o<bry7Ddd`Z(6GVDZmABJUji)Q2*AKNrMa6icYQfM=;_Ms<kWOs6}=Zru8
zJV1+(o5H+^l?N_Mk|NYCsrrq6o}^{*goaKFyxQpC7u9rh0}6N_c at Zy`w&KUb!xi99
z8Ox)b;uLYaE-JZ%N+i-y`*y-Mz%7#4vCoPmBrtFxTF22bfZaDobc^!-j8zbbVAN><
zHUy=I_i(Yio(E9)Hnxc=D7H1XIo!Bk#I at 1JZ#%W0F=rt2=J7&6!@K}6GU)=i__D3)
zX)4$GD*!fMgF-Ga=o^$yQPzC`w<?kA?Y#8N)Coe>g@^X_IGCBSy>;$0cKNXgvhO}p
zUK_8tYSu4~TiW+$*#2$#TbQ2Zr at Np*$Qqy%4+8Q|g|x%T0zJ;8VIM|)EgGC4?*t^j
zhwzK(pK9z$wEbyy?xqi;s3loSOvXUIzL7zWB=&*W*3UC0+rOoGiGd`LDad`p!~tVe
z&ckBzk_z|u?A at 6sR+bralz>ECE=iu-^Ji&+bbv`irD5oP$r)Ed(UeIdxvZ}cn=#?x
z^y14Rl&5+VIBJ;Z)LNJT_z!Tk2!)R0u&oW)<G&FBFEK1CZQl0yNtN?$*E=+kRYyV9
z(p>20+5B^<K;&G#pL?O*c&YLa#3X at OJ#<2Ai*o?WP(aXknM-Y;HOEp(6IA7|$QX{(
z8}Hpn!;c{+fnkO=g{j#^?oL~{ZgnrpP+HGVWO^5wpj|MwFpyR at HJ76_z%^YIQn~p+
zb0P4Mor&0V?_qQ`k(`{IjqqG};9?3>u9=qVhmYCzxfgLWR7NKJ6 at K`Ub#*L_pvaBM
z53qq|V#-DWmv0zry+00aO|~wK8oZRCRENt_Id$k(k8Gkp<=BYVsT-esUqAYwhB6-(
zq`g1qodg5}bHGvTYR>p_H!JZX{ZMG~1Y%et6IuuOET4anU`wi>8_4fS at 2MSJ#dBVq
zFs8M-w;R>$?E=E~)DB@~&<&I>N0l`(Xi7U#yIWYCoV<Zz80pk|;Q6eTCCT;?^kMw-
zOs$jS%id@@E{iLW=0cD@;T%VyIB^$Gtk at afCCwDn)B3#5Be`?za&qHa?@W*<yQ!P=
zq4SuK`=UqQ!lvNnL|y6b0Z%LMPV(ahaeTeHioiRNcDLTk581#7JDYzXYJGwUm~ckR
zuV)ce&&Xv=1E326PZZd)$zuQ{9Hxe~)g|EZd|;w^1)0a2IykTg=IzOf%pqu~vRWYR
z|6}j1!?N7gK0uI?Mg*imx{>ad?rst3j+aJ!1qtaAknRrY4n^ti?(UE-=fS;o!=7{Y
zIp558&CDNjc_Hu3`^1WS-MxORFy!s8GVhGcj1&}SVc at 7o*mz(C4S?4ld>TPUyb;8;
zxpoKa`xJyGozm$=_hNwCZZo5QRkP=5b(Oo(U%l at Ri9Td5jX(&?@M7u}<+!Anx^a5}
z=q<~+D_tHl07=<NFD>T>gy9sB$daDBLIz$Fn0`qy_t#apus)kKp(nCUS4V(S`#jME
zSns6(K-XI4-VH3^p|lL7n7;Vmc?Q^U3jDhj*`~dNrj_Ut+q)+RWi05@=qVAPURf+5
zBa5^hpd3a>hpl?pT7Rs7?r43 at ck#lJ16?XbOb%{_H^J5(s4%F>i^hGeMr3xV**8h)
zI_KKgJ>JV#jh!dg(lWu4<Stz~w&U<&n1R at ro{9JCV>E4xo%Ss+m$f3!J*D#}+#zLl
zSpY_z?$ngo{<oACiU|s1on7NL{^OeK<R>RhC(&!)uR_a~NUdk4Y7L6ONKSc^AwE`M
zhbn%np2BurUu`pX7RJ>1p;eJpaoR$@E>pJ=pPuO|D7iQ+Nqfj}o$ImVR?sowNFf-e
zCn)YsxK-|pR~IHGHF2_oG<okT<X~B*X3H1%uf5$=F5a}6X0NInchxK*$>$Ig$(skA
zIZaBVmg?xd(6Nf_4ly(84o`q!uS#i?am?=a>kjJjFRJIt6-R%eqbI~yKL}mpu^WsV
z4aaNiX0WBkYQ?>~IXk2ruTgK7IYld0z+0FdQRn8cG(K{P3_IAlu!}1lSTp0~8l&Xu
z&6skMV|B%1Vr3hO+_EVgLZ+?Hb@&b{{P1{_Wi+ZEL=sicp>=$dc4+SG6t1nqR<Eed
z-)(h6aSV55?i~~Jgq_=5SArsXOh+0-h5h}(+Uhk}YOY~_r*LFChT6{6j4lOUw!9Ad
z<1scH<q2}cq~?^^Cw#R?`eqoL`mN-8_v4f7?su`cQ7taoH|VHm;ye6buVYU1l1-9V
zZ7z2bmO^T-X)B7HP~lsw&|6>&^ILF_p4T9xkG-uX07=o)X31ztX0a%QtbT84?&R5u
zw*cv{a{BDc?>$^9=HuG(3`k+aJ3G>(hnd1 at st_m}T{$<>+k1RIrhI3TNpbL;c4vkN
zKxYOO#&hc at 17^71V3VW+Yf27A+~T?F at Cd&t9H=JJ2KclO(JiDfqp^!txMP=cyQ{PM
zOw_FL{!dvtc3Fa;`rn0=Zxj{5tOa7oQ=w_6KZi;XlaZ0s6A4cUmC8-qIPCj0IZNM|
zj?e!7JYGSdA*^^31LgArdBWoX_E77=P5UP?+!1v4K|xdqDws<}@LNe{m0|1vMuEYl
zV*)O0+GNylk2Be`uE$mFs$)T at 3UJCDyax)i<{TW8nHTMjExwb+zHCDV=VG%ctG$x^
ziTOD(;#JW@@|7krMnT~nkDv`dU_(|ft8g?DRo2$Jf6T5cOOkO7wO|rHn>TrP-k;$q
zsUwf1Gpl13d~&vnC6P&0$m>qf|I(NApbckZc(g at jsQj#a9hWLelcUDTRnMIT4Hk6t
zpm3NyzTbDq8PM-aWVBok9i+3w!$phY;mhM`B#_2*Vp5NTZV!=6^i2#PdI=cMDHVIy
z2dF<F at Y1)R2pnMnBBv2>3zr<<1UMs>`i(Py*Xe<f$ZH_R(7JvJ({~N?3B30nd^V$(
z7U(k}a_=4o53jWwud$}nZvt1=p9Vymw^L6LlP?ef`8S6K*rK)kUjWG~WHEQSNOZ#i
zY^Pu7?)(pJP)9LVxck=om**cT+O8VWy2A$SSd-UR*Ej^HD-sJryvj(966aFfQa4TW
z-9?@T<TUh88zzYW5bWCEv5~%;P&cjny1_y^w}LRM5JOR$0P<I+<6ju`LgZ1It4*Mj
zd<~NcE0j4lD66in1QX6)xDDkyhLIxbmER|Utx42t6s^_GUg?SAo0h?*bsVD~5N{q|
zQQakIt}!`0I_h>6?*8#_S77L#A~vqdIH^UuT~|$5>A&+RpQ_J5tV6|O2{OSED6~+8
zZ(0_(WAfRgOf>*TUWTNIF3 at 9?bU>920wx|Tpv#<Z1i3 at 6-GLqODY#AYDHnjHCLyNg
zUo^QD-g%Ns<Lc7cnSzdDD+b>_Ke&OrQdB}es8x`@z2f{t0m`};;d=xM&tmm8`{+3@
zU)Ho)Nm+*@My34@^fodnCMe|Nc?}<DVIsqb)v-Lf))&T!QBL|LN%+f;?}NWxIu_!q
zWff6vt`|{?l;FzM8Cf}-V{IDQ$0$sf(Tyv~t?(6t`t9Kw)O>qKzc-&=G|YT^r)2bj
z=R5<0ip3Hs&Bi)(6o#BhRZOp%`|I`Nl^9%eHZQFB5_O$D?)oZN476jiJt(LozV
zmwBgosuha%IV7aGOrz^o4{MQc)Ig1>!lg&w^#dF5UNsnq3EJGQ1=+ppQ(!INr!|)`
z4yf?Y0qiOmKX<uH1jGZUcERh9fu(R;;@?J9tQoVOcmJmJol}7$_}c}v04$&n8gyMp
zs;E^!jG>?6^ybnJ2sJ96Tqa*Y13|qxTVOeK(qS20(0G|Mp1dcCYu-fV3!tqDDiSUO
zRdnD(O at 10Q|2*>BQvb=4;&#m1Qysj8m7M)`mGI4;5w={sP=k433vF;8&)n4mz?OKQ
z))P!|0<>MAqOfT#aqk8au+G0JUKaue1n(YrAHn8^B#{4$$#|2f!p&$U5EFE6I)mW=
z4zCkGsi?a84z&I=b<<e at -6c%&D`1>2wWe-dfd<cG%w35RO^(ie?*)R05P+^;q$=CS
z@(2XFxMN{+z6K1;TV?P8#{I_h6>9>G(d;Xj6vvY!nMos_?j8j}qz|ufJ}5UeOED{K
z{^g2 at Vrp$(AWD*k=&{y&#;S3?5EfpdT!AAH*VCf6mAb?-&HLNi at 3&qwS%2dH-EL3r
z3N~+W)TS_+(G;x`Lr5yXN<u}>{xR17<Y!&P4g70AOW>!UH46Aycf|=S(K&=!)A7!e
zo3~VcHzH%n$2)L0e)J%|iS|>Yi_Q@?-?I9E&cR;kt`4+zQZ)ojar-vUJ|e#%?h2p0
zjf?!PXLB1?hCWZ3n3CV<7$#;Q?Q at c1^SLXjHKqlq50_+aA-lTLN6lnJJC~${ee1?%
zUdz$nYg;-l)LL08KA<sInu{JJbPjrsi1W-pVFv>83m+&_myFORoM at 8WbxJ&TWzBjU
z6!bmlsE;#iP#Z;(J|Oa+to6(z@}KUg=zYPY!Zvpfu_<u8!+7)7{2FkpFw__TM3*V^
zt?UW2WUH{xl$3ps7MwBp#EY*kR1+E10u at RkRz6H&C#J`ek)B#MxL%2~YECZMndy`n
znAqr>M_1;qG{;WX`L{|^Mo4bTyO|E{JRh?^CvI5G>(y9ufyjY&!scAUq^}rhn0!*1
zL>-2Y75o*#gBd<c8f=2Jh+{o3sf`_+q0q+1Ny%zqXJKFH>zTPx1?$cu8luEhXZH%O
zQnGC!V68$HeRK0<tG#~*U322Yi`W~50=w#L<1fpeV`S<=*QtzGWK9!w${m+PlN>L1
zXPT^85)23@`rN9kqKhJGr<=RrX`iA$KnqDp*nd;LJD_f%Xt~ppnSiw}sbQe;g>N5%
zs52|a>Jxp at NO0jd7N1Q6+s{O=9|st!pMY4UF=y(7sVJV6QZvBX3&JHBBsg5rSay`r
zjUb}}V&D0f3!}m+a1OCC_mhdJk5GIrsM5l2CC!6Be-Lo}uHJ0u9UsnJP)IeYu%Vt^
z4>oPBG#$xAz47a}`OCcmTr4V!rbEbKAReH14KZ?&T68OcfjyX?%)m!b>KjKyf`9jv
z{&vZxZygXYBM`r7$OHr>vK|np0-BLQoCe^s<o$!Ar_#Sv^Co~cdYBCr9jN6q{vIP?
zQ?5gXST14PlVCFSE)KnVu(3^9^kwX^27ljmzN?Yhs!nV8bu=ZIgQpD7`m*NpSCo%z
z*5AM3>IMyoIy#kfz$<L3`s4YEJ>yy$;@k`1(i2?qK<1{UuNTUs|AxkP^ulmK3Nix4
z%@EkS!5yw_&fS`vxpV0_D6H{1->chWACmL51?yEAqoU at L)=<dD$$%MJt*4L~3Ubq7
zu!1-Q;?~DRS7h93T``xI?(`q&3LrmIazcfEOdyQi5dFB_Bb5q7Y1%%3kH6ah?rB6~
z*5)X89*@Q}yqAIP7Z^~2AsQ;DKokey>V{GQ;ha?<8Y)f>&w30UXU2Se&$Eq={MvMg
zXmfD^sI-ASE#*xM@!Wh-47JvUzbqS32+&C!f3}$ka=BY at s)T{Ya7DZc1O$t}T at nqR
zhV|1i8?05+EO42iPzWhDltcZ?GIW>YJze@&faO3+K6mXV1Z25-G<#@SxLT-6TR<;J
zft>{wI|n}?08P`@_W_7A7F@=1Ue-JIH8 at mgE4ii*G0aXmCl%L;mRs|99e7T!Rk^h?
zbgu=LafKp&%?d at EkxUm{?c1Jr?P-GSLduyLKVD1XUaUhX%&H$Wiw*_V^_ePWPS?pT
z)-OA8^%3OtaPbsJ*0ETpR>$j>X`7|_-1Kl!NCdqN9<Y1cDYQ0Lp>xwW(zO260MJbG
zK%2};!0jDofXNUEm_oLrGZxSZiuEK3s@*o**V;E;SORCzKkGs7QI)27%esNfp$0Ky
zbQP at q6<84`0|QnWbor*A!^@BO^}n39m=B(X-iS7+-!zclaxl4xqE;)Ll3GdUrrK!?
zKf6jv!zIQ$Ip~fRc;-g^_Am4Hpky02UAc!2s3;#b(T|IY)Vtl29wml2H}X745AChh
zBViQiJB;j|DC?f&u<CVMD`M&8HjXxZl|&I>I;b)Jx_uLyD2Ep}$?E;u_|j{J?MB*a
z(VISm6Zem1=ZF3%&s_Js-0aR)<I{dX=>LE)(E(ds$6t!~fXlD5 at lLG;oOzFq_H at 13
zor<~Z+55k3CTV>LqDD;EfkuIKZIc<4);D2YbhRROjlm92SNcluOri%x^22Iu`&r6t
z=;M;o at 6T8l*3R{|pkLVZlnJz4vnXyg8FRk~2K+>u?IxzG^UM0ealF?(^=mw3dqD at f
zRpC!2d4E7q^X|m+_M|&auUWe- at h)YVg->XGiy(9APivP9S9=C^u?*R_B>t>spxty`
z+ at EemI}wGibA7<9dm_w=GTe)kl#BDqC4UxnXy=JgdHo?N2c6D&K}zXWcQsrUR8{VA
zk=JZ<??9lomUeSny<J}9*;^V3;($r=fQJ!OW4mcosyJMnP at Wx>2dSM~f}qB4Q6eI@
zrtxnR^1g^n@^)Yx5H%40^zF4IdnlPU0?Y)%wja5O0rvd-LC1z9#R+u$_(WQcmbrl0
zh$A8+t<CsVQ?m2iY+bEWn_TvOZb+(em4FF{)1tlIN-PGu2UEg;F_uvrErrLM!8si(
zZdd*Hbc7Z%-(esFUp|FP{nDqiROL3BnpFiG_tRf7F}LfXAB_%^2yeHJM_Shv-!<fY
zE5x4&q=nxU0zMQX(bWT>C|VJS+_o!A0P+^F$Oh0yk8PmixT#w>Hx0lhn{!8KC4f0c
z2^gd0_sNPfQFW4Svkt;mj(d+2zej1aDz?|bk-1~fO<Yu6L(_rx;cu&~iSALB1L7px
z_~G037c0?Oaad`CFDAL^flQDUHadHfUpFWJS)rC5kXvuB$^&9_8BG_E83miP8m)O!
zqd?5H)LiY6%&X{=3350XTvg=cRIY-8S5aLms;-Pyi?slVB}CksoxiQzPC8{aK2(xK
zJ_d|e01e_+mph_*Z7rAqqjhysj#&^N#VvgDt<>@nP?WAP2402)B5Y&jz`ODV?lo5J
z_Pbp0&##K!tCf!oG_b}Ex89c%ruRt;<A9-XCpJI?;9Ux+0X~9H=nW*0LnlbTPZq{e
zE$hCE;0wAhPIAki*jk(<TU2{ljh5{krZO$91#)#&y1%Iy9AyVo-qTCei{s=U5F)-G
z(jq7nB_FGy27p(Qfu+j{J~5w7s~IY*=}t`SCc^ju(T#W03^)$k!#&K{%Lg{^aV17V
zh;_n~5bXK=pxF8cRC_W5luAoJ(@8WTbmdXmPj$5M)Nh+?h44q2Xg%Qu`xn_5HhJ*F
z4?u$gg}8%|Fu7Lb>}}!LrWEB9USDjxPIuwC*t|&M*<gI$3TuY^DZ=O7_i_iyG0(}`
z&_`1n8yO-5bMZDd7y$+mGw_ERP`<<tP~rW at L|W5nkx}2)KneSq)2BACwVdjz#6-O|
z7RLvX(1mtwJv`)#Xyvuk-JvxmR3}%bX_<E+&O5w-Tui0-u$qt32zY4{_2Lq6Mg}&7
zog<)!A}H at qf%$BV at d>*y4!Ne`ce3zA<(K3$3G;?adj`4r!|KA4mX2i6&A^U^)}K!P
zpZgNXe=90&K-`1WX71A}@(YG4XVRE$U<(WIe&o(ghsO^HgT4v=@jAPvYhyQ)N~Q#N
zbps9V%H+JpGMJm_Q3<8TcsQrKb`eftoNdBP at 5U+&uxb<b%`ZZSU<*M7lFlC1mWAI4
zm8Q-Y1$*jv&XzcrGOb39<F6~$s!ozmz>(!Um+aS5rA&#_2A`G!hBE0}=sOSoR7o*J
zw+T><@Oz~8*9CD|0UtzQPm87zpV-*Wq}2$p%o%qc at oFC=)_LNzy{QoYI}=i~*|8?A
z{`0a{TGsXX-(35D6E+oqJTfoJWXqo14vj&w- at 7FJUXx&OZDJLB+T$?d&v+FS13umW
zycRZ at 2&rW`YO(cqog)Hkhg|?*2NB>U51(Mo85CDl%r&+98_Wb*PeSxE+a);kXrvX_
z^iJvP1jV at yJN7*i$Fr;sF3j+f+aO8(a+93sID#vLVc$XXJdmvVJAn%9Yb2zkY$(s9
z4 at 1R^JD{-sPYgD at ypnbpR+vj7=-#`f1(q48XK0)S(k|W?0v`rloG55Sc)Pc33pXX#
zZ24n}eB5N5_C at F-T9C}hvYEM4VES^~blfk7l{P9_WY?sPEDzUsCdui~!{Ztq^tiX}
z!;6x5MNJrTESDwAGmN8+ at W>-=&l_KIeV$LUYjLW8>4_>F%$uGP;nb at o3u5Pwg~Q~N
zb|#TK0TLA%5t)5gKCGb^7dF?A=xx-H?^5cb0>FXF`+=eus&T*aYB))NxfO1s3J88l
zz|fYIUKBXTI at Y^5iU9%^tWi;NzL>Q*{8Y7ZeP64}rXou7-eNoAyhPzcfnSXn`Wp9~
z-T%pW;p)7xxL|i1e!}JP{Al90M>E<T(4B<Tu%mF*E&6S#=4zY7<zra^11=h~7*2}o
zc%S6Wd-&*#lcMo*grjfB_lKDn#+S6cReNorUqbc>nXuOtOtNR1cpw)r^ek(C6oEsK
zIRAJE-(n$|5zAPfA8EhsaO`;Ax*B8e)4{dXDqpC;k6-g%7TLFuE%QUS#%EGB+N4x%
zF1_Zw&9AbUFUJ^<n~Jr~`Un!rv at 5<1mfB20VGu*L(V at Qhrd%(JxlD*SjDEAikbx9$
zsL_mGH-rNfbbQ66ZnD2 at L6Ci7)g8@&<-gKK^Y-$?p$G~~WaKgHWg+_Fcj20$-HIo1
z+P+98rf(6wvLS^&6EVshlMOgJ at D$Q<NH^AvS~pHXH;P4j7g$tC_X;g1T=%024UeuE
z38i0$s=C)vU%fY8b+?KSpSt`S{_e(3s{&=L-(Twj<JM_4*Zb+TTBKq2ZZ#c33^)Ds
zjK-eRYPsVnjRR-9Rj?xfu%?i-q{`(DF$xv6Wzw#^Ihn!HW}5s5!VjA(-z*fmQCm|K
zy>iv+yE-2S&ja at BK^||R$<6C+fqf=G1CLk at D0dKkyW~7!y7y;7wXz=&{7yt?z=^F_
z=9TN|5q$3^+DiOafZa at MfF<Y|3dC at 84l9z+zZU~`K^tHfs8)#rx|^_<FG!via4eE}
zP_K}{H*DfjxBILT{cd0ZMTc8hOSK&O^2BcYm8;vGCIz_jX{~jHNV`)lX)cDxTI;@t
zCn@=S__?nuB&`;KI)@D5kyZvqxuS~3-)kG9e#rp9(Mj#mcDzR_3iyO8FBk=^)XUOu
z*ZP-xZCYqVw at P~-YFkK?tRW^<L%~5+zNr`<1FUU$nI8~SmjY$S@?{?J&J&H at yggGl
zH!O+OOZ*M_*bLdcSXCtZQLWbslqbP6Czlwp^&MRq))LbhM2km!e)z{loi#J)k|v|s
z(Q~pf@<ZKF_R`iREM0ZL9!OFg0y|yI$J3NGFBs2p=moY0mu{7G<reyV()2aediH**
zX<9&O^Bor&@L^M+3nanIhJbtfNg7DKn@`;*{jcZ(k`d_?MKOErapD+K8B(uP*Jv0g
zK9N=FrUd0JL9g+Cp9(d1N-x}~)$u3T8T_`(oL^o-%GvRm6KB_BD|wLZnnfyE-Roqn
zGrR&Pfib!#Jd7pm;U;xl)+e^!g8i?qCp at mHK~qts-{W>POz=Zu$qYiWKE)`@>Anuh
zvXUBnNSq`&EXqS8Hjkx7KNR*5;vxY!HeqC^AzrYoqar&@9QzCT$CG(5KW*~<al~d4
zq}QPGO$C9FK(k2Y&>5NlO>TDK-g{ivY^8{CZN37-q$+5%zT_;b6`{@|>ni_pquNWR
zruWhb4&>rjh}hTWGrPG)+4YjnoIaUI%C?+O#k9lQAVM14P%Jz{<Qb)Jy6Z<X%Fmtn
z;?3SBoPHs2T?fJ<b4eH;%FT+MxS|>veQ_UOJvSF3O~8L^7umzF!v8!ZqVo`0c!a at 9
zuiNu`MP+ybIx1XJ%1Q2eS{XCV-%aC}KqU5_t?ZzDCYJ|(Sgt6lner!#LdM<`q#qjZ
zi&;&Tb$#trn3-vIwFsSQ{`PbngP at ytxqa9Lm#An$9(EWcgBMT?udCva^Uk36?ZpLU
zwMKU6_sTnVisrh!6fi31pV<SW4upKhG7b28v(skBMIFIdsWy(xpE1+nN!HSl2nBcL
zKQ$OfnD&qj3 at Pd$H|v;Bj%HNhDg)^CL3XJPXzxEbJawTYO*qPyMcy*4bu3(}S7P{t
zZx_zoevk at hN{7PC>~?7vq`XW-89S?}(=ZxqwQw!geACH-PfzW;#?TXX1xuFn_AMAU
z+u4z~l6$qzQbIq_!kFN>{##?hZxNs=j2fHb8pbjnDcXP$)+uBnsdvhQYd~RwJcAIm
z0Vsm`VJ{aBubaAsWWKrgpw#-$fDV+>pD`a at v?6C+z_Wq{Y}-GQ4z*9UhQ=pvEi}yB
z;=hZ6f2M^%b_9h5Rgc-nQ^lMuO%Pxu7vEC+UPclEM_hS&XA+itjNpA(@C`}M)U2q-
zrx%i_@#Wfr+1>t2Tyu=Z5KBOo{rL at +sOWB)Sk1dd#F~08<Yj4u>DlS)q!~d at of0T#
zO0t)i4vV#JtyTLyK8-0=5<_7jh7!0k-$LAqb-;SdwAdBHtAWz?^fYS<?{wm&hpm<N
zuM>2{nOh%2ISoGJyYS%Ka?7JgMmLYx#7oSIV8X%<&Py-G-gV`o+|PH2m-UXb5&m1T
z+f2){n$~ebq-&jQ`6h35l1So#lY$%{5Up3;yVIvCzh;La`6%3BHTf$tcEXsZGblhN
zKN7!7sHSGd7K$1Ih)g2-8 at GXY7C>bHAZK?eQ!L{OOd#?BkL(B35kU0aW(Dq3DVlNr
z^b#>oV+%!1f&H-YpY;q#fQ}I*x<}!{#1?8q0E?`{8JPPp at H$AP>D~p3RrLXvFX|(R
zI7}-&<zpYWf*N0RL$)=Y;l^H3I6kTE!KF&8=>;N&R1X at qmHSK&Ca15XomwZH;HJFb
z&*gRAiZ8{5N*Bwrx6ltv3d5U(*Va9bkUu}l|DfnW$5W^AE77L{#J$3@!U2$H*&h&T
z<b|7SY_#uptdDXt=Sr+YoJe^<NN at F3(A`cjoo~?bP=PWG$YdaOQWW4$pb}I2A9|_g
zTls%~Bqi^OnW)&Jt!R)bcaSN=SH6Ng4$ba#a(N|X;Dk-?&QBr;U9V{4O>O9B5#Nt$
zBtwSR;^T)7L!GI4g!(xo^3Tg#80S*0x5Snl0pJ1V1DX`0zVPJ*K{Mh)Lu``JG12~H
z at 6Nu^v<;D=$uCJ0`e%#UAU&GQ6NT>Lril=6frUaxk!&ln5DRd2*Ku~5_T3%Do6 at A;
zHTeECOh1OqB;gpFG>lu>s-SSkGP)N#%4$UFeXqoqUZGx4Lc&^+c?(9-3Fb^tz8;Dh
zmb36GBAKR8JJ&0?YUuFf<_aMFIyH at FWB)V6i!A+Th}S{5X2u<;sEPSA3kgit9i~(p
zKH;XyPuB|Q0OE<hUk7iqO+aRI5eRwSC23`m3wq3HDeul7a1QrhlbJ49#+Fl%d}k$v
z-uWE-pnEi}6uj`X_-Bm`4q#OU2;8Gs5n29I8>sr}{vU4w&5UoVO%7S8W$$yFlCQ1p
zs>LYJJ`;KqwSFvowQ{%U&e#PAN*4YKN}7$Dya$Tjv_`;NgFv9ZAI3Z%h=e|!0t!>#
z{eTdTD^%5%KSu%bd}HNA7Z at Xf6Y+~+Uo*fq317K&IdH1LhXkd!68;Co5taFfDqz%v
z?En)9#8p#8)bdqJ`E)q;`vGCz`?HiP$J&~t!n=p*<=ecgJdFqmU at sF{gy6h$Zl3Qa
z;80&X;5|J}Hz87Je3)M^)#iW<BX}Nv?n;(u5(ol(@BI$U`6 at 8y=GPde^iqHOqv_2^
z)ot1``3#QZ2Lu4hpDs9 at dYuoLlDvBY`zSUy6p=EC437D0K~gE&pHX8Ui-BsD2i}r_
z#;~vQh4UuC?M|mR4Zxm`{;Z4>6Ml-DK4qx at s)A6ud*xg}X;&{0m)Md5I(-{jV^)nT
z1OixXqfviK6~lbCyz!&`rdt?=-V{J&LEMV1-^mQ13b$X at VpI$msGrG=Kj`5;Lt6t5
zpfp3x<l!C72buf{D5Jd9pqhX)r$*D*a9cwRtQVu(fU-s*Y03J_3m*(c6(``oe-CZP
z^A$)7RM=cx5?xTxSly#?K8~Kr=24{;tp47K(tSX573lWq+U;j4z)(Toqq{-66oe72
zCHd02FiDV^4U1<vYj{2yvmCR~64{NTaX|ZC#VHl|)du(o3E+w*L;z9q4uSb5fT5+c
z4nv^Cq3Hrv6FlbwU~JwafI2??&N|G(qf1&zr0CJ;{Te)Jaok)|;w54fX1EnK(=^Xa
zE3aSXbdEWo&en*oUrzy%oMoWqQXNQvaQ;q#AlmW8z5%Y)7gZT{*wU4Q at jYQ=XL|``
z2bUkf5r=#mW6C}QNyY1p(q~l6AU8#>Fn(OJUM4$4al42^e_lCM%Do&%87&c^Vn*6}
zgoMcOt48<}TV%tOj)<$B<!&I}!)7$$Xwp6EFI|Km3qBUL1Wj(MjrNPNUb=J}Qcur|
zNuYqDp&(Gsa$xK7MPYoVPt<m7Tq_=t+T+(TsC at qn9l{;PoiJ3wY-c{m!GEhCh1~zB
zf)Sr!0D8p;XziaRHk(QDIdklV49U5o)@2W0pG`h+it3%lKkXYVo%%MW4l7x(GMH8+
zESx#gI$;P^jRRp)X<CAUF(#672_$7;_Wr2NA-eTCQX2j&?8!EB?#MMOej%2rrrJ9+
z?j^L=_>h#%I;K5SEMC1tdOHSu^aS9zKgxde<fNwxxknde)wX&2GlDRmLZGK47@^{@
z<*TsDpF;uy;CP4V#*6A4uEghr;6%FV0`az<;g9m3yB$+E%{(M3$sW)A$7|A;30sq2
zg_VRC5l6dU#(N^hJa-IYSj at C{O5wvk%`aD!scsQ}4g~g~elhOTT2JdjimI?;$?)`7
z84n<#@lAN=hlvaU<l5go&M#MxNyT|B%2*1RqWv#UZ_a?~i{OF#EvXY_4Ce=49&>*N
z*6$9aZdz!VJFZ87-n{`$zdvM70sKPSW+&7h5qmdYr+-vGh2DGf$mH#KWsmnJ%Ox$&
zYEX<YzMxh}FB<n%*%y26ivM`9+%zp#(c54jSl>c`c=)#)jh|(^fUli^1c08w4~XY2
zviC{V#|U!>>^W%xWgmYDGg_8!;6r-zrsEADlWrkb#w!j0M~&tK_E`kLcCc at 0Z)N=K
z-N5Ou&zOH(X24d4rYlz<XrKqA_Aa=nwEefcj&A3rr?fbFPIGvVAgs-K#S$X%tNHx0
z0aRowd_J{@3E7RX?&l#BLap{p&kLxys8xUR5XvYlNtO~sh#t>K&*6D{yFpJLJ{f=3
z*x98QVZ0i{Pc;^oS0se9o$-;9CKmmVVwZ;;EF4|ljrSl$;3uF3zN-oWO>th)S_d#N
zs2E>+>77Xmlsi*Kp2(A0%k4~^nqapw?IoNP*qn*&S`iF(B2z5FzUn$@hj)Oh6kIzf
zDhgou(!!h-Yk3m==nHkA+j26+v-ixBpB2YV2(R5oYN=8(3JNeqo&zcl3or{`3;+W~
z0#r^tz7f;{U%LYXm=eH^nEQvw!BBvqI_7b%YpfzqXBR>$Iz#kT`ur~GGboJOVe4r4
zB})rN2iD at FWrftX(+?(`e5Ek<&L`ic7iG2d at pQBVPxkCAvtd=o?Lu`3oldW4<-fT_
z|A0XG!XHnepQI8T$MH}Yf7zOsx*Z~Nh<B(YE|Ob|_I+7tUD>duH9c#3cDhtPZTxY#
zk+7{0^r35e+nb3N-Tv}A_qrXa&3WEn*i34F=dhg5 at 9J<iU9NJ9ho`vnX#H>vpSXeF
zTW2x5MxQz(V~r7$QuPYr8y+O<x8O$g%xu?+mKXC)vuKj_pjD%t7rCv3>LB5xxb|Q@
zDd)l43N6)`*4dt0{x)dU%V7n*#L3yqy%>lWS}{H~y*;t^yc9 at T?m{ea;<UOg)*!mO
zcky_<z$#;Bm)9qyLv|<)n>Qd$cq_CvN7!8J0aPXY9#Y)mC5j1PP&58d;i5a5^520L
zAyLl~{eaNT2ak;d%9=^p>>jE5yt%45_+O|$t-q<iieEXZ6og?#QA6i{scscUfWTR=
zz{NRWh75G5Z{9a)uom5!uZ3J6_nGiAHMT{ti-LC}9>VQYvhj)Y8f%aR at wDnn`HA at I
z1Q`;3bHqU2w#OQ1Nf)(@%ksguK>T#!AXE4q>MOqf+g|SEuNB${$PLqt6azjLdlM6*
zig>O<CulVW at 5YEd-womHsgd_HGBke$7f6H5!escRQY{@|>TXn>)dpv8w6z1>(=9-j
z8g0aa^RU=l1So=eHOaIkQH7gI<Mj&cbub0F2)jQZ#AXKt>;<8zc+uc;PqKBkoS9un
zFO{&+ro)<^DF~7XX3Vd>>swQk^RZ{_nRd$v=N(AXiSh?gE5Hf~fH-#s0bJsy#ps)s
zOZDr at Lm(J8cM3XlJUvMlEH6?^TGFl<v9fxx3y+dggyE`zwIWDKFb*Nd?8*ibsjau@
z5o^9Ms*V_lME8cT=8o*QWeOcFoY|N&8)3bi<FJ|F?TfT_-qUgH!`|v&n+%lY*`CZw
zbzj}R)QdT^YV8ROsafN5LvkOiEgm#WSV`P%uv-|&M_ji<ulpGH=yF5}6gj>x*vgG!
zZ;@YC{lrN at Cx?b2SO;nx4Mjj}`L#g0Jb0nbNLR|#iehf{a~{ez7e0q7HWX>olI{n^
z3??$g9F<_<9D=vlP=~4BjCDl!^dHT&!hpasO!lqC48C}n*<X|iRMB|eG7RQoxffo3
z;K!vYw$?ees>gMvpXU>h+#wakGc~?3VA2nhcXa1SQnihlal);`=mKT5;gMDg4J4xr
zCcYh5s#cxSED)eC5D}dGBX33r5#@Q>LroHCr764R)+`MV<ybsFFJ`9&mXJlaJRd4l
zLd;R46L;$U&am_y3)^5UC{)Ivf_X`@xTZ8<(B065M4`l&k_H0mT|x*fOTx$|RkfxQ
z3%vs}!yt9U?0}-N2)-W(z0)lyLZYuEe_w>4{*eg!P6+wKzzAE2&p5nEaBdL+46G63
zzx2uf at HZ^R9_kN>qLHSpZSeZX-156rUYfr4g4hU#q8bS1ms$ItycVjoQl92tcH!OM
z|Fw<yH{JSAlbW7V8?*io=y$&~<^SmS-4Xcvco=9e{44Ff`Ad7JMYBdzhCEjfZXH#_
zK|)pV$bljW0Al|40q_5Y at QKNrbu-sqC<=u4{}BYAOMK at lU|2Qz9?<xGu>JH8d`;n6
z|M86&_I_1|w2Er?F+uM`>W3(t+=!af^!<DEQpHX}pxKH&Na~MRmA^%_j(2kVD!&U4
zbK!k!zW2v%bON{N{C`5DYk+To`|;CGX2k?f*kgb^*p$HKe$#^ZT|)cMR@~-4fOr|g
zpHQbja^&_{0Q<nnqSpV*H~UrS27$M&7;ii)!KVx0=FmHzEAPMPkMjpe0DtZFgn|`R
z^Ex19GY*u{L0JM21<k;oETVgq<^3Ce&7`Q`&l3+i2}himen6A}1rM+jfGz%qyLFcW
zl;R(A|K}aQSSAql_6!AJTJyF6d(r@?*+T}{3GnAIomeH7G?rAKD`2V$LO?u&fJ%7)
zSe-u+-+wjx*E7WcBkAYp1*=sFS74glg`idZ^`>8^F{}{%ud&8|=4g?rbIhE4fqwwf
z7pHOull;dvquMO1r2!eS`+)a<PAQ-QellL_{}-3f|360mS;YKzW0ZT(qErFUHhzNB
z{t_e=7A96kk3t4#@m#vBY0;sfFXlZ2q|i$eHbEpkK;*{wf21V-m#{Ve+joNh6(kkM
zEy~nCqe|U at l44>(V7i5&0x+xoJx~?r{|Kn+AD(jued>RVmh}@_>(71(>w}EMY^)ss
zOe#w!BL_1BeR^RVOG8GLTRf}VFAx|-tpN-wGiwuK&Y!4TjK9#XZZBc_3lQt)w|_qM
z=iR^VV|;FAY;0t2WNlys(nernWoNW=v;pv~ZZWWQ|GJ-z?XS`P-6|C`D<crSq=UYt
znZeyQ{hK!Y1pE4X&y-!QUfEdw7k4u;{o58O*;wgY{}+$oxP=(|djmvl9PP~jJg+-r
z`fu9*6Y1>lx8I)``B#=^c8*5>&BN~S+V27V>tX-mLBD2<nClPFw13mepWpsBGtKeG
zOdGzkv@|llJJ)x(_jk(_#8t(G#c74?&Gaqh^zH3!oPYA)b0d&}y_v0pjXm)#BAe3P
zj*FSugB(Om_3eq77-jT-ePsRvY7L}K%*@P548Vc?{(bxTAAWT2 at H*EmFDU)R>-LUD
zx6}6*yZ*f3Uk<`;kA96L&=hMSYmnJ*`u<vA1pP#b1AyNcL6-U;Q$~=P3!{Ux4Womp
zy^#^4F~B&C#%4~yw?<Az){H-4;~1^YfXm&C>}~EWo;#B2_kjMM>bpbst0BZ(e}Lir
zyD7fI$N#XHS-Ad~VGtl5ERFuQw(m{;Z{p{&M_^)m#s~oa0bNF5e#XQos&C-vV1&TT
z!p3N12rLyNkQwMs at 9seH{a!<V at x@Ozk<+&_V*EEOBrI&>qWz4R9#~zh%uK`_%$&N6
zVwU<QAYyhlHdX+GPY7gii at XPX0f6`E+x`j9_mjMR&Fy!<%?ONt$RVNIz7hksDLWWh
zsS&fYATY|Bfo_N34~Rd2>i`HpKy(5yen!m1oIlz2FUI*v|H(Li0{sDvaWMJ=>JQ;h
ztUo4VE)G`KU%-ECob2qsasONrcDOAxRK_YSc0j!eIL5HRKWuDUSlDL>4+9vH!qa(?
z6fjyI8a#)!g(ii4ojK?Y9YKYFYc|3g<iEUX^!PEVsl(@tFy|p{gUR~%^2uS=2D2Or
zridxu*t%XE*2&ean7z6myFC*H*BTay4P&4^4+uesXTo^43Y96m1_(LwgB&XKx!TG@
z__%dd59ZTq<JqgNU+1h-dY_ at a{iHjg)0E{pO1}sB5Iq=f3r}0k_l(pT%DbU|K40 at -
zk_A;0GQuA&ivx{Bhf+yEMmIyh$5;00&2o#%2s``8h<ED}jahFu8(VxF8&5UKqC82)
zR%;wvBhxv*%E!3)Q5$UcxljG^W9M<QL at zFx4%VR7R}rDmM9^q?QJ7vMj6- at Qqg{!b
zLFIda71yP at 8sTqbT?py6Plr+7lAnn~oFEo|K6R-UD|(a`Mc8nji24RWJh#RjsWF}s
zk<fQ03{w6}MKO}Kr*1zAUop{~kJw=kybcWBJR#>>J2cUw8Czl(=qw>bJ98ma_xU&N
z?|i($-m|ZRD5jA;=f#HydA?jOB)tR`2fCm@>-lk3qo{q|+2+-F3i~P*xAO7no1pnu
z at 9b^Slor&QrxCBeh<gR7lA5(}ulq69m@@cWl0T#JCK7_0mYAHDwcvwno3EK)BYqof
z_{mpSqZ|4}cwgF09q!<n-`SI|$7CJ)rC-skTdyri#-2<IM&Lb7SWSkG)F+>Y{{gv|
zPGIvT at r<T1&_t8 at 5=KA(+nem;gB3$RJbi*`p^F{Q%X!7H)F*PyIA_ at WUzlIxfIH@J
z>nS(|G^8V_%H+Hq+-zXYv{4iLVNk<-;1*a(C|<!bd=-0*p4^Pe8D3^*HSJ`Ldiuy_
zO&m%Omf*|vm$7$ykP$`rJs6TVvR?V^%s63^EWO at 3AK}Q0AJTL@>5 at Fblc+sj4pb}E
z#WO2~+4Zt6-GnMmt{)eP<CAexk`jo5<V0cos{KJkPZ+JjRETyX at tse#cPF!sABR4o
z^_Rlxcb0aHgx}ItlG-BAAT`wtu~v=CCc3smx8)Q3rC02$GpE<;eUkh3mgXMWpniZO
zgs;c4q};$n)cy=P at k~0(`4OF>Q4wi8Ygl)1arj~ZRAn=JsUqbzSu7EWCXsLU!o#ST
zdFZPUx+`^T^8Daz=11Ae)Wn+7cKi&jTN1M)hFv`_MEr7Z+!5#6nigSbe!OgV=>F2m
zL<uTkWskuuMpx^ME?p^zEi|br<J%@2{n}gleVdlSs|@5nO(wUvs+hZ=2HH_cT9d7h
z())6G#^V|b7KeDMr$Of8Ez-;-`>{2J$s)_VlZ}T>&vXU at laK5g6SWQ!b>Z4J$?{s=
z#gk9`AXW^Q-Uri`(rB1!48p>e!y3uKPKcpBq#2$e^3dxbDy(<sOK1neiVi*{!jG1b
zhie)IXkT|0=33oOlIx?lnY;xagcbGHxT&3V<0zM9$@F%A{E!sQZP%>2q#O}B?GMke
zR(-}PZW+WkX&f9-{!#YPg4jELnQTn92YpQ5<qw=w$H=o?jQCqTb}BYH$8|;C$918-
zm=Mirrgb%pQ8xnVc2GZB>jW=;5Kb5*mt(E5C#K}<QJr_{pgf#m5Ma>Nkh~^1w+ at vi
zRJ+VHPnK7N6T>AV>&Gj4;4acAE%qs2kfA;|wd`}e{P*L{uZPpFjm1Z->&QdO>hgRw
z{WH^nK4pqK#m!!t4H#i!5mF3JYfsumcVCM_$4WEgXg-ahFQ998CL?nh95U?A2aR>k
zC((*$<k5v?Gmu&w8Y=3xR!Awr)#@~ui6l;4Q{)_H;i^@u57Xs;sKE^__2p7cTI-Ej
zyIv0B<Le0xr7!y6==#RHCF)uM-*SuIysPh&Dqr)EhKIq#*)|d8M3up4d}3lD!W~QP
zg`BK{d8i+SEKowK5-X at whgeyM!G@)phufcF+DH5pGa=1bLO;1q at QaI$Z3p+Mj<d)b
z@(X)4%WR}TE%b|5ftfe+W>gHejtoT(l<hSrRoN5*MH~p+Pb%;kB6LL6l?I30WLLv7
z-M^Bya>FjI5X_OsO)5`B5lVUt4ChEJGo+z~>pC(-WDL at mEyqf&#KR{#viJuRqDF|S
zhhNtxW@!eHdK*SgjhtgD5Lf#N)S)Z0_zsZ8H$KSB_wLFJBfV&8Oo*eAPsn*5 at KL;o
zyu&zu$!x=nql0|cI8vJ7X{3-QdBbvSln{lyghKuFN=!;Lto>ATXa6`%MF at Wwvl1Wk
zcbX5WfdH-*Lgo(1%HY-u4xjIj6c_~5G~?|zD4Puf=}bS8r|-;#c|7ILl=sY!c at n|X
zMFVUgtiz|C?cE`?mwlS~%ETmfN_Bi=r{Dl?%6x_)vaYNuBcrV5v%4ZW*fyu at sq_>d
zvV>Svneb>B>313~xbNO>-CtZAf4qib)mv~1wLHvIj{Z<5jm1#tfa&1YcE#r#i-X%z
zZz&HaDt-|C`8CAOrl5L?2vVMw-pzArY$w#$kX#OL9y%0#Tqmpd-Q}lUcUXlW_ZY&s
zns<o>^)xcCL4Z8f5j}dx>fdDMp7h41z}R{P%BJ}<+tTZ at k2XK`67o`pHi7qoCXp<|
zl7Sw9u6neMzcifl0mfj at o=&b4KX$hpKU}O+EKYn<tXAAj%vY?OLYOtUo~ilkdo!{~
zQ!7%hUe-kd{Rza??U!!r6zfmJ;C&y;JvNnK=964CDtR8NI-JfOlM<KWKhN%!2z?1J
z_sH~%B%3frpeOSKMj<>Q4k1)enSIzVW?mp at hpEiT=kp}<Di>&w?<h*Qt)O|}Paflw
zV3YN~^$xK3a3k4rk>O6NhzCC;QE3jHL^mO^$EwuHHkcrnhbhA*6e{J+#3E at 5>z at +z
zmNHS8G%nbjQHVtdk40K;mYNRD3E|y?z2ztSJm*45eVGQ)D~EnA#>2Tu!?eMyZMjg1
zl52xTn`hhBx}kET$Q!f$iKB at _{<RMWsEyy(EIugFvP5s$zFvH4r@%{{gx(h*q`2(k
zko}pu>>Ib^qlCz=`ReWK>8I9VJROOPy at O12)`tfEDt_@!3t9Tp!vXQU)f>js!=CXp
z{WxXBzFa$q+eoium};1mWI#a>PLX_L3={D?UMR27wsQ(AcgssRa}pJi3Med!R7(%h
zi~>4?Bhp+5G#;fXT&58bczAtpF&TKon?C*GAgVE&E1O7<>yUK at p7y$W-Zo3kik#Fj
zr8vy~%c}vYH}(5S>%1I&6svw0TIe|wb7Q6B=X5ik_7gAcTdo?TIh${CJ{e)LiF(kZ
zvwgqNx)GF39qji1%(V>R<IJw&ueVrM{)Sye|AEBtkW_0lb#EE{ac@%vE0;jwVOfKC
z;-?BjZauSX>tqnIH`g?C-^5hfE0l`UsVaEo&`H9~iWAlI8dX0yt5fxJriEg|sU=m{
z%F45n@;+A+(~v2V^kkKu0c!itSz+77kRYF2kKT%-BC)}qQZ{uD?vc at yXGEZ)0yq8<
zHSrHMerpp{CCJSxgY%I4IXf?^H8nilBAPP#l=l)0zFMSsA$}%$$nuy*LApu-&pbtB
zMEfm*(_3px_T^l4uw~#*!Wzxme$REu5816L{;hS6ZGCt0h8@*Dq6Bf?z{EOQl^SQ&
zGhmNmbs-z84ybIBm4>uqRc9e~T}SQFM>E=Ggmd8q?y}y3E&h<E)P_vF_za70yLy2$
zq5d;f0W<t1bz<8uNSyR2Da;;MFzj$zC9tbmj7^p+Y6XsFAdlge>uVLNrLR=TNNa}L
zlc_gLb*{bg=T(6tuV6SAu9*teM~vz8a%bp_MyJx`9b at Q|p-&~&BkPl(4{s^mqIG|?
zmj-2;@AhNgAc%Q(TcL>XT5ygu-kI|Qd$p at y8&u^m*ke1!O+#sPP<~X~LB-+CQ*L#C
zZ(3?fl`5~z8{us=#S at qF-H1b}bAOjmUeOKcB|dU{<r)i3BY`);PY at 0-?Vg+QMqMNA
zTh!K}B|+-?OI_utpMMS{{RT%bCGxr%W1r^rJZ6^n89=BW at gI!PA|DLA(9JwlH52|`
z(tx06<L)8!#>S(e)8HfG4vl=Beo-f>401=b^xo>Jnrg1j0FizXji&XH_%|5xLkGvK
z0EEQ at 6)N5aJykSun-=v>I{SmF<#<(l0R}Gw$5aHJj8ddGU4xHaEyOXD+RA?O>dBah
zS&8!Sf%k#YJtxCE^=Hmg6!)N?ZT5oYm~sEXr|mIvs*m^9Y0xF;apTv6+%w&Fu*Yi<
zDkHcdU%bZ9Z3c$_!KZ0Q<#)Q)`HnSibuWnOJhEIYws1dGwo*3jo~(4~?!H#vJ51Um
zKBZb-*}ZzPPOYIcF-3ZQco{9-g*eT2o&@A`<96#_6<WoNnok}QL|Q3QSSXF=HSF=U
zpHeSt85LfBsMu8=&5JT0sVIIK6=7o()u8opt3w;h9F at fb+bDD_d_LBKg{@Cbd1Dj$
zvM>aaa35lQqtPS<^&zRJuvBs+zxdOGl!)din?R<B>(Ttj_)sn{gA7T^W9?nL_lsX&
zyIo8aFTAQdSS)*eeK at R=iE at mAYdu|j(+Qlhrtd-X<)Yt#nbvflMMUTwo#Q}eJU<O9
zLNlkYgu~h=b$*=<9?W9(6k04R9TZ7Ml2Bq>Zo={`XSse<L&L;&P9`pvu7sCza?dC;
z7FIt$c2S+ at gS>$pIwL%O@#G464r4Zht0?@HS#^YgB7Pe-37p$}m`Te6tb?QDG50_|
zHsi*Ppp8CbX(#q^5Xc_H?L8=&TbLC?VWP!^Xg3iGh1&Ko`I=8EoPqHSUY!BS6LPy)
zDIh7cqQc%Qm#rInf&r at ycPf=?HOqY`_dBpX%Wo~7;RA}uuI=vra(6wJe`Bz>VHv}q
z-y^ql)B at h)G at N78wqv1c&pfK&9zUmy%_$_)P*%;-tmjkR6svNE;S}1VA2y7Y19%i8
zPd5?aw%8jE7r&tHPE<}-IZnl45^T%r1XgVmJRF95gIf*NWve%T!^rrOzUXT4!NkhL
z-2Uf-enQ6!RwSis`^4*lB5NPtGD;kIFhJls=*y6v9USl at K?$r%QA|>>C^j1}&k24b
zxrShWNt+{8RM<&P(8HwL{&dUpRo#I8W3xb2cxw=6z>x7$5Z!1w&(?f}09VrDicPPX
zBcJXW?We_bBzrsRj>WeGN(H0d(yScVHtg1cv*ZZuRx;%&3&$b*0}ll*$kH1}NZuM?
z#|lkZA1_%&AO|dcg(C at Z<Z;6<efOO^j=&0~cfmxzuQ;t*nlLAi`;*%OZ_QH^R!V1=
zqo4`&#KKxqUS_E$SXjBosSHx`ux-O9PLuul5ry#7(KuyxlPWE(qX%V776CoDEkNgK
zuU8GML3<sQJoU%UvliUvpdX+*`B_bK at Mf-R`)T7%Tihza=`7~?>Xe>9H6PmR>~tsQ
zjaZ^97Ih&vxwoMiXPBeGmhX<Smk>2!w7*opD(o^e9ocAYn-ojG#(u+E%BJ>NvGh`r
zI^Kf7h82HkEAsR38|E98tBJFhyhM+hh?82{EGFS1<tP%R784{#B<XTFN7&Ikkl<2G
z7s*#tN5%^233_`qwAo}(Iys>NV1(7$d$C19?dU`J)C*WI;y>aiS=xPk4V)GEI!898
zb3G%KE98Ny#OiC}T1S4F_t+-;o<lHM3Bw6CryD0HP6c at Z2|^{@x@?J at q;3V-j~H<k
zNka?_rJP?$-Rw%XdDK;n$|hKayeM;=N!wzNb82ZAJ}%Q!?{0EBFKFVcJSC|AO2~P=
zJX-0Yh+0bDb6n_b>BPr#G)qc*k|jz%rG1?*xL+O-8jc~l(hMP5{)q5{gMNMMXP0*a
z>ur`DUk*MT*L$u#Un-7f at _f?#`K2N6`^hD$!5$7I5Gvk!dS|GJQgko;$A-tSo{ifH
z7)dn>$g;M0+uql<4;>z*5a*jcdq}x}p&LM)P*81cHFB7 at vIdgD&|3r1O{^*q8k+Qf
zge7Rs7!DbY8UFB$QsyfiD0;+dwYz;$OkCqI%a3T5U_4+X)Ly)a`VH$o+R{YQzz7Cg
zC;t*A;X4->-BOw+=I!@O#?P at z$uZdKwJrREj+QJt9uYngwZkWTgky}f_7%`?eiO`=
z0rMM^;!hodY at +bRy0pLX$OoV}$I9CH at 5s4VBqC4BPs&elxtBzuZ)Zn*;Y2GE>#Occ
zTSM~cn{*uR@(*STPD*WHQu8Mq3>+rRni-6kNlpAflf#plxY%wTi0Qx`FEM~F9mUSw
z?H7kV3zs71k4`N3h#94<5jm(G+TsBRpG5#U2A1oowzYQs=B%!)E8gQe_R_5f at E|pt
z+O^?alhcMktE`==B=D7Iw$!2J_V;(5 at zF3|P3|1Fiyi at 9_@$%hj*K{{l;mqPMLtu#
zJ4-Gxh7!F)n+d+7%ggQ9SHX)SV$tw11;Q3V^OnJ^n~RniXC>cG-Y>%29R(i-6|U#e
zI`4n~y16Pgb!HkB$C*(V$DzC!8RQhKCc~a>y3I{gP3O;#{{(aG9hpH|T9e?!EBg!L
zS*a)<-t$8%T}f;zt!aB!qt_qbyw)fsdP0DubVSyi#{Y2WsnHW}Co(3Z`Up8(IZ2XI
zWzIpamue_5cw!R_6A|%cUEB+shbQN*A1j<YdaKll_6e at H1J^2#Lr>;()k~}_3Y{tS
z2pieUinCeveX1CqUECCvZS8B-BZEWoL%`pAZ`MM0=bZN?0~(Y%a}gHoxT(uBpYb8F
zEVHEv$qP9Ip$xoA&(b|gKSRR6VZfD|F}IhJ{hG=`Y+`S`Tn?QN%XxaFk)YV=!$)w&
zz4 at YO!^ik$Pu}W>DJxrXUug1T+2y)-l60fCRAV44$I2MTV8pQ&6zAZVXT7_ovhr1A
z<>uZ8g;-0~Cl3jdh7$)9M_?T5cMBgTTEDtXegEaX@(~w-BEc7u0LrPU4O^nc*?Bq8
z8fTJwSErri&S+Y<G)hnJv&2tYmpiRFAu4k;Z$yt#QdIK5v`kAh-=7m at J*Nl<wb=2?
zU~Kch7p)7G4Uju5;k at P~iZM=DU!wTfdP&R`K$Fe>#m_n^4|{bV(s+Du<D=i%Gu(CM
zuVyb<b9mOqPh2NNs|Y9ua#^ky<@{BYrO+r2?K$*D+c5f$8TV=~N2L1Vg2CMT-;%Yk
z3TXAJtqn9ME;<B`3+uN`Xgzp1+f?1-2!HJG2sG)Bc at L-7y}-qBwHhOi=BS*hO}h!H
zw7D5LFzt&D5huc_o1i1;O4XD0ryYl#bbQlF<ri`6qgALZ&xNsM$%a^7^_KdBSf6bn
zlUQK{yOMW>i!fDqGoy4${{c^41}eFHd#Aw!3mbpO%QdzEYGHdr`-rClaP^5!!FJJ{
zM71)k4tS9sDJ26nkBL at PdI{Dr&q10GHkw<vhM^agn}?x=6Hlg~0}@g-y6utk{8_B>
zA~>;XWvN?R`k#K990sW`8nn;{QtoJ1Z|%$pj9)b)Z at sXbST!M^`t)fQq^`T8M#pj8
z;<x=WA{+8xI9aiZ$Ci8Fb-v28v7xOd78M_7bK}k7%k@)V|G9L!x7iE4?8T-|a8J7U
z`aF6BXos&9-cSjAAnN>)o%ZH<X?#wb^B64r3ZAvI>Em<FR|S=(?ggX_hnH8<oj)r4
z{9?^}XivCI9>V5|Wo9v<cJ4`}eSX075(aN3c^gd{C_?p?@p-PV@<AXM8)CIUTXkqX
zPOr>bcPLbq<|T&*q*+ro*^h6|D7`SB1cR<ukmkJ0yC7~jKJ&r4+IsJLpJyl%F84P$
zFjSxggoD3Rl at _nuyg2U{bbQ{cKK~H at NAb{)gh3P!PIu813x_)F;ZD|Jy${!5h+(j$
z#Q*t}Ag(((t)Hm?StAF1LwyH*AR7PYAv_wgGQYy+OdK3+zr*Id0xm$Z#lpye_?3}~
znKd8fUR at I<v6&$sr8=AJGg(^^BU3X8H+v&xH#rppHwyzULrMXD1YQCC+XHs!UG#0~
zjej1?10n`e0(>MQN-|<3w->l at yIB1n#a(S{<3<qv?q4A&haFm5yDMo|D`G<u$04+#
zAs0%ZDMe??7dN)$ljSDqukSPR%Sp4LcUMv>$gmpC+s at 3+I_$u%7BItmUd)&4<hkFj
zSQ3m0?YF&(q<rkR at 2;-BGSBCav(i&N+ml at x{NPOs99}%-#iJ*Cx($)VWSZOzM>~Od
z>$e}ua+Uc0)6-M$Dd^?JM_*cNeW84%x}fN;UzTP1++D7}gTBg;+1-I{x$adq^~U+a
zpWXSgC;Yvl{XxnsUwWiC{g7|UemhH}IGAR#n|%n=u9TCkEAVtpI96$Frm2didrhmF
zg$-5s09^re9b=eW7x`o}&Wgds^-tc#L%uGzXM0}U)8Vn-AZb+`KV~m*%w#?29BscJ
z5UpdMtgTtUb6gGcZ|8%6zV13?!OnNbph>Q9E<Sx3;_zmmgs?ax>dNp|$wb*i=<kJ0
zggEvYB{BxAXSn+cXFgxSTBEG7=EW`YXmB4GjX!gb&|DK35*QIUBTx{?2&4o`0y%-|
zgGZxZ2vj5$ZcVQ<0u|koMirf)QICK<jN<L=Die1W{g+`$S65g69GJy|mK)I}!+gA1
z;OOph_?5}*zg+ at N#s at Q3n_^zA*U8w=<{4SEMh;2XIT<I at yjY}IZRu)-4u`bFBBW)2
z^@5M(tej_qsv4Zk%k5x3qt%_v)8)s0`?-rtt<ufBY&R0&lHzmT1d?7O0`UR-Nu%n+
z?=-4jtMFH^-y}tC!#<x4wu8!E|MMI$8dJvS#daF+vrqf^G)%uBL#kB052Aox=1g+M
zNs<rpFrMt11mHebK9vqC6$j%EA{uP3l#>dDLX5=l4`qJP^4{oqJ3*iKHXN$=+fTG}
zIXCRpj`a71xbs>eKEfe5#R|@(`qUXFRNSPJI6POjs%vc_+8{&y`vS!+rEsTSrM at Yx
z-Y7ZuGs=sGDgQ^ogefPR at _||MO4@^Pzt?pz#;=<{vT at lt8Vq(9CO#M0v?V+nwy;We
z4>dFdkvH|WxLAe}Iz#C3mRp?C)+n^!)Nvw4TtkL4ajY3H at kn%OHF$-xF*2);Q(8*x
z;tYB(I9+I&4y8q;(0$aDCt}Qi<80vIq!8}hBIV4 at 3hMQ$>ktCBF*2Kl2xEaCoDI5I
zYDb5$lDgL#bl`RL3WJ^wMhAIx^<{(CSjGK<i5bmxNEsRC1H{sV4o;ca(W?yivxt?6
zm=B~1RLtcW8#?&{Tc{<fF%PNXm?jSLf{@c0s{`%>@CLfcE0mHvZjcu`aWmHNoFv|W
z%}ppB)6JoEn at OH#IAN?+jx9QNd=Q{eGG54ws4cXvgR<F)8+e_335*Kd^<lWLNQYx{
zEKsnSUdYotmXUOF97LMi2OZk651bK?8{{b#a}gocm at SZJs9Uq{7s5#<PsUQZ`HULD
z;{hFxUX4=1at-p3flM!OoKA(<@E9H9rk$8WhvIo<q at YI7`rP2tShLzhIt=p*bi_Qq
z0`lk|-?}`3l}g>BbsXdc)P>7&5!b6x>>RzQo!qC;!F?r+R&*6^LkHx=42L>|9=|3X
z at mfxYeI{fvhp%4Qx1_TMDNni0*n5O!^8hYzeSq4 at eT9o-O)YoJB1H#Zfg6cE|NZQ*
z412BT-{g7eA*s9@&%azwa}PoAZqOSz^nN`)Js08V?0hsjwI?Dxk4M80QP-zRo`%Dr
f4hLVR_^pLj!F8DyWwpU8Bcv6rckj-B9<}}kk59z)
literal 0
HcmV?d00001
diff --git a/rtemsbsd/ptpd/doc/PTPBASE-MIB.txt b/rtemsbsd/ptpd/doc/PTPBASE-MIB.txt
new file mode 100644
index 00000000..43030e6a
--- /dev/null
+++ b/rtemsbsd/ptpd/doc/PTPBASE-MIB.txt
@@ -0,0 +1,5710 @@
+PTPBASE-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+ enterprises,
+ MODULE-IDENTITY,
+ OBJECT-TYPE,
+ NOTIFICATION-TYPE,
+ Counter32,
+ Counter64,
+ Gauge32,
+ Integer32,
+ Unsigned32
+ FROM SNMPv2-SMI
+ TruthValue,
+ DisplayString,
+ TEXTUAL-CONVENTION
+ FROM SNMPv2-TC
+ InterfaceIndexOrZero
+ FROM IF-MIB
+ InetAddress,
+ InetAddressType
+ FROM INET-ADDRESS-MIB
+ OBJECT-GROUP,
+ NOTIFICATION-GROUP,
+ MODULE-COMPLIANCE
+ FROM SNMPv2-CONF;
+
+ptpbaseMIB MODULE-IDENTITY
+ LAST-UPDATED "201510272300Z" -- Oct 27, 2015 11:00:00 PM
+ ORGANIZATION "TICTOC Working Group, PTPd project"
+ CONTACT-INFO
+ "WG Email: tictoc at ietf.org
+
+ Vinay Shankarkumar
+ Cisco Systems,
+ Email: vinays at cisco.com
+
+ Laurent Montini,
+ Cisco Systems,
+ Email: lmontini at cisco.com
+
+ Tim Frost,
+ Symmetricom Inc.,
+ Email: tfrost at symmetricom.com
+
+ Greg Dowd,
+ Symmetricom Inc.,
+ Email: gdowd at symmetricom.com
+
+ Wojciech Owczarek
+ PTPd project,
+ Email: wojciech at owczarek.co.uk"
+ DESCRIPTION
+ "The MIB module for PTP version 2 (IEEE Std. 1588(TM)-2008)
+
+ Overview of PTP version 2 (IEEE Std. 1588(TM)-2008)
+
+ [IEEE 1588-2008] defines a protocol enabling precise
+ synchronization of clocks in measurement and control systems
+ implemented with packet-based networks, the Precision Time
+ Protocol Version 2 (PTPv2). This MIB does not address the
+ earlier version IEEE Std. 1588(TM)-2002 (PTPv1). The protocol
+ is applicable to network elements communicating using IP. The
+ protocol enables heterogeneous systems that include clocks of
+ various inherent precision, resolution, and stability to
+ synchronize to a grandmaster clock.
+
+ The protocol supports system-wide synchronization accuracy in
+ the sub-microsecond range with minimal network and local clock
+ computing resources. [IEEE 1588-2008] uses UDP/IP or
+ Ethernet and can be adapted to other mappings. It includes
+ formal mechanisms for message extensions, higher sampling rates,
+ correction for asymmetry, a clock type to reduce error
+ accumulation in large topologies, and specifications on how to
+ incorporate the resulting additional data into the
+ synchronization protocol. The [IEEE 1588-2008] defines
+ conformance and management capability also.
+
+ MIB description
+
+ This MIB is to support the Precision Time Protocol version 2
+ (PTPv2, hereafter designated as PTP) features of network element
+ system devices, when using the default PTP profile described in
+ [IEEE 1588-2008], or the Telecom Profile described in
+ [G.8265.1], when running over the IP network layer.
+
+ This MIB is based on the TICTOC-PTPBASE-MIB from 2012, with
+ added enhancements such as GM address, Offset from Master as
+ string, Mean Path Delay as string, parent IP address, and a set
+ of PTP traps.
+
+ It is envisioned this MIB will complement other managed objects
+ to be defined to monitor, measure the performance of the PTP
+ devices and telecom clocks.
+
+ Some other PTP profiles have their own MIBs defined as part of
+ the profile, and this MIB is not intended to replace those MIBs.
+
+
+ Acronyms:
+ ARB Arbitrary Timescale
+ E2E End-to-End
+ EUI Extended Unique Identifier.
+ GPS Global Positioning System
+ IANA Internet Assigned Numbers Authority
+ IP Internet Protocol
+
+ MAC Media Access Control
+ according to [IEEE 802.3-2008]
+ NIST National Institute of Standards and Technology
+ NTP Network Time Protocol (see IETF [RFC 5905])
+ OUI Organizational Unique Identifier
+ (allocated by the IEEE)
+ P2P Peer-to-Peer
+ PTP Precision Time Protocol
+ TAI International Atomic Time
+ TC Transparent Clock
+ UDP User Datagram Protocol
+ UTC Coordinated Universal Time
+
+ References:
+ [IEEE 1588-2008] IEEE Standard for A Precision Clock
+ Synchronization Protocol for Networked Measurement and
+ Control Systems, IEEE Std. 1588(TM)-2008, 24 July 2008.
+
+ [G.8265.1] Precision Time Protocol Telecom Profile for
+ Frequency Synchronization, ITU-T Recommendation G.8265.1,
+ October 2010.
+
+
+ As defined in [IEEE 1588-2008]:
+
+ Accuracy:
+ The mean of the time or frequency error between the clock under
+ test and a perfect reference clock, over an ensemble of
+ measurements. Stability is a measure of how the mean varies
+ with respect to variables such as time, temperature, and so on,
+ while the precision is a measure of the deviation of the error
+ from the mean.
+
+ Atomic process:
+ A process is atomic if the values of all inputs to the process
+ are not permitted to change until all of the results of the
+ process are instantiated, and the outputs of the process are
+ not visible to other processes until the processing of each
+ output is complete.
+
+ Boundary clock:
+ A clock that has multiple Precision Time Protocol (PTP) ports in
+ a domain and maintains the timescale used in the domain. It
+ may serve as the source of time, i.e., be a master clock, and
+ may synchronize to another clock, i.e., be a slave clock.
+
+ Boundary node clock:
+ A clock that has multiple Precision Time Protocol(PTP) ports in
+ a domain and maintains the timescale used in the domain. It
+
+ differs from a boundary clock in that the clock roles can
+ change.
+
+ Clock:
+ A node participating in the Precision Time Protocol (PTP) that
+ is capable of providing a measurement of the passage of time
+ since a defined epoch.
+
+ Domain:
+ A logical grouping of clocks that synchronize to each other
+ using the protocol, but that are not necessarily synchronized
+ to clocks in another domain.
+
+ End-to-end transparent clock:
+ A transparent clock that supports the use of the end-to-end
+ delay measurement mechanism between slave clocks and the master
+ clock. Each node must measure the residence time of PTP event
+ messages and accumulate it in Correction Field.
+
+ Epoch:
+ The origin of a timescale.
+
+ Event:
+ An abstraction of the mechanism by which signals or conditions
+ are generated and represented.
+
+ Foreign master:
+ An ordinary or boundary clock sending Announce messages to
+ another clock that is not the current master recognized by the
+ other clock.
+
+ Grandmaster clock:
+ Within a domain, a clock that is the ultimate source of time
+ for clock synchronization using the protocol.
+
+ Holdover:
+ A clock previously synchronized/syntonized to another clock
+ (normally a primary reference or a master clock) but now
+ free-running based on its own internal oscillator, whose
+ frequency is being adjusted using data acquired while it had
+ been synchronized/syntonized to the other clock. It is said to
+ be in holdover or in the holdover mode, as long as it is within
+ its accuracy requirements.
+
+ Link:
+ A network segment between two Precision Time Protocol ports
+ supporting the peer delay mechanism of this standard. The peer
+ delay mechanism is designed to measure the propagation time
+ over such a link.
+
+
+ Management node:
+ A device that configures and monitors clocks.
+
+ Master clock:
+ In the context of a single Precision Time Protocol
+ communication path, a clock that is the source of time to which
+ all other clocks on that path synchronize.
+
+ Message timestamp point:
+ A point within a Precision Time Protocol event message serving
+ as a reference point in the message. A timestamp is defined by
+ the instant a message timestamp point passes the reference
+ plane of a clock.
+
+ Multicast communication:
+ A communication model in which each Precision Time Protocol
+ message sent from any PTP port is capable of being received and
+ processed by all PTP ports on the same PTP communication path.
+
+ Node:
+ A device that can issue or receive Precision Time Protocol
+ communications on a network.
+
+ One-step clock:
+ A clock that provides time information using a single event
+ message.
+
+ On-pass support:
+ Indicates that each node in the synchronization chain from
+ master to slave can support IEEE-1588.
+
+ Ordinary clock:
+ A clock that has a single Precision Time Protocol port in a
+ domain and maintains the timescale used in the domain. It may
+ serve as a source of time, i.e., be a master clock, or may
+ synchronize to another clock, i.e., be a slave clock.
+
+ Parent clock:
+ The master clock to which a clock is synchronized.
+
+ Peer-to-peer transparent clock:
+ A transparent clock that, in addition to providing Precision
+ Time Protocol event transit time information, also provides
+ corrections for the propagation delay of the link connected to
+ the port receiving the PTP event message. In the presence of
+ peer-to-peer transparent clocks, delay measurements between
+ slave clocks and the master clock are performed using the
+ peer-to-peer delay measurement mechanism.
+
+
+ Phase change rate:
+ The observed rate of change in the measured time with respect
+ to the reference time. The phase change rate is equal to the
+ fractional frequency offset between the measured frequency and
+ the reference frequency.
+
+ PortNumber:
+ An index identifying a specific Precision Time Protocol port on
+ a PTP node.
+
+ Primary reference:
+ A source of time and or frequency that is traceable to
+ international standards.
+
+ Profile:
+ The set of allowed Precision Time Protocol features applicable
+ to a device.
+
+ Precision Time Protocol communication:
+ Information used in the operation of the protocol, transmitted
+ in a PTP message over a PTP communication path.
+
+ Precision Time Protocol communication path:
+ The signaling path portion of a particular network enabling
+ direct communication among ordinary and boundary clocks.
+
+ Precision Time Protocol node:
+ PTP ordinary, boundary, or transparent clock or a device that
+ generates or parses PTP messages.
+
+ Precision Time Protocol port:
+ A logical access point of a clock for PTP communications to the
+ communications network.
+
+ Recognized standard time source:
+ A recognized standard time source is a source external to
+ Precision Time Protocol that provides time and/or frequency as
+ appropriate that is traceable to the international standards
+ laboratories maintaining clocks that form the basis for the
+ International Atomic Time and Universal Coordinated Time
+ timescales. Examples of these are GPS, NTP, and NIST
+ timeservers.
+
+ Requestor:
+ The port implementing the peer-to-peer delay mechanism that
+ initiates the mechanism by sending a Pdelay_Req message.
+
+ Responder:
+
+ The port responding to the receipt of a Pdelay_Req message as
+ part of the operation of the peer-to-peer delay mechanism.
+
+ Synchronized clocks:
+ Two clocks are synchronized to a specified uncertainty if they
+ have the same epoch and their measurements of the time of a
+ single event at an arbitrary time differ by no more than that
+ uncertainty.
+
+ Syntonized clocks:
+ Two clocks are syntonized if the duration of the second is the
+ same on both, which means the time as measured by each advances
+ at the same rate. They may or may not share the same epoch.
+
+ Timeout:
+ A mechanism for terminating requested activity that, at least
+ from the requester's perspective, does not complete within the
+ specified time.
+
+ Timescale:
+ A linear measure of time from an epoch.
+
+ Traceability:
+ A property of the result of a measurement or the value of a
+ standard whereby it can be related to stated references,
+ usually national or international standards, through an
+ unbroken chain of comparisons all having stated uncertainties.
+
+ Translation device:
+ A boundary clock or, in some cases, a transparent clock that
+ translates the protocol messages between regions implementing
+ different transport and messaging protocols, between different
+ versions of [IEEE 1588-2008], or different PTP profiles.
+
+ Transparent clock:
+ A device that measures the time taken for a Precision Time
+ Protocol event message to transit the device and provides this
+ information to clocks receiving this PTP event message.
+
+ Two-step clock:
+ A clock that provides time information using the combination of
+ an event message and a subsequent general message.
+
+ The below table specifies the object formats of the various
+ textual conventions used.
+
+ Data type mapping Textual Convention SYNTAX
+ ------------------- ------------------ ---------------------
+ 5.3.2 TimeInterval ClockTimeInterval OCTET STRING(SIZE(1..255))
+
+ 5.3.3 Timestamp ClockTimestamp OCTET STRING(SIZE(6))
+ 5.3.4 ClockIdentity ClockIdentity OCTET STRING(SIZE(1..255))
+ 5.3.5 PortIdentity ClockPortNumber INTEGER(1..65535)
+ 5.3.7 ClockQuality ClockQualityClassType
+
+
+ Simple master-slave hierarchy, section 6.6.2.4 [IEEE 1588-2008]:
+
+ +---------------+
+ | Ordinary |
+ | Clock -1 |
+ | (GrandMaster) |
+ +-------M-------+
+ |
+ 1
+ |
+ +---------------S-----------------+
+ | Boundary |
+ | Clock -1 |
+ +-----M---------------------M-----+
+ | |
+ 2 3
+ | |
+ +-----S-----+ +------------S-------------+
+ | Ordinary | | Boundary |
+ | Clock -2 | | Clock -2 |
+ +-----------+ +-----M--------------M-----+
+ | |
+ 4 5
+ | |
+ +-----S-----+ +-----S-----+
+ | Ordinary | | Ordinary |
+ | Clock -3 | | Clock -4 |
+ +-----------+ +-----------+
+
+ Grandmaster
+
+ Boundary Clock(0-N) Ordinary Clocks(0-N)
+ Ordinary Clocks(0-N)
+
+
+ Relationship cardinality:
+ PTP system 1 : N PTP Clocks
+ PTP Clock 1 : 1 Domain
+ PTP Clock 1 : N PTP Ports
+ PTP Ports N : M Physical Ports (interface in IF-MIB)
+
+ Transparent clock diagram, section 6.7.1.3 of [IEEE 1588-2008]:
+
+
+ +-----------------------------+
+ | Boundary clock - 1 |
+ +-----------------------------+
+ | |
+ | |
+ +-- A --+ B
+ | |
+ +----------------------+ |
+ | Ordinary clock | |
+ +----------------------+ |
+ +----------------------+
+ +----------------------+ | End-to-end |
+ | Ordinary clock 1-1 |----------| transparent clock- |
+ +----------------------+ | 1 - 1 |
+ +----------------------+
+ |
+ C
+ |
+ +----------------------+
+ +----------------------+ | End-to-end |
+ | Ordinary clock 1-2 |----------| transparent clock- |
+ +----------------------+ | 1 - 2 |
+ +----------------------+
+
+
+ The MIB refers to the sections of [IEEE 1588-2008]."
+ -- revision log
+ -- ::= { mib-2 XXX }
+ -- 1.3.6.1.4.1.46649.1.1
+::= { enterprises 46649 1 1 }
+
+
+-- PTPd Private Enterprise Number (PEN)
+
+ClockDomainType ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "d"
+ STATUS current
+ DESCRIPTION
+ "The Domain is identified by an integer, the domainNumber, in
+ the range of 0 to 255. An integer value that is used to assign
+ each PTP device to a particular domain. The following values
+ define the valid domains.
+
+ Value Definition
+ --------- -------------------
+ 0 Default domain
+ 1 Alternate domain 1
+ 2 Alternate domain 2
+ 3 Alternate domain 3
+ 4 - 127 User-defined domains
+
+ 128 - 255 Reserved"
+ REFERENCE
+ "Section 7.1 Domains, Table 2 of [IEEE 1588-2008]"
+ SYNTAX Unsigned32 (0..255)
+
+
+ClockIdentity ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "The clock Identity is an 8-octet array and will be presented in
+ the form of a character array. The value of the
+ ClockIdentity should be taken from the IEEE EUI-64 individual
+ assigned numbers as indicated in Section 7.5.2.2.2 of
+ [IEEE 1588-2008]. The EUI-64 address is divided into the
+ following fields:
+
+ OUI bytes (0-2)
+ Extension identifier bytes (3-7)
+
+ The clock identifier can be constructed from existing EUI-48
+ assignments and here is an abbreviated example extracted from
+ section 7.5.2.2.2 [IEEE 1588-2008].
+
+ Company EUI-48 = 0xACDE4823456716
+ EUI-64 = ACDE48FFFE23456716
+
+ It is important to note the IEEE Registration Authority has
+ deprecated the use of MAC-48 in any new design."
+ REFERENCE
+ "Section 7.5.2.2.1 of [IEEE 1588-2008]"
+ SYNTAX OCTET STRING (SIZE (1..255))
+
+
+ClockIntervalBase2 ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "d"
+ STATUS current
+ DESCRIPTION
+ "The interval included in message types Announce, Sync,
+ Delay_Req, and Pdelay_Req as indicated in section 7.7.2.1 of
+ [IEEE 1588-2008].
+
+ The mean time interval between successive messages shall be
+ represented as the logarithm to the base 2 of this time
+ interval measured in seconds on the local clock of the device
+ sending the message. The values of these logarithmic attributes
+ shall be selected from integers in the range -128 to 127 subject
+ to further limits established in an applicable PTP profile."
+ REFERENCE
+ "Section 7.7.2.1 General interval specification of
+ [IEEE 1588-2008]"
+ SYNTAX Integer32 (-128..127)
+
+
+ClockMechanismType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "The clock type based on whether End to End or peer to peer
+ mechanisms are used. The mechanism used to calculate the Mean
+ Path Delay as indicated in Table 9 of [IEEE 1588-2008].
+
+ Delay mechanism Value(hex) Specification
+ --------------- ---------- -------------
+ E2E 01 The port is configured to use the
+ delay request-response mechanism.
+ P2P 02 The port is configured to use the
+ peer delay mechanism.
+ DISABLED FE The port does not implement the
+ delay mechanism."
+ REFERENCE
+ "Sections 8.2.5.4.4, 6.6.4, 7.4.2 of [IEEE 1588-2008]."
+ SYNTAX INTEGER {
+ e2e(1),
+ p2p(2),
+ disabled(254) }
+
+
+ClockInstanceType ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "d"
+ STATUS current
+ DESCRIPTION
+ "The instance of the Clock of a given clock type in a given
+ domain."
+ SYNTAX Unsigned32 (0..255)
+
+
+ClockPortNumber ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "d"
+ STATUS current
+ DESCRIPTION
+ "An index identifying a specific Precision Time Protocol (PTP)
+ port on a PTP node."
+ REFERENCE
+ "Sections 7.5.2.3 and 5.3.5 of [IEEE 1588-2008]"
+ SYNTAX Unsigned32 (0..65535)
+
+
+ClockPortState ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "This is the value of the current state of the protocol engine
+ associated with this port.
+ Port state Value Description
+ -----------------------------------------------------------
+
+ initializing 1 In this state a port initializes
+ its data sets, hardware, and
+ communication facilities.
+ faulty 2 The fault state of the protocol.
+ disabled 3 The port shall not place any
+ messages on its communication path.
+ listening 4 The port is waiting for the
+ announceReceiptTimeout to expire or
+ to receive an Announce message from
+ a master.
+ preMaster 5 The port shall behave in all respects
+ as though it were in the MASTER state
+ except that it shall not place any
+ messages on its communication path
+ except for Pdelay_Req, Pdelay_Resp,
+ Pdelay_Resp_Follow_Up, signaling, or
+ management messages.
+ master 6 The port is behaving as a master port.
+ passive 7 The port shall not place any messages
+ on its communication path except for
+ Pdelay_Req, Pdelay_Resp,
+ Pdelay_Resp_Follow_Up, or signaling
+ messages, or management messages that
+ are a required response to another
+ management message
+ uncalibrated 8 The local port is preparing to
+ synchronize to the master port.
+ slave 9 The port is synchronizing to the
+ selected master port."
+ REFERENCE
+ "Section 8.2.5.3.1 portState and 9.2.5 of
+ [IEEE 1588-2008]"
+ SYNTAX INTEGER {
+ initializing(1),
+ faulty(2),
+ disabled(3),
+ listening(4),
+ preMaster(5),
+ master(6),
+ passive(7),
+ uncalibrated(8),
+ slave(9) }
+
+
+ClockProfileType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "Clock Profile used. A profile is the set of allowed Precision
+ Time Protocol (PTP) features applicable to a device."
+ REFERENCE
+ "Section 3.1.30 and 19.3 PTP profiles of
+ [IEEE 1588-2008]"
+ SYNTAX INTEGER {
+ default(1),
+ telecom(2),
+ vendorspecific(3) }
+
+
+ClockQualityAccuracyType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "The ClockQuality as specified in section 5.3.7, 7.6.2.5 and
+ Table 6 of [IEEE 1588-2008].
+
+ The following values are not represented in the enumerated
+ values.
+
+ 0x01-0x1F Reserved
+ 0x32-0x7F Reserved
+
+ It is important to note that section 7.1.1 RFC2578 allows for
+ gaps and enumerate values to start with zero when indicated by
+ the protocol."
+ REFERENCE
+ "Section 5.3.7, 7.6.2.5 and Table 6 of
+ [IEEE 1588-2008]"
+ SYNTAX INTEGER {
+ reserved00(1), -- 0
+ nanoSecond25(32), -- 0x20
+ nanoSecond100(33), -- 0x21
+ nanoSecond250(34), -- 0x22
+ microSec1(35), -- 0x23
+ microSec2dot5(36), -- 0x24
+ microSec10(37), -- 0x25
+ microSec25(38), -- 0x26
+ microSec100(39), -- 0x27
+ microSec250(40), -- 0x28
+ milliSec1(41), -- 0x29
+ milliSec2dot5(42), -- 0x2A
+ milliSec10(43), -- 0x2B
+ milliSec25(44), -- 0x2C
+ milliSec100(45), -- 0x2D
+ milliSec250(46), -- 0x2E
+ second1(47), -- 0x2F
+ second10(48), -- 0x30
+ secondGreater10(49), -- 0x31
+ unknown(254), -- 0xFE
+ reserved255(255) -- 0xFF
+ }
+
+
+ClockQualityClassType ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "d"
+ STATUS current
+ DESCRIPTION
+ "The ClockQuality as specified in section 5.3.7, 7.6.2.4 and
+ Table 5 of [IEEE 1588-2008].
+
+ Value Description
+ -------- ------------------------------------------------
+ 0 Reserved to enable compatibility with future
+ versions.
+ 1-5 Reserved
+ 6 Shall designate a clock that is synchronized
+ to a primary reference time source. The
+ timescale distributed shall be PTP. A
+ clockClass 6 clock shall not be a slave to
+ another clock in the domain.
+ 7 Shall designate a clock that has previously
+ been designated as clockClass 6 but that has
+ lost the ability to synchronize to a primary
+ reference time source and is in holdover mode
+ and within holdover specifications. The
+ timescale distributed shall be PTP. A
+ clockClass 7 clock shall not be a slave to
+ another clock in the domain.
+ 8 Reserved.
+ 9-10 Reserved to enable compatibility with future
+ versions.
+ 11-12 Reserved.
+ 13 Shall designate a clock that is synchronized
+ to an application-specific source of time.
+ The timescale distributed shall be ARB. A
+ clockClass 13 clock shall not be a slave to
+ another clock in the domain.
+ 14 Shall designate a clock that has previously
+ been designated as clockClass 13 but that
+ has lost the ability to synchronize to an
+ application-specific source of time and is
+ in holdover mode and within holdover
+ specifications. The timescale distributed
+ shall be ARB. A clockClass 14 clock shall
+ not be a slave to another clock in the domain.
+ 15-51 Reserved.
+ 52 Degradation alternative A for a clock of
+ clockClass 7 that is not within holdover
+ specification. A clock of clockClass 52
+ shall not be a slave to another clock in
+
+ the domain.
+ 53-57 Reserved.
+ 58 Degradation alternative A for a clock of
+ clockClass 14 that is not within holdover
+ specification. A clock of clockClass 58 shall
+ not be a slave to another clock in the domain.
+ 59-67 Reserved.
+ 68-122 For use by alternate PTP profiles.
+ 123-127 Reserved.
+ 128-132 Reserved.
+ 133-170 For use by alternate PTP profiles.
+ 171-186 Reserved.
+
+ 187 Degradation alternative B for a clock of
+ clockClass 7 that is not within holdover
+ specification. A clock of clockClass 187 may
+ be a slave to another clock in the domain.
+ 188-192 Reserved.
+ 193 Degradation alternative B for a clock of
+ clockClass 14 that is not within holdover
+ specification. A clock of clockClass 193 may
+ be a slave to another clock in the domain.
+ 194-215 Reserved.
+ 216-232 For use by alternate PTP profiles.
+ 233-247 Reserved.
+ 248 Default. This clockClass shall be used if
+ none of the other clockClass definitions apply.
+ 249-250 Reserved.
+ 251 Reserved for version 1 compatibility; see Clause 18.
+ 252-254 Reserved.
+ 255 Shall be the clockClass of a slave-only clock; see
+ 9.2.2."
+ REFERENCE
+ "Section 5.3.7, 7.6.2.4 and Table 5 of
+ [IEEE 1588-2008]."
+ SYNTAX Unsigned32 (0..255)
+
+
+ClockRoleType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "The Clock Role. The protocol generates a Master Slave
+ relationship among the clocks in the system.
+
+ Clock Role Value Description
+ --------------------------------------------------------------
+ Master clock 1 A clock that is the source of
+ time to which all other clocks on
+ that path synchronize.
+
+
+ Slave clock 2 A clock which synchronizes to
+ another clock (master)."
+ SYNTAX INTEGER {
+ master(1),
+ slave(2) }
+
+
+ClockStateType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "The clock state returned by PTP engine.
+
+ Clock State Value Description
+ --------------------------------------------------------------
+ Freerun state 1 Applies to a slave device that is not
+ locked to a master. This is the initial
+ state a slave starts out with when it
+ is not getting any PTP packets from the
+ master or because of some other input
+ error (erroneous packets, etc).
+
+ Holdover state 2 In this state the slave device is
+ locked to a master but communication
+ with the master is lost or the
+ timestamps in the ptp packets are
+ incorrect. But since the slave was
+ locked to the master, it can run with
+ the same accuracy for sometime. The
+ slave can continue to operate in this
+ state for some time. If communication
+ with the master is not restored for a
+ while, the device is moved to the
+ FREERUN state.
+
+ Acquiring state 3 The slave device is receiving packets
+ from a master and is trying to acquire
+ a lock.
+
+ Freq_locked state 4 Slave device is locked to the Master
+ with respect to frequency, but not phase
+ aligned
+
+ Phase_aligned state 5 Locked to the master with respect to
+ frequency and phase."
+ SYNTAX INTEGER {
+ freerun(1),
+ holdover(2),
+ acquiring(3),
+ frequencyLocked(4),
+ phaseAligned(5) }
+
+
+ClockTimeSourceType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "The ClockQuality as specified in section 5.3.7, 7.6.2.6 and
+ Table 7 of [IEEE 1588-2008].
+
+ The following values are not represented in the enumerated
+ values.
+
+ 0xF0-0xFE For use by alternate PTP profiles
+ 0xFF Reserved
+
+ It is important to note that section 7.1.1 RFC2578 allows for
+ gaps and enumerate values to start with zero when indicated by
+ the protocol."
+ REFERENCE
+ "Section 5.3.7, 7.6.2.6 and Table 7 of
+ [IEEE 1588-2008]."
+ SYNTAX INTEGER {
+ atomicClock(16), -- 0x10
+ gps(32), -- 0x20
+ terrestrialRadio(48), -- 0x22
+ ptp(64), -- 0x40
+ ntp(80), -- 0x50
+ handSet(96), -- 0x60
+ other(144), -- 0x90
+ internalOsillator(160) -- 0xA0
+ }
+
+
+ClockTimeInterval ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "This textual convention corresponds to the TimeInterval
+ structure indicated in section 5.3.2 of [IEEE 1588-2008].
+ It will be presented in the form of a character array.
+
+ The TimeInterval type represents time intervals.
+
+ struct TimeInterval
+ {
+ Integer64 scaledNanoseconds;
+ };
+
+ The scaledNanoseconds member is the time interval expressed in
+ units of nanoseconds and multiplied by 2**16.
+
+
+ Positive or negative time intervals outside the maximum range
+ of this data type shall be encoded as the largest positive and
+ negative values of the data type, respectively.
+
+ For example, 2.5 ns is expressed as 0000 0000 0002 8000 in
+ Base16."
+ REFERENCE
+ "Section 5.3.2 and setion 7.7.2.1 Timer interval
+ specification of [IEEE 1588-2008]"
+ SYNTAX OCTET STRING (SIZE (1..255))
+
+
+ClockTxModeType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "Transmission mode.
+
+ unicast. Using unicast communication channel.
+
+ multicast. Using Multicast communication channel.
+
+ multicast-mix. Using multicast-unicast communication channel"
+ SYNTAX INTEGER {
+ unicast(1),
+ multicast(2),
+ multicastmix(3) }
+
+
+ClockType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "The clock types as defined in the MIB module description."
+ REFERENCE
+ "Section 6.5.1 of [IEEE 1588-2008]."
+ SYNTAX INTEGER {
+ ordinaryClock(1),
+ boundaryClock(2),
+ transparentClock(3),
+ boundaryNode(4) }
+
+PtpdAlarmType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "Alarm identifier (type) as used by PTPd"
+ REFERENCE
+ "PTPd 2.3.2"
+ SYNTAX INTEGER {
+ portState(0),
+ ofmThreshold(1),
+ ofmSeconds(2),
+ clockStep(3),
+ noSync(4),
+ noDelay(5),
+ masterChange(6),
+ networkFault(7),
+ fastAdj(8),
+ timePropertiesChange(9),
+ domainMismatch(10) }
+
+PtpdAlarmState ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "Alarm state as used by PTPd"
+ REFERENCE
+ "PTPd 2.3.2"
+ SYNTAX INTEGER {
+ unset(0),
+ set(1),
+ cleared(2) }
+
+ptpbaseMIBNotifs OBJECT IDENTIFIER
+ -- 1.3.6.1.4.1.46649.1.1.0
+::= { ptpbaseMIB 0 }
+
+ptpBasePortUnexpectedState NOTIFICATION-TYPE
+ OBJECTS {
+ ptpbaseClockPortRunningState}
+ STATUS current
+ DESCRIPTION
+ "Alarm: port has gone into a state other than expected in current configuration."
+ -- 1.3.6.1.4.1.46649.1.1.0.1
+::= { ptpbaseMIBNotifs 1 }
+
+
+ptpBasePortExpectedState NOTIFICATION-TYPE
+ OBJECTS {
+ ptpbaseClockPortRunningState}
+ STATUS current
+ DESCRIPTION
+ "Alarm cleared: port has returned to expected state."
+ -- 1.3.6.1.4.1.46649.1.1.0.2
+::= { ptpbaseMIBNotifs 2 }
+
+
+ptpBaseSlaveOfmThresholdExceeded NOTIFICATION-TYPE
+ OBJECTS {
+ ptpbaseClockCurrentDSOffsetFromMaster,
+ ptpbaseClockCurrentDSOffsetFromMasterString,
+ ptpbaseClockCurrentDSOffsetFromMasterThresholdNs}
+ STATUS current
+ DESCRIPTION
+ "Alarm: slave Offset From Master has exceeded defined threshold."
+ -- 1.3.6.1.4.1.46649.1.1.0.3
+::= { ptpbaseMIBNotifs 3 }
+
+
+ptpBaseSlaveOfmThresholdAcceptable NOTIFICATION-TYPE
+ OBJECTS {
+ ptpbaseClockCurrentDSOffsetFromMaster,
+ ptpbaseClockCurrentDSOffsetFromMasterString,
+ ptpbaseClockCurrentDSOffsetFromMasterThresholdNs}
+ STATUS current
+ DESCRIPTION
+ "Alarm cleared: slave Offset From Master has returned to a value below threshold."
+ -- 1.3.6.1.4.1.46649.1.1.0.4
+::= { ptpbaseMIBNotifs 4 }
+
+
+ptpBaseSaveClockStep NOTIFICATION-TYPE
+ OBJECTS {
+ ptpbaseClockCurrentDSOffsetFromMaster,
+ ptpbaseClockCurrentDSOffsetFromMasterString}
+ STATUS current
+ DESCRIPTION
+ "Event: Clock has been stepped"
+ -- 1.3.6.1.4.1.46649.1.1.0.5
+::= { ptpbaseMIBNotifs 5 }
+
+
+ptpBaseSlaveNoSync NOTIFICATION-TYPE
+ STATUS current
+ DESCRIPTION
+ "Alarm: PTP slave stopped receiving Sync messages"
+ -- 1.3.6.1.4.1.46649.1.1.0.6
+::= { ptpbaseMIBNotifs 6 }
+
+
+ptpBaseSlaveReceivingSync NOTIFICATION-TYPE
+ STATUS current
+ DESCRIPTION
+ "Alarm cleared: PTP slave is receiving sync after loss of sync."
+ -- 1.3.6.1.4.1.46649.1.1.0.7
+::= { ptpbaseMIBNotifs 7 }
+
+
+ptpBaseSlaveNoDelay NOTIFICATION-TYPE
+ STATUS current
+ DESCRIPTION
+ "Alarm: PTP slave is not receiving Delay / pDelay response."
+ -- 1.3.6.1.4.1.46649.1.1.0.8
+::= { ptpbaseMIBNotifs 8 }
+
+
+ptpBaseSlaveReceivingDelay NOTIFICATION-TYPE
+ STATUS current
+ DESCRIPTION
+ "Alarm cleared: PTP slave is receiving Delay / pDelay response after loss of Delay / pDelay."
+ -- 1.3.6.1.4.1.46649.1.1.0.9
+::= { ptpbaseMIBNotifs 9 }
+
+
+ptpBaseBestMasterChange NOTIFICATION-TYPE
+ OBJECTS {
+ ptpbaseClockParentDSParentPortIdentity,
+ ptpbaseClockParentDSParentPortAddressType,
+ ptpbaseClockParentDSParentPortAddress,
+ ptpbaseClockParentDSGMClockIdentity,
+ ptpbaseClockParentDSGMClockPriority1,
+ ptpbaseClockParentDSGMClockPriority2,
+ ptpbaseClockParentDSGMClockQualityClass,
+ ptpbaseClockTimePropertiesDSCurrentUTCOffsetValid,
+ ptpbaseClockTimePropertiesDSCurrentUTCOffset,
+ ptpbaseClockPortRunningState}
+ STATUS current
+ DESCRIPTION
+ "Event: PTP node has changed its Best Master (can be self)."
+ -- 1.3.6.1.4.1.46649.1.1.0.10
+::= { ptpbaseMIBNotifs 10 }
+
+
+ptpBasePortNetworkFault NOTIFICATION-TYPE
+ OBJECTS {
+ ptpbaseClockPortRunningInterfaceIndex}
+ STATUS current
+ DESCRIPTION
+ "Alarm: a network fault has occured and PTP port is not usable."
+ -- 1.3.6.1.4.1.46649.1.1.0.11
+::= { ptpbaseMIBNotifs 11 }
+
+
+ptpBasePortNetworkFaultCleared NOTIFICATION-TYPE
+ OBJECTS {
+ ptpbaseClockPortRunningInterfaceIndex}
+ STATUS current
+ DESCRIPTION
+ "Alarm cleared: PTP port has recovered from a network fault."
+ -- 1.3.6.1.4.1.46649.1.1.0.12
+::= { ptpbaseMIBNotifs 12 }
+
+
+ptpBaseClockFreqAdjFast NOTIFICATION-TYPE
+ OBJECTS {
+ freqAdjCurrentValue,
+ freqAdjStatsPeriodSeconds,
+ freqAdjStatsMean,
+ freqAdjStatsStatsMedian,
+ freqAdjStatsStdDev}
+ STATUS current
+ DESCRIPTION
+ "Alarm: PTP Slave clock frequency adjustment is changing rapidly."
+ -- 1.3.6.1.4.1.46649.1.1.0.13
+::= { ptpbaseMIBNotifs 13 }
+
+
+ptpBaseClockFreqAdjNormal NOTIFICATION-TYPE
+ OBJECTS {
+ freqAdjCurrentValue,
+ freqAdjStatsPeriodSeconds,
+ freqAdjStatsMean,
+ freqAdjStatsStatsMedian,
+ freqAdjStatsStdDev}
+ STATUS current
+ DESCRIPTION
+ "Alarm cleared: PTP Slave clock frequency adjustment has returned to stable levels."
+ -- 1.3.6.1.4.1.46649.1.1.0.14
+::= { ptpbaseMIBNotifs 14 }
+
+
+ptpBaseSlaveOffsetFromMasterSeconds NOTIFICATION-TYPE
+ OBJECTS {
+ ptpbaseClockCurrentDSOffsetFromMaster,
+ ptpbaseClockCurrentDSOffsetFromMasterString}
+ STATUS current
+ DESCRIPTION
+ "Alarm: PTP Slave Offset From Master has reached over 1 second."
+ -- 1.3.6.1.4.1.46649.1.1.0.15
+::= { ptpbaseMIBNotifs 15 }
+
+
+ptpBaseSlaveOffsetFromMasterSubSeconds NOTIFICATION-TYPE
+ OBJECTS {
+ ptpbaseClockCurrentDSOffsetFromMaster,
+ ptpbaseClockCurrentDSOffsetFromMasterString}
+ STATUS current
+ DESCRIPTION
+ "Alarm cleared: PTP Slave Offset From Master dropped below 1 second after a previous value of over 1 second."
+ -- 1.3.6.1.4.1.46649.1.1.0.16
+::= { ptpbaseMIBNotifs 16 }
+
+
+ptpBaseTimePropertiesChange NOTIFICATION-TYPE
+ OBJECTS {
+ ptpbaseClockTimePropertiesDSCurrentUTCOffsetValid,
+ ptpbaseClockTimePropertiesDSCurrentUTCOffset,
+ ptpbaseClockTimePropertiesDSLeap59,
+ ptpbaseClockTimePropertiesDSLeap61,
+ ptpbaseClockTimePropertiesDSTimeTraceable,
+ ptpbaseClockTimePropertiesDSFreqTraceable,
+ ptpbaseClockTimePropertiesDSPTPTimescale,
+ ptpbaseClockTimePropertiesDSSource}
+ STATUS current
+ DESCRIPTION
+ "Event: change in the Time Properties dataset."
+ -- 1.3.6.1.4.1.46649.1.1.0.17
+::= { ptpbaseMIBNotifs 17 }
+
+ptpBaseDomainMismatch NOTIFICATION-TYPE
+ OBJECTS {
+ ptpbaseClockDefaultDSDomainNumber,
+ ptpBaseClockPortDSLastMismatchedDomain }
+ STATUS current
+ DESCRIPTION
+ "Alarm: no packets from configured PTP domain are being received."
+ -- 1.3.6.1.4.1.46649.1.1.0.18
+::= { ptpbaseMIBNotifs 18 }
+
+ptpBaseDomainMismatchCleared NOTIFICATION-TYPE
+ OBJECTS {
+ ptpbaseClockDefaultDSDomainNumber}
+ STATUS current
+ DESCRIPTION
+ "Alarm cleared: Clock is receiving packets from configured PTP domain."
+ -- 1.3.6.1.4.1.46649.1.1.0.19
+::= { ptpbaseMIBNotifs 19 }
+
+
+ptpbaseMIBObjects OBJECT IDENTIFIER
+ -- 1.3.6.1.4.1.46649.1.1.1
+::= { ptpbaseMIB 1 }
+
+ptpbaseMIBSystemInfo OBJECT IDENTIFIER
+ -- 1.3.6.1.4.1.46649.1.1.1.1
+::= { ptpbaseMIBObjects 1 }
+
+ptpbaseMIBClockInfo OBJECT IDENTIFIER
+ -- 1.3.6.1.4.1.46649.1.1.1.2
+::= { ptpbaseMIBObjects 2 }
+
+
+ptpbaseSystemTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseSystemEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of count information about the PTP system for all
+ domains."
+ -- 1.3.6.1.4.1.46649.1.1.1.1.1
+::= { ptpbaseMIBSystemInfo 1 }
+
+
+ptpbaseSystemEntry OBJECT-TYPE
+ SYNTAX PtpbaseSystemEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing count information about a
+ single domain. New row entries are added when the PTP clock for
+ this domain is configured, while the unconfiguration of the PTP
+ clock removes it."
+ INDEX {
+ ptpDomainIndex,
+ ptpInstanceIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.1.1.1
+::= { ptpbaseSystemTable 1 }
+
+
+PtpbaseSystemEntry ::= SEQUENCE {
+
+ ptpDomainIndex ClockDomainType,
+ ptpInstanceIndex ClockInstanceType,
+ ptpDomainClockPortsTotal Gauge32 }
+
+
+ptpDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices. The Clock Domain is a logical group of
+ clocks and devices that synchronize with each other using the
+ PTP protocol.
+
+ 0 Default domain
+ 1 Alternate domain 1
+ 2 Alternate domain 2
+ 3 Alternate domain 3
+ 4 - 127 User-defined domains
+ 128 - 255 Reserved"
+ -- 1.3.6.1.4.1.46649.1.1.1.1.1.1.1
+::= { ptpbaseSystemEntry 1 }
+
+
+ptpInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the Clock for this
+ domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.1.1.1.2
+::= { ptpbaseSystemEntry 2 }
+
+
+ptpDomainClockPortsTotal OBJECT-TYPE
+ SYNTAX Gauge32
+ UNITS "ptp ports"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the total number of clock ports
+ configured within a domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.1.1.1.3
+::= { ptpbaseSystemEntry 3 }
+
+
+ptpbaseSystemDomainTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseSystemDomainEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the PTP system for all clock modes
+ -- ordinary, boundary or transparent."
+ -- 1.3.6.1.4.1.46649.1.1.1.1.2
+::= { ptpbaseMIBSystemInfo 2 }
+
+
+ptpbaseSystemDomainEntry OBJECT-TYPE
+ SYNTAX PtpbaseSystemDomainEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing information about a single
+ clock mode for the PTP system. A row entry gets added when PTP
+ clocks are configured on the router."
+ INDEX {
+ ptpbaseSystemDomainClockTypeIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.1.2.1
+::= { ptpbaseSystemDomainTable 1 }
+
+
+PtpbaseSystemDomainEntry ::= SEQUENCE {
+
+ ptpbaseSystemDomainClockTypeIndex ClockType,
+ ptpbaseSystemDomainTotals Unsigned32 }
+
+
+ptpbaseSystemDomainClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.1.2.1.1
+::= { ptpbaseSystemDomainEntry 1 }
+
+
+ptpbaseSystemDomainTotals OBJECT-TYPE
+ SYNTAX Unsigned32
+ UNITS "domains"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the total number of PTP domains for this
+ particular clock type configured in this node."
+ -- 1.3.6.1.4.1.46649.1.1.1.1.2.1.2
+::= { ptpbaseSystemDomainEntry 2 }
+
+
+ptpbaseSystemProfile OBJECT-TYPE
+ SYNTAX ClockProfileType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP Profile implemented on the
+
+ system."
+ REFERENCE
+ "Section 19.3 PTP profiles of [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.1.3
+::= { ptpbaseMIBSystemInfo 3 }
+
+
+ptpbaseClockCurrentDSTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockCurrentDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the PTP clock Current Datasets for
+ all domains."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.1
+::= { ptpbaseMIBClockInfo 1 }
+
+
+ptpbaseClockCurrentDSEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockCurrentDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing information about a single
+ PTP clock Current Datasets for a domain."
+ REFERENCE
+ "1588 Version 2.0 Section 8.2.2 currentDS data set member
+ specifications of [IEEE 1588-2008]"
+ INDEX {
+ ptpbaseClockCurrentDSDomainIndex,
+ ptpbaseClockCurrentDSClockTypeIndex,
+ ptpbaseClockCurrentDSInstanceIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.1.1
+::= { ptpbaseClockCurrentDSTable 1 }
+
+
+PtpbaseClockCurrentDSEntry ::= SEQUENCE {
+
+ ptpbaseClockCurrentDSDomainIndex ClockDomainType,
+ ptpbaseClockCurrentDSClockTypeIndex ClockType,
+ ptpbaseClockCurrentDSInstanceIndex ClockInstanceType,
+ ptpbaseClockCurrentDSStepsRemoved Unsigned32,
+ ptpbaseClockCurrentDSOffsetFromMaster ClockTimeInterval,
+ ptpbaseClockCurrentDSMeanPathDelay ClockTimeInterval,
+ ptpbaseClockCurrentDSOffsetFromMasterString DisplayString,
+ ptpbaseClockCurrentDSMeanPathDelayString DisplayString,
+ ptpbaseClockCurrentDSOffsetFromMasterThresholdNs Unsigned32 }
+
+
+ptpbaseClockCurrentDSDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.1.1.1
+::= { ptpbaseClockCurrentDSEntry 1 }
+
+
+ptpbaseClockCurrentDSClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.1.1.2
+::= { ptpbaseClockCurrentDSEntry 2 }
+
+
+ptpbaseClockCurrentDSInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.1.1.3
+::= { ptpbaseClockCurrentDSEntry 3 }
+
+
+ptpbaseClockCurrentDSStepsRemoved OBJECT-TYPE
+ SYNTAX Unsigned32
+ UNITS "Steps"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The current clock dataset StepsRemoved value.
+
+ This object specifies the distance measured by the number of
+ Boundary clocks between the local clock and the Foreign master
+ as indicated in the stepsRemoved field of Announce messages."
+ REFERENCE
+ "1588 Version 2.0 Section 8.2.2.2 stepsRemoved"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.1.1.4
+::= { ptpbaseClockCurrentDSEntry 4 }
+
+
+ptpbaseClockCurrentDSOffsetFromMaster OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ UNITS "Time Interval"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the current clock dataset ClockOffset
+ value. The value of the computation of the offset in time
+ between a slave and a master clock."
+ REFERENCE
+ "1588 Version 2.0 Section 8.2.2.3 of
+ [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.1.1.5
+::= { ptpbaseClockCurrentDSEntry 5 }
+
+
+ptpbaseClockCurrentDSMeanPathDelay OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ UNITS "Time Interval"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the current clock dataset
+ MeanPathDelay value.
+
+ The mean path delay between a pair of ports as measure by the
+ delay request-response mechanism."
+ REFERENCE
+ "1588 Version 2.0 Section 8.2.2.4 mean path delay"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.1.1.6
+::= { ptpbaseClockCurrentDSEntry 6 }
+
+
+ptpbaseClockCurrentDSOffsetFromMasterString OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the current clock dataset ClockOffset
+ value. The value of the computation of the offset in time
+ between a slave and a master clock, as a text representation
+ of a floating point number."
+ REFERENCE
+ "1588 Version 2.0 Section 8.2.2.3 of
+ [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.1.1.7
+::= { ptpbaseClockCurrentDSEntry 7 }
+
+
+ptpbaseClockCurrentDSMeanPathDelayString OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the current clock dataset
+ MeanPathDelay value, as a text representation
+ of a floating point number.
+
+ The mean path delay between a pair of ports as measure by the
+ delay request-response mechanism."
+ REFERENCE
+ "1588 Version 2.0 Section 8.2.2.4 mean path delay"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.1.1.8
+::= { ptpbaseClockCurrentDSEntry 8 }
+
+
+ptpbaseClockCurrentDSOffsetFromMasterThresholdNs OBJECT-TYPE
+ SYNTAX Unsigned32
+ UNITS "Nanoseconds"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Offset from master alarm threshold value in nanoseconds (absolute value is checked against the threshold)."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.1.1.9
+::= { ptpbaseClockCurrentDSEntry 9 }
+
+
+ptpbaseClockParentDSTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockParentDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the PTP clock Parent Datasets for
+ all domains."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.2
+::= { ptpbaseMIBClockInfo 2 }
+
+
+ptpbaseClockParentDSEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockParentDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing information about a single
+ PTP clock Parent Datasets for a domain."
+ REFERENCE
+ "Section 8.2.3 parentDS data set member specifications of
+ [IEEE 1588-2008]"
+ INDEX {
+ ptpbaseClockParentDSDomainIndex,
+ ptpbaseClockParentDSClockTypeIndex,
+ ptpbaseClockParentDSInstanceIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.2.1
+::= { ptpbaseClockParentDSTable 1 }
+
+
+PtpbaseClockParentDSEntry ::= SEQUENCE {
+
+ ptpbaseClockParentDSDomainIndex ClockDomainType,
+ ptpbaseClockParentDSClockTypeIndex ClockType,
+ ptpbaseClockParentDSInstanceIndex ClockInstanceType,
+ ptpbaseClockParentDSParentPortIdentity OCTET STRING,
+ ptpbaseClockParentDSParentStats TruthValue,
+ ptpbaseClockParentDSOffset ClockIntervalBase2,
+ ptpbaseClockParentDSClockPhChRate Integer32,
+ ptpbaseClockParentDSGMClockIdentity ClockIdentity,
+ ptpbaseClockParentDSGMClockPriority1 Unsigned32,
+ ptpbaseClockParentDSGMClockPriority2 Unsigned32,
+ ptpbaseClockParentDSGMClockQualityClass ClockQualityClassType,
+ ptpbaseClockParentDSGMClockQualityAccuracy ClockQualityAccuracyType,
+ ptpbaseClockParentDSGMClockQualityOffset Unsigned32,
+ ptpbaseClockParentDSParentPortAddressType InetAddressType,
+ ptpbaseClockParentDSParentPortAddress InetAddress }
+
+
+ptpbaseClockParentDSDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.2.1.1
+::= { ptpbaseClockParentDSEntry 1 }
+
+
+ptpbaseClockParentDSClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.2.1.2
+::= { ptpbaseClockParentDSEntry 2 }
+
+
+ptpbaseClockParentDSInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.2.1.3
+::= { ptpbaseClockParentDSEntry 3 }
+
+
+ptpbaseClockParentDSParentPortIdentity OBJECT-TYPE
+ SYNTAX OCTET STRING (SIZE (1..256))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the value of portIdentity of the port on
+ the master that issues the Sync messages used in synchronizing
+ this clock."
+ REFERENCE
+ "Section 8.2.3.2 parentDS.parentPortIdentity of
+ [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.2.1.4
+::= { ptpbaseClockParentDSEntry 4 }
+
+
+ptpbaseClockParentDSParentStats OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Parent Dataset ParentStats value.
+
+
+ This value indicates whether the values of ParentDSOffset
+ and ParentDSClockPhChRate have been measured and are valid.
+ A TRUE value shall indicate valid data."
+ REFERENCE
+ "Section 8.2.3.3 parentDS.parentStats of
+ [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.2.1.5
+::= { ptpbaseClockParentDSEntry 5 }
+
+
+ptpbaseClockParentDSOffset OBJECT-TYPE
+ SYNTAX ClockIntervalBase2 (-128..127)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Parent Dataset
+ ParentOffsetScaledLogVariance value.
+
+ This value is the variance of the parent clocks phase as
+ measured by the local clock."
+ REFERENCE
+ "Section 8.2.3.4
+ parentDS.observedParentOffsetScaledLogVariance
+ [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.2.1.6
+::= { ptpbaseClockParentDSEntry 6 }
+
+
+ptpbaseClockParentDSClockPhChRate OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock's parent dataset
+ ParentClockPhaseChangeRate value.
+
+ This value is an estimate of the parent clocks phase change
+ rate as measured by the slave clock."
+ REFERENCE
+ "Section 8.2.3.5
+ parentDS.observedParentClockPhaseChangeRate of
+ [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.2.1.7
+::= { ptpbaseClockParentDSEntry 7 }
+
+
+ptpbaseClockParentDSGMClockIdentity OBJECT-TYPE
+ SYNTAX ClockIdentity
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the parent dataset Grandmaster clock
+ identity."
+ REFERENCE
+ "Section 8.2.3.6 parentDS.grandmasterIdentity of
+
+ [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.2.1.8
+::= { ptpbaseClockParentDSEntry 8 }
+
+
+ptpbaseClockParentDSGMClockPriority1 OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the parent dataset Grandmaster clock
+ priority1."
+ REFERENCE
+ "Section 8.2.3.8 parentDS.grandmasterPriority1 of
+ [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.2.1.9
+::= { ptpbaseClockParentDSEntry 9 }
+
+
+ptpbaseClockParentDSGMClockPriority2 OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the parent dataset grandmaster clock
+ priority2."
+ REFERENCE
+ "Section 8.2.3.9 parentDS.grandmasterPriority2 of
+ [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.2.1.10
+::= { ptpbaseClockParentDSEntry 10 }
+
+
+ptpbaseClockParentDSGMClockQualityClass OBJECT-TYPE
+ SYNTAX ClockQualityClassType (0..255)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the parent dataset grandmaster clock
+ quality class."
+ REFERENCE
+ "Section 8.2.3.7 parentDS.grandmasterClockQuality of
+ [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.2.1.11
+::= { ptpbaseClockParentDSEntry 11 }
+
+
+ptpbaseClockParentDSGMClockQualityAccuracy OBJECT-TYPE
+ SYNTAX ClockQualityAccuracyType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the parent dataset grandmaster clock
+ quality accuracy."
+ REFERENCE
+ "Section 8.2.3.7 parentDS.grandmasterClockQuality of
+ [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.2.1.12
+::= { ptpbaseClockParentDSEntry 12 }
+
+
+ptpbaseClockParentDSGMClockQualityOffset OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the parent dataset grandmaster clock
+ quality offset."
+ REFERENCE
+ "Section 8.2.3.7 parentDS.grandmasterClockQuality of
+ [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.2.1.13
+::= { ptpbaseClockParentDSEntry 13 }
+
+
+ptpbaseClockParentDSParentPortAddressType OBJECT-TYPE
+ SYNTAX InetAddressType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Parent port transport address type"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.2.1.14
+::= { ptpbaseClockParentDSEntry 14 }
+
+
+ptpbaseClockParentDSParentPortAddress OBJECT-TYPE
+ SYNTAX InetAddress
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Parent port transport address, if available"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.2.1.15
+::= { ptpbaseClockParentDSEntry 15 }
+
+
+ptpbaseClockDefaultDSTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockDefaultDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the PTP clock Default Datasets for
+ all domains."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.3
+::= { ptpbaseMIBClockInfo 3 }
+
+
+ptpbaseClockDefaultDSEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockDefaultDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing information about a single
+ PTP clock Default Datasets for a domain."
+ INDEX {
+ ptpbaseClockDefaultDSDomainIndex,
+ ptpbaseClockDefaultDSClockTypeIndex,
+ ptpbaseClockDefaultDSInstanceIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.3.1
+::= { ptpbaseClockDefaultDSTable 1 }
+
+
+PtpbaseClockDefaultDSEntry ::= SEQUENCE {
+
+ ptpbaseClockDefaultDSDomainIndex ClockDomainType,
+ ptpbaseClockDefaultDSClockTypeIndex ClockType,
+ ptpbaseClockDefaultDSInstanceIndex ClockInstanceType,
+ ptpbaseClockDefaultDSTwoStepFlag TruthValue,
+ ptpbaseClockDefaultDSClockIdentity ClockIdentity,
+ ptpbaseClockDefaultDSPriority1 Unsigned32,
+ ptpbaseClockDefaultDSPriority2 Unsigned32,
+ ptpbaseClockDefaultDSSlaveOnly TruthValue,
+ ptpbaseClockDefaultDSQualityClass ClockQualityClassType,
+ ptpbaseClockDefaultDSQualityAccuracy ClockQualityAccuracyType,
+ ptpbaseClockDefaultDSQualityOffset Integer32,
+ ptpbaseClockDefaultDSDomainNumber ClockDomainType }
+
+
+ptpbaseClockDefaultDSDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.3.1.1
+::= { ptpbaseClockDefaultDSEntry 1 }
+
+
+ptpbaseClockDefaultDSClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.3.1.2
+::= { ptpbaseClockDefaultDSEntry 2 }
+
+
+ptpbaseClockDefaultDSInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.3.1.3
+::= { ptpbaseClockDefaultDSEntry 3 }
+
+
+ptpbaseClockDefaultDSTwoStepFlag OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies whether the Two Step process is used."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.3.1.4
+::= { ptpbaseClockDefaultDSEntry 4 }
+
+
+ptpbaseClockDefaultDSClockIdentity OBJECT-TYPE
+ SYNTAX ClockIdentity
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the default Datasets clock identity."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.3.1.5
+::= { ptpbaseClockDefaultDSEntry 5 }
+
+
+ptpbaseClockDefaultDSPriority1 OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the default Datasets clock Priority1."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.3.1.6
+::= { ptpbaseClockDefaultDSEntry 6 }
+
+
+ptpbaseClockDefaultDSPriority2 OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the default Datasets clock Priority2."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.3.1.7
+::= { ptpbaseClockDefaultDSEntry 7 }
+
+
+ptpbaseClockDefaultDSSlaveOnly OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Whether the SlaveOnly flag is set."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.3.1.8
+::= { ptpbaseClockDefaultDSEntry 8 }
+
+
+ptpbaseClockDefaultDSQualityClass OBJECT-TYPE
+ SYNTAX ClockQualityClassType (0..255)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the default dataset Quality Class."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.3.1.9
+::= { ptpbaseClockDefaultDSEntry 9 }
+
+
+ptpbaseClockDefaultDSQualityAccuracy OBJECT-TYPE
+ SYNTAX ClockQualityAccuracyType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the default dataset Quality Accurarcy."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.3.1.10
+::= { ptpbaseClockDefaultDSEntry 10 }
+
+
+ptpbaseClockDefaultDSQualityOffset OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the default dataset Quality offset."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.3.1.11
+::= { ptpbaseClockDefaultDSEntry 11 }
+
+
+ptpbaseClockDefaultDSDomainNumber OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of PTP domain the clock is part of."
+ REFERENCE
+ "IEEE 1588-2008 7.1, 8.2.1.1"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.3.1.12
+ ::= { ptpbaseClockDefaultDSEntry 12 }
+
+
+ptpbaseClockRunningTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockRunningEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the PTP clock Running Datasets for
+ all domains."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.4
+::= { ptpbaseMIBClockInfo 4 }
+
+
+ptpbaseClockRunningEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockRunningEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing information about a single
+ PTP clock running Datasets for a domain."
+ INDEX {
+ ptpbaseClockRunningDomainIndex,
+ ptpbaseClockRunningClockTypeIndex,
+ ptpbaseClockRunningInstanceIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.4.1
+::= { ptpbaseClockRunningTable 1 }
+
+
+PtpbaseClockRunningEntry ::= SEQUENCE {
+
+ ptpbaseClockRunningDomainIndex ClockDomainType,
+ ptpbaseClockRunningClockTypeIndex ClockType,
+ ptpbaseClockRunningInstanceIndex ClockInstanceType,
+ ptpbaseClockRunningState ClockStateType,
+ ptpbaseClockRunningPacketsSent Counter64,
+ ptpbaseClockRunningPacketsReceived Counter64 }
+
+
+ptpbaseClockRunningDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.4.1.1
+::= { ptpbaseClockRunningEntry 1 }
+
+
+ptpbaseClockRunningClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.4.1.2
+::= { ptpbaseClockRunningEntry 2 }
+
+
+ptpbaseClockRunningInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.4.1.3
+::= { ptpbaseClockRunningEntry 3 }
+
+
+ptpbaseClockRunningState OBJECT-TYPE
+ SYNTAX ClockStateType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Clock state returned by PTP engine
+ which was described earlier.
+
+ Freerun state. Applies to a slave device that is not locked to
+ a master. This is the initial state a slave starts out with
+ when
+ it is not getting any PTP packets from the master or because of
+ some other input error (erroneous packets, etc).
+
+ Holdover state. In this state the slave device is locked to a
+ master but communication with the master is lost or the
+ timestamps in the ptp packets are incorrect. But since the
+ slave was locked to the master, it can run with the same
+ accuracy for
+ sometime. The slave can continue to operate in this state for
+ some time. If communication with the master is not restored for
+ a while, the device is moved to the FREERUN state.
+
+ Acquiring state. The slave device is receiving packets from a
+ master and is trying to acquire a lock.
+
+ Freq_locked state. Slave device is locked to the Master with
+ respect to frequency, but not phase aligned
+
+ Phase_aligned state. Locked to the master with respect to
+ frequency and phase."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.4.1.4
+::= { ptpbaseClockRunningEntry 4 }
+
+
+ptpbaseClockRunningPacketsSent OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the total number of all packet Unicast
+ and multicast that have been sent out for this clock in this
+
+ domain for this type."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.4.1.5
+::= { ptpbaseClockRunningEntry 5 }
+
+
+ptpbaseClockRunningPacketsReceived OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the total number of all packet Unicast
+ and multicast that have been received for this clock in this
+ domain for this type."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.4.1.6
+::= { ptpbaseClockRunningEntry 6 }
+
+
+ptpbaseClockTimePropertiesDSTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockTimePropertiesDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the PTP clock Timeproperties
+ Datasets for all domains."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.5
+::= { ptpbaseMIBClockInfo 5 }
+
+
+ptpbaseClockTimePropertiesDSEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockTimePropertiesDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing information about a single
+ PTP clock timeproperties Datasets for a domain."
+ REFERENCE
+ "Section 8.2.4 of [IEEE 1588-2008]"
+ INDEX {
+ ptpbaseClockTimePropertiesDSDomainIndex,
+ ptpbaseClockTimePropertiesDSClockTypeIndex,
+ ptpbaseClockTimePropertiesDSInstanceIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.5.1
+::= { ptpbaseClockTimePropertiesDSTable 1 }
+
+
+PtpbaseClockTimePropertiesDSEntry ::= SEQUENCE {
+
+ ptpbaseClockTimePropertiesDSDomainIndex ClockDomainType,
+ ptpbaseClockTimePropertiesDSClockTypeIndex ClockType,
+ ptpbaseClockTimePropertiesDSInstanceIndex ClockInstanceType,
+ ptpbaseClockTimePropertiesDSCurrentUTCOffsetValid TruthValue,
+ ptpbaseClockTimePropertiesDSCurrentUTCOffset Integer32,
+ ptpbaseClockTimePropertiesDSLeap59 TruthValue,
+ ptpbaseClockTimePropertiesDSLeap61 TruthValue,
+ ptpbaseClockTimePropertiesDSTimeTraceable TruthValue,
+ ptpbaseClockTimePropertiesDSFreqTraceable TruthValue,
+ ptpbaseClockTimePropertiesDSPTPTimescale TruthValue,
+ ptpbaseClockTimePropertiesDSSource ClockTimeSourceType }
+
+
+ptpbaseClockTimePropertiesDSDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.5.1.1
+::= { ptpbaseClockTimePropertiesDSEntry 1 }
+
+
+ptpbaseClockTimePropertiesDSClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.5.1.2
+::= { ptpbaseClockTimePropertiesDSEntry 2 }
+
+
+ptpbaseClockTimePropertiesDSInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.5.1.3
+::= { ptpbaseClockTimePropertiesDSEntry 3 }
+
+
+ptpbaseClockTimePropertiesDSCurrentUTCOffsetValid OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the timeproperties dataset value of
+ whether current UTC offset is valid."
+ REFERENCE
+ "Section 8.2.4.2 of [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.5.1.4
+::= { ptpbaseClockTimePropertiesDSEntry 4 }
+
+
+ptpbaseClockTimePropertiesDSCurrentUTCOffset OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the timeproperties dataset value of
+ current UTC offset.
+
+
+ In PTP systems whose epoch is the PTP epoch, the value of
+ timePropertiesDS.currentUtcOffset is the offset
+ between TAI and UTC; otherwise the value has no meaning. The
+ value shall be in units of seconds.
+ The initialization value shall be selected as follows:
+ a) If the timePropertiesDS.ptpTimescale (see 8.2.4.8) is TRUE,
+ the value is the value obtained from a
+ primary reference if the value is known at the time of
+ initialization, else.
+ b) The value shall be the current number of leap seconds (7.2.3)
+ when the node is designed."
+ REFERENCE
+ "Section 8.2.4.3 of [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.5.1.5
+::= { ptpbaseClockTimePropertiesDSEntry 5 }
+
+
+ptpbaseClockTimePropertiesDSLeap59 OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Leap59 value in the clock Current
+ Dataset."
+ REFERENCE
+ "Section 8.2.4.4 of [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.5.1.6
+::= { ptpbaseClockTimePropertiesDSEntry 6 }
+
+
+ptpbaseClockTimePropertiesDSLeap61 OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Leap61 value in the clock Current
+ Dataset."
+ REFERENCE
+ "Section 8.2.4.5 of [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.5.1.7
+::= { ptpbaseClockTimePropertiesDSEntry 7 }
+
+
+ptpbaseClockTimePropertiesDSTimeTraceable OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Timetraceable value in the clock
+ Current Dataset."
+ REFERENCE
+ "Section 8.2.4.6 of [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.5.1.8
+::= { ptpbaseClockTimePropertiesDSEntry 8 }
+
+
+ptpbaseClockTimePropertiesDSFreqTraceable OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Frequency Traceable value in the
+ clock Current Dataset."
+ REFERENCE
+ "Section 8.2.4.7 of [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.5.1.9
+::= { ptpbaseClockTimePropertiesDSEntry 9 }
+
+
+ptpbaseClockTimePropertiesDSPTPTimescale OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP Timescale value in the clock
+ Current Dataset."
+ REFERENCE
+ "Section 8.2.4.8 of [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.5.1.10
+::= { ptpbaseClockTimePropertiesDSEntry 10 }
+
+
+ptpbaseClockTimePropertiesDSSource OBJECT-TYPE
+ SYNTAX ClockTimeSourceType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Timesource value in the clock Current
+ Dataset."
+ REFERENCE
+ "Section 8.2.4.9 of [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.5.1.11
+::= { ptpbaseClockTimePropertiesDSEntry 11 }
+
+
+ptpbaseClockTransDefaultDSTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockTransDefaultDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the PTP Transparent clock Default
+ Datasets for all domains."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.6
+::= { ptpbaseMIBClockInfo 6 }
+
+
+ptpbaseClockTransDefaultDSEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockTransDefaultDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing information about a single
+ PTP Transparent clock Default Datasets for a domain."
+ REFERENCE
+ "Section 8.3.2 of [IEEE 1588-2008]"
+ INDEX {
+ ptpbaseClockTransDefaultDSDomainIndex,
+ ptpbaseClockTransDefaultDSInstanceIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.6.1
+::= { ptpbaseClockTransDefaultDSTable 1 }
+
+
+PtpbaseClockTransDefaultDSEntry ::= SEQUENCE {
+
+ ptpbaseClockTransDefaultDSDomainIndex ClockDomainType,
+ ptpbaseClockTransDefaultDSInstanceIndex ClockInstanceType,
+ ptpbaseClockTransDefaultDSClockIdentity ClockIdentity,
+ ptpbaseClockTransDefaultDSNumOfPorts Counter32,
+ ptpbaseClockTransDefaultDSDelay ClockMechanismType,
+ ptpbaseClockTransDefaultDSPrimaryDomain Integer32 }
+
+
+ptpbaseClockTransDefaultDSDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.6.1.1
+::= { ptpbaseClockTransDefaultDSEntry 1 }
+
+
+ptpbaseClockTransDefaultDSInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.6.1.2
+::= { ptpbaseClockTransDefaultDSEntry 2 }
+
+
+ptpbaseClockTransDefaultDSClockIdentity OBJECT-TYPE
+ SYNTAX ClockIdentity
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the value of the clockIdentity attribute
+ of the local clock."
+ REFERENCE
+ "Section 8.3.2.2.1 of [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.6.1.3
+::= { ptpbaseClockTransDefaultDSEntry 3 }
+
+
+ptpbaseClockTransDefaultDSNumOfPorts OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the number of PTP ports of the device."
+ REFERENCE
+ "Section 8.3.2.2.2 of [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.6.1.4
+::= { ptpbaseClockTransDefaultDSEntry 4 }
+
+
+ptpbaseClockTransDefaultDSDelay OBJECT-TYPE
+ SYNTAX ClockMechanismType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object, if the transparent clock is an end-to-end
+ transparent clock, has the value shall be E2E; If the
+ transparent clock is a peer-to-peer transparent clock, the
+ value
+ shall be P2P."
+ REFERENCE
+ "Section 8.3.2.3.1 of [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.6.1.5
+::= { ptpbaseClockTransDefaultDSEntry 5 }
+
+
+ptpbaseClockTransDefaultDSPrimaryDomain OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the value of the primary syntonization
+ domain. The initialization value shall be 0."
+ REFERENCE
+ "Section 8.3.2.3.2 of [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.6.1.6
+::= { ptpbaseClockTransDefaultDSEntry 6 }
+
+
+ptpbaseClockPortTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the clock ports for a particular
+ domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.7
+::= { ptpbaseMIBClockInfo 7 }
+
+
+ptpbaseClockPortEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing information about a single
+ clock port."
+ INDEX {
+ ptpbaseClockPortDomainIndex,
+ ptpbaseClockPortClockTypeIndex,
+ ptpbaseClockPortClockInstanceIndex,
+ ptpbaseClockPortTablePortNumberIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.7.1
+::= { ptpbaseClockPortTable 1 }
+
+
+PtpbaseClockPortEntry ::= SEQUENCE {
+
+ ptpbaseClockPortDomainIndex ClockDomainType,
+ ptpbaseClockPortClockTypeIndex ClockType,
+ ptpbaseClockPortClockInstanceIndex ClockInstanceType,
+ ptpbaseClockPortTablePortNumberIndex ClockPortNumber,
+ ptpbaseClockPortName DisplayString,
+ ptpbaseClockPortRole ClockRoleType,
+ ptpbaseClockPortSyncOneStep TruthValue,
+ ptpbaseClockPortCurrentPeerAddressType InetAddressType,
+ ptpbaseClockPortCurrentPeerAddress InetAddress,
+ ptpbaseClockPortNumOfAssociatedPorts Gauge32 }
+
+
+ptpbaseClockPortDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.7.1.1
+::= { ptpbaseClockPortEntry 1 }
+
+
+ptpbaseClockPortClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.7.1.2
+::= { ptpbaseClockPortEntry 2 }
+
+
+ptpbaseClockPortClockInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.7.1.3
+::= { ptpbaseClockPortEntry 3 }
+
+
+ptpbaseClockPortTablePortNumberIndex OBJECT-TYPE
+ SYNTAX ClockPortNumber (1..65535)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP Portnumber for this port."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.7.1.4
+::= { ptpbaseClockPortEntry 4 }
+
+
+ptpbaseClockPortName OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP clock port name configured on the
+ router."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.7.1.5
+::= { ptpbaseClockPortEntry 5 }
+
+
+ptpbaseClockPortRole OBJECT-TYPE
+ SYNTAX ClockRoleType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object describes the current role (slave/master) of the
+ port."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.7.1.6
+::= { ptpbaseClockPortEntry 6 }
+
+
+ptpbaseClockPortSyncOneStep OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies that one-step clock operation between
+ the PTP master and slave device is enabled."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.7.1.7
+::= { ptpbaseClockPortEntry 7 }
+
+
+ptpbaseClockPortCurrentPeerAddressType OBJECT-TYPE
+ SYNTAX InetAddressType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the current peer's network address used
+ for PTP communication. Based on the scenario and the setup
+ involved, the values might look like these -
+ Scenario Value
+ ------------------- ----------------
+ Single Master master port
+ Multiple Masters selected master port
+ Single Slave slave port
+ Multiple Slaves <empty>
+
+ (In relevant setups, information on available
+ slaves and available masters will be available through
+ ptpClockPortAssociateTable)"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.7.1.8
+::= { ptpbaseClockPortEntry 8 }
+
+
+ptpbaseClockPortCurrentPeerAddress OBJECT-TYPE
+ SYNTAX InetAddress
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the current peer's network address used
+ for PTP communication. Based on the scenario and the setup
+ involved, the values might look like these -
+ Scenario Value
+ ------------------- ----------------
+ Single Master master port
+ Multiple Masters selected master port
+ Single Slave slave port
+ Multiple Slaves <empty>
+
+ (In relevant setups, information on available
+ slaves and available masters will be available through
+ ptpClockPortAssociateTable)"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.7.1.9
+::= { ptpbaseClockPortEntry 9 }
+
+
+ptpbaseClockPortNumOfAssociatedPorts OBJECT-TYPE
+ SYNTAX Gauge32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies -
+ For a master port - the number of PTP slave sessions (peers)
+ associated with this PTP port.
+ For a slave port - the number of masters available to this slave
+ port (might or might not be peered)."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.7.1.10
+::= { ptpbaseClockPortEntry 10 }
+
+
+ptpbaseClockPortDSTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockPortDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the clock ports dataset for a
+ particular domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8
+::= { ptpbaseMIBClockInfo 8 }
+
+
+ptpbaseClockPortDSEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockPortDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing port dataset information for
+ a single clock port."
+ INDEX {
+ ptpbaseClockPortDSDomainIndex,
+ ptpbaseClockPortDSClockTypeIndex,
+ ptpbaseClockPortDSClockInstanceIndex,
+ ptpbaseClockPortDSPortNumberIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8.1
+::= { ptpbaseClockPortDSTable 1 }
+
+
+PtpbaseClockPortDSEntry ::= SEQUENCE {
+
+ ptpbaseClockPortDSDomainIndex ClockDomainType,
+ ptpbaseClockPortDSClockTypeIndex ClockType,
+ ptpbaseClockPortDSClockInstanceIndex ClockInstanceType,
+ ptpbaseClockPortDSPortNumberIndex ClockPortNumber,
+ ptpbaseClockPortDSName DisplayString,
+ ptpbaseClockPortDSPortIdentity OCTET STRING,
+ ptpbaseClockPortDSAnnouncementInterval Integer32,
+ ptpbaseClockPortDSAnnounceRctTimeout Integer32,
+ ptpbaseClockPortDSSyncInterval Integer32,
+ ptpbaseClockPortDSMinDelayReqInterval Integer32,
+ ptpbaseClockPortDSPeerDelayReqInterval Integer32,
+ ptpbaseClockPortDSDelayMech ClockMechanismType,
+ ptpbaseClockPortDSPeerMeanPathDelay ClockTimeInterval,
+ ptpbaseClockPortDSGrantDuration Unsigned32,
+ ptpbaseClockPortDSPTPVersion Integer32,
+ ptpBaseClockPortDSPeerMeanPathDelayString DisplayString,
+ ptpBaseClockPortDSLastMismatchedDomain Integer32
+ }
+
+
+ptpbaseClockPortDSDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8.1.1
+::= { ptpbaseClockPortDSEntry 1 }
+
+
+ptpbaseClockPortDSClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8.1.2
+::= { ptpbaseClockPortDSEntry 2 }
+
+
+ptpbaseClockPortDSClockInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8.1.3
+::= { ptpbaseClockPortDSEntry 3 }
+
+
+ptpbaseClockPortDSPortNumberIndex OBJECT-TYPE
+ SYNTAX ClockPortNumber (1..65535)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP portnumber associated with this
+ PTP port."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8.1.4
+::= { ptpbaseClockPortDSEntry 4 }
+
+
+ptpbaseClockPortDSName OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP clock port name."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8.1.5
+::= { ptpbaseClockPortDSEntry 5 }
+
+
+ptpbaseClockPortDSPortIdentity OBJECT-TYPE
+ SYNTAX OCTET STRING (SIZE (1..256))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP clock port Identity."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8.1.6
+::= { ptpbaseClockPortDSEntry 6 }
+
+
+ptpbaseClockPortDSAnnouncementInterval OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Announce message transmission
+ interval associated with this clock port."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8.1.7
+::= { ptpbaseClockPortDSEntry 7 }
+
+
+ptpbaseClockPortDSAnnounceRctTimeout OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Announce receipt timeout associated
+ with this clock port."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8.1.8
+::= { ptpbaseClockPortDSEntry 8 }
+
+
+ptpbaseClockPortDSSyncInterval OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Sync message transmission interval."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8.1.9
+::= { ptpbaseClockPortDSEntry 9 }
+
+
+ptpbaseClockPortDSMinDelayReqInterval OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Delay_Req message transmission
+ interval."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8.1.10
+::= { ptpbaseClockPortDSEntry 10 }
+
+
+ptpbaseClockPortDSPeerDelayReqInterval OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Pdelay_Req message transmission
+ interval."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8.1.11
+::= { ptpbaseClockPortDSEntry 11 }
+
+
+ptpbaseClockPortDSDelayMech OBJECT-TYPE
+ SYNTAX ClockMechanismType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the delay mechanism used. If the clock
+ is an end-to-end clock, the value of the is e2e, else if the
+ clock is a peer to-peer clock, the value shall be p2p."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8.1.12
+::= { ptpbaseClockPortDSEntry 12 }
+
+
+ptpbaseClockPortDSPeerMeanPathDelay OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the peer meanPathDelay."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8.1.13
+::= { ptpbaseClockPortDSEntry 13 }
+
+
+ptpbaseClockPortDSGrantDuration OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the grant duration allocated by the
+ master."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8.1.14
+::= { ptpbaseClockPortDSEntry 14 }
+
+
+ptpbaseClockPortDSPTPVersion OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP version being used."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8.1.15
+::= { ptpbaseClockPortDSEntry 15 }
+
+ptpBaseClockPortDSPeerMeanPathDelayString OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Current value of Peer Mean Path Delay, presented as text."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8.1.16
+::= { ptpbaseClockPortDSEntry 16 }
+
+ptpBaseClockPortDSLastMismatchedDomain OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "PTP domain number of last mismatched domain (-1 = no mismatch)."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.8.1.17
+::= { ptpbaseClockPortDSEntry 17 }
+
+
+ptpbaseClockPortRunningTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockPortRunningEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the clock ports running dataset for
+ a particular domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.9
+::= { ptpbaseMIBClockInfo 9 }
+
+
+ptpbaseClockPortRunningEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockPortRunningEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing runing dataset information
+ about a single clock port."
+ INDEX {
+ ptpbaseClockPortRunningDomainIndex,
+ ptpbaseClockPortRunningClockTypeIndex,
+ ptpbaseClockPortRunningClockInstanceIndex,
+ ptpbaseClockPortRunningPortNumberIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.9.1
+::= { ptpbaseClockPortRunningTable 1 }
+
+
+PtpbaseClockPortRunningEntry ::= SEQUENCE {
+
+ ptpbaseClockPortRunningDomainIndex ClockDomainType,
+ ptpbaseClockPortRunningClockTypeIndex ClockType,
+ ptpbaseClockPortRunningClockInstanceIndex ClockInstanceType,
+ ptpbaseClockPortRunningPortNumberIndex ClockPortNumber,
+ ptpbaseClockPortRunningName DisplayString,
+ ptpbaseClockPortRunningState ClockPortState,
+ ptpbaseClockPortRunningRole ClockRoleType,
+ ptpbaseClockPortRunningInterfaceIndex InterfaceIndexOrZero,
+ ptpbaseClockPortRunningIPversion Integer32,
+ ptpbaseClockPortRunningEncapsulationType Integer32,
+ ptpbaseClockPortRunningTxMode ClockTxModeType,
+ ptpbaseClockPortRunningRxMode ClockTxModeType,
+ ptpbaseClockPortRunningPacketsReceived Counter64,
+ ptpbaseClockPortRunningPacketsSent Counter64 }
+
+
+ptpbaseClockPortRunningDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.9.1.1
+::= { ptpbaseClockPortRunningEntry 1 }
+
+
+ptpbaseClockPortRunningClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.9.1.2
+::= { ptpbaseClockPortRunningEntry 2 }
+
+
+ptpbaseClockPortRunningClockInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.9.1.3
+::= { ptpbaseClockPortRunningEntry 3 }
+
+
+ptpbaseClockPortRunningPortNumberIndex OBJECT-TYPE
+ SYNTAX ClockPortNumber (1..65535)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP portnumber associated with this
+ clock port."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.9.1.4
+::= { ptpbaseClockPortRunningEntry 4 }
+
+
+ptpbaseClockPortRunningName OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP clock port name."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.9.1.5
+::= { ptpbaseClockPortRunningEntry 5 }
+
+
+ptpbaseClockPortRunningState OBJECT-TYPE
+ SYNTAX ClockPortState
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the port state returned by PTP engine.
+
+ initializing - In this state a port initializes
+ its data sets, hardware, and
+ communication facilities.
+ faulty - The fault state of the protocol.
+ disabled - The port shall not place any
+ messages on its communication path.
+ listening - The port is waiting for the
+ announceReceiptTimeout to expire or
+ to receive an Announce message from
+ a master.
+ preMaster - The port shall behave in all respects
+ as though it were in the MASTER state
+ except that it shall not place any
+ messages on its communication path
+ except for Pdelay_Req, Pdelay_Resp,
+ Pdelay_Resp_Follow_Up, signaling, or
+ management messages.
+ master - The port is behaving as a master port.
+ passive - The port shall not place any
+ messages on its communication path
+ except for Pdelay_Req, Pdelay_Resp,
+ Pdelay_Resp_Follow_Up, or signaling
+ messages, or management messages
+ that are a required response to
+ another management message
+ uncalibrated - The local port is preparing to
+ synchronize to the master port.
+ slave - The port is synchronizing to the
+ selected master port."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.9.1.6
+::= { ptpbaseClockPortRunningEntry 6 }
+
+
+ptpbaseClockPortRunningRole OBJECT-TYPE
+ SYNTAX ClockRoleType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Clock Role."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.9.1.7
+::= { ptpbaseClockPortRunningEntry 7 }
+
+
+ptpbaseClockPortRunningInterfaceIndex OBJECT-TYPE
+ SYNTAX InterfaceIndexOrZero
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the interface on the router being used by
+ the PTP Clock for PTP communication."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.9.1.8
+::= { ptpbaseClockPortRunningEntry 8 }
+
+
+ptpbaseClockPortRunningIPversion OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the IP version being used for PTP
+ communication (the mapping used)."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.9.1.9
+::= { ptpbaseClockPortRunningEntry 9 }
+
+
+ptpbaseClockPortRunningEncapsulationType OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the type of encapsulation if the
+ interface is adding extra layers (eg. VLAN, Pseudowire
+ encapsulation...) for the PTP messages."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.9.1.10
+::= { ptpbaseClockPortRunningEntry 10 }
+
+
+ptpbaseClockPortRunningTxMode OBJECT-TYPE
+ SYNTAX ClockTxModeType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock transmission mode as
+
+ unicast: Using unicast communication channel.
+ multicast: Using Multicast communication channel.
+ multicast-mix: Using multicast-unicast communication channel"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.9.1.11
+::= { ptpbaseClockPortRunningEntry 11 }
+
+
+ptpbaseClockPortRunningRxMode OBJECT-TYPE
+ SYNTAX ClockTxModeType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifie the clock receive mode as
+
+ unicast: Using unicast communication channel.
+ multicast: Using Multicast communication channel.
+ multicast-mix: Using multicast-unicast communication channel"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.9.1.12
+::= { ptpbaseClockPortRunningEntry 12 }
+
+
+ptpbaseClockPortRunningPacketsReceived OBJECT-TYPE
+ SYNTAX Counter64
+ UNITS "packets"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the packets received on the clock port
+ (cummulative)."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.9.1.13
+::= { ptpbaseClockPortRunningEntry 13 }
+
+
+ptpbaseClockPortRunningPacketsSent OBJECT-TYPE
+ SYNTAX Counter64
+ UNITS "packets"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the packets sent on the clock port
+ (cummulative)."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.9.1.14
+::= { ptpbaseClockPortRunningEntry 14 }
+
+
+ptpbaseClockPortTransDSTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockPortTransDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the Transparent clock ports running
+ dataset for a particular domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.10
+::= { ptpbaseMIBClockInfo 10 }
+
+
+ptpbaseClockPortTransDSEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockPortTransDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing clock port Transparent
+ dataset information about a single clock port"
+ INDEX {
+ ptpbaseClockPortTransDSDomainIndex,
+ ptpbaseClockPortTransDSInstanceIndex,
+ ptpbaseClockPortTransDSPortNumberIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.10.1
+::= { ptpbaseClockPortTransDSTable 1 }
+
+
+PtpbaseClockPortTransDSEntry ::= SEQUENCE {
+
+ ptpbaseClockPortTransDSDomainIndex ClockDomainType,
+ ptpbaseClockPortTransDSInstanceIndex ClockInstanceType,
+ ptpbaseClockPortTransDSPortNumberIndex ClockPortNumber,
+ ptpbaseClockPortTransDSPortIdentity ClockIdentity,
+ ptpbaseClockPortTransDSlogMinPdelayReqInt Integer32,
+ ptpbaseClockPortTransDSFaultyFlag TruthValue,
+ ptpbaseClockPortTransDSPeerMeanPathDelay ClockTimeInterval }
+
+
+ptpbaseClockPortTransDSDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.10.1.1
+::= { ptpbaseClockPortTransDSEntry 1 }
+
+
+ptpbaseClockPortTransDSInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.10.1.2
+::= { ptpbaseClockPortTransDSEntry 2 }
+
+
+ptpbaseClockPortTransDSPortNumberIndex OBJECT-TYPE
+ SYNTAX ClockPortNumber (1..65535)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP port number associated with this
+ port."
+ REFERENCE
+ "Section 7.5.2 Port Identity [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.10.1.3
+::= { ptpbaseClockPortTransDSEntry 3 }
+
+
+ptpbaseClockPortTransDSPortIdentity OBJECT-TYPE
+ SYNTAX ClockIdentity
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the value of the PortIdentity
+ attribute of the local port."
+ REFERENCE
+ "Section 8.3.3.2.1 of [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.10.1.4
+::= { ptpbaseClockPortTransDSEntry 4 }
+
+
+ptpbaseClockPortTransDSlogMinPdelayReqInt OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the value of the logarithm to the
+ base 2 of the minPdelayReqInterval."
+ REFERENCE
+ "Section 8.3.3.3.1 of [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.10.1.5
+::= { ptpbaseClockPortTransDSEntry 5 }
+
+
+ptpbaseClockPortTransDSFaultyFlag OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the value TRUE if the port is faulty
+ and FALSE if the port is operating normally."
+ REFERENCE
+ "Section 8.3.3.3.2 of [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.10.1.6
+::= { ptpbaseClockPortTransDSEntry 6 }
+
+
+ptpbaseClockPortTransDSPeerMeanPathDelay OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies, (if the delayMechanism used is P2P) the
+ value is the estimate of the current one-way propagation delay,
+ i.e., <meanPathDelay> on the link attached to this port
+ computed
+ using the peer delay mechanism. If the value of the
+ delayMechanism
+ used is E2E, then the value will be zero."
+ REFERENCE
+ "Section 8.3.3.3.3 of [IEEE 1588-2008]"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.10.1.7
+::= { ptpbaseClockPortTransDSEntry 7 }
+
+
+ptpbaseClockPortAssociateTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockPortAssociateEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about a given port's associated ports.
+
+ For a master port - multiple slave ports which have established
+ sessions with the current master port.
+ For a slave port - the list of masters available for a given
+ slave port.
+
+ Session information (pkts, errors) to be displayed based on
+ availability and scenario."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.11
+::= { ptpbaseMIBClockInfo 11 }
+
+
+ptpbaseClockPortAssociateEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockPortAssociateEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing information about a single
+
+ associated port for the given clockport."
+ INDEX {
+ ptpClockPortCurrentDomainIndex,
+ ptpClockPortCurrentClockTypeIndex,
+ ptpClockPortCurrentClockInstanceIndex,
+ ptpClockPortCurrentPortNumberIndex,
+ ptpbaseClockPortAssociatePortIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.11.1
+::= { ptpbaseClockPortAssociateTable 1 }
+
+
+PtpbaseClockPortAssociateEntry ::= SEQUENCE {
+
+ ptpClockPortCurrentDomainIndex ClockDomainType,
+ ptpClockPortCurrentClockTypeIndex ClockType,
+ ptpClockPortCurrentClockInstanceIndex ClockInstanceType,
+ ptpClockPortCurrentPortNumberIndex ClockPortNumber,
+ ptpbaseClockPortAssociatePortIndex Unsigned32,
+ ptpbaseClockPortAssociateAddressType InetAddressType,
+ ptpbaseClockPortAssociateAddress InetAddress,
+ ptpbaseClockPortAssociatePacketsSent Counter64,
+ ptpbaseClockPortAssociatePacketsReceived Counter64,
+ ptpbaseClockPortAssociateInErrors Counter64,
+ ptpbaseClockPortAssociateOutErrors Counter64 }
+
+
+ptpClockPortCurrentDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the given port's domain number."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.11.1.1
+::= { ptpbaseClockPortAssociateEntry 1 }
+
+
+ptpClockPortCurrentClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the given port's clock type."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.11.1.2
+::= { ptpbaseClockPortAssociateEntry 2 }
+
+
+ptpClockPortCurrentClockInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.11.1.3
+::= { ptpbaseClockPortAssociateEntry 3 }
+
+
+ptpClockPortCurrentPortNumberIndex OBJECT-TYPE
+ SYNTAX ClockPortNumber
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP Port Number for the given port."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.11.1.4
+::= { ptpbaseClockPortAssociateEntry 4 }
+
+
+ptpbaseClockPortAssociatePortIndex OBJECT-TYPE
+ SYNTAX Unsigned32 (1..65535)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the associated port's serial number in
+ the current port's context."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.11.1.5
+::= { ptpbaseClockPortAssociateEntry 5 }
+
+
+ptpbaseClockPortAssociateAddressType OBJECT-TYPE
+ SYNTAX InetAddressType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the peer port's network address type used
+ for PTP communication."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.11.1.6
+::= { ptpbaseClockPortAssociateEntry 6 }
+
+
+ptpbaseClockPortAssociateAddress OBJECT-TYPE
+ SYNTAX InetAddress
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the peer port's network address used for
+ PTP communication."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.11.1.7
+::= { ptpbaseClockPortAssociateEntry 7 }
+
+
+ptpbaseClockPortAssociatePacketsSent OBJECT-TYPE
+ SYNTAX Counter64
+ UNITS "packets"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of packets sent to this peer port from the current
+ port."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.11.1.8
+::= { ptpbaseClockPortAssociateEntry 8 }
+
+
+ptpbaseClockPortAssociatePacketsReceived OBJECT-TYPE
+ SYNTAX Counter64
+ UNITS "packets"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of packets received from this peer port by the
+ current port."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.11.1.9
+::= { ptpbaseClockPortAssociateEntry 9 }
+
+
+ptpbaseClockPortAssociateInErrors OBJECT-TYPE
+ SYNTAX Counter64
+ UNITS "packets"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the input errors associated with the
+ peer port."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.11.1.10
+::= { ptpbaseClockPortAssociateEntry 10 }
+
+
+ptpbaseClockPortAssociateOutErrors OBJECT-TYPE
+ SYNTAX Counter64
+ UNITS "packets"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the output errors associated with the
+ peer port."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.11.1.11
+::= { ptpbaseClockPortAssociateEntry 11 }
+
+
+ptpbasePtpPortMessageCountersTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbasePtpPortMessageCountersEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table describing counters for sent and received messages of different types for a given port."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12
+::= { ptpbaseMIBClockInfo 12 }
+
+ptpbasePtpPortMessageCountersEntry OBJECT-TYPE
+ SYNTAX PtpbasePtpPortMessageCountersEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in a table describing counters for sent and received messages of different types for a given port."
+ INDEX {
+ ptpbasePortMessageCountersDomainIndex,
+ ptpbasePortMessageCountersClockTypeIndex,
+ ptpbasePortMessageCountersInstanceIndex,
+ ptpbasePortMessageCountersClockPortNumberIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1
+::= { ptpbasePtpPortMessageCountersTable 1 }
+
+
+PtpbasePtpPortMessageCountersEntry ::= SEQUENCE {
+
+ ptpbasePortMessageCountersDomainIndex ClockDomainType,
+ ptpbasePortMessageCountersClockTypeIndex ClockType,
+ ptpbasePortMessageCountersInstanceIndex ClockInstanceType,
+ ptpbasePortMessageCountersClockPortNumberIndex ClockPortNumber,
+ ptpbasePortMessageCountersClearAll TruthValue,
+ ptpbasePortMessageCountersClear TruthValue,
+ totalMessagesSent Unsigned32,
+ totalMessagesReceived Unsigned32,
+ announceMessagesSent Unsigned32,
+ announceMessagesReceived Unsigned32,
+ syncMessagesSent Unsigned32,
+ syncMessagesReceived Unsigned32,
+ followUpMessagesSent Unsigned32,
+ followUpMessagesReceived Unsigned32,
+ delayReqMessagesSent Unsigned32,
+ delayReqMessagesReceived Unsigned32,
+ delayRespMessagesSent Unsigned32,
+ delayRespMessagesReceived Unsigned32,
+ pdelayReqMessagesSent Unsigned32,
+ pdelayReqMessagesReceived Unsigned32,
+ pdelayRespMessagesSent Unsigned32,
+ pdelayRespMessagesReceived Unsigned32,
+ pdelayRespFollowUpMessagesSent Unsigned32,
+ pdelayRespFollowUpMessagesReceived Unsigned32,
+ signalingMessagesSent Unsigned32,
+ signalingMessagesReceived Unsigned32,
+ managementMessagesSent Unsigned32,
+ managementMessagesReceived Unsigned32,
+ discardedMessages Unsigned32,
+ unknownMessages Unsigned32 }
+
+
+
+
+ptpbasePortMessageCountersDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.1
+::= { ptpbasePtpPortMessageCountersEntry 1 }
+
+
+ptpbasePortMessageCountersClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.2
+::= { ptpbasePtpPortMessageCountersEntry 2 }
+
+
+ptpbasePortMessageCountersInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.3
+::= { ptpbasePtpPortMessageCountersEntry 3 }
+
+
+ptpbasePortMessageCountersClockPortNumberIndex OBJECT-TYPE
+ SYNTAX ClockPortNumber
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION ""
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.4
+::= { ptpbasePtpPortMessageCountersEntry 4 }
+
+
+ptpbasePortMessageCountersClearAll OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "Handle allowing to clear all PTP counters"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.5
+::= { ptpbasePtpPortMessageCountersEntry 5 }
+
+
+ptpbasePortMessageCountersClear OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "Handle allowing to clear PTP message counters"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.6
+::= { ptpbasePtpPortMessageCountersEntry 6 }
+
+
+totalMessagesSent OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "total number of messages sent by PTP port"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.7
+::= { ptpbasePtpPortMessageCountersEntry 7 }
+
+
+totalMessagesReceived OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Total number of messages received by PTP port"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.8
+::= { ptpbasePtpPortMessageCountersEntry 8 }
+
+
+announceMessagesSent OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of sent Announce messages"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.9
+::= { ptpbasePtpPortMessageCountersEntry 9 }
+
+
+announceMessagesReceived OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of Announce messages received"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.10
+::= { ptpbasePtpPortMessageCountersEntry 10 }
+
+
+syncMessagesSent OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of sent Sync messages."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.11
+::= { ptpbasePtpPortMessageCountersEntry 11 }
+
+
+syncMessagesReceived OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of Sync messages sent"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.12
+::= { ptpbasePtpPortMessageCountersEntry 12 }
+
+
+followUpMessagesSent OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of FollowUp messages sent (two-step operation)"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.13
+::= { ptpbasePtpPortMessageCountersEntry 13 }
+
+
+followUpMessagesReceived OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of received FollowUp messages (two-step operation)"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.14
+::= { ptpbasePtpPortMessageCountersEntry 14 }
+
+
+delayReqMessagesSent OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of sent Delay Request messages"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.15
+::= { ptpbasePtpPortMessageCountersEntry 15 }
+
+
+delayReqMessagesReceived OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of Delay Request messages received"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.16
+::= { ptpbasePtpPortMessageCountersEntry 16 }
+
+
+delayRespMessagesSent OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of sent Delay Response messages"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.17
+::= { ptpbasePtpPortMessageCountersEntry 17 }
+
+
+delayRespMessagesReceived OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of Delay Response messages received"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.18
+::= { ptpbasePtpPortMessageCountersEntry 18 }
+
+
+pdelayReqMessagesSent OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of sent Peer Delay Request messages (P2P operation)"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.19
+::= { ptpbasePtpPortMessageCountersEntry 19 }
+
+
+pdelayReqMessagesReceived OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of Peer Delay Request messages received"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.20
+::= { ptpbasePtpPortMessageCountersEntry 20 }
+
+
+pdelayRespMessagesSent OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of sent Peer Delay Response messages"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.21
+::= { ptpbasePtpPortMessageCountersEntry 21 }
+
+
+pdelayRespMessagesReceived OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of Peer Delay Response messages received"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.22
+::= { ptpbasePtpPortMessageCountersEntry 22 }
+
+
+pdelayRespFollowUpMessagesSent OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of Peer Delay Response FollowUp messages sent (two-step operation)"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.23
+::= { ptpbasePtpPortMessageCountersEntry 23 }
+
+
+pdelayRespFollowUpMessagesReceived OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of received Peer Delay Response Follow-Up messages"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.24
+::= { ptpbasePtpPortMessageCountersEntry 24 }
+
+
+signalingMessagesSent OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of sent Signaling messages (unicast negotiation)"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.25
+::= { ptpbasePtpPortMessageCountersEntry 25 }
+
+
+signalingMessagesReceived OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of received Signaling messages (unicast negotiation)"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.26
+::= { ptpbasePtpPortMessageCountersEntry 26 }
+
+
+managementMessagesSent OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of sent Management messages"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.27
+::= { ptpbasePtpPortMessageCountersEntry 27 }
+
+
+managementMessagesReceived OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of Management messages received"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.28
+::= { ptpbasePtpPortMessageCountersEntry 28 }
+
+
+discardedMessages OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of discarded PTP messages"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.29
+::= { ptpbasePtpPortMessageCountersEntry 29 }
+
+
+unknownMessages OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of unknown type PTP messages received"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.12.1.30
+::= { ptpbasePtpPortMessageCountersEntry 30 }
+
+
+ptpbasePtpProtocolCountersTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbasePtpProtocolCountersEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table describing common PTP protocol counters."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.13
+::= { ptpbaseMIBClockInfo 13 }
+
+ptpbasePtpProtocolCountersEntry OBJECT-TYPE
+ SYNTAX PtpbasePtpProtocolCountersEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in a table describing common PTP protocol counters."
+ INDEX {
+ ptpbaseProtocolCountersDomainIndex,
+ ptpbaseProtocolCountersClockTypeIndex,
+ ptpbaseProtocolCountersInstanceIndex,
+ ptpbaseProtocolCountersClockPortNumberIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.13.1
+::= { ptpbasePtpProtocolCountersTable 1 }
+
+PtpbasePtpProtocolCountersEntry ::= SEQUENCE {
+
+ ptpbaseProtocolCountersDomainIndex ClockDomainType,
+ ptpbaseProtocolCountersClockTypeIndex ClockType,
+ ptpbaseProtocolCountersInstanceIndex ClockInstanceType,
+ ptpbaseProtocolCountersClockPortNumberIndex ClockPortNumber,
+ ptpbaseProtocolCountersClear TruthValue,
+ foreignAdded Unsigned32,
+ foreignCount Unsigned32,
+ foreignRemoved Unsigned32,
+ foreignOverflows Unsigned32,
+ stateTransitions Unsigned32,
+ bestMasterChanges Unsigned32,
+ announceTimeouts Unsigned32 }
+
+
+ptpbaseProtocolCountersDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.13.1.1
+::= { ptpbasePtpProtocolCountersEntry 1 }
+
+
+ptpbaseProtocolCountersClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.13.1.2
+::= { ptpbasePtpProtocolCountersEntry 2 }
+
+
+ptpbaseProtocolCountersInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.13.1.3
+::= { ptpbasePtpProtocolCountersEntry 3 }
+
+
+ptpbaseProtocolCountersClockPortNumberIndex OBJECT-TYPE
+ SYNTAX ClockPortNumber
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION ""
+ -- 1.3.6.1.4.1.46649.1.1.1.2.13.1.4
+::= { ptpbasePtpProtocolCountersEntry 4 }
+
+
+ptpbaseProtocolCountersClear OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "Handle allowing to clear PTP protocol counters"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.13.1.5
+::= { ptpbasePtpProtocolCountersEntry 5 }
+
+
+foreignAdded OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of additions to Foreign Master Record"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.13.1.6
+::= { ptpbasePtpProtocolCountersEntry 6 }
+
+
+foreignCount OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Total number of foreign masters seen"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.13.1.7
+::= { ptpbasePtpProtocolCountersEntry 7 }
+
+
+foreignRemoved OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Total number of foreign masters removed because of inactivity"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.13.1.8
+::= { ptpbasePtpProtocolCountersEntry 8 }
+
+
+foreignOverflows OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of overwritten Foreign Master entries due to lack of space in the record"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.13.1.9
+::= { ptpbasePtpProtocolCountersEntry 9 }
+
+
+stateTransitions OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of PTP protocol state transitions"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.13.1.10
+::= { ptpbasePtpProtocolCountersEntry 10 }
+
+
+bestMasterChanges OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of PTP best master changes"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.13.1.11
+::= { ptpbasePtpProtocolCountersEntry 11 }
+
+
+announceTimeouts OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of Announce Timeout events"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.13.1.12
+::= { ptpbasePtpProtocolCountersEntry 12 }
+
+
+ptpbasePtpErrorCountersTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbasePtpErrorCountersEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table describing common PTP protocol error counters."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.14
+::= { ptpbaseMIBClockInfo 14 }
+
+
+ptpbasePtpErrorCountersEntry OBJECT-TYPE
+ SYNTAX PtpbasePtpErrorCountersEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in a table describing common PTP protocol error counters."
+ INDEX {
+ ptpbaseErrorCountersDomainIndex,
+ ptpbaseErrorCountersClockTypeIndex,
+ ptpbaseErrorCountersInstanceIndex,
+ ptpbaseErrorCountersClockPortNumberIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.14.1
+::= { ptpbasePtpErrorCountersTable 1 }
+
+
+PtpbasePtpErrorCountersEntry ::= SEQUENCE {
+
+ ptpbaseErrorCountersDomainIndex ClockDomainType,
+ ptpbaseErrorCountersClockTypeIndex ClockType,
+ ptpbaseErrorCountersInstanceIndex ClockInstanceType,
+ ptpbaseErrorCountersClockPortNumberIndex ClockPortNumber,
+ ptpbaseErrorCountersClear TruthValue,
+ messageRecvErrors Unsigned32,
+ messageSendErrors Unsigned32,
+ messageFormatErrors Unsigned32,
+ protocolErrors Unsigned32,
+ versionMismatchErrors Unsigned32,
+ domainMismatchErrors Unsigned32,
+ sequenceMismatchErrors Unsigned32,
+ delayMechanismMismatchErrors Unsigned32 }
+
+
+ptpbaseErrorCountersDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.14.1.1
+::= { ptpbasePtpErrorCountersEntry 1 }
+
+
+ptpbaseErrorCountersClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.14.1.2
+::= { ptpbasePtpErrorCountersEntry 2 }
+
+
+ptpbaseErrorCountersInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.14.1.3
+::= { ptpbasePtpErrorCountersEntry 3 }
+
+
+ptpbaseErrorCountersClockPortNumberIndex OBJECT-TYPE
+ SYNTAX ClockPortNumber
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION ""
+ -- 1.3.6.1.4.1.46649.1.1.1.2.14.1.4
+::= { ptpbasePtpErrorCountersEntry 4 }
+
+
+ptpbaseErrorCountersClear OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "Handle allowing to clear PTP error counters"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.14.1.5
+::= { ptpbasePtpErrorCountersEntry 5 }
+
+
+messageRecvErrors OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of message receive errors"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.14.1.6
+::= { ptpbasePtpErrorCountersEntry 6 }
+
+
+messageSendErrors OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of message send errors"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.14.1.7
+::= { ptpbasePtpErrorCountersEntry 7 }
+
+
+messageFormatErrors OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of message format errors"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.14.1.8
+::= { ptpbasePtpErrorCountersEntry 8 }
+
+
+protocolErrors OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of PTP protocol errors - condition which should not happen"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.14.1.9
+::= { ptpbasePtpErrorCountersEntry 9 }
+
+
+versionMismatchErrors OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of messages with incorrect PTP version received"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.14.1.10
+::= { ptpbasePtpErrorCountersEntry 10 }
+
+
+domainMismatchErrors OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of messages received from different PTP domains than configured"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.14.1.11
+::= { ptpbasePtpErrorCountersEntry 11 }
+
+
+sequenceMismatchErrors OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of sequence mismatch errors observed"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.14.1.12
+::= { ptpbasePtpErrorCountersEntry 12 }
+
+
+delayMechanismMismatchErrors OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of delay mechanism mismatch errors - P2P messages seen when E2E operation used or vice versa"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.14.1.13
+::= { ptpbasePtpErrorCountersEntry 13 }
+
+
+ptpbasePtpUnicastNegotiationCountersTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbasePtpUnicastNegotiationCountersEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table describing PTP unicast negotiation counters."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.15
+::= { ptpbaseMIBClockInfo 15 }
+
+
+ptpbasePtpUnicastNegotiationCountersEntry OBJECT-TYPE
+ SYNTAX PtpbasePtpUnicastNegotiationCountersEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in a table describing PTP unicast negotiation counters."
+ INDEX {
+ ptpbaseUnicastNegotiationCountersDomainIndex,
+ ptpbaseUnicastNegotiationCountersClockTypeIndex,
+ ptpbaseUnicastNegotiationCountersInstanceIndex,
+ ptpbaseUnicastNegotiationCountersClockPortNumberIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.15.1
+::= { ptpbasePtpUnicastNegotiationCountersTable 1 }
+
+
+PtpbasePtpUnicastNegotiationCountersEntry ::= SEQUENCE {
+
+ ptpbaseUnicastNegotiationCountersDomainIndex ClockDomainType,
+ ptpbaseUnicastNegotiationCountersClockTypeIndex ClockType,
+ ptpbaseUnicastNegotiationCountersInstanceIndex ClockInstanceType,
+ ptpbaseUnicastNegotiationCountersClockPortNumberIndex ClockPortNumber,
+ ptpbaseUnicastNegotiationCountersClear TruthValue,
+ unicastGrantsRequested Unsigned32,
+ unicastGrantsGranted Unsigned32,
+ unicastGrantsDenied Unsigned32,
+ unicastGrantsCancelSent Unsigned32,
+ unicastGrantsCancelReceived Unsigned32,
+ unicastGrantsCancelAckSent Unsigned32,
+ unicastGrantsCancelAckReceived Unsigned32 }
+
+
+ptpbaseUnicastNegotiationCountersDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.15.1.1
+::= { ptpbasePtpUnicastNegotiationCountersEntry 1 }
+
+
+ptpbaseUnicastNegotiationCountersClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.15.1.2
+::= { ptpbasePtpUnicastNegotiationCountersEntry 2 }
+
+
+ptpbaseUnicastNegotiationCountersInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.15.1.3
+::= { ptpbasePtpUnicastNegotiationCountersEntry 3 }
+
+
+ptpbaseUnicastNegotiationCountersClockPortNumberIndex OBJECT-TYPE
+ SYNTAX ClockPortNumber
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION ""
+ -- 1.3.6.1.4.1.46649.1.1.1.2.15.1.4
+::= { ptpbasePtpUnicastNegotiationCountersEntry 4 }
+
+
+ptpbaseUnicastNegotiationCountersClear OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "Handle allowing to clear PTP unicast negotiation counters"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.15.1.5
+::= { ptpbasePtpUnicastNegotiationCountersEntry 5 }
+
+
+unicastGrantsRequested OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of unicast grants requested by slave, or requested from master"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.15.1.6
+::= { ptpbasePtpUnicastNegotiationCountersEntry 6 }
+
+
+unicastGrantsGranted OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of unicast grants successfully granted to slaves (master), or received from master(s) (slave)"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.15.1.7
+::= { ptpbasePtpUnicastNegotiationCountersEntry 7 }
+
+
+unicastGrantsDenied OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of unicast grants denied to slaves (master), or denied by master(s) (slave)"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.15.1.8
+::= { ptpbasePtpUnicastNegotiationCountersEntry 8 }
+
+
+unicastGrantsCancelSent OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of unicast grants cancelations sent"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.15.1.9
+::= { ptpbasePtpUnicastNegotiationCountersEntry 9 }
+
+
+unicastGrantsCancelReceived OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of unicast grant cancellations received"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.15.1.10
+::= { ptpbasePtpUnicastNegotiationCountersEntry 10 }
+
+
+unicastGrantsCancelAckSent OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of sent acknowledgments to unicast grant cancellation"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.15.1.11
+::= { ptpbasePtpUnicastNegotiationCountersEntry 11 }
+
+
+unicastGrantsCancelAckReceived OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of received acknowledgments to unicast grant cancellation."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.15.1.12
+::= { ptpbasePtpUnicastNegotiationCountersEntry 12 }
+
+
+ptpbasePtpPerformanceCountersTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbasePtpPerformanceCountersEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table describing common PTP protocol message performance counters."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.16
+::= { ptpbaseMIBClockInfo 16 }
+
+
+ptpbasePtpPerformanceCountersEntry OBJECT-TYPE
+ SYNTAX PtpbasePtpPerformanceCountersEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in a table describing common PTP protocol message performance counters."
+ INDEX {
+ ptpbasePerformanceCountersDomainIndex,
+ ptpbasePerformanceCountersClockTypeIndex,
+ ptpbasePerformanceCountersInstanceIndex,
+ ptpbasePerformanceCountersClockPortNumberIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.16.1
+::= { ptpbasePtpPerformanceCountersTable 1 }
+
+
+PtpbasePtpPerformanceCountersEntry ::= SEQUENCE {
+
+ ptpbasePerformanceCountersDomainIndex ClockDomainType,
+ ptpbasePerformanceCountersClockTypeIndex ClockType,
+ ptpbasePerformanceCountersInstanceIndex ClockInstanceType,
+ ptpbasePerformanceCountersClockPortNumberIndex ClockPortNumber,
+ messageSendRate Unsigned32,
+ messageReceiveRate Unsigned32 }
+
+
+ptpbasePerformanceCountersDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.16.1.1
+::= { ptpbasePtpPerformanceCountersEntry 1 }
+
+
+ptpbasePerformanceCountersClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.16.1.2
+::= { ptpbasePtpPerformanceCountersEntry 2 }
+
+
+ptpbasePerformanceCountersInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.16.1.3
+::= { ptpbasePtpPerformanceCountersEntry 3 }
+
+
+ptpbasePerformanceCountersClockPortNumberIndex OBJECT-TYPE
+ SYNTAX ClockPortNumber
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION ""
+ -- 1.3.6.1.4.1.46649.1.1.1.2.16.1.4
+::= { ptpbasePtpPerformanceCountersEntry 4 }
+
+
+messageSendRate OBJECT-TYPE
+ SYNTAX Unsigned32
+ UNITS "Messages per second"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Message transmission rate (messages per second)"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.16.1.5
+::= { ptpbasePtpPerformanceCountersEntry 5 }
+
+
+messageReceiveRate OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Message receive rate (messages per second)"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.16.1.6
+::= { ptpbasePtpPerformanceCountersEntry 6 }
+
+
+ptpbasePtpSecurityCountersTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbasePtpSecurityCountersEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table describing PTP message security (access control) counters."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.17
+::= { ptpbaseMIBClockInfo 17 }
+
+
+ptpbasePtpSecurityCountersEntry OBJECT-TYPE
+ SYNTAX PtpbasePtpSecurityCountersEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Entry in a table describing PTP message security (access control) counters."
+ INDEX {
+ ptpbaseSecurityCountersDomainIndex,
+ ptpbaseSecurityCountersClockTypeIndex,
+ ptpbaseSecurityCountersInstanceIndex,
+ ptpbaseSecurityCountersClockPortNumberIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.17.1
+::= { ptpbasePtpSecurityCountersTable 1 }
+
+
+PtpbasePtpSecurityCountersEntry ::= SEQUENCE {
+
+ ptpbaseSecurityCountersDomainIndex ClockDomainType,
+ ptpbaseSecurityCountersClockTypeIndex ClockType,
+ ptpbaseSecurityCountersInstanceIndex ClockInstanceType,
+ ptpbaseSecurityCountersClockPortNumberIndex ClockPortNumber,
+ ptpbaseSecurityCountersClear TruthValue,
+ aclTimingMessagesDiscarded Unsigned32,
+ aclManagementMessagesDiscarded Unsigned32 }
+
+
+ptpbaseSecurityCountersDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.17.1.1
+::= { ptpbasePtpSecurityCountersEntry 1 }
+
+
+ptpbaseSecurityCountersClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.17.1.2
+::= { ptpbasePtpSecurityCountersEntry 2 }
+
+
+ptpbaseSecurityCountersInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.17.1.3
+::= { ptpbasePtpSecurityCountersEntry 3 }
+
+
+ptpbaseSecurityCountersClockPortNumberIndex OBJECT-TYPE
+ SYNTAX ClockPortNumber
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION ""
+ -- 1.3.6.1.4.1.46649.1.1.1.2.17.1.4
+::= { ptpbasePtpSecurityCountersEntry 4 }
+
+
+ptpbaseSecurityCountersClear OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "Handle allowing to clear PTP access control counters"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.17.1.5
+::= { ptpbasePtpSecurityCountersEntry 5 }
+
+
+aclTimingMessagesDiscarded OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of timing (non-management) messages discarded by access control (includes signaling messages)."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.17.1.6
+::= { ptpbasePtpSecurityCountersEntry 6 }
+
+
+aclManagementMessagesDiscarded OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of management messages discarded by access control"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.17.1.7
+::= { ptpbasePtpSecurityCountersEntry 7 }
+
+
+ptpbaseSlaveOfmStatisticsTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseSlaveOfmStatisticsEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of statistics describing PTP slave Offset from Master."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18
+::= { ptpbaseMIBClockInfo 18 }
+
+
+ptpbaseSlaveOfmStatisticsEntry OBJECT-TYPE
+ SYNTAX PtpbaseSlaveOfmStatisticsEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in a table of statistics describing PTP slave Offset from Master."
+ INDEX {
+ ptpbaseSlaveOfmStatisticsDomainIndex,
+ ptpbaseSlaveOfmStatisticsClockTypeIndex,
+ ptpbaseSlaveOfmStatisticsInstanceIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18.1
+::= { ptpbaseSlaveOfmStatisticsTable 1 }
+
+
+PtpbaseSlaveOfmStatisticsEntry ::= SEQUENCE {
+
+ ptpbaseSlaveOfmStatisticsDomainIndex ClockDomainType,
+ ptpbaseSlaveOfmStatisticsClockTypeIndex ClockType,
+ ptpbaseSlaveOfmStatisticsInstanceIndex ClockInstanceType,
+ ofmCurrentValue ClockTimeInterval,
+ ofmCurrentStringValue DisplayString,
+ ofmStatsPeriodSeconds Unsigned32,
+ ofmStatsValid TruthValue,
+ ofmStatsMin ClockTimeInterval,
+ ofmStatsMax ClockTimeInterval,
+ ofmStatsMean ClockTimeInterval,
+ ofmStatsStdDev ClockTimeInterval,
+ ofmStatsMedian ClockTimeInterval,
+ ofmStatsMinStringValue DisplayString,
+ ofmStatsMaxStringValue DisplayString,
+ ofmStatsMeanStringValue DisplayString,
+ ofmStatsStdDevStringValue DisplayString,
+ ofmStatsMedianStringValue DisplayString }
+
+
+ptpbaseSlaveOfmStatisticsDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18.1.1
+::= { ptpbaseSlaveOfmStatisticsEntry 1 }
+
+
+ptpbaseSlaveOfmStatisticsClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18.1.2
+::= { ptpbaseSlaveOfmStatisticsEntry 2 }
+
+
+ptpbaseSlaveOfmStatisticsInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18.1.3
+::= { ptpbaseSlaveOfmStatisticsEntry 3 }
+
+
+ofmCurrentValue OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Current value of Offset From Master."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18.1.4
+::= { ptpbaseSlaveOfmStatisticsEntry 4 }
+
+
+ofmCurrentStringValue OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Current value of Offset From Master, presented as text."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18.1.5
+::= { ptpbaseSlaveOfmStatisticsEntry 5 }
+
+
+ofmStatsPeriodSeconds OBJECT-TYPE
+ SYNTAX Unsigned32
+ UNITS "Seconds"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Update interval for Offset From Master statistics."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18.1.6
+::= { ptpbaseSlaveOfmStatisticsEntry 6 }
+
+
+ofmStatsValid OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Determines if current Offset From Master statistics are valid."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18.1.7
+::= { ptpbaseSlaveOfmStatisticsEntry 7 }
+
+
+ofmStatsMin OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ UNITS "Time Interval"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Minimum Offset From Master in the last statistics update window."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18.1.8
+::= { ptpbaseSlaveOfmStatisticsEntry 8 }
+
+
+ofmStatsMax OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Maximum Offset From Master in the last statistics update window."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18.1.9
+::= { ptpbaseSlaveOfmStatisticsEntry 9 }
+
+
+ofmStatsMean OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ UNITS "Time Interval"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Mean Offset From Master in the last statistics update window."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18.1.10
+::= { ptpbaseSlaveOfmStatisticsEntry 10 }
+
+
+ofmStatsStdDev OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ UNITS "Time Interval"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Standard deviation of Offset From Master in the last statistics update window."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18.1.11
+::= { ptpbaseSlaveOfmStatisticsEntry 11 }
+
+
+ofmStatsMedian OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ UNITS "Time Interval"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Median of Offset From Master in the last statistics update window."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18.1.12
+::= { ptpbaseSlaveOfmStatisticsEntry 12 }
+
+
+ofmStatsMinStringValue OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Minimum Offset From Master in the last statistics update window, presented as textual value."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18.1.13
+::= { ptpbaseSlaveOfmStatisticsEntry 13 }
+
+
+ofmStatsMaxStringValue OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Maximum Offset From Master in the last statistics update window, presented as textual value."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18.1.14
+::= { ptpbaseSlaveOfmStatisticsEntry 14 }
+
+
+ofmStatsMeanStringValue OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Mean of Offset From Master in the last statistics update window, presented as textual value."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18.1.15
+::= { ptpbaseSlaveOfmStatisticsEntry 15 }
+
+
+ofmStatsStdDevStringValue OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Standard deviation of Offset From Master in the last statistics update window, presented as textual value."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18.1.16
+::= { ptpbaseSlaveOfmStatisticsEntry 16 }
+
+
+ofmStatsMedianStringValue OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Median of Offset From Master in the last statistics update window, presented as textual value."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.18.1.17
+::= { ptpbaseSlaveOfmStatisticsEntry 17 }
+
+
+ptpbaseSlaveMpdStatisticsTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseSlaveMpdStatisticsEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of statistics describing PTP slave Mean Path Delay."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19
+::= { ptpbaseMIBClockInfo 19 }
+
+
+ptpbaseSlaveMpdStatisticsEntry OBJECT-TYPE
+ SYNTAX PtpbaseSlaveMpdStatisticsEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in a table of statistics describing PTP slave Mean Path Delay."
+ INDEX {
+ ptpbaseSlaveMpdStatisticsDomainIndex,
+ ptpbaseSlaveMpdStatisticsClockTypeIndex,
+ ptpbaseSlaveMpdStatisticsInstanceIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19.1
+::= { ptpbaseSlaveMpdStatisticsTable 1 }
+
+
+PtpbaseSlaveMpdStatisticsEntry ::= SEQUENCE {
+
+ ptpbaseSlaveMpdStatisticsDomainIndex ClockDomainType,
+ ptpbaseSlaveMpdStatisticsClockTypeIndex ClockType,
+ ptpbaseSlaveMpdStatisticsInstanceIndex ClockInstanceType,
+ mpdCurrentValue ClockTimeInterval,
+ mpdCurrentStringValue DisplayString,
+ mpdStatsPeriodSeconds Integer32,
+ mpdStatsValid TruthValue,
+ mpdStatsMin ClockTimeInterval,
+ mpdStatsMax ClockTimeInterval,
+ mpdStatsMean ClockTimeInterval,
+ mpdStatsStdDev ClockTimeInterval,
+ mpdStatsMedian ClockTimeInterval,
+ mpdStatsMinStringValue DisplayString,
+ mpdStatsMaxStringValue DisplayString,
+ mpdStatsMeanStringValue DisplayString,
+ mpdStatsStdDevStringValue DisplayString,
+ mpdStatsMedianStringValue DisplayString }
+
+
+ptpbaseSlaveMpdStatisticsDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19.1.1
+::= { ptpbaseSlaveMpdStatisticsEntry 1 }
+
+
+ptpbaseSlaveMpdStatisticsClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19.1.2
+::= { ptpbaseSlaveMpdStatisticsEntry 2 }
+
+
+ptpbaseSlaveMpdStatisticsInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19.1.3
+::= { ptpbaseSlaveMpdStatisticsEntry 3 }
+
+
+mpdCurrentValue OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ UNITS "Time Interval"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Current value of Mean Path Delay."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19.1.4
+::= { ptpbaseSlaveMpdStatisticsEntry 4 }
+
+
+mpdCurrentStringValue OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Current value of Mean Path Delay, presented as text."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19.1.5
+::= { ptpbaseSlaveMpdStatisticsEntry 5 }
+
+
+mpdStatsPeriodSeconds OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Update interval for Mean Path Delay statistics."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19.1.6
+::= { ptpbaseSlaveMpdStatisticsEntry 6 }
+
+
+mpdStatsValid OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Determines if current Mean Path Delay statistics are valid."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19.1.7
+::= { ptpbaseSlaveMpdStatisticsEntry 7 }
+
+
+mpdStatsMin OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ UNITS "Time Interval"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Minimum Mean Path Delay in the last statistics update window."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19.1.8
+::= { ptpbaseSlaveMpdStatisticsEntry 8 }
+
+
+mpdStatsMax OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ UNITS "Time Interval"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Maximum Mean Path Delay in the last statistics update window."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19.1.9
+::= { ptpbaseSlaveMpdStatisticsEntry 9 }
+
+
+mpdStatsMean OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ UNITS "Time Interval"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Mean of Mean Path Delay in the last statistics update window."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19.1.10
+::= { ptpbaseSlaveMpdStatisticsEntry 10 }
+
+
+mpdStatsStdDev OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ UNITS "Time Interval"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Standard Deviation of Mean Path Delay in the last statistics update window."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19.1.11
+::= { ptpbaseSlaveMpdStatisticsEntry 11 }
+
+
+mpdStatsMedian OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ UNITS "Time Interval"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Median of Mean Path Delay in the last statistics update window."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19.1.12
+::= { ptpbaseSlaveMpdStatisticsEntry 12 }
+
+
+mpdStatsMinStringValue OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Minimum Mean Path Delay in the last statistics update window, presented as textual value."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19.1.13
+::= { ptpbaseSlaveMpdStatisticsEntry 13 }
+
+
+mpdStatsMaxStringValue OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Maximum Mean Path Delay in the last statistics update window, presented as textual value."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19.1.14
+::= { ptpbaseSlaveMpdStatisticsEntry 14 }
+
+
+mpdStatsMeanStringValue OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Mean of Mean Path Delay in the last statistics update window, presented as textual value."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19.1.15
+::= { ptpbaseSlaveMpdStatisticsEntry 15 }
+
+
+mpdStatsStdDevStringValue OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Standard Deviation Path Delay in the last statistics update window, presented as textual value."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19.1.16
+::= { ptpbaseSlaveMpdStatisticsEntry 16 }
+
+
+mpdStatsMedianStringValue OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Median of Mean Path Delay in the last statistics update window, presented as textual value."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.19.1.17
+::= { ptpbaseSlaveMpdStatisticsEntry 17 }
+
+
+ptpbaseSlaveFreqAdjStatisticsTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseSlaveFreqAdjStatisticsEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of statistics describing PTP slave clock frequency adjustment."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20
+::= { ptpbaseMIBClockInfo 20 }
+
+
+ptpbaseSlaveFreqAdjStatisticsEntry OBJECT-TYPE
+ SYNTAX PtpbaseSlaveFreqAdjStatisticsEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in a table of statistics describing PTP slave clock frequency adjustment."
+ INDEX {
+ ptpbaseSlaveFreqAdjStatisticsDomainIndex,
+ ptpbaseSlaveFreqAdjStatisticsClockTypeIndex,
+ ptpbaseSlaveFreqAdjStatisticsInstanceIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1
+::= { ptpbaseSlaveFreqAdjStatisticsTable 1 }
+
+
+PtpbaseSlaveFreqAdjStatisticsEntry ::= SEQUENCE {
+
+ ptpbaseSlaveFreqAdjStatisticsDomainIndex ClockDomainType,
+ ptpbaseSlaveFreqAdjStatisticsClockTypeIndex ClockType,
+ ptpbaseSlaveFreqAdjStatisticsInstanceIndex ClockInstanceType,
+ freqAdjCurrentValue Integer32,
+ freqAdjStatsPeriodSeconds Unsigned32,
+ freqAdjStatsValid TruthValue,
+ freqAdjStatsMin Integer32,
+ freqAdjStatsMax Integer32,
+ freqAdjStatsMean Integer32,
+ freqAdjStatsStdDev Integer32,
+ freqAdjStatsStatsMedian Integer32 }
+
+
+ptpbaseSlaveFreqAdjStatisticsDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1.1
+::= { ptpbaseSlaveFreqAdjStatisticsEntry 1 }
+
+
+ptpbaseSlaveFreqAdjStatisticsClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1.2
+::= { ptpbaseSlaveFreqAdjStatisticsEntry 2 }
+
+
+ptpbaseSlaveFreqAdjStatisticsInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1.3
+::= { ptpbaseSlaveFreqAdjStatisticsEntry 3 }
+
+
+freqAdjCurrentValue OBJECT-TYPE
+ SYNTAX Integer32
+ UNITS "Parts per Billion / 10E-9"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Current clock frequency adjustment value."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1.4
+::= { ptpbaseSlaveFreqAdjStatisticsEntry 4 }
+
+
+freqAdjStatsPeriodSeconds OBJECT-TYPE
+ SYNTAX Unsigned32
+ UNITS "Seconds"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Update interval for clock frequency adjustment statistics."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1.5 --
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1.5
+ ::= { ptpbaseSlaveFreqAdjStatisticsEntry 5 }
+
+
+freqAdjStatsValid OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Determines if current clock frequency adjustment statistics are valid."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1.6 --
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1.6
+ ::= { ptpbaseSlaveFreqAdjStatisticsEntry 6 }
+
+
+freqAdjStatsMin OBJECT-TYPE
+ SYNTAX Integer32
+ UNITS "Parts per Billion / 10E-9"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Minimum clock frequency adjustment in the last statistics update window."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1.7 --
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1.7
+ ::= { ptpbaseSlaveFreqAdjStatisticsEntry 7 }
+
+
+freqAdjStatsMax OBJECT-TYPE
+ SYNTAX Integer32
+ UNITS "Parts per Billion / 10E-9"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Maximum clock frequency adjustment in the last statistics update window."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1.8 --
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1.8
+ ::= { ptpbaseSlaveFreqAdjStatisticsEntry 8 }
+
+
+freqAdjStatsMean OBJECT-TYPE
+ SYNTAX Integer32
+ UNITS "Parts per Billion / 10E-9"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Mean clock frequency adjustment in the last statistics update window."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1.9 --
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1.9
+ ::= { ptpbaseSlaveFreqAdjStatisticsEntry 9 }
+
+
+freqAdjStatsStdDev OBJECT-TYPE
+ SYNTAX Integer32
+ UNITS "Parts per Billion / 10E-9"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Standard deviation of clock frequency adjustment in the last statistics update window."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1.10 --
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1.10
+ ::= { ptpbaseSlaveFreqAdjStatisticsEntry 10 }
+
+
+freqAdjStatsStatsMedian OBJECT-TYPE
+ SYNTAX Integer32
+ UNITS "Parts per Billion / 10E-9"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Median of clock frequency adjustment in the last statistics update window."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1.11 --
+ -- 1.3.6.1.4.1.46649.1.1.1.2.20.1.11
+ ::= { ptpbaseSlaveFreqAdjStatisticsEntry 11 }
+
+
+ptpbasePtpdSpecificCountersTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbasePtpdSpecificCountersEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table describing PTPd-specific counters."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.21
+::= { ptpbaseMIBClockInfo 21 }
+
+
+ptpbasePtpdSpecificCountersEntry OBJECT-TYPE
+ SYNTAX PtpbasePtpdSpecificCountersEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in a table describing PTPd-specific counters."
+ INDEX {
+ ptpbasePtpdSpecificCountersDomainIndex,
+ ptpbasePtpdSpecificCountersClockTypeIndex,
+ ptpbasePtpdSpecificCountersInstanceIndex,
+ ptpbasePtpdSpecificCountersClockPortNumberIndex }
+ -- 1.3.6.1.4.1.46649.1.1.1.2.21.1
+::= { ptpbasePtpdSpecificCountersTable 1 }
+
+
+PtpbasePtpdSpecificCountersEntry ::= SEQUENCE {
+
+ ptpbasePtpdSpecificCountersDomainIndex ClockDomainType,
+ ptpbasePtpdSpecificCountersClockTypeIndex ClockType,
+ ptpbasePtpdSpecificCountersInstanceIndex ClockInstanceType,
+ ptpbasePtpdSpecificCountersClockPortNumberIndex ClockPortNumber,
+ ptpbasePtpdSpecificCountersClear TruthValue,
+ consecutiveSequenceErrors Unsigned32,
+ ignoredAnnounce Unsigned32,
+ delayMSOutliersFound Unsigned32,
+ delaySMOutliersFound Unsigned32,
+ maxDelayDrops Unsigned32 }
+
+
+ptpbasePtpdSpecificCountersDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.21.1.1
+::= { ptpbasePtpdSpecificCountersEntry 1 }
+
+
+ptpbasePtpdSpecificCountersClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.21.1.2
+::= { ptpbasePtpdSpecificCountersEntry 2 }
+
+
+ptpbasePtpdSpecificCountersInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.21.1.3
+::= { ptpbasePtpdSpecificCountersEntry 3 }
+
+
+ptpbasePtpdSpecificCountersClockPortNumberIndex OBJECT-TYPE
+ SYNTAX ClockPortNumber
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION ""
+ -- 1.3.6.1.4.1.46649.1.1.1.2.21.1.4
+::= { ptpbasePtpdSpecificCountersEntry 4 }
+
+
+ptpbasePtpdSpecificCountersClear OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "Handle allowing to clear all PTPd-specific counters"
+ -- 1.3.6.1.4.1.46649.1.1.1.2.21.1.5
+::= { ptpbasePtpdSpecificCountersEntry 5 }
+
+
+consecutiveSequenceErrors OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of consecutive message sequence errors."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.21.1.6
+::= { ptpbasePtpdSpecificCountersEntry 6 }
+
+
+ignoredAnnounce OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of ignored Announce messages (BMCA disabled or UTC parameter preference)."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.21.1.7
+::= { ptpbasePtpdSpecificCountersEntry 7 }
+
+
+delayMSOutliersFound OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of DelayMS (Sync or FollowUp) outliers found."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.21.1.8
+::= { ptpbasePtpdSpecificCountersEntry 8 }
+
+
+delaySMOutliersFound OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of delaySM (Delay / pDelay response) outliers found."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.21.1.9
+::= { ptpbasePtpdSpecificCountersEntry 9 }
+
+
+maxDelayDrops OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of messages ignored because of maximum delay setting."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.21.1.10
+::= { ptpbasePtpdSpecificCountersEntry 10 }
+
+
+ptpbasePtpdSpecificDataTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbasePtpdSpecificDataEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table describing other PTPd-specific data."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.22
+::= { ptpbaseMIBClockInfo 22 }
+
+
+ptpbasePtpdSpecificDataEntry OBJECT-TYPE
+ SYNTAX PtpbasePtpdSpecificDataEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in a table describing other PTPd-specific data."
+ INDEX {
+ ptpbasePtpdSpecificDataDomainIndex,
+ ptpbasePtpdSpecificDataClockTypeIndex,
+ ptpbasePtpdSpecificDataInstanceIndex}
+ -- 1.3.6.1.4.1.46649.1.1.1.2.22.1
+::= { ptpbasePtpdSpecificDataTable 1 }
+
+
+PtpbasePtpdSpecificDataEntry ::= SEQUENCE {
+
+ ptpbasePtpdSpecificDataDomainIndex ClockDomainType,
+ ptpbasePtpdSpecificDataClockTypeIndex ClockType,
+ ptpbasePtpdSpecificDataInstanceIndex ClockInstanceType,
+ rawDelayMS ClockTimeInterval,
+ rawDelayMSStringValue DisplayString,
+ rawDelaySM ClockTimeInterval,
+ rawDelaySMStringValue DisplayString }
+
+
+ptpbasePtpdSpecificDataDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.22.1.1
+::= { ptpbasePtpdSpecificDataEntry 1 }
+
+
+ptpbasePtpdSpecificDataClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.22.1.2
+::= { ptpbasePtpdSpecificDataEntry 2 }
+
+
+ptpbasePtpdSpecificDataInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.22.1.3
+::= { ptpbasePtpdSpecificDataEntry 3 }
+
+
+rawDelayMS OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Last raw (unfiltered) value of Master to Slave delay (Sync / FollowUp): T2 - T1."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.22.1.4
+::= { ptpbasePtpdSpecificDataEntry 4 }
+
+
+rawDelayMSStringValue OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Last raw (unfiltered) value of Master to Slave delay (Sync / FollowUp): T2 - T1, presented as text value."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.22.1.5
+::= { ptpbasePtpdSpecificDataEntry 5 }
+
+
+rawDelaySM OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Last raw (unfiltered) value of Slave to Master delay (Delay Request / Response): T4 - T3."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.22.1.6
+::= { ptpbasePtpdSpecificDataEntry 6 }
+
+
+rawDelaySMStringValue OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Last raw (unfiltered) value of Slave to Master delay (Delay Request / Response): T4 - T3, presented as text value."
+ -- 1.3.6.1.4.1.46649.1.1.1.2.22.1.7
+::= { ptpbasePtpdSpecificDataEntry 7 }
+
+
+ptpbaseMIBConformance OBJECT IDENTIFIER
+ -- 1.3.6.1.4.1.46649.1.1.2
+::= { ptpbaseMIB 2 }
+
+-- Conformance Information Definition
+
+ptpbaseMIBCompliances OBJECT IDENTIFIER
+ -- 1.3.6.1.4.1.46649.1.1.2.1
+::= { ptpbaseMIBConformance 1 }
+
+ptpbaseMIBGroups OBJECT IDENTIFIER
+ -- 1.3.6.1.4.1.46649.1.1.2.2
+::= { ptpbaseMIBConformance 2 }
+
+
+ptpbaseMIBCompliances1 MODULE-COMPLIANCE
+ STATUS current
+ DESCRIPTION
+ "Compliance statement for agents that provide read-only support
+ for PTPBASE-MIB. Such devices can only be monitored using this
+ MIB module.
+
+ The Module is implemented with support for read-only. In other
+ words, only monitoring is available by implementing this
+ MODULE-COMPLIANCE."
+
+ MODULE
+ MANDATORY-GROUPS {
+ ptpbaseMIBSystemInfoGroup }
+
+ -- 1.3.6.1.4.1.46649.1.1.2.1.1
+::= { ptpbaseMIBCompliances 1 }
+
+
+ptpbaseMIBCompliances2 MODULE-COMPLIANCE
+ STATUS current
+ DESCRIPTION
+ "Compliance statement for agents that provide read-only support
+ for PTPBASE-MIB. Such devices can only be monitored using this
+ MIB module.
+
+ The Module is implemented with support for read-only. In other
+ words, only monitoring is available by implementing this
+ MODULE-COMPLIANCE."
+
+ MODULE
+ MANDATORY-GROUPS {
+ ptpbaseMIBClockCurrentDSGroup,
+ ptpbaseMIBClockParentDSGroup,
+ ptpbaseMIBClockDefaultDSGroup,
+ ptpbaseMIBClockRunningGroup,
+ ptpbaseMIBClockTimepropertiesGroup }
+
+ -- 1.3.6.1.4.1.46649.1.1.2.1.2
+::= { ptpbaseMIBCompliances 2 }
+
+
+ptpbaseMIBCompliances3 MODULE-COMPLIANCE
+ STATUS current
+ DESCRIPTION
+ "Compliance statement for agents that provide read-only support
+ for PTPBASE-MIB. Such devices can only be monitored using this
+ MIB module.
+
+ The Module is implemented with support for read-only. In other
+ words, only monitoring is available by implementing this
+ MODULE-COMPLIANCE."
+
+ MODULE
+ MANDATORY-GROUPS {
+ ptpbaseMIBClockPortGroup,
+ ptpbaseMIBClockPortDSGroup,
+ ptpbaseMIBClockPortRunningGroup,
+ ptpbaseMIBClockPortAssociateGroup }
+
+ -- 1.3.6.1.4.1.46649.1.1.2.1.3
+::= { ptpbaseMIBCompliances 3 }
+
+
+ptpbaseMIBCompliances4 MODULE-COMPLIANCE
+ STATUS current
+ DESCRIPTION
+ "Compliance statement for agents that provide read-only support
+ for PTPBASE-MIB. Such devices can only be monitored using this
+ MIB module.
+
+ The Module is implemented with support for read-only. In other
+ words, only monitoring is available by implementing this
+ MODULE-COMPLIANCE."
+
+ MODULE
+ MANDATORY-GROUPS {
+ ptpbaseMIBClockTranparentDSGroup,
+ ptpbaseMIBClockPortTransDSGroup }
+
+ -- 1.3.6.1.4.1.46649.1.1.2.1.4
+::= { ptpbaseMIBCompliances 4 }
+
+ptpbaseMIBSystemInfoGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseSystemDomainTotals,
+ ptpDomainClockPortsTotal,
+ ptpbaseSystemProfile }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing system-wide
+ information"
+ -- 1.3.6.1.4.1.46649.1.1.2.2.1
+::= { ptpbaseMIBGroups 1 }
+
+ptpbaseMIBClockCurrentDSGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockCurrentDSStepsRemoved,
+ ptpbaseClockCurrentDSOffsetFromMaster,
+ ptpbaseClockCurrentDSMeanPathDelay,
+ ptpbaseClockCurrentDSOffsetFromMasterString,
+ ptpbaseClockCurrentDSMeanPathDelayString,
+ ptpbaseClockCurrentDSOffsetFromMasterThresholdNs }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing PTP Current Dataset
+ information"
+ -- 1.3.6.1.4.1.46649.1.1.2.2.2
+::= { ptpbaseMIBGroups 2 }
+
+ptpbaseMIBClockParentDSGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockParentDSParentPortIdentity,
+ ptpbaseClockParentDSParentStats,
+ ptpbaseClockParentDSOffset,
+ ptpbaseClockParentDSClockPhChRate,
+ ptpbaseClockParentDSGMClockIdentity,
+ ptpbaseClockParentDSGMClockPriority1,
+ ptpbaseClockParentDSGMClockPriority2,
+ ptpbaseClockParentDSGMClockQualityClass,
+ ptpbaseClockParentDSGMClockQualityAccuracy,
+ ptpbaseClockParentDSGMClockQualityOffset,
+ ptpbaseClockParentDSParentPortAddressType,
+ ptpbaseClockParentDSParentPortAddress }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing PTP Parent Dataset
+ information"
+ -- 1.3.6.1.4.1.46649.1.1.2.2.3
+::= { ptpbaseMIBGroups 3 }
+
+ptpbaseMIBClockDefaultDSGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockDefaultDSTwoStepFlag,
+ ptpbaseClockDefaultDSClockIdentity,
+ ptpbaseClockDefaultDSPriority1,
+ ptpbaseClockDefaultDSPriority2,
+ ptpbaseClockDefaultDSSlaveOnly,
+ ptpbaseClockDefaultDSQualityClass,
+ ptpbaseClockDefaultDSQualityAccuracy,
+ ptpbaseClockDefaultDSQualityOffset,
+ ptpbaseClockDefaultDSDomainNumber }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing PTP Default Dataset
+ information"
+ -- 1.3.6.1.4.1.46649.1.1.2.2.4
+::= { ptpbaseMIBGroups 4 }
+
+ptpbaseMIBClockRunningGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockRunningState,
+ ptpbaseClockRunningPacketsSent,
+ ptpbaseClockRunningPacketsReceived }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing PTP running state
+ information"
+ -- 1.3.6.1.4.1.46649.1.1.2.2.5
+::= { ptpbaseMIBGroups 5 }
+
+ptpbaseMIBClockTimepropertiesGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockTimePropertiesDSCurrentUTCOffsetValid,
+ ptpbaseClockTimePropertiesDSCurrentUTCOffset,
+ ptpbaseClockTimePropertiesDSLeap59,
+ ptpbaseClockTimePropertiesDSLeap61,
+ ptpbaseClockTimePropertiesDSTimeTraceable,
+ ptpbaseClockTimePropertiesDSFreqTraceable,
+ ptpbaseClockTimePropertiesDSPTPTimescale,
+ ptpbaseClockTimePropertiesDSSource }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing PTP Time Properties
+ information"
+ -- 1.3.6.1.4.1.46649.1.1.2.2.6
+::= { ptpbaseMIBGroups 6 }
+
+ptpbaseMIBClockTranparentDSGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockTransDefaultDSClockIdentity,
+ ptpbaseClockTransDefaultDSNumOfPorts,
+ ptpbaseClockTransDefaultDSDelay,
+ ptpbaseClockTransDefaultDSPrimaryDomain }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing PTP Transparent
+ Dataset
+ information"
+ -- 1.3.6.1.4.1.46649.1.1.2.2.7
+::= { ptpbaseMIBGroups 7 }
+
+ptpbaseMIBClockPortGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockPortCurrentPeerAddress,
+ ptpbaseClockPortCurrentPeerAddressType,
+ ptpbaseClockPortName,
+ ptpbaseClockPortSyncOneStep,
+ ptpbaseClockPortNumOfAssociatedPorts,
+ ptpbaseClockPortRole }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing information for a
+ given PTP Port."
+ -- 1.3.6.1.4.1.46649.1.1.2.2.8
+::= { ptpbaseMIBGroups 8 }
+
+ptpbaseMIBClockPortDSGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockPortDSName,
+ ptpbaseClockPortDSPortIdentity,
+ ptpbaseClockPortDSAnnouncementInterval,
+ ptpbaseClockPortDSAnnounceRctTimeout,
+ ptpbaseClockPortDSSyncInterval,
+ ptpbaseClockPortDSMinDelayReqInterval,
+ ptpbaseClockPortDSPeerDelayReqInterval,
+ ptpbaseClockPortDSDelayMech,
+ ptpbaseClockPortDSPeerMeanPathDelay,
+ ptpbaseClockPortDSGrantDuration,
+ ptpbaseClockPortDSPTPVersion,
+ ptpBaseClockPortDSPeerMeanPathDelayString,
+ ptpBaseClockPortDSLastMismatchedDomain }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing PTP Port Dataset
+ information"
+ -- 1.3.6.1.4.1.46649.1.1.2.2.9
+::= { ptpbaseMIBGroups 9 }
+
+ptpbaseMIBClockPortRunningGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockPortRunningName,
+ ptpbaseClockPortRunningState,
+ ptpbaseClockPortRunningRole,
+ ptpbaseClockPortRunningInterfaceIndex,
+ ptpbaseClockPortRunningIPversion,
+ ptpbaseClockPortRunningEncapsulationType,
+ ptpbaseClockPortRunningTxMode,
+ ptpbaseClockPortRunningRxMode,
+ ptpbaseClockPortRunningPacketsReceived,
+ ptpbaseClockPortRunningPacketsSent }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing PTP running interface
+ information"
+ -- 1.3.6.1.4.1.46649.1.1.2.2.10
+::= { ptpbaseMIBGroups 10 }
+
+ptpbaseMIBClockPortTransDSGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockPortTransDSPortIdentity,
+ ptpbaseClockPortTransDSlogMinPdelayReqInt,
+ ptpbaseClockPortTransDSFaultyFlag,
+ ptpbaseClockPortTransDSPeerMeanPathDelay }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing PTP TransparentDS
+ Dataset
+ information"
+ -- 1.3.6.1.4.1.46649.1.1.2.2.11
+::= { ptpbaseMIBGroups 11 }
+
+ptpbaseMIBClockPortAssociateGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockPortAssociatePacketsSent,
+ ptpbaseClockPortAssociatePacketsReceived,
+ ptpbaseClockPortAssociateAddress,
+ ptpbaseClockPortAssociateAddressType,
+ ptpbaseClockPortAssociateInErrors,
+ ptpbaseClockPortAssociateOutErrors }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing information on peer
+ PTP ports for a given PTP clock-port."
+ -- 1.3.6.1.4.1.46649.1.1.2.2.12
+::= { ptpbaseMIBGroups 12 }
+
+ptpbaseMIBPtpPortMessageCountersGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbasePortMessageCountersClearAll,
+ ptpbasePortMessageCountersClear,
+ announceMessagesSent,
+ announceMessagesReceived,
+ syncMessagesSent,
+ syncMessagesReceived,
+ followUpMessagesSent,
+ followUpMessagesReceived,
+ delayReqMessagesSent,
+ delayReqMessagesReceived,
+ delayRespMessagesSent,
+ delayRespMessagesReceived,
+ pdelayReqMessagesSent,
+ pdelayReqMessagesReceived,
+ pdelayRespMessagesSent,
+ pdelayRespMessagesReceived,
+ pdelayRespFollowUpMessagesSent,
+ pdelayRespFollowUpMessagesReceived,
+ signalingMessagesSent,
+ signalingMessagesReceived,
+ managementMessagesSent,
+ managementMessagesReceived,
+ discardedMessages,
+ unknownMessages,
+ totalMessagesSent,
+ totalMessagesReceived }
+ STATUS current
+ DESCRIPTION
+ "A grouping of PTP port message counters."
+ -- 1.3.6.1.4.1.46649.1.1.2.2.13 ---- 1.3.6.1.4.1.46649.1.1.2.2.13
+::= { ptpbaseMIBGroups 13 }
+
+ptpbaseMIBPtpProtocolCountersGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseProtocolCountersClear,
+ foreignAdded,
+ foreignCount,
+ foreignRemoved,
+ foreignOverflows,
+ stateTransitions,
+ bestMasterChanges,
+ announceTimeouts }
+ STATUS current
+ DESCRIPTION
+ "A grouping of PTP protocol counters."
+ -- 1.3.6.1.4.1.46649.1.1.2.2.14 ---- 1.3.6.1.4.1.46649.1.1.2.2.14
+::= { ptpbaseMIBGroups 14 }
+
+ptpbaseMIBPtpErrorCountersGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseErrorCountersClear,
+ messageRecvErrors,
+ messageSendErrors,
+ messageFormatErrors,
+ protocolErrors,
+ versionMismatchErrors,
+ domainMismatchErrors,
+ sequenceMismatchErrors,
+ delayMechanismMismatchErrors }
+ STATUS current
+ DESCRIPTION
+ "A grouping of PTP protocol error counters."
+ -- 1.3.6.1.4.1.46649.1.1.2.2.15 ---- 1.3.6.1.4.1.46649.1.1.2.2.15
+::= { ptpbaseMIBGroups 15 }
+
+ptpbaseMIBPtpUnicastNegotiationCountersGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseUnicastNegotiationCountersClear,
+ unicastGrantsRequested,
+ unicastGrantsGranted,
+ unicastGrantsDenied,
+ unicastGrantsCancelSent,
+ unicastGrantsCancelReceived,
+ unicastGrantsCancelAckSent,
+ unicastGrantsCancelAckReceived }
+ STATUS current
+ DESCRIPTION
+ "A grouping of PTP unicast negotiation counters."
+ -- 1.3.6.1.4.1.46649.1.1.2.2.16 ---- 1.3.6.1.4.1.46649.1.1.2.2.16
+::= { ptpbaseMIBGroups 16 }
+
+ptpbaseMIBPtpPerformanceCountersGroup OBJECT-GROUP
+ OBJECTS {
+ messageSendRate,
+ messageReceiveRate }
+ STATUS current
+ DESCRIPTION
+ "A grouping of PTP message performance counters."
+ -- 1.3.6.1.4.1.46649.1.1.2.2.17 ---- 1.3.6.1.4.1.46649.1.1.2.2.17
+::= { ptpbaseMIBGroups 17 }
+
+ptpbaseMIBPtpSecurityCountersGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseSecurityCountersClear,
+ aclTimingMessagesDiscarded,
+ aclManagementMessagesDiscarded }
+ STATUS current
+ DESCRIPTION
+ "A grouping of PTP security (access control) counters."
+ -- 1.3.6.1.4.1.46649.1.1.2.2.18 ---- 1.3.6.1.4.1.46649.1.1.2.2.18
+::= { ptpbaseMIBGroups 18 }
+
+ptpbaseMIBSlaveOfmStatisticsGroup OBJECT-GROUP
+ OBJECTS {
+ ofmCurrentValue,
+ ofmCurrentStringValue,
+ ofmStatsPeriodSeconds,
+ ofmStatsValid,
+ ofmStatsMin,
+ ofmStatsMax,
+ ofmStatsMean,
+ ofmStatsStdDev,
+ ofmStatsMedian,
+ ofmStatsMinStringValue,
+ ofmStatsMaxStringValue,
+ ofmStatsMeanStringValue,
+ ofmStatsStdDevStringValue,
+ ofmStatsMedianStringValue }
+ STATUS current
+ DESCRIPTION
+ "A grouping of PTP slave Offset from Master statistics."
+ -- 1.3.6.1.4.1.46649.1.1.2.2.19
+::= { ptpbaseMIBGroups 19 }
+
+ptpbaseMIBSlaveMpdStatisticsGroup OBJECT-GROUP
+ OBJECTS {
+ mpdCurrentValue,
+ mpdCurrentStringValue,
+ mpdStatsPeriodSeconds,
+ mpdStatsValid,
+ mpdStatsMin,
+ mpdStatsMax,
+ mpdStatsMean,
+ mpdStatsStdDev,
+ mpdStatsMedian,
+ mpdStatsMinStringValue,
+ mpdStatsMaxStringValue,
+ mpdStatsMeanStringValue,
+ mpdStatsStdDevStringValue,
+ mpdStatsMedianStringValue }
+ STATUS current
+ DESCRIPTION
+ "A grouping of PTP slave Mean Path Delay statistics."
+ -- 1.3.6.1.4.1.46649.1.1.2.2.20
+::= { ptpbaseMIBGroups 20 }
+
+ptpbaseMIBSlaveFreqAdjStatisticsGroup OBJECT-GROUP
+ OBJECTS {
+ freqAdjCurrentValue,
+ freqAdjStatsPeriodSeconds,
+ freqAdjStatsValid,
+ freqAdjStatsMin,
+ freqAdjStatsMax,
+ freqAdjStatsMean,
+ freqAdjStatsStdDev,
+ freqAdjStatsStatsMedian }
+ STATUS current
+ DESCRIPTION
+ "A grouping of PTP slave clock frequency adjustment statistics."
+ -- 1.3.6.1.4.1.46649.1.1.2.2.21
+::= { ptpbaseMIBGroups 21 }
+
+ptpbaseMIBPtpdCountersGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbasePtpdSpecificCountersClear,
+ consecutiveSequenceErrors,
+ ignoredAnnounce,
+ delayMSOutliersFound,
+ delaySMOutliersFound,
+ maxDelayDrops }
+ STATUS current
+ DESCRIPTION
+ "A grouping of PTPd-specific counters."
+ -- 1.3.6.1.4.1.46649.1.1.2.2.22 ---- 1.3.6.1.4.1.46649.1.1.2.2.22
+::= { ptpbaseMIBGroups 22 }
+
+ptpbaseMIBPtpdSpecificDataGroup OBJECT-GROUP
+ OBJECTS {
+ rawDelayMS,
+ rawDelayMSStringValue,
+ rawDelaySM,
+ rawDelaySMStringValue }
+ STATUS current
+ DESCRIPTION
+ "A grouping of PTPd-specific data."
+ -- 1.3.6.1.4.1.46649.1.1.2.2.23
+::= { ptpbaseMIBGroups 23 }
+
+ptpbaseMIBNotificationGroup NOTIFICATION-GROUP
+ NOTIFICATIONS {
+ ptpBasePortExpectedState,
+ ptpBasePortUnexpectedState,
+ ptpBaseSlaveOfmThresholdExceeded,
+ ptpBaseSlaveOfmThresholdAcceptable,
+ ptpBaseSaveClockStep,
+ ptpBaseSlaveNoSync,
+ ptpBaseSlaveReceivingSync,
+ ptpBaseSlaveNoDelay,
+ ptpBaseSlaveReceivingDelay,
+ ptpBaseBestMasterChange,
+ ptpBasePortNetworkFault,
+ ptpBasePortNetworkFaultCleared,
+ ptpBaseClockFreqAdjFast,
+ ptpBaseClockFreqAdjNormal,
+ ptpBaseSlaveOffsetFromMasterSeconds,
+ ptpBaseSlaveOffsetFromMasterSubSeconds,
+ ptpBaseTimePropertiesChange,
+ ptpBaseDomainMismatch,
+ ptpBaseDomainMismatchCleared}
+ STATUS current
+ DESCRIPTION
+ "A grouping of notification objects defined in the PTPBASE-MIB MIB."
+ -- 1.3.6.1.4.1.46649.1.1.2.2.24
+::= { ptpbaseMIBGroups 24 }
+
+END
diff --git a/rtemsbsd/ptpd/doc/draft-ietf-tictoc-ptp-mib-01.txt b/rtemsbsd/ptpd/doc/draft-ietf-tictoc-ptp-mib-01.txt
new file mode 100644
index 00000000..f8389430
--- /dev/null
+++ b/rtemsbsd/ptpd/doc/draft-ietf-tictoc-ptp-mib-01.txt
@@ -0,0 +1,3693 @@
+TICTOC Working Group Vinay Shankarkumar
+INTERNET DRAFT Laurent Montini
+Intended status: Standards Track Cisco Systems
+
+ Tim Frost
+ Greg Dowd
+ Symmetricom
+
+Expires: August 6, 2012 February 6, 2012
+
+
+
+
+ Precision Time Protocol Version 2 (PTPv2)
+ Management Information Base
+ draft-ietf-tictoc-ptp-mib-01.txt
+
+
+Status of this Memo
+
+ This Internet-Draft is submitted to IETF in full conformance with the
+ provisions of BCP 78 and BCP 79.
+
+ Internet-Drafts are working documents of the Internet Engineering
+ Task Force (IETF), its areas, and its working groups. Note that
+ other groups may also distribute working documents as Internet-
+ Drafts.
+
+ Internet-Drafts are draft documents valid for a maximum of six months
+ and may be updated, replaced, or obsoleted by other documents at any
+ time. It is inappropriate to use Internet-Drafts as reference
+ material or to cite them other than as "work in progress."
+
+ The list of current Internet-Drafts can be accessed at
+ http://www.ietf.org/ietf/1id-abstracts.txt
+
+ The list of Internet-Draft Shadow Directories can be accessed at
+ http://www.ietf.org/shadow.html
+
+ This Internet-Draft will expire on August 6, 2012.
+
+Copyright Notice
+
+ Copyright (c) 2012 IETF Trust and the persons identified as the
+ document authors. All rights reserved.
+
+ This document is subject to BCP 78 and the IETF Trust's Legal
+ Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info) in effect on the date of
+ publication of this document. Please review these documents
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 1]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ carefully, as they describe your rights and restrictions with respect
+ to this document. Code Components extracted from this document must
+ include Simplified BSD License text as described in Section 4.e of
+ the Trust Legal Provisions and are provided without warranty as
+ described in the Simplified BSD License.
+
+Abstract
+
+ This memo defines a portion of the Management Information Base (MIB)
+ for use with network management protocols in TCP/IP-based internets.
+ In particular, it defines objects for managing networks using
+ Precision Time Protocol.
+
+ This memo specifies a MIB module in a manner that is both compliant
+ to the SNMPv2 SMI, and semantically identical to the peer SNMPv1
+ definitions.
+
+Table of Contents
+
+ 1. Introduction...................................................2
+ 1.1. Relationship to other Profiles and MIBs...................3
+ 1.2. Change Log................................................3
+ 2. The SNMP Management Framework..................................3
+ 3. Overview.......................................................4
+ 4. IETF PTP MIB Definition........................................5
+ 5. Security Considerations.......................................63
+ 6. IANA Considerations...........................................64
+ 7. References....................................................64
+ 7.1. Normative References.....................................64
+ 7.2. Informative References...................................64
+ 8. Acknowledgements..............................................66
+ 9. Author's Addresses............................................66
+
+
+
+1. Introduction
+
+ This memo defines a portion of the Management Information Base (MIB)
+ for use with network management protocols in the Internet Community.
+ In particular, it describes managed objects used for managing PTP
+ devices including the ordinary clock, transparent clock, boundary
+ clocks.
+
+ This MIB is restricted to reading standard PTP data elements, as
+ described in [IEEE 1588-2008]. It is envisioned this MIB will
+ complement other managed objects to be defined to monitor, measure
+ the performance of the PTP devices and telecom clocks. Those objects
+ are considered out of scope for the current draft.
+
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 2]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ Similarly, this MIB is read-only and not intended to provide the
+ ability to configure PTP clocks. Since PTP clocks are often embedded
+ in other network elements such as routers, switches and gateways,
+ this ability is generally provided via the configuration interface
+ for the network element.
+
+1.1. Relationship to other Profiles and MIBs
+
+ This MIB is intended to be used with the default PTP profile
+ described in [IEEE 1588-2008], and the Telecom Profile described in
+ [G.8265.1], when running over the IP network layer. As stated above,
+ it is envisioned this MIB will complement other managed objects to be
+ defined to monitor, measure the performance of the PTP devices and
+ telecom clocks.
+
+ Some other PTP profiles have their own MIBs defined as part of the
+ profile, and this MIB is not intended to replace those MIBs.
+
+1.2. Change Log
+
+ This section tracks changes made to the revisions of the Internet
+ Drafts of this document. It will be *deleted* when the document is
+ published as an RFC. This section tracks changes made to the
+ visions of the Internet Drafts of this document. It will be
+ *deleted* when the document is published as an RFC.
+
+ draft-vinay-tictoc-ptp-mib
+
+ -00 Mar 11 Initial version; showed structure of MIB
+
+ draft-ietf-tictoc-ptp-mib
+
+ -00 Jul 11 First full, syntactically correct and compileable MIB
+ -01 Jan 12 Revised following comments from Bert Wijnen:
+ - revised introduction to clarify the scope, and the
+ relationship to other MIBs and profiles
+ - changed name to "ptpbase"
+ - corrected some data types
+ - corrected references and typos
+
+2. The SNMP Management Framework
+
+ The SNMP Management Framework presently consists of five major
+ components:
+
+ o An overall architecture, described in STD62, [RFC 3411].
+
+ o Mechanisms for describing and naming objects and events for the
+ purpose of management. The first version of this Structure of
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 3]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ Management Information (SMI) is called SMIv1 and described in
+ STD 16: [RFC 1155], [RFC 1212] and [RFC 1215].
+ The second version, called SMIv2, is described in STD 58:
+ [RFC 2578], [RFC 2579] and [RFC 2580].
+
+ o Message protocols for transferring management information. The
+ first version of the SNMP message protocol is called SNMPv1 and
+ described in STD 15 [RFC 1157]. A second version of the SNMP
+ message protocol, which is not an Internet standards track
+ protocol, is called SNMPv2c and described in [RFC 1901] and
+ [RFC 1906]. The third version of the message protocol is called
+ SNMPv3 and described in STD62: [RFC 3417], [RFC 3412] and [RFC
+ 3414].
+
+ o Protocol operations for accessing management information. The
+ first set of protocol operations and associated PDU formats is
+ described in STD 15 [RFC 1157]. A second set of protocol
+ operations and associated PDU formats is described in STD 62
+ [RFC 3416].
+
+ o A set of fundamental applications described in STD 62 [RFC 3413]
+ and the view-based access control mechanism described in STD 62
+ [RFC 3415].
+
+ Managed objects are accessed via a virtual information store, termed
+ the Management Information Base or MIB. Objects in the MIB are
+ defined using the mechanisms defined in the SMI.
+
+ This memo specifies a MIB module that is compliant to the SMIv2. A
+ MIB conforming to the SMIv1 can be produced through the appropriate
+ translations. The resulting translated MIB must be semantically
+ equivalent, except where objects or events are omitted because no
+ translation is possible (e.g., use of Counter64). Some machine
+ readable information in SMIv2 will be converted into textual
+ descriptions in SMIv1 during the translation process. However, this
+ loss of machine readable information is not considered to change the
+ semantics of the MIB.
+
+3. Overview
+
+ The objects defined in this MIB are to be used when describing the
+ Precision Time Protocol (PTPv2).
+
+
+
+
+
+
+
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 4]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+4. IETF PTP MIB Definition
+
+PTPBASE-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+ MODULE-IDENTITY,
+ OBJECT-TYPE,
+ Integer32,
+ Gauge32,
+ Unsigned32,
+ Counter32,
+ Counter64
+ FROM SNMPv2-SMI
+ OBJECT-GROUP,
+ MODULE-COMPLIANCE
+ FROM SNMPv2-CONF
+ TEXTUAL-CONVENTION,
+ TruthValue,
+ DisplayString
+ FROM SNMPv2-TC
+ InterfaceIndexOrZero
+ FROM IF-MIB
+ InetAddressType,
+ InetAddress
+ FROM INET-ADDRESS-MIB;
+
+
+ptpbaseMIB MODULE-IDENTITY
+ LAST-UPDATED "201201230000Z"
+ ORGANIZATION "TICTOC Working Group"
+ CONTACT-INFO
+ "WG Email: tictoc at ietf.org
+
+ Vinay Shankarkumar
+ Cisco Systems,
+ Email: vinays at cisco.com
+
+ Laurent Montini,
+ Cisco Systems,
+ Email: lmontini at cisco.com
+
+ Tim Frost,
+ Symmetricom Inc.,
+ Email: tfrost at symmetricom.com
+
+ Greg Dowd,
+ Symmetricom Inc.,
+ Email: gdowd at symmetricom.com"
+
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 5]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ DESCRIPTION
+ "The MIB module for PTP version 2 (IEEE Std. 1588(TM)-2008)
+
+ Overview of PTP version 2 (IEEE Std. 1588(TM)-2008)
+
+ [IEEE 1588-2008] defines a protocol enabling precise
+ synchronization of clocks in measurement and control systems
+ implemented with packet-based networks, the Precision Time
+ Protocol Version 2 (PTPv2). This MIB does not address the
+ earlier version IEEE Std. 1588(TM)-2002 (PTPv1). The protocol
+ is applicable to network elements communicating using IP. The
+ protocol enables heterogeneous systems that include clocks of
+ various inherent precision, resolution, and stability to
+ synchronize to a grandmaster clock.
+
+ The protocol supports system-wide synchronization accuracy in
+ the sub-microsecond range with minimal network and local clock
+ computing resources. [IEEE 1588-2008] uses UDP/IP or
+ Ethernet and can be adapted to other mappings. It includes
+ formal mechanisms for message extensions, higher sampling rates,
+ correction for asymmetry, a clock type to reduce error
+ accumulation in large topologies, and specifications on how to
+ incorporate the resulting additional data into the
+ synchronization protocol. The [IEEE 1588-2008] defines
+ conformance and management capability also.
+
+ MIB description
+
+ This MIB is to support the Precision Time Protocol version 2
+ (PTPv2, hereafter designated as PTP) features of network element
+ system devices, when using the default PTP profile described in
+ [IEEE 1588-2008], or the Telecom Profile described in
+ [G.8265.1], when running over the IP network layer.
+
+ It is envisioned this MIB will complement other managed objects
+ to be defined to monitor, measure the performance of the PTP
+ devices and telecom clocks.
+
+ Some other PTP profiles have their own MIBs defined as part of
+ the profile, and this MIB is not intended to replace those MIBs.
+
+
+ Acronyms:
+ ARB Arbitrary Timescale
+ E2E End-to-End
+ EUI Extended Unique Identifier.
+ GPS Global Positioning System
+ IANA Internet Assigned Numbers Authority
+ IP Internet Protocol
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 6]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ MAC Media Access Control
+ according to [IEEE 802.3-2008]
+ NIST National Institute of Standards and Technology
+ NTP Network Time Protocol (see IETF [RFC 5905])
+ OUI Organizational Unique Identifier
+ (allocated by the IEEE)
+ P2P Peer-to-Peer
+ PTP Precision Time Protocol
+ TAI International Atomic Time
+ TC Transparent Clock
+ UDP User Datagram Protocol
+ UTC Coordinated Universal Time
+
+ References:
+ [IEEE 1588-2008] IEEE Standard for A Precision Clock
+ Synchronization Protocol for Networked Measurement and
+ Control Systems, IEEE Std. 1588(TM)-2008, 24 July 2008.
+
+ [G.8265.1] Precision Time Protocol Telecom Profile for
+ Frequency Synchronization, ITU-T Recommendation G.8265.1,
+ October 2010.
+
+
+ As defined in [IEEE 1588-2008]:
+
+ Accuracy:
+ The mean of the time or frequency error between the clock under
+ test and a perfect reference clock, over an ensemble of
+ measurements. Stability is a measure of how the mean varies
+ with respect to variables such as time, temperature, and so on,
+ while the precision is a measure of the deviation of the error
+ from the mean.
+
+ Atomic process:
+ A process is atomic if the values of all inputs to the process
+ are not permitted to change until all of the results of the
+ process are instantiated, and the outputs of the process are
+ not visible to other processes until the processing of each
+ output is complete.
+
+ Boundary clock:
+ A clock that has multiple Precision Time Protocol (PTP) ports in
+ a domain and maintains the timescale used in the domain. It
+ may serve as the source of time, i.e., be a master clock, and
+ may synchronize to another clock, i.e., be a slave clock.
+
+ Boundary node clock:
+ A clock that has multiple Precision Time Protocol(PTP) ports in
+ a domain and maintains the timescale used in the domain. It
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 7]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ differs from a boundary clock in that the clock roles can
+ change.
+
+ Clock:
+ A node participating in the Precision Time Protocol (PTP) that
+ is capable of providing a measurement of the passage of time
+ since a defined epoch.
+
+ Domain:
+ A logical grouping of clocks that synchronize to each other
+ using the protocol, but that are not necessarily synchronized
+ to clocks in another domain.
+
+ End-to-end transparent clock:
+ A transparent clock that supports the use of the end-to-end
+ delay measurement mechanism between slave clocks and the master
+ clock. Each node must measure the residence time of PTP event
+ messages and accumulate it in Correction Field.
+
+ Epoch:
+ The origin of a timescale.
+
+ Event:
+ An abstraction of the mechanism by which signals or conditions
+ are generated and represented.
+
+ Foreign master:
+ An ordinary or boundary clock sending Announce messages to
+ another clock that is not the current master recognized by the
+ other clock.
+
+ Grandmaster clock:
+ Within a domain, a clock that is the ultimate source of time
+ for clock synchronization using the protocol.
+
+ Holdover:
+ A clock previously synchronized/syntonized to another clock
+ (normally a primary reference or a master clock) but now
+ free-running based on its own internal oscillator, whose
+ frequency is being adjusted using data acquired while it had
+ been synchronized/syntonized to the other clock. It is said to
+ be in holdover or in the holdover mode, as long as it is within
+ its accuracy requirements.
+
+ Link:
+ A network segment between two Precision Time Protocol ports
+ supporting the peer delay mechanism of this standard. The peer
+ delay mechanism is designed to measure the propagation time
+ over such a link.
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 8]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+
+ Management node:
+ A device that configures and monitors clocks.
+
+ Master clock:
+ In the context of a single Precision Time Protocol
+ communication path, a clock that is the source of time to which
+ all other clocks on that path synchronize.
+
+ Message timestamp point:
+ A point within a Precision Time Protocol event message serving
+ as a reference point in the message. A timestamp is defined by
+ the instant a message timestamp point passes the reference
+ plane of a clock.
+
+ Multicast communication:
+ A communication model in which each Precision Time Protocol
+ message sent from any PTP port is capable of being received and
+ processed by all PTP ports on the same PTP communication path.
+
+ Node:
+ A device that can issue or receive Precision Time Protocol
+ communications on a network.
+
+ One-step clock:
+ A clock that provides time information using a single event
+ message.
+
+ On-pass support:
+ Indicates that each node in the synchronization chain from
+ master to slave can support IEEE-1588.
+
+ Ordinary clock:
+ A clock that has a single Precision Time Protocol port in a
+ domain and maintains the timescale used in the domain. It may
+ serve as a source of time, i.e., be a master clock, or may
+ synchronize to another clock, i.e., be a slave clock.
+
+ Parent clock:
+ The master clock to which a clock is synchronized.
+
+ Peer-to-peer transparent clock:
+ A transparent clock that, in addition to providing Precision
+ Time Protocol event transit time information, also provides
+ corrections for the propagation delay of the link connected to
+ the port receiving the PTP event message. In the presence of
+ peer-to-peer transparent clocks, delay measurements between
+ slave clocks and the master clock are performed using the
+ peer-to-peer delay measurement mechanism.
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 9]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+
+ Phase change rate:
+ The observed rate of change in the measured time with respect
+ to the reference time. The phase change rate is equal to the
+ fractional frequency offset between the measured frequency and
+ the reference frequency.
+
+ PortNumber:
+ An index identifying a specific Precision Time Protocol port on
+ a PTP node.
+
+ Primary reference:
+ A source of time and or frequency that is traceable to
+ international standards.
+
+ Profile:
+ The set of allowed Precision Time Protocol features applicable
+ to a device.
+
+ Precision Time Protocol communication:
+ Information used in the operation of the protocol, transmitted
+ in a PTP message over a PTP communication path.
+
+ Precision Time Protocol communication path:
+ The signaling path portion of a particular network enabling
+ direct communication among ordinary and boundary clocks.
+
+ Precision Time Protocol node:
+ PTP ordinary, boundary, or transparent clock or a device that
+ generates or parses PTP messages.
+
+ Precision Time Protocol port:
+ A logical access point of a clock for PTP communications to the
+ communications network.
+
+ Recognized standard time source:
+ A recognized standard time source is a source external to
+ Precision Time Protocol that provides time and/or frequency as
+ appropriate that is traceable to the international standards
+ laboratories maintaining clocks that form the basis for the
+ International Atomic Time and Universal Coordinated Time
+ timescales. Examples of these are GPS, NTP, and NIST
+ timeservers.
+
+ Requestor:
+ The port implementing the peer-to-peer delay mechanism that
+ initiates the mechanism by sending a Pdelay_Req message.
+
+ Responder:
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 10]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ The port responding to the receipt of a Pdelay_Req message as
+ part of the operation of the peer-to-peer delay mechanism.
+
+ Synchronized clocks:
+ Two clocks are synchronized to a specified uncertainty if they
+ have the same epoch and their measurements of the time of a
+ single event at an arbitrary time differ by no more than that
+ uncertainty.
+
+ Syntonized clocks:
+ Two clocks are syntonized if the duration of the second is the
+ same on both, which means the time as measured by each advances
+ at the same rate. They may or may not share the same epoch.
+
+ Timeout:
+ A mechanism for terminating requested activity that, at least
+ from the requester's perspective, does not complete within the
+ specified time.
+
+ Timescale:
+ A linear measure of time from an epoch.
+
+ Traceability:
+ A property of the result of a measurement or the value of a
+ standard whereby it can be related to stated references,
+ usually national or international standards, through an
+ unbroken chain of comparisons all having stated uncertainties.
+
+ Translation device:
+ A boundary clock or, in some cases, a transparent clock that
+ translates the protocol messages between regions implementing
+ different transport and messaging protocols, between different
+ versions of [IEEE 1588-2008], or different PTP profiles.
+
+ Transparent clock:
+ A device that measures the time taken for a Precision Time
+ Protocol event message to transit the device and provides this
+ information to clocks receiving this PTP event message.
+
+ Two-step clock:
+ A clock that provides time information using the combination of
+ an event message and a subsequent general message.
+
+ The below table specifies the object formats of the various
+ textual conventions used.
+
+ Data type mapping Textual Convention SYNTAX
+ ------------------- ------------------ ---------------------
+ 5.3.2 TimeInterval ClockTimeInterval OCTET STRING(SIZE(1..255))
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 11]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ 5.3.3 Timestamp ClockTimestamp OCTET STRING(SIZE(6))
+ 5.3.4 ClockIdentity ClockIdentity OCTET STRING(SIZE(1..255))
+ 5.3.5 PortIdentity ClockPortNumber INTEGER(1..65535)
+ 5.3.7 ClockQuality ClockQualityClassType
+
+
+ Simple master-slave hierarchy, section 6.6.2.4 [IEEE 1588-2008]:
+
+ +---------------+
+ | Ordinary |
+ | Clock -1 |
+ | (GrandMaster) |
+ +-------M-------+
+ |
+ 1
+ |
+ +---------------S-----------------+
+ | Boundary |
+ | Clock -1 |
+ +-----M---------------------M-----+
+ | |
+ 2 3
+ | |
+ +-----S-----+ +------------S-------------+
+ | Ordinary | | Boundary |
+ | Clock -2 | | Clock -2 |
+ +-----------+ +-----M--------------M-----+
+ | |
+ 4 5
+ | |
+ +-----S-----+ +-----S-----+
+ | Ordinary | | Ordinary |
+ | Clock -3 | | Clock -4 |
+ +-----------+ +-----------+
+
+ Grandmaster
+
+ Boundary Clock(0-N) Ordinary Clocks(0-N)
+ Ordinary Clocks(0-N)
+
+
+ Relationship cardinality:
+ PTP system 1 : N PTP Clocks
+ PTP Clock 1 : 1 Domain
+ PTP Clock 1 : N PTP Ports
+ PTP Ports N : M Physical Ports (interface in IF-MIB)
+
+ Transparent clock diagram, section 6.7.1.3 of [IEEE 1588-2008]:
+
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 12]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ +-----------------------------+
+ | Boundary clock - 1 |
+ +-----------------------------+
+ | |
+ | |
+ +-- A --+ B
+ | |
+ +----------------------+ |
+ | Ordinary clock | |
+ +----------------------+ |
+ +----------------------+
+ +----------------------+ | End-to-end |
+ | Ordinary clock 1-1 |----------| transparent clock- |
+ +----------------------+ | 1 - 1 |
+ +----------------------+
+ |
+ C
+ |
+ +----------------------+
+ +----------------------+ | End-to-end |
+ | Ordinary clock 1-2 |----------| transparent clock- |
+ +----------------------+ | 1 - 2 |
+ +----------------------+
+
+
+ The MIB refers to the sections of [IEEE 1588-2008]."
+
+ -- revision log
+
+ ::= { mib-2 XXX }_-- XXX to be assigned by IANA
+
+
+
+ClockDomainType ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "d"
+ STATUS current
+ DESCRIPTION
+ "The Domain is identified by an integer, the domainNumber, in
+ the range of 0 to 255. An integer value that is used to assign
+ each PTP device to a particular domain. The following values
+ define the valid domains.
+
+ Value Definition
+ --------- -------------------
+ 0 Default domain
+ 1 Alternate domain 1
+ 2 Alternate domain 2
+ 3 Alternate domain 3
+ 4 - 127 User-defined domains
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 13]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ 128 - 255 Reserved"
+
+ REFERENCE "Section 7.1 Domains, Table 2 of [IEEE 1588-2008]"
+ SYNTAX Unsigned32 (0..255)
+
+ClockIdentity ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "The clock Identity is an 8-octet array and will be presented in
+ the form of a character array. The value of the
+ ClockIdentity should be taken from the IEEE EUI-64 individual
+ assigned numbers as indicated in Section 7.5.2.2.2 of
+ [IEEE 1588-2008]. The EUI-64 address is divided into the
+ following fields:
+
+ OUI bytes (0-2)
+ Extension identifier bytes (3-7)
+
+ The clock identifier can be constructed from existing EUI-48
+ assignments and here is an abbreviated example extracted from
+ section 7.5.2.2.2 [IEEE 1588-2008].
+
+ Company EUI-48 = 0xACDE4823456716
+ EUI-64 = ACDE48FFFE23456716
+
+ It is important to note the IEEE Registration Authority has
+ deprecated the use of MAC-48 in any new design."
+
+ REFERENCE "Section 7.5.2.2.1 of [IEEE 1588-2008]"
+ SYNTAX OCTET STRING (SIZE (1..255))
+
+ClockIntervalBase2 ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "d"
+ STATUS current
+ DESCRIPTION
+ "The interval included in message types Announce, Sync,
+ Delay_Req, and Pdelay_Req as indicated in section 7.7.2.1 of
+ [IEEE 1588-2008].
+
+ The mean time interval between successive messages shall be
+ represented as the logarithm to the base 2 of this time
+ interval measured in seconds on the local clock of the device
+ sending the message. The values of these logarithmic attributes
+ shall be selected from integers in the range -128 to 127 subject
+ to further limits established in an applicable PTP profile."
+
+ REFERENCE "Section 7.7.2.1 General interval specification of
+ [IEEE 1588-2008]"
+ SYNTAX Integer32 (-128..127)
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 14]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+
+ClockMechanismType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "The clock type based on whether End to End or peer to peer
+ mechanisms are used. The mechanism used to calculate the Mean
+ Path Delay as indicated in Table 9 of [IEEE 1588-2008].
+
+ Delay mechanism Value(hex) Specification
+ --------------- ---------- -------------
+ E2E 01 The port is configured to use the
+ delay request-response mechanism.
+ P2P 02 The port is configured to use the
+ peer delay mechanism.
+ DISABLED FE The port does not implement the
+ delay mechanism."
+
+ REFERENCE "Sections 8.2.5.4.4, 6.6.4, 7.4.2 of [IEEE 1588-2008]."
+ SYNTAX INTEGER {
+ e2e(1),
+ p2p(2),
+ disabled(254)
+ }
+
+ClockInstanceType ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "d"
+ STATUS current
+ DESCRIPTION
+ "The instance of the Clock of a given clock type in a given
+ domain."
+ SYNTAX Unsigned32 (0..255)
+
+ClockPortNumber ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "d"
+ STATUS current
+ DESCRIPTION
+ "An index identifying a specific Precision Time Protocol (PTP)
+ port on a PTP node."
+
+ REFERENCE "Sections 7.5.2.3 and 5.3.5 of [IEEE 1588-2008]"
+ SYNTAX Unsigned32 (0..65535)
+
+ClockPortState ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "This is the value of the current state of the protocol engine
+ associated with this port.
+ Port state Value Description
+ -----------------------------------------------------------
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 15]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ initializing 1 In this state a port initializes
+ its data sets, hardware, and
+ communication facilities.
+ faulty 2 The fault state of the protocol.
+ disabled 3 The port shall not place any
+ messages on its communication path.
+ listening 4 The port is waiting for the
+ announceReceiptTimeout to expire or
+ to receive an Announce message from
+ a master.
+ preMaster 5 The port shall behave in all respects
+ as though it were in the MASTER state
+ except that it shall not place any
+ messages on its communication path
+ except for Pdelay_Req, Pdelay_Resp,
+ Pdelay_Resp_Follow_Up, signaling, or
+ management messages.
+ master 6 The port is behaving as a master port.
+ passive 7 The port shall not place any messages
+ on its communication path except for
+ Pdelay_Req, Pdelay_Resp,
+ Pdelay_Resp_Follow_Up, or signaling
+ messages, or management messages that
+ are a required response to another
+ management message
+ uncalibrated 8 The local port is preparing to
+ synchronize to the master port.
+ slave 9 The port is synchronizing to the
+ selected master port."
+
+ REFERENCE "Section 8.2.5.3.1 portState and 9.2.5 of
+ [IEEE 1588-2008]"
+ SYNTAX INTEGER {
+ initializing(1),
+ faulty(2),
+ disabled(3),
+ listening(4),
+ preMaster(5),
+ master(6),
+ passive(7),
+ uncalibrated(8),
+ slave(9)
+ }
+
+ClockProfileType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "Clock Profile used. A profile is the set of allowed Precision
+ Time Protocol (PTP) features applicable to a device."
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 16]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+
+ REFERENCE "Section 3.1.30 and 19.3 PTP profiles of
+ [IEEE 1588-2008]"
+ SYNTAX INTEGER {
+ default(1),
+ telecom(2),
+ vendorspecific(3)
+ }
+
+ClockQualityAccuracyType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "The ClockQuality as specified in section 5.3.7, 7.6.2.5 and
+ Table 6 of [IEEE 1588-2008].
+
+ The following values are not represented in the enumerated
+ values.
+
+ 0x01-0x1F Reserved
+ 0x32-0x7F Reserved
+
+ It is important to note that section 7.1.1 RFC2578 allows for
+ gaps and enumerate values to start with zero when indicated by
+ the protocol."
+
+ REFERENCE "Section 5.3.7, 7.6.2.5 and Table 6 of
+ [IEEE 1588-2008]"
+ SYNTAX INTEGER {
+ reserved00(1), -- 0
+ nanoSecond25(32), -- 0x20
+ nanoSecond100(33), -- 0x21
+ nanoSecond250(34), -- 0x22
+ microSec1(35), -- 0x23
+ microSec2dot5(36), -- 0x24
+ microSec10(37), -- 0x25
+ microSec25(38), -- 0x26
+ microSec100(39), -- 0x27
+ microSec250(40), -- 0x28
+ milliSec1(41), -- 0x29
+ milliSec2dot5(42), -- 0x2A
+ milliSec10(43), -- 0x2B
+ milliSec25(44), -- 0x2C
+ milliSec100(45), -- 0x2D
+ milliSec250(46), -- 0x2E
+ second1(47), -- 0x2F
+ second10(48), -- 0x30
+ secondGreater10(49), -- 0x31
+ unknown(254), -- 0xFE
+ reserved255(255) -- 0xFF
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 17]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ }
+
+ClockQualityClassType ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "d"
+ STATUS current
+ DESCRIPTION
+ "The ClockQuality as specified in section 5.3.7, 7.6.2.4 and
+ Table 5 of [IEEE 1588-2008].
+
+ Value Description
+ -------- ------------------------------------------------
+ 0 Reserved to enable compatibility with future
+ versions.
+ 1-5 Reserved
+ 6 Shall designate a clock that is synchronized
+ to a primary reference time source. The
+ timescale distributed shall be PTP. A
+ clockClass 6 clock shall not be a slave to
+ another clock in the domain.
+ 7 Shall designate a clock that has previously
+ been designated as clockClass 6 but that has
+ lost the ability to synchronize to a primary
+ reference time source and is in holdover mode
+ and within holdover specifications. The
+ timescale distributed shall be PTP. A
+ clockClass 7 clock shall not be a slave to
+ another clock in the domain.
+ 8 Reserved.
+ 9-10 Reserved to enable compatibility with future
+ versions.
+ 11-12 Reserved.
+ 13 Shall designate a clock that is synchronized
+ to an application-specific source of time.
+ The timescale distributed shall be ARB. A
+ clockClass 13 clock shall not be a slave to
+ another clock in the domain.
+ 14 Shall designate a clock that has previously
+ been designated as clockClass 13 but that
+ has lost the ability to synchronize to an
+ application-specific source of time and is
+ in holdover mode and within holdover
+ specifications. The timescale distributed
+ shall be ARB. A clockClass 14 clock shall
+ not be a slave to another clock in the domain.
+ 15-51 Reserved.
+ 52 Degradation alternative A for a clock of
+ clockClass 7 that is not within holdover
+ specification. A clock of clockClass 52
+ shall not be a slave to another clock in
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 18]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ the domain.
+ 53-57 Reserved.
+ 58 Degradation alternative A for a clock of
+ clockClass 14 that is not within holdover
+ specification. A clock of clockClass 58 shall
+ not be a slave to another clock in the domain.
+ 59-67 Reserved.
+ 68-122 For use by alternate PTP profiles.
+ 123-127 Reserved.
+ 128-132 Reserved.
+ 133-170 For use by alternate PTP profiles.
+ 171-186 Reserved.
+
+ 187 Degradation alternative B for a clock of
+ clockClass 7 that is not within holdover
+ specification. A clock of clockClass 187 may
+ be a slave to another clock in the domain.
+ 188-192 Reserved.
+ 193 Degradation alternative B for a clock of
+ clockClass 14 that is not within holdover
+ specification. A clock of clockClass 193 may
+ be a slave to another clock in the domain.
+ 194-215 Reserved.
+ 216-232 For use by alternate PTP profiles.
+ 233-247 Reserved.
+ 248 Default. This clockClass shall be used if
+ none of the other clockClass definitions apply.
+ 249-250 Reserved.
+ 251 Reserved for version 1 compatibility; see Clause 18.
+ 252-254 Reserved.
+ 255 Shall be the clockClass of a slave-only clock; see
+ 9.2.2."
+
+ REFERENCE "Section 5.3.7, 7.6.2.4 and Table 5 of
+ [IEEE 1588-2008]."
+ SYNTAX Unsigned32 (0..255)
+
+ClockRoleType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "The Clock Role. The protocol generates a Master Slave
+ relationship among the clocks in the system.
+
+ Clock Role Value Description
+ --------------------------------------------------------------
+ Master clock 1 A clock that is the source of
+ time to which all other clocks on
+ that path synchronize.
+
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 19]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ Slave clock 2 A clock which synchronizes to
+ another clock (master)."
+ SYNTAX INTEGER {
+ master(1),
+ slave(2)
+ }
+
+ClockStateType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "The clock state returned by PTP engine.
+
+ Clock State Value Description
+ --------------------------------------------------------------
+ Freerun state 1 Applies to a slave device that is not
+ locked to a master. This is the initial
+ state a slave starts out with when it
+ is not getting any PTP packets from the
+ master or because of some other input
+ error (erroneous packets, etc).
+
+ Holdover state 2 In this state the slave device is
+ locked to a master but communication
+ with the master is lost or the
+ timestamps in the ptp packets are
+ incorrect. But since the slave was
+ locked to the master, it can run with
+ the same accuracy for sometime. The
+ slave can continue to operate in this
+ state for some time. If communication
+ with the master is not restored for a
+ while, the device is moved to the
+ FREERUN state.
+
+ Acquiring state 3 The slave device is receiving packets
+ from a master and is trying to acquire
+ a lock.
+
+ Freq_locked state 4 Slave device is locked to the Master
+ with respect to frequency, but not phase
+ aligned
+
+ Phase_aligned state 5 Locked to the master with respect to
+ frequency and phase."
+ SYNTAX INTEGER {
+ freerun(1),
+ holdover(2),
+ acquiring(3),
+ frequencyLocked(4),
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 20]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ phaseAligned(5)
+ }
+
+ClockTimeSourceType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "The ClockQuality as specified in section 5.3.7, 7.6.2.6 and
+ Table 7 of [IEEE 1588-2008].
+
+ The following values are not represented in the enumerated
+ values.
+
+ 0xF0-0xFE For use by alternate PTP profiles
+ 0xFF Reserved
+
+ It is important to note that section 7.1.1 RFC2578 allows for
+ gaps and enumerate values to start with zero when indicated by
+ the protocol."
+
+ REFERENCE "Section 5.3.7, 7.6.2.6 and Table 7 of
+ [IEEE 1588-2008]."
+ SYNTAX INTEGER {
+ atomicClock(16), -- 0x10
+ gps(32), -- 0x20
+ terrestrialRadio(48), -- 0x22
+ ptp(64), -- 0x40
+ ntp(80), -- 0x50
+ handSet(96), -- 0x60
+ other(144), -- 0x90
+ internalOsillator(160) -- 0xA0
+ }
+
+ClockTimeInterval ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "This textual convention corresponds to the TimeInterval
+ structure indicated in section 5.3.2 of [IEEE 1588-2008].
+ It will be presented in the form of a character array.
+
+ The TimeInterval type represents time intervals.
+
+ struct TimeInterval
+ {
+ Integer64 scaledNanoseconds;
+ };
+
+ The scaledNanoseconds member is the time interval expressed in
+ units of nanoseconds and multiplied by 2**16.
+
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 21]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ Positive or negative time intervals outside the maximum range
+ of this data type shall be encoded as the largest positive and
+ negative values of the data type, respectively.
+
+ For example, 2.5 ns is expressed as 0000 0000 0002 8000 in
+ Base16."
+
+ REFERENCE
+ "Section 5.3.2 and setion 7.7.2.1 Timer interval
+ specification of [IEEE 1588-2008]"
+ SYNTAX OCTET STRING (SIZE (1..255))
+
+ClockTxModeType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "Transmission mode.
+
+ unicast. Using unicast communication channel.
+
+ multicast. Using Multicast communication channel.
+
+ multicast-mix. Using multicast-unicast communication channel"
+ SYNTAX INTEGER {
+ unicast(1),
+ multicast(2),
+ multicastmix(3)
+ }
+
+ClockType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "The clock types as defined in the MIB module description."
+
+ REFERENCE "Section 6.5.1 of [IEEE 1588-2008]."
+ SYNTAX INTEGER {
+ ordinaryClock(1),
+ boundaryClock(2),
+ transparentClock(3),
+ boundaryNode(4)
+ }
+ptpbaseMIBNotifs OBJECT IDENTIFIER
+ ::= { ptpbaseMIB 0 }
+
+ptpbaseMIBObjects OBJECT IDENTIFIER
+ ::= { ptpbaseMIB 1 }
+
+ptpbaseMIBConformance OBJECT IDENTIFIER
+ ::= { ptpbaseMIB 2 }
+
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 22]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ptpbaseMIBSystemInfo OBJECT IDENTIFIER
+ ::= { ptpbaseMIBObjects 1 }
+
+-- Conformance Information Definition
+
+ptpbaseMIBCompliances OBJECT IDENTIFIER
+ ::= { ptpbaseMIBConformance 1 }
+
+ptpbaseMIBGroups OBJECT IDENTIFIER
+ ::= { ptpbaseMIBConformance 2 }
+
+
+ptpbaseMIBCompliances1 MODULE-COMPLIANCE
+ STATUS current
+ DESCRIPTION
+ "Compliance statement for agents that provide read-only support
+ for PTPBASE-MIB. Such devices can only be monitored using this
+ MIB module.
+
+ The Module is implemented with support for read-only. In other
+ words, only monitoring is available by implementing this
+ MODULE-COMPLIANCE."
+ MODULE -- this module
+ MANDATORY-GROUPS { ptpbaseMIBSystemInfoGroup }
+ ::= { ptpbaseMIBCompliances 1 }
+
+ptpbaseMIBCompliances2 MODULE-COMPLIANCE
+ STATUS current
+ DESCRIPTION
+ "Compliance statement for agents that provide read-only support
+ for PTPBASE-MIB. Such devices can only be monitored using this
+ MIB module.
+
+ The Module is implemented with support for read-only. In other
+ words, only monitoring is available by implementing this
+ MODULE-COMPLIANCE."
+ MODULE -- this module
+ MANDATORY-GROUPS {
+ ptpbaseMIBClockCurrentDSGroup,
+ ptpbaseMIBClockParentDSGroup,
+ ptpbaseMIBClockDefaultDSGroup,
+ ptpbaseMIBClockRunningGroup,
+ ptpbaseMIBClockTimepropertiesGroup
+ }
+ ::= { ptpbaseMIBCompliances 2 }
+
+ptpbaseMIBCompliances3 MODULE-COMPLIANCE
+ STATUS current
+ DESCRIPTION
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 23]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ "Compliance statement for agents that provide read-only support
+ for PTPBASE-MIB. Such devices can only be monitored using this
+ MIB module.
+
+ The Module is implemented with support for read-only. In other
+ words, only monitoring is available by implementing this
+ MODULE-COMPLIANCE."
+ MODULE -- this module
+ MANDATORY-GROUPS {
+ ptpbaseMIBClockPortGroup,
+ ptpbaseMIBClockPortDSGroup,
+ ptpbaseMIBClockPortRunningGroup,
+ ptpbaseMIBClockPortAssociateGroup
+ }
+ ::= { ptpbaseMIBCompliances 3 }
+
+ptpbaseMIBCompliances4 MODULE-COMPLIANCE
+ STATUS current
+ DESCRIPTION
+ "Compliance statement for agents that provide read-only support
+ for PTPBASE-MIB. Such devices can only be monitored using this
+ MIB module.
+
+ The Module is implemented with support for read-only. In other
+ words, only monitoring is available by implementing this
+ MODULE-COMPLIANCE."
+ MODULE -- this module
+ MANDATORY-GROUPS {
+ ptpbaseMIBClockTranparentDSGroup,
+ ptpbaseMIBClockPortTransDSGroup
+ }
+ ::= { ptpbaseMIBCompliances 4 }
+
+ptpbaseMIBSystemInfoGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseSystemDomainTotals,
+ ptpDomainClockPortsTotal,
+ ptpbaseSystemProfile
+ }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing system-wide
+ information"
+ ::= { ptpbaseMIBGroups 1 }
+
+ptpbaseMIBClockCurrentDSGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockCurrentDSStepsRemoved,
+ ptpbaseClockCurrentDSOffsetFromMaster,
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 24]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ ptpbaseClockCurrentDSMeanPathDelay
+ }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing PTP Current Dataset
+ information"
+ ::= { ptpbaseMIBGroups 2 }
+
+ptpbaseMIBClockParentDSGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockParentDSParentPortIdentity,
+ ptpbaseClockParentDSParentStats,
+ ptpbaseClockParentDSOffset,
+ ptpbaseClockParentDSClockPhChRate,
+ ptpbaseClockParentDSGMClockIdentity,
+ ptpbaseClockParentDSGMClockPriority1,
+ ptpbaseClockParentDSGMClockPriority2,
+ ptpbaseClockParentDSGMClockQualityClass,
+ ptpbaseClockParentDSGMClockQualityAccuracy,
+ ptpbaseClockParentDSGMClockQualityOffset
+ }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing PTP Parent Dataset
+ information"
+ ::= { ptpbaseMIBGroups 3 }
+
+ptpbaseMIBClockDefaultDSGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockDefaultDSTwoStepFlag,
+ ptpbaseClockDefaultDSClockIdentity,
+ ptpbaseClockDefaultDSPriority1,
+ ptpbaseClockDefaultDSPriority2,
+ ptpbaseClockDefaultDSSlaveOnly,
+ ptpbaseClockDefaultDSQualityClass,
+ ptpbaseClockDefaultDSQualityAccuracy,
+ ptpbaseClockDefaultDSQualityOffset
+ }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing PTP Default Dataset
+ information"
+ ::= { ptpbaseMIBGroups 4 }
+
+ptpbaseMIBClockRunningGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockRunningState,
+ ptpbaseClockRunningPacketsSent,
+ ptpbaseClockRunningPacketsReceived
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 25]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing PTP running state
+ information"
+ ::= { ptpbaseMIBGroups 5 }
+
+ptpbaseMIBClockTimepropertiesGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockTimePropertiesDSCurrentUTCOffsetValid,
+ ptpbaseClockTimePropertiesDSCurrentUTCOffset,
+ ptpbaseClockTimePropertiesDSLeap59,
+ ptpbaseClockTimePropertiesDSLeap61,
+ ptpbaseClockTimePropertiesDSTimeTraceable,
+ ptpbaseClockTimePropertiesDSFreqTraceable,
+ ptpbaseClockTimePropertiesDSPTPTimescale,
+ ptpbaseClockTimePropertiesDSSource
+ }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing PTP Time Properties
+ information"
+ ::= { ptpbaseMIBGroups 6 }
+
+ptpbaseMIBClockTranparentDSGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockTransDefaultDSClockIdentity,
+ ptpbaseClockTransDefaultDSNumOfPorts,
+ ptpbaseClockTransDefaultDSDelay,
+ ptpbaseClockTransDefaultDSPrimaryDomain
+ }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing PTP Transparent
+ Dataset
+ information"
+ ::= { ptpbaseMIBGroups 7 }
+
+ptpbaseMIBClockPortGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockPortName,
+ ptpbaseClockPortSyncOneStep,
+ ptpbaseClockPortCurrentPeerAddress,
+ ptpbaseClockPortNumOfAssociatedPorts,
+ ptpbaseClockPortCurrentPeerAddressType,
+ ptpbaseClockPortRole
+ }
+ STATUS current
+ DESCRIPTION
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 26]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ "Group which aggregates objects describing information for a
+ given PTP Port."
+ ::= { ptpbaseMIBGroups 8 }
+
+ptpbaseMIBClockPortDSGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockPortDSName,
+ ptpbaseClockPortDSPortIdentity,
+ ptpbaseClockPortDSAnnouncementInterval,
+ ptpbaseClockPortDSAnnounceRctTimeout,
+ ptpbaseClockPortDSSyncInterval,
+ ptpbaseClockPortDSMinDelayReqInterval,
+ ptpbaseClockPortDSPeerDelayReqInterval,
+ ptpbaseClockPortDSDelayMech,
+ ptpbaseClockPortDSPeerMeanPathDelay,
+ ptpbaseClockPortDSGrantDuration,
+ ptpbaseClockPortDSPTPVersion
+ }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing PTP Port Dataset
+ information"
+ ::= { ptpbaseMIBGroups 9 }
+
+ptpbaseMIBClockPortRunningGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockPortRunningName,
+ ptpbaseClockPortRunningState,
+ ptpbaseClockPortRunningRole,
+ ptpbaseClockPortRunningInterfaceIndex,
+ ptpbaseClockPortRunningIPversion,
+ ptpbaseClockPortRunningEncapsulationType,
+ ptpbaseClockPortRunningTxMode,
+ ptpbaseClockPortRunningRxMode,
+ ptpbaseClockPortRunningPacketsReceived,
+ ptpbaseClockPortRunningPacketsSent
+ }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing PTP running interface
+ information"
+ ::= { ptpbaseMIBGroups 10 }
+
+ptpbaseMIBClockPortTransDSGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockPortTransDSPortIdentity,
+ ptpbaseClockPortTransDSlogMinPdelayReqInt,
+ ptpbaseClockPortTransDSFaultyFlag,
+ ptpbaseClockPortTransDSPeerMeanPathDelay
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 27]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing PTP TransparentDS
+ Dataset
+ information"
+ ::= { ptpbaseMIBGroups 11 }
+
+ptpbaseMIBClockPortAssociateGroup OBJECT-GROUP
+ OBJECTS {
+ ptpbaseClockPortAssociatePacketsSent,
+ ptpbaseClockPortAssociatePacketsReceived,
+ ptpbaseClockPortAssociateAddress,
+ ptpbaseClockPortAssociateAddressType,
+ ptpbaseClockPortAssociateInErrors,
+ ptpbaseClockPortAssociateOutErrors
+ }
+ STATUS current
+ DESCRIPTION
+ "Group which aggregates objects describing information on peer
+ PTP ports for a given PTP clock-port."
+ ::= { ptpbaseMIBGroups 12 }
+ptpbaseMIBClockInfo OBJECT IDENTIFIER
+ ::= { ptpbaseMIBObjects 2 }
+
+
+ptpbaseSystemTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseSystemEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of count information about the PTP system for all
+ domains."
+ ::= { ptpbaseMIBSystemInfo 1 }
+
+ptpbaseSystemEntry OBJECT-TYPE
+ SYNTAX PtpbaseSystemEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing count information about a
+ single domain. New row entries are added when the PTP clock for
+ this domain is configured, while the unconfiguration of the PTP
+ clock removes it."
+ INDEX {
+ ptpDomainIndex,
+ ptpInstanceIndex
+ }
+ ::= { ptpbaseSystemTable 1 }
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 28]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+
+PtpbaseSystemEntry ::= SEQUENCE {
+ ptpDomainIndex ClockDomainType,
+ ptpInstanceIndex ClockInstanceType,
+ ptpDomainClockPortsTotal Gauge32
+}
+
+ptpDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices. The Clock Domain is a logical group of
+ clocks and devices that synchronize with each other using the
+ PTP protocol.
+
+ 0 Default domain
+ 1 Alternate domain 1
+ 2 Alternate domain 2
+ 3 Alternate domain 3
+ 4 - 127 User-defined domains
+ 128 - 255 Reserved"
+ ::= { ptpbaseSystemEntry 1 }
+
+ptpInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the Clock for this
+ domain."
+ ::= { ptpbaseSystemEntry 2 }
+
+ptpDomainClockPortsTotal OBJECT-TYPE
+ SYNTAX Gauge32
+ UNITS "ptp ports"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the total number of clock ports
+ configured within a domain."
+ ::= { ptpbaseSystemEntry 3 }
+
+
+
+ptpbaseSystemDomainTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseSystemDomainEntry
+ MAX-ACCESS not-accessible
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 29]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ STATUS current
+ DESCRIPTION
+ "Table of information about the PTP system for all clock modes
+ -- ordinary, boundary or transparent."
+ ::= { ptpbaseMIBSystemInfo 2 }
+
+ptpbaseSystemDomainEntry OBJECT-TYPE
+ SYNTAX PtpbaseSystemDomainEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing information about a single
+ clock mode for the PTP system. A row entry gets added when PTP
+ clocks are configured on the router."
+ INDEX { ptpbaseSystemDomainClockTypeIndex }
+ ::= { ptpbaseSystemDomainTable 1 }
+
+PtpbaseSystemDomainEntry ::= SEQUENCE {
+ ptpbaseSystemDomainClockTypeIndex ClockType,
+ ptpbaseSystemDomainTotals Unsigned32
+}
+
+ptpbaseSystemDomainClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ ::= { ptpbaseSystemDomainEntry 1 }
+
+ptpbaseSystemDomainTotals OBJECT-TYPE
+ SYNTAX Unsigned32
+ UNITS "domains"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the total number of PTP domains for this
+ particular clock type configured in this node."
+ ::= { ptpbaseSystemDomainEntry 2 }
+
+
+
+ptpbaseSystemProfile OBJECT-TYPE
+ SYNTAX ClockProfileType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP Profile implemented on the
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 30]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ system."
+ REFERENCE "Section 19.3 PTP profiles of [IEEE 1588-2008]"
+ ::= { ptpbaseMIBSystemInfo 3 }
+
+ptpbaseClockCurrentDSTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockCurrentDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the PTP clock Current Datasets for
+ all domains."
+ ::= { ptpbaseMIBClockInfo 1 }
+
+ptpbaseClockCurrentDSEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockCurrentDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing information about a single
+ PTP clock Current Datasets for a domain."
+ REFERENCE
+ "1588 Version 2.0 Section 8.2.2 currentDS data set member
+ specifications of [IEEE 1588-2008]"
+ INDEX {
+ ptpbaseClockCurrentDSDomainIndex,
+ ptpbaseClockCurrentDSClockTypeIndex,
+ ptpbaseClockCurrentDSInstanceIndex
+ }
+ ::= { ptpbaseClockCurrentDSTable 1 }
+
+PtpbaseClockCurrentDSEntry ::= SEQUENCE {
+ ptpbaseClockCurrentDSDomainIndex ClockDomainType,
+ ptpbaseClockCurrentDSClockTypeIndex ClockType,
+ ptpbaseClockCurrentDSInstanceIndex ClockInstanceType,
+ ptpbaseClockCurrentDSStepsRemoved Unsigned32,
+ ptpbaseClockCurrentDSOffsetFromMaster ClockTimeInterval,
+ ptpbaseClockCurrentDSMeanPathDelay ClockTimeInterval
+}
+
+ptpbaseClockCurrentDSDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ ::= { ptpbaseClockCurrentDSEntry 1 }
+
+ptpbaseClockCurrentDSClockTypeIndex OBJECT-TYPE
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 31]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ ::= { ptpbaseClockCurrentDSEntry 2 }
+
+ptpbaseClockCurrentDSInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ ::= { ptpbaseClockCurrentDSEntry 3 }
+
+ptpbaseClockCurrentDSStepsRemoved OBJECT-TYPE
+ SYNTAX Unsigned32
+ UNITS "Steps"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The current clock dataset StepsRemoved value.
+
+ This object specifies the distance measured by the number of
+ Boundary clocks between the local clock and the Foreign master
+ as indicated in the stepsRemoved field of Announce messages."
+ REFERENCE "1588 Version 2.0 Section 8.2.2.2 stepsRemoved"
+ ::= { ptpbaseClockCurrentDSEntry 4 }
+
+ptpbaseClockCurrentDSOffsetFromMaster OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ UNITS "Time Interval"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the current clock dataset ClockOffset
+ value. The value of the computation of the offset in time
+ between a slave and a master clock."
+ REFERENCE "1588 Version 2.0 Section 8.2.2.3 of
+ [IEEE 1588-2008]"
+ ::= { ptpbaseClockCurrentDSEntry 5 }
+
+ptpbaseClockCurrentDSMeanPathDelay OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 32]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ "This object specifies the current clock dataset
+ MeanPathDelay value.
+
+ The mean path delay between a pair of ports as measure by the
+ delay request-response mechanism."
+ REFERENCE "1588 Version 2.0 Section 8.2.2.4 mean path delay"
+ ::= { ptpbaseClockCurrentDSEntry 6 }
+
+
+
+ptpbaseClockParentDSTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockParentDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the PTP clock Parent Datasets for
+ all domains."
+ ::= { ptpbaseMIBClockInfo 2 }
+
+ptpbaseClockParentDSEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockParentDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing information about a single
+ PTP clock Parent Datasets for a domain."
+ REFERENCE
+ "Section 8.2.3 parentDS data set member specifications of
+ [IEEE 1588-2008]"
+ INDEX {
+ ptpbaseClockParentDSDomainIndex,
+ ptpbaseClockParentDSClockTypeIndex,
+ ptpbaseClockParentDSInstanceIndex
+ }
+ ::= { ptpbaseClockParentDSTable 1 }
+
+PtpbaseClockParentDSEntry ::= SEQUENCE {
+ ptpbaseClockParentDSDomainIndex ClockDomainType,
+ ptpbaseClockParentDSClockTypeIndex ClockType,
+ ptpbaseClockParentDSInstanceIndex ClockInstanceType,
+ ptpbaseClockParentDSParentPortIdentity OCTET STRING,
+ ptpbaseClockParentDSParentStats TruthValue,
+ ptpbaseClockParentDSOffset ClockIntervalBase2,
+ ptpbaseClockParentDSClockPhChRate Integer32,
+ ptpbaseClockParentDSGMClockIdentity ClockIdentity,
+ ptpbaseClockParentDSGMClockPriority1 Unsigned32,
+ ptpbaseClockParentDSGMClockPriority2 Unsigned32,
+ ptpbaseClockParentDSGMClockQualityClass ClockQualityClassType,
+ ptpbaseClockParentDSGMClockQualityAccuracy ClockQualityAccuracyType,
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 33]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ ptpbaseClockParentDSGMClockQualityOffset Unsigned32
+}
+
+ptpbaseClockParentDSDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ ::= { ptpbaseClockParentDSEntry 1 }
+
+ptpbaseClockParentDSClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ ::= { ptpbaseClockParentDSEntry 2 }
+
+ptpbaseClockParentDSInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ ::= { ptpbaseClockParentDSEntry 3 }
+
+ptpbaseClockParentDSParentPortIdentity OBJECT-TYPE
+ SYNTAX OCTET STRING(SIZE(1..256))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the value of portIdentity of the port on
+ the master that issues the Sync messages used in synchronizing
+ this clock."
+ REFERENCE
+ "Section 8.2.3.2 parentDS.parentPortIdentity of
+ [IEEE 1588-2008]"
+ ::= { ptpbaseClockParentDSEntry 4 }
+
+ptpbaseClockParentDSParentStats OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Parent Dataset ParentStats value.
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 34]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+
+ This value indicates whether the values of ParentDSOffset
+ and ParentDSClockPhChRate have been measured and are valid.
+ A TRUE value shall indicate valid data."
+ REFERENCE "Section 8.2.3.3 parentDS.parentStats of
+ [IEEE 1588-2008]"
+ ::= { ptpbaseClockParentDSEntry 5 }
+
+ptpbaseClockParentDSOffset OBJECT-TYPE
+ SYNTAX ClockIntervalBase2 (-128..127)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Parent Dataset
+ ParentOffsetScaledLogVariance value.
+
+ This value is the variance of the parent clocks phase as
+ measured by the local clock."
+ REFERENCE
+ "Section 8.2.3.4
+ parentDS.observedParentOffsetScaledLogVariance
+ [IEEE 1588-2008]"
+ ::= { ptpbaseClockParentDSEntry 6 }
+
+ptpbaseClockParentDSClockPhChRate OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock's parent dataset
+ ParentClockPhaseChangeRate value.
+
+ This value is an estimate of the parent clocks phase change
+ rate as measured by the slave clock."
+ REFERENCE
+ "Section 8.2.3.5
+ parentDS.observedParentClockPhaseChangeRate of
+ [IEEE 1588-2008]"
+ ::= { ptpbaseClockParentDSEntry 7 }
+
+ptpbaseClockParentDSGMClockIdentity OBJECT-TYPE
+ SYNTAX ClockIdentity
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the parent dataset Grandmaster clock
+ identity."
+ REFERENCE
+ "Section 8.2.3.6 parentDS.grandmasterIdentity of
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 35]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ [IEEE 1588-2008]"
+ ::= { ptpbaseClockParentDSEntry 8 }
+
+ptpbaseClockParentDSGMClockPriority1 OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the parent dataset Grandmaster clock
+ priority1."
+ REFERENCE
+ "Section 8.2.3.8 parentDS.grandmasterPriority1 of
+ [IEEE 1588-2008]"
+ ::= { ptpbaseClockParentDSEntry 9 }
+
+ptpbaseClockParentDSGMClockPriority2 OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the parent dataset grandmaster clock
+ priority2."
+ REFERENCE
+ "Section 8.2.3.9 parentDS.grandmasterPriority2 of
+ [IEEE 1588-2008]"
+ ::= { ptpbaseClockParentDSEntry 10 }
+
+ptpbaseClockParentDSGMClockQualityClass OBJECT-TYPE
+ SYNTAX ClockQualityClassType (0..255)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the parent dataset grandmaster clock
+ quality class."
+ REFERENCE
+ "Section 8.2.3.7 parentDS.grandmasterClockQuality of
+ [IEEE 1588-2008]"
+ ::= { ptpbaseClockParentDSEntry 11 }
+
+ptpbaseClockParentDSGMClockQualityAccuracy OBJECT-TYPE
+ SYNTAX ClockQualityAccuracyType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the parent dataset grandmaster clock
+ quality accuracy."
+ REFERENCE
+ "Section 8.2.3.7 parentDS.grandmasterClockQuality of
+ [IEEE 1588-2008]"
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 36]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ ::= { ptpbaseClockParentDSEntry 12 }
+
+ptpbaseClockParentDSGMClockQualityOffset OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the parent dataset grandmaster clock
+ quality offset."
+ REFERENCE
+ "Section 8.2.3.7 parentDS.grandmasterClockQuality of
+ [IEEE 1588-2008]"
+ ::= { ptpbaseClockParentDSEntry 13 }
+
+
+
+ptpbaseClockDefaultDSTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockDefaultDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the PTP clock Default Datasets for
+ all domains."
+ ::= { ptpbaseMIBClockInfo 3 }
+
+ptpbaseClockDefaultDSEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockDefaultDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing information about a single
+ PTP clock Default Datasets for a domain."
+ INDEX {
+ ptpbaseClockDefaultDSDomainIndex,
+ ptpbaseClockDefaultDSClockTypeIndex,
+ ptpbaseClockDefaultDSInstanceIndex
+ }
+ ::= { ptpbaseClockDefaultDSTable 1 }
+
+PtpbaseClockDefaultDSEntry ::= SEQUENCE {
+ ptpbaseClockDefaultDSDomainIndex ClockDomainType,
+ ptpbaseClockDefaultDSClockTypeIndex ClockType,
+ ptpbaseClockDefaultDSInstanceIndex ClockInstanceType,
+ ptpbaseClockDefaultDSTwoStepFlag TruthValue,
+ ptpbaseClockDefaultDSClockIdentity ClockIdentity,
+ ptpbaseClockDefaultDSPriority1 Unsigned32,
+ ptpbaseClockDefaultDSPriority2 Unsigned32,
+ ptpbaseClockDefaultDSSlaveOnly TruthValue,
+ ptpbaseClockDefaultDSQualityClass ClockQualityClassType,
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 37]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ ptpbaseClockDefaultDSQualityAccuracy ClockQualityAccuracyType,
+ ptpbaseClockDefaultDSQualityOffset Integer32
+}
+
+ptpbaseClockDefaultDSDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ ::= { ptpbaseClockDefaultDSEntry 1 }
+
+ptpbaseClockDefaultDSClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ ::= { ptpbaseClockDefaultDSEntry 2 }
+
+ptpbaseClockDefaultDSInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ ::= { ptpbaseClockDefaultDSEntry 3 }
+
+ptpbaseClockDefaultDSTwoStepFlag OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies whether the Two Step process is used."
+ ::= { ptpbaseClockDefaultDSEntry 4 }
+
+ptpbaseClockDefaultDSClockIdentity OBJECT-TYPE
+ SYNTAX ClockIdentity
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the default Datasets clock identity."
+ ::= { ptpbaseClockDefaultDSEntry 5 }
+
+ptpbaseClockDefaultDSPriority1 OBJECT-TYPE
+ SYNTAX Unsigned32
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 38]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the default Datasets clock Priority1."
+ ::= { ptpbaseClockDefaultDSEntry 6 }
+
+ptpbaseClockDefaultDSPriority2 OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the default Datasets clock Priority2."
+ ::= { ptpbaseClockDefaultDSEntry 7 }
+
+ptpbaseClockDefaultDSSlaveOnly OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Whether the SlaveOnly flag is set."
+ ::= { ptpbaseClockDefaultDSEntry 8 }
+
+ptpbaseClockDefaultDSQualityClass OBJECT-TYPE
+ SYNTAX ClockQualityClassType (0..255)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the default dataset Quality Class."
+ ::= { ptpbaseClockDefaultDSEntry 9 }
+
+ptpbaseClockDefaultDSQualityAccuracy OBJECT-TYPE
+ SYNTAX ClockQualityAccuracyType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the default dataset Quality Accurarcy."
+ ::= { ptpbaseClockDefaultDSEntry 10 }
+
+ptpbaseClockDefaultDSQualityOffset OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the default dataset Quality offset."
+ ::= { ptpbaseClockDefaultDSEntry 11 }
+
+
+
+ptpbaseClockRunningTable OBJECT-TYPE
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 39]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ SYNTAX SEQUENCE OF PtpbaseClockRunningEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the PTP clock Running Datasets for
+ all domains."
+ ::= { ptpbaseMIBClockInfo 4 }
+
+ptpbaseClockRunningEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockRunningEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing information about a single
+ PTP clock running Datasets for a domain."
+ INDEX {
+ ptpbaseClockRunningDomainIndex,
+ ptpbaseClockRunningClockTypeIndex,
+ ptpbaseClockRunningInstanceIndex
+ }
+ ::= { ptpbaseClockRunningTable 1 }
+
+PtpbaseClockRunningEntry ::= SEQUENCE {
+ ptpbaseClockRunningDomainIndex ClockDomainType,
+ ptpbaseClockRunningClockTypeIndex ClockType,
+ ptpbaseClockRunningInstanceIndex ClockInstanceType,
+ ptpbaseClockRunningState ClockStateType,
+ ptpbaseClockRunningPacketsSent Counter64,
+ ptpbaseClockRunningPacketsReceived Counter64
+}
+
+ptpbaseClockRunningDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ ::= { ptpbaseClockRunningEntry 1 }
+
+ptpbaseClockRunningClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ ::= { ptpbaseClockRunningEntry 2 }
+
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 40]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ptpbaseClockRunningInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ ::= { ptpbaseClockRunningEntry 3 }
+
+ptpbaseClockRunningState OBJECT-TYPE
+ SYNTAX ClockStateType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Clock state returned by PTP engine
+ which was described earlier.
+
+ Freerun state. Applies to a slave device that is not locked to
+ a master. This is the initial state a slave starts out with
+ when
+ it is not getting any PTP packets from the master or because of
+ some other input error (erroneous packets, etc).
+
+ Holdover state. In this state the slave device is locked to a
+ master but communication with the master is lost or the
+ timestamps in the ptp packets are incorrect. But since the
+ slave was locked to the master, it can run with the same
+ accuracy for
+ sometime. The slave can continue to operate in this state for
+ some time. If communication with the master is not restored for
+ a while, the device is moved to the FREERUN state.
+
+ Acquiring state. The slave device is receiving packets from a
+ master and is trying to acquire a lock.
+
+ Freq_locked state. Slave device is locked to the Master with
+ respect to frequency, but not phase aligned
+
+ Phase_aligned state. Locked to the master with respect to
+ frequency and phase."
+ ::= { ptpbaseClockRunningEntry 4 }
+
+ptpbaseClockRunningPacketsSent OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the total number of all packet Unicast
+ and multicast that have been sent out for this clock in this
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 41]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ domain for this type."
+ ::= { ptpbaseClockRunningEntry 5 }
+
+ptpbaseClockRunningPacketsReceived OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the total number of all packet Unicast
+ and multicast that have been received for this clock in this
+ domain for this type."
+ ::= { ptpbaseClockRunningEntry 6 }
+
+
+
+ptpbaseClockTimePropertiesDSTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockTimePropertiesDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the PTP clock Timeproperties
+ Datasets for all domains."
+ ::= { ptpbaseMIBClockInfo 5 }
+
+ptpbaseClockTimePropertiesDSEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockTimePropertiesDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing information about a single
+ PTP clock timeproperties Datasets for a domain."
+ REFERENCE "Section 8.2.4 of [IEEE 1588-2008]"
+ INDEX {
+ ptpbaseClockTimePropertiesDSDomainIndex,
+ ptpbaseClockTimePropertiesDSClockTypeIndex,
+ ptpbaseClockTimePropertiesDSInstanceIndex
+ }
+ ::= { ptpbaseClockTimePropertiesDSTable 1 }
+
+PtpbaseClockTimePropertiesDSEntry ::= SEQUENCE {
+ ptpbaseClockTimePropertiesDSDomainIndex ClockDomainType,
+ ptpbaseClockTimePropertiesDSClockTypeIndex ClockType,
+ ptpbaseClockTimePropertiesDSInstanceIndex ClockInstanceType,
+ ptpbaseClockTimePropertiesDSCurrentUTCOffsetValid TruthValue,
+ ptpbaseClockTimePropertiesDSCurrentUTCOffset Integer32,
+ ptpbaseClockTimePropertiesDSLeap59 TruthValue,
+ ptpbaseClockTimePropertiesDSLeap61 TruthValue,
+ ptpbaseClockTimePropertiesDSTimeTraceable TruthValue,
+ ptpbaseClockTimePropertiesDSFreqTraceable TruthValue,
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 42]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ ptpbaseClockTimePropertiesDSPTPTimescale TruthValue,
+ ptpbaseClockTimePropertiesDSSource ClockTimeSourceType
+}
+
+ptpbaseClockTimePropertiesDSDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ ::= { ptpbaseClockTimePropertiesDSEntry 1 }
+
+ptpbaseClockTimePropertiesDSClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ ::= { ptpbaseClockTimePropertiesDSEntry 2 }
+
+ptpbaseClockTimePropertiesDSInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ ::= { ptpbaseClockTimePropertiesDSEntry 3 }
+
+ptpbaseClockTimePropertiesDSCurrentUTCOffsetValid OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the timeproperties dataset value of
+ whether current UTC offset is valid."
+ REFERENCE "Section 8.2.4.2 of [IEEE 1588-2008]"
+ ::= { ptpbaseClockTimePropertiesDSEntry 4 }
+
+ptpbaseClockTimePropertiesDSCurrentUTCOffset OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the timeproperties dataset value of
+ current UTC offset.
+
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 43]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ In PTP systems whose epoch is the PTP epoch, the value of
+ timePropertiesDS.currentUtcOffset is the offset
+ between TAI and UTC; otherwise the value has no meaning. The
+ value shall be in units of seconds.
+ The initialization value shall be selected as follows:
+ a) If the timePropertiesDS.ptpTimescale (see 8.2.4.8) is TRUE,
+ the value is the value obtained from a
+ primary reference if the value is known at the time of
+ initialization, else.
+ b) The value shall be the current number of leap seconds (7.2.3)
+ when the node is designed."
+ REFERENCE "Section 8.2.4.3 of [IEEE 1588-2008]"
+ ::= { ptpbaseClockTimePropertiesDSEntry 5 }
+
+ptpbaseClockTimePropertiesDSLeap59 OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Leap59 value in the clock Current
+ Dataset."
+ REFERENCE "Section 8.2.4.4 of [IEEE 1588-2008]"
+ ::= { ptpbaseClockTimePropertiesDSEntry 6 }
+
+ptpbaseClockTimePropertiesDSLeap61 OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Leap61 value in the clock Current
+ Dataset."
+ REFERENCE "Section 8.2.4.5 of [IEEE 1588-2008]"
+ ::= { ptpbaseClockTimePropertiesDSEntry 7 }
+
+ptpbaseClockTimePropertiesDSTimeTraceable OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Timetraceable value in the clock
+ Current Dataset."
+ REFERENCE "Section 8.2.4.6 of [IEEE 1588-2008]"
+ ::= { ptpbaseClockTimePropertiesDSEntry 8 }
+
+ptpbaseClockTimePropertiesDSFreqTraceable OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 44]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ "This object specifies the Frequency Traceable value in the
+ clock Current Dataset."
+ REFERENCE "Section 8.2.4.7 of [IEEE 1588-2008]"
+ ::= { ptpbaseClockTimePropertiesDSEntry 9 }
+
+ptpbaseClockTimePropertiesDSPTPTimescale OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP Timescale value in the clock
+ Current Dataset."
+ REFERENCE "Section 8.2.4.8 of [IEEE 1588-2008]"
+ ::= { ptpbaseClockTimePropertiesDSEntry 10 }
+
+ptpbaseClockTimePropertiesDSSource OBJECT-TYPE
+ SYNTAX ClockTimeSourceType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Timesource value in the clock Current
+ Dataset."
+ REFERENCE "Section 8.2.4.9 of [IEEE 1588-2008]"
+ ::= { ptpbaseClockTimePropertiesDSEntry 11 }
+
+
+
+ptpbaseClockTransDefaultDSTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockTransDefaultDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the PTP Transparent clock Default
+ Datasets for all domains."
+ ::= { ptpbaseMIBClockInfo 6 }
+
+ptpbaseClockTransDefaultDSEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockTransDefaultDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing information about a single
+ PTP Transparent clock Default Datasets for a domain."
+ REFERENCE "Section 8.3.2 of [IEEE 1588-2008]"
+ INDEX {
+ ptpbaseClockTransDefaultDSDomainIndex,
+ ptpbaseClockTransDefaultDSInstanceIndex
+ }
+ ::= { ptpbaseClockTransDefaultDSTable 1 }
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 45]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+
+PtpbaseClockTransDefaultDSEntry ::= SEQUENCE {
+ ptpbaseClockTransDefaultDSDomainIndex ClockDomainType,
+ ptpbaseClockTransDefaultDSInstanceIndex ClockInstanceType,
+ ptpbaseClockTransDefaultDSClockIdentity ClockIdentity,
+ ptpbaseClockTransDefaultDSNumOfPorts Counter32,
+ ptpbaseClockTransDefaultDSDelay ClockMechanismType,
+ ptpbaseClockTransDefaultDSPrimaryDomain Integer32
+}
+
+ptpbaseClockTransDefaultDSDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ ::= { ptpbaseClockTransDefaultDSEntry 1 }
+
+ptpbaseClockTransDefaultDSInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ ::= { ptpbaseClockTransDefaultDSEntry 2 }
+
+ptpbaseClockTransDefaultDSClockIdentity OBJECT-TYPE
+ SYNTAX ClockIdentity
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the value of the clockIdentity attribute
+ of the local clock."
+ REFERENCE "Section 8.3.2.2.1 of [IEEE 1588-2008]"
+ ::= { ptpbaseClockTransDefaultDSEntry 3 }
+
+ptpbaseClockTransDefaultDSNumOfPorts OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the number of PTP ports of the device."
+ REFERENCE "Section 8.3.2.2.2 of [IEEE 1588-2008]"
+ ::= { ptpbaseClockTransDefaultDSEntry 4 }
+
+ptpbaseClockTransDefaultDSDelay OBJECT-TYPE
+ SYNTAX ClockMechanismType
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 46]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object, if the transparent clock is an end-to-end
+ transparent clock, has the value shall be E2E; If the
+ transparent clock is a peer-to-peer transparent clock, the
+ value
+ shall be P2P."
+ REFERENCE "Section 8.3.2.3.1 of [IEEE 1588-2008]"
+ ::= { ptpbaseClockTransDefaultDSEntry 5 }
+
+ptpbaseClockTransDefaultDSPrimaryDomain OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the value of the primary syntonization
+ domain. The initialization value shall be 0."
+ REFERENCE "Section 8.3.2.3.2 of [IEEE 1588-2008]"
+ ::= { ptpbaseClockTransDefaultDSEntry 6 }
+
+
+
+ptpbaseClockPortTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the clock ports for a particular
+ domain."
+ ::= { ptpbaseMIBClockInfo 7 }
+
+ptpbaseClockPortEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing information about a single
+ clock port."
+ INDEX {
+ ptpbaseClockPortDomainIndex,
+ ptpbaseClockPortClockTypeIndex,
+ ptpbaseClockPortClockInstanceIndex,
+ ptpbaseClockPortTablePortNumberIndex
+ }
+ ::= { ptpbaseClockPortTable 1 }
+
+PtpbaseClockPortEntry ::= SEQUENCE {
+ ptpbaseClockPortDomainIndex ClockDomainType,
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 47]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ ptpbaseClockPortClockTypeIndex ClockType,
+ ptpbaseClockPortClockInstanceIndex ClockInstanceType,
+ ptpbaseClockPortTablePortNumberIndex ClockPortNumber,
+ ptpbaseClockPortName DisplayString,
+ ptpbaseClockPortRole ClockRoleType,
+ ptpbaseClockPortSyncOneStep TruthValue,
+ ptpbaseClockPortCurrentPeerAddressType InetAddressType,
+ ptpbaseClockPortCurrentPeerAddress InetAddress,
+ ptpbaseClockPortNumOfAssociatedPorts Gauge32
+}
+
+ptpbaseClockPortDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ ::= { ptpbaseClockPortEntry 1 }
+
+ptpbaseClockPortClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ ::= { ptpbaseClockPortEntry 2 }
+
+ptpbaseClockPortClockInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ ::= { ptpbaseClockPortEntry 3 }
+
+ptpbaseClockPortTablePortNumberIndex OBJECT-TYPE
+ SYNTAX ClockPortNumber (1..65535)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP Portnumber for this port."
+ ::= { ptpbaseClockPortEntry 4 }
+
+ptpbaseClockPortName OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 48]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP clock port name configured on the
+ router."
+ ::= { ptpbaseClockPortEntry 5 }
+
+ptpbaseClockPortRole OBJECT-TYPE
+ SYNTAX ClockRoleType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object describes the current role (slave/master) of the
+ port."
+ ::= { ptpbaseClockPortEntry 6 }
+
+ptpbaseClockPortSyncOneStep OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies that one-step clock operation between
+ the PTP master and slave device is enabled."
+ ::= { ptpbaseClockPortEntry 7 }
+
+ptpbaseClockPortCurrentPeerAddressType OBJECT-TYPE
+ SYNTAX InetAddressType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the current peer's network address used
+ for PTP communication. Based on the scenario and the setup
+ involved, the values might look like these -
+ Scenario Value
+ ------------------- ----------------
+ Single Master master port
+ Multiple Masters selected master port
+ Single Slave slave port
+ Multiple Slaves <empty>
+
+ (In relevant setups, information on available
+ slaves and available masters will be available through
+ ptpClockPortAssociateTable)"
+ ::= { ptpbaseClockPortEntry 8 }
+
+ptpbaseClockPortCurrentPeerAddress OBJECT-TYPE
+ SYNTAX InetAddress
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 49]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ "This object specifies the current peer's network address used
+ for PTP communication. Based on the scenario and the setup
+ involved, the values might look like these -
+ Scenario Value
+ ------------------- ----------------
+ Single Master master port
+ Multiple Masters selected master port
+ Single Slave slave port
+ Multiple Slaves <empty>
+
+ (In relevant setups, information on available
+ slaves and available masters will be available through
+ ptpClockPortAssociateTable)"
+ ::= { ptpbaseClockPortEntry 9 }
+
+ptpbaseClockPortNumOfAssociatedPorts OBJECT-TYPE
+ SYNTAX Gauge32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies -
+ For a master port - the number of PTP slave sessions (peers)
+ associated with this PTP port.
+ For a slave port - the number of masters available to this slave
+ port (might or might not be peered)."
+ ::= { ptpbaseClockPortEntry 10 }
+
+
+
+ptpbaseClockPortDSTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockPortDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the clock ports dataset for a
+ particular domain."
+ ::= { ptpbaseMIBClockInfo 8 }
+
+ptpbaseClockPortDSEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockPortDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing port dataset information for
+ a single clock port."
+ INDEX {
+ ptpbaseClockPortDSDomainIndex,
+ ptpbaseClockPortDSClockTypeIndex,
+ ptpbaseClockPortDSClockInstanceIndex,
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 50]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ ptpbaseClockPortDSPortNumberIndex
+ }
+ ::= { ptpbaseClockPortDSTable 1 }
+
+PtpbaseClockPortDSEntry ::= SEQUENCE {
+ ptpbaseClockPortDSDomainIndex ClockDomainType,
+ ptpbaseClockPortDSClockTypeIndex ClockType,
+ ptpbaseClockPortDSClockInstanceIndex ClockInstanceType,
+ ptpbaseClockPortDSPortNumberIndex ClockPortNumber,
+ ptpbaseClockPortDSName DisplayString,
+ ptpbaseClockPortDSPortIdentity OCTET STRING,
+ ptpbaseClockPortDSAnnouncementInterval Integer32,
+ ptpbaseClockPortDSAnnounceRctTimeout Integer32,
+ ptpbaseClockPortDSSyncInterval Integer32,
+ ptpbaseClockPortDSMinDelayReqInterval Integer32,
+ ptpbaseClockPortDSPeerDelayReqInterval Integer32,
+ ptpbaseClockPortDSDelayMech ClockMechanismType,
+ ptpbaseClockPortDSPeerMeanPathDelay ClockTimeInterval,
+ ptpbaseClockPortDSGrantDuration Unsigned32,
+ ptpbaseClockPortDSPTPVersion Integer32
+}
+
+ptpbaseClockPortDSDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ ::= { ptpbaseClockPortDSEntry 1 }
+
+ptpbaseClockPortDSClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ ::= { ptpbaseClockPortDSEntry 2 }
+
+ptpbaseClockPortDSClockInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ ::= { ptpbaseClockPortDSEntry 3 }
+
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 51]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ptpbaseClockPortDSPortNumberIndex OBJECT-TYPE
+ SYNTAX ClockPortNumber (1..65535)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP portnumber associated with this
+ PTP port."
+ ::= { ptpbaseClockPortDSEntry 4 }
+
+ptpbaseClockPortDSName OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP clock port name."
+ ::= { ptpbaseClockPortDSEntry 5 }
+
+ptpbaseClockPortDSPortIdentity OBJECT-TYPE
+ SYNTAX OCTET STRING(SIZE(1..256))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP clock port Identity."
+ ::= { ptpbaseClockPortDSEntry 6 }
+
+ptpbaseClockPortDSAnnouncementInterval OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Announce message transmission
+ interval associated with this clock port."
+ ::= { ptpbaseClockPortDSEntry 7 }
+
+ptpbaseClockPortDSAnnounceRctTimeout OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Announce receipt timeout associated
+ with this clock port."
+ ::= { ptpbaseClockPortDSEntry 8 }
+
+ptpbaseClockPortDSSyncInterval OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Sync message transmission interval."
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 52]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ ::= { ptpbaseClockPortDSEntry 9 }
+
+ptpbaseClockPortDSMinDelayReqInterval OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Delay_Req message transmission
+ interval."
+ ::= { ptpbaseClockPortDSEntry 10 }
+
+ptpbaseClockPortDSPeerDelayReqInterval OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Pdelay_Req message transmission
+ interval."
+ ::= { ptpbaseClockPortDSEntry 11 }
+
+ptpbaseClockPortDSDelayMech OBJECT-TYPE
+ SYNTAX ClockMechanismType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the delay mechanism used. If the clock
+ is an end-to-end clock, the value of the is e2e, else if the
+ clock is a peer to-peer clock, the value shall be p2p."
+ ::= { ptpbaseClockPortDSEntry 12 }
+
+ptpbaseClockPortDSPeerMeanPathDelay OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the peer meanPathDelay."
+ ::= { ptpbaseClockPortDSEntry 13 }
+
+ptpbaseClockPortDSGrantDuration OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the grant duration allocated by the
+ master."
+ ::= { ptpbaseClockPortDSEntry 14 }
+
+ptpbaseClockPortDSPTPVersion OBJECT-TYPE
+ SYNTAX Integer32
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 53]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP version being used."
+ ::= { ptpbaseClockPortDSEntry 15 }
+
+
+
+ptpbaseClockPortRunningTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockPortRunningEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the clock ports running dataset for
+ a particular domain."
+ ::= { ptpbaseMIBClockInfo 9 }
+
+ptpbaseClockPortRunningEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockPortRunningEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing runing dataset information
+ about a single clock port."
+ INDEX {
+ ptpbaseClockPortRunningDomainIndex,
+ ptpbaseClockPortRunningClockTypeIndex,
+ ptpbaseClockPortRunningClockInstanceIndex,
+ ptpbaseClockPortRunningPortNumberIndex
+ }
+ ::= { ptpbaseClockPortRunningTable 1 }
+
+PtpbaseClockPortRunningEntry ::= SEQUENCE {
+ ptpbaseClockPortRunningDomainIndex ClockDomainType,
+ ptpbaseClockPortRunningClockTypeIndex ClockType,
+ ptpbaseClockPortRunningClockInstanceIndex ClockInstanceType,
+ ptpbaseClockPortRunningPortNumberIndex ClockPortNumber,
+ ptpbaseClockPortRunningName DisplayString,
+ ptpbaseClockPortRunningState ClockPortState,
+ ptpbaseClockPortRunningRole ClockRoleType,
+ ptpbaseClockPortRunningInterfaceIndex InterfaceIndexOrZero,
+ ptpbaseClockPortRunningIPversion Integer32,
+ ptpbaseClockPortRunningEncapsulationType Integer32,
+ ptpbaseClockPortRunningTxMode ClockTxModeType,
+ ptpbaseClockPortRunningRxMode ClockTxModeType,
+ ptpbaseClockPortRunningPacketsReceived Counter64,
+ ptpbaseClockPortRunningPacketsSent Counter64
+}
+
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 54]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ptpbaseClockPortRunningDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ ::= { ptpbaseClockPortRunningEntry 1 }
+
+ptpbaseClockPortRunningClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock type as defined in the
+ Textual convention description."
+ ::= { ptpbaseClockPortRunningEntry 2 }
+
+ptpbaseClockPortRunningClockInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ ::= { ptpbaseClockPortRunningEntry 3 }
+
+ptpbaseClockPortRunningPortNumberIndex OBJECT-TYPE
+ SYNTAX ClockPortNumber (1..65535)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP portnumber associated with this
+ clock port."
+ ::= { ptpbaseClockPortRunningEntry 4 }
+
+ptpbaseClockPortRunningName OBJECT-TYPE
+ SYNTAX DisplayString (SIZE (1..64))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP clock port name."
+ ::= { ptpbaseClockPortRunningEntry 5 }
+
+ptpbaseClockPortRunningState OBJECT-TYPE
+ SYNTAX ClockPortState
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 55]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ "This object specifies the port state returned by PTP engine.
+
+ initializing - In this state a port initializes
+ its data sets, hardware, and
+ communication facilities.
+ faulty - The fault state of the protocol.
+ disabled - The port shall not place any
+ messages on its communication path.
+ listening - The port is waiting for the
+ announceReceiptTimeout to expire or
+ to receive an Announce message from
+ a master.
+ preMaster - The port shall behave in all respects
+ as though it were in the MASTER state
+ except that it shall not place any
+ messages on its communication path
+ except for Pdelay_Req, Pdelay_Resp,
+ Pdelay_Resp_Follow_Up, signaling, or
+ management messages.
+ master - The port is behaving as a master port.
+ passive - The port shall not place any
+ messages on its communication path
+ except for Pdelay_Req, Pdelay_Resp,
+ Pdelay_Resp_Follow_Up, or signaling
+ messages, or management messages
+ that are a required response to
+ another management message
+ uncalibrated - The local port is preparing to
+ synchronize to the master port.
+ slave - The port is synchronizing to the
+ selected master port."
+ ::= { ptpbaseClockPortRunningEntry 6 }
+
+ptpbaseClockPortRunningRole OBJECT-TYPE
+ SYNTAX ClockRoleType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the Clock Role."
+ ::= { ptpbaseClockPortRunningEntry 7 }
+
+ptpbaseClockPortRunningInterfaceIndex OBJECT-TYPE
+ SYNTAX InterfaceIndexOrZero
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the interface on the router being used by
+ the PTP Clock for PTP communication."
+ ::= { ptpbaseClockPortRunningEntry 8 }
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 56]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+
+ptpbaseClockPortRunningIPversion OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the IP version being used for PTP
+ communication (the mapping used)."
+ ::= { ptpbaseClockPortRunningEntry 9 }
+
+ptpbaseClockPortRunningEncapsulationType OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the type of encapsulation if the
+ interface is adding extra layers (eg. VLAN, Pseudowire
+ encapsulation...) for the PTP messages."
+ ::= { ptpbaseClockPortRunningEntry 10 }
+
+ptpbaseClockPortRunningTxMode OBJECT-TYPE
+ SYNTAX ClockTxModeType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the clock transmission mode as
+
+ unicast: Using unicast communication channel.
+ multicast: Using Multicast communication channel.
+ multicast-mix: Using multicast-unicast communication channel"
+ ::= { ptpbaseClockPortRunningEntry 11 }
+
+ptpbaseClockPortRunningRxMode OBJECT-TYPE
+ SYNTAX ClockTxModeType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifie the clock receive mode as
+
+ unicast: Using unicast communication channel.
+ multicast: Using Multicast communication channel.
+ multicast-mix: Using multicast-unicast communication channel"
+ ::= { ptpbaseClockPortRunningEntry 12 }
+
+ptpbaseClockPortRunningPacketsReceived OBJECT-TYPE
+ SYNTAX Counter64
+ UNITS "packets"
+ MAX-ACCESS read-only
+ STATUS current
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 57]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ DESCRIPTION
+ "This object specifies the packets received on the clock port
+ (cummulative)."
+ ::= { ptpbaseClockPortRunningEntry 13 }
+
+ptpbaseClockPortRunningPacketsSent OBJECT-TYPE
+ SYNTAX Counter64
+ UNITS "packets"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the packets sent on the clock port
+ (cummulative)."
+ ::= { ptpbaseClockPortRunningEntry 14 }
+
+
+
+ptpbaseClockPortTransDSTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockPortTransDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about the Transparent clock ports running
+ dataset for a particular domain."
+ ::= { ptpbaseMIBClockInfo 10 }
+
+ptpbaseClockPortTransDSEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockPortTransDSEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing clock port Transparent
+ dataset information about a single clock port"
+ INDEX {
+ ptpbaseClockPortTransDSDomainIndex,
+ ptpbaseClockPortTransDSInstanceIndex,
+ ptpbaseClockPortTransDSPortNumberIndex
+ }
+ ::= { ptpbaseClockPortTransDSTable 1 }
+
+PtpbaseClockPortTransDSEntry ::= SEQUENCE {
+ ptpbaseClockPortTransDSDomainIndex ClockDomainType,
+ ptpbaseClockPortTransDSInstanceIndex ClockInstanceType,
+ ptpbaseClockPortTransDSPortNumberIndex ClockPortNumber,
+ ptpbaseClockPortTransDSPortIdentity ClockIdentity,
+ ptpbaseClockPortTransDSlogMinPdelayReqInt Integer32,
+ ptpbaseClockPortTransDSFaultyFlag TruthValue,
+ ptpbaseClockPortTransDSPeerMeanPathDelay ClockTimeInterval
+}
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 58]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+
+ptpbaseClockPortTransDSDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the domain number used to create logical
+ group of PTP devices."
+ ::= { ptpbaseClockPortTransDSEntry 1 }
+
+ptpbaseClockPortTransDSInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ ::= { ptpbaseClockPortTransDSEntry 2 }
+
+ptpbaseClockPortTransDSPortNumberIndex OBJECT-TYPE
+ SYNTAX ClockPortNumber (1..65535)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP port number associated with this
+ port."
+ REFERENCE "Section 7.5.2 Port Identity [IEEE 1588-2008]"
+ ::= { ptpbaseClockPortTransDSEntry 3 }
+
+ptpbaseClockPortTransDSPortIdentity OBJECT-TYPE
+ SYNTAX ClockIdentity
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the value of the PortIdentity
+ attribute of the local port."
+ REFERENCE "Section 8.3.3.2.1 of [IEEE 1588-2008]"
+ ::= { ptpbaseClockPortTransDSEntry 4 }
+
+ptpbaseClockPortTransDSlogMinPdelayReqInt OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the value of the logarithm to the
+ base 2 of the minPdelayReqInterval."
+ REFERENCE "Section 8.3.3.3.1 of [IEEE 1588-2008]"
+ ::= { ptpbaseClockPortTransDSEntry 5 }
+
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 59]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ptpbaseClockPortTransDSFaultyFlag OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the value TRUE if the port is faulty
+ and FALSE if the port is operating normally."
+ REFERENCE "Section 8.3.3.3.2 of [IEEE 1588-2008]"
+ ::= { ptpbaseClockPortTransDSEntry 6 }
+
+ptpbaseClockPortTransDSPeerMeanPathDelay OBJECT-TYPE
+ SYNTAX ClockTimeInterval
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies, (if the delayMechanism used is P2P) the
+ value is the estimate of the current one-way propagation delay,
+ i.e., <meanPathDelay> on the link attached to this port
+ computed
+ using the peer delay mechanism. If the value of the
+ delayMechanism
+ used is E2E, then the value will be zero."
+ REFERENCE "Section 8.3.3.3.3 of [IEEE 1588-2008]"
+ ::= { ptpbaseClockPortTransDSEntry 7 }
+
+
+
+ptpbaseClockPortAssociateTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PtpbaseClockPortAssociateEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of information about a given port's associated ports.
+
+ For a master port - multiple slave ports which have established
+ sessions with the current master port.
+ For a slave port - the list of masters available for a given
+ slave port.
+
+ Session information (pkts, errors) to be displayed based on
+ availability and scenario."
+ ::= { ptpbaseMIBClockInfo 11 }
+
+ptpbaseClockPortAssociateEntry OBJECT-TYPE
+ SYNTAX PtpbaseClockPortAssociateEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table, containing information about a single
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 60]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ associated port for the given clockport."
+ INDEX {
+ ptpClockPortCurrentDomainIndex,
+ ptpClockPortCurrentClockTypeIndex,
+ ptpClockPortCurrentClockInstanceIndex,
+ ptpClockPortCurrentPortNumberIndex,
+ ptpbaseClockPortAssociatePortIndex
+ }
+ ::= { ptpbaseClockPortAssociateTable 1 }
+
+PtpbaseClockPortAssociateEntry ::= SEQUENCE {
+ ptpClockPortCurrentDomainIndex ClockDomainType,
+ ptpClockPortCurrentClockTypeIndex ClockType,
+ ptpClockPortCurrentClockInstanceIndex ClockInstanceType,
+ ptpClockPortCurrentPortNumberIndex ClockPortNumber,
+ ptpbaseClockPortAssociatePortIndex Unsigned32,
+ ptpbaseClockPortAssociateAddressType InetAddressType,
+ ptpbaseClockPortAssociateAddress InetAddress,
+ ptpbaseClockPortAssociatePacketsSent Counter64,
+ ptpbaseClockPortAssociatePacketsReceived Counter64,
+ ptpbaseClockPortAssociateInErrors Counter64,
+ ptpbaseClockPortAssociateOutErrors Counter64
+}
+
+ptpClockPortCurrentDomainIndex OBJECT-TYPE
+ SYNTAX ClockDomainType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the given port's domain number."
+ ::= { ptpbaseClockPortAssociateEntry 1 }
+
+ptpClockPortCurrentClockTypeIndex OBJECT-TYPE
+ SYNTAX ClockType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the given port's clock type."
+ ::= { ptpbaseClockPortAssociateEntry 2 }
+
+ptpClockPortCurrentClockInstanceIndex OBJECT-TYPE
+ SYNTAX ClockInstanceType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the instance of the clock for this clock
+ type in the given domain."
+ ::= { ptpbaseClockPortAssociateEntry 3 }
+
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 61]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ptpClockPortCurrentPortNumberIndex OBJECT-TYPE
+ SYNTAX ClockPortNumber
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the PTP Port Number for the given port."
+ ::= { ptpbaseClockPortAssociateEntry 4 }
+
+ptpbaseClockPortAssociatePortIndex OBJECT-TYPE
+ SYNTAX Unsigned32 (1..65535)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This object specifies the associated port's serial number in
+ the current port's context."
+ ::= { ptpbaseClockPortAssociateEntry 5 }
+
+ptpbaseClockPortAssociateAddressType OBJECT-TYPE
+ SYNTAX InetAddressType
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the peer port's network address type used
+ for PTP communication."
+ ::= { ptpbaseClockPortAssociateEntry 6 }
+
+ptpbaseClockPortAssociateAddress OBJECT-TYPE
+ SYNTAX InetAddress
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the peer port's network address used for
+ PTP communication."
+ ::= { ptpbaseClockPortAssociateEntry 7 }
+
+ptpbaseClockPortAssociatePacketsSent OBJECT-TYPE
+ SYNTAX Counter64
+ UNITS "packets"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of packets sent to this peer port from the current
+ port."
+ ::= { ptpbaseClockPortAssociateEntry 8 }
+
+ptpbaseClockPortAssociatePacketsReceived OBJECT-TYPE
+ SYNTAX Counter64
+ UNITS "packets"
+ MAX-ACCESS read-only
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 62]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ STATUS current
+ DESCRIPTION
+ "The number of packets received from this peer port by the
+ current port."
+ ::= { ptpbaseClockPortAssociateEntry 9 }
+
+ptpbaseClockPortAssociateInErrors OBJECT-TYPE
+ SYNTAX Counter64
+ UNITS "packets"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the input errors associated with the
+ peer port."
+ ::= { ptpbaseClockPortAssociateEntry 10 }
+
+ptpbaseClockPortAssociateOutErrors OBJECT-TYPE
+ SYNTAX Counter64
+ UNITS "packets"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This object specifies the output errors associated with the
+ peer port."
+ ::= { ptpbaseClockPortAssociateEntry 11 }
+
+
+END
+
+
+5. Security Considerations
+
+ This MIB contains readable objects whose values provide information
+ related to PTP objects. While unauthorized access to the readable
+ objects is relatively innocuous, unauthorized access to the write-
+ able objects could cause a denial of service, or could cause
+ unauthorized creation and/or manipulation of tunnels. Hence, the
+ support for SET operations in a non-secure environment without proper
+ protection can have a negative effect on network operations.
+
+ SNMPv1 by itself is such an insecure environment. Even if the
+ network itself is secure (for example by using IPSec), even then,
+ there is no control as to who on the secure network is allowed to
+ access and SET (change/create/delete) the objects in this MIB.
+
+ It is recommended that the implementers consider the security
+ features as provided by the SNMPv3 framework. Specifically, the use
+ of the User-based Security Model [RFC 3414] and the View-based Access
+ Control Model [RFC 3415] is recommended.
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 63]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ It is then a customer/user responsibility to ensure that the SNMP
+ entity giving access to this MIB, is properly configured to give
+ access to those objects only to those principals (users) that have
+ legitimate rights to access them.
+
+6. IANA Considerations
+
+ To be added.
+
+7. References
+
+7.1. Normative References
+
+ [IEEE 1588-2008] "IEEE Standard for A Precision Clock Synchronization
+ Protocol for Networked Measurement and Control Systems", IEEE Std.
+ 1588(TM)-2008, 24 July 2008
+
+7.2. Informative References
+
+ [RFC 1155] Rose, M., and K. McCloghrie, "Structure and Identification
+ of Management Information for TCP/IP-based Internets", STD 16, RFC
+ 1155, Performance Systems International, Hughes LAN Systems, May 1990
+
+ [RFC 1157] Case, J., Fedor, M., Schoffstall, M., and J. Davin,
+ "Simple Network Management Protocol", STD 15, RFC 1157, SNMP
+ Research, Performance Systems International, Performance Systems
+ International, MIT Laboratory for Computer Science, May 1990.
+
+ [RFC 1212] Rose, M., and K. McCloghrie, "Concise MIB Definitions",
+ STD 16, RFC 1212, Performance Systems International, Hughes LAN
+ Systems, March 1991
+
+ [RFC 1215] M. Rose, "A Convention for Defining Traps for use with the
+ SNMP", RFC 1215, Performance Systems International, March 1991
+
+ [RFC 1901] SNMPv2 Working Group, Case, J., McCloghrie, K., Rose, M.,
+ and S. Waldbusser, "Introduction to Community-based SNMPv2", RFC
+ 1901, SNMP Research, Inc., Cisco Systems, Inc., Dover Beach
+ Consulting, Inc., International Network Services, January 1996.
+
+ [RFC 1906] SNMPv2 Working Group, Case, J., McCloghrie, K., Rose, M.,
+ and S. Waldbusser, "Transport Mappings for Version 2 of the Simple
+ Network Management Protocol (SNMPv2)", RFC 1906, SNMP Research, Inc.,
+ Cisco Systems, Inc., Dover Beach Consulting, Inc., International
+ Network Services, January 1996.
+
+ [RFC 2578] McCloghrie, K., Perkins, D., and J. Schoenwaelder,
+ "Structure of Management Information Version 2 (SMIv2)", STD 58, RFC
+ 2578, April 1999.
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 64]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ [RFC 2579] McCloghrie, K., Perkins, D., and J. Schoenwaelder,
+ "Textual Conventions for SMIv2", STD 58, RFC 2579, April 1999.
+
+ [RFC 2580] McCloghrie, K., Perkins, D., and J. Schoenwaelder,
+ "Conformance Statements for SMIv2", STD 58, RFC 2580, April 1999.
+
+ [RFC 3411] Harrington, D., Presuhn, R., and B. Wijnen, "An
+ Architecture for Describing Simple Network Management Protocol (SNMP)
+ Management Frameworks", STD 62, RFC 3411, Enterasys Networks, BMC
+ Software, Inc., Lucent Technologies, December 2002
+
+ [RFC 3412] Case, J., Harrington D., Presuhn R., and B. Wijnen,
+ "Message Processing and Dispatching for the Simple Network Management
+ Protocol (SNMP)", STD 62, RFC 3412, SNMP Research, Inc., Enterasys
+ Networks, BMC Software, Inc., Lucent Technologies, December 2002.
+
+ [RFC 3413] Levi, D., Meyer, P., and B. Stewart, "Simple Network
+ Management Protocol (SNMP) Applications", STD 62, RFC 3413, Nortel
+ Networks, Secure Computing Corporation, December 2002.
+
+ [RFC 3414] Blumenthal, U., and B. Wijnen, "User-based Security Model
+ (USM) for version 3 of the Simple Network Management Protocol
+ (SNMPv3)", STD 62, RFC 3414, Lucent Technologies, December 2002.
+
+ [RFC 3415] Wijnen, B., Presuhn, R., and K. McCloghrie, "View-based
+ Access Control Model (VACM) for the Simple Network Management
+ Protocol (SNMP)", STD 62, RFC 3415, Lucent Technologies, BMC
+ Software, Inc., Cisco Systems, Inc., December 2002.
+
+ [RFC 3416] Presuhn, R. (Ed.), "Version 2 of the Protocol Operations
+ for the Simple Network Management Protocol (SNMP)", STD 62, RFC 3416,
+ BMC Software, Inc., December 2002.
+
+ [RFC 3417] Presuhn, R. (Ed.), "Transport Mappings for the Simple
+ Network Management Protocol (SNMP)", STD 62, RFC 3417, BMC Software,
+ Inc., December 2002.
+
+ [RFC 5905] David L. Mills, " Network Time Protocol Version 4:
+ Protocol and Algorithms Specification", RFC 5905, University of
+ Delaware, June 2010.
+
+ [IEEE 802.3-2008] "IEEE Standard for Information technology -
+ Telecommunications and information exchange between systems - Local
+ and Metropolitan area networks - Specific requirements Part 3:
+ Carrier sense multiple access with Collision Detection (CSMA/CD)
+ Access Method and Physical Layer Specifications", IEEE Std. 802.3 -
+ 2008, 26 December 2008
+
+ [G.8265.1] "Precision time protocol telecom profile for frequency
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 65]
+
+Internet-Draft draft-ietf-tictoc-ptp-mib-01.txt February 6, 2012
+
+
+ synchronization", ITU-T Recommendation G.8265.1, October 2010.
+
+8. Acknowledgements
+
+ Thanks to John Linton and Danny Lee for valuable comments.
+
+9. Author's Addresses
+
+ Vinay Shankarkumar
+ Cisco Systems,
+ 7025-4 Kit Creek Road,
+ Research Triangle Park,
+ NC 27560,
+ USA.
+ Email: vinays at cisco.com
+
+ Laurent Montini,
+ Cisco Systems,
+ 11, rue Camille Desmoulins,
+ 92782 Issy-les-Moulineaux,
+ France.
+ Email: lmontini at cisco.com
+
+ Tim Frost,
+ Symmetricom Inc.,
+ 2300 Orchard Parkway,
+ San Jose,
+ CA 95131,
+ USA.
+ Email: tfrost at symmetricom.com
+
+ Greg Dowd,
+ Symmetricom Inc.,
+ 2300 Orchard Parkway,
+ San Jose,
+ CA 95131,
+ USA.
+ Email: gdowd at symmetricom.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+Shankarkumar et al. Expires August 6, 2012 [Page 66]
diff --git a/rtemsbsd/ptpd/doc/index.html b/rtemsbsd/ptpd/doc/index.html
new file mode 100644
index 00000000..3e367d24
--- /dev/null
+++ b/rtemsbsd/ptpd/doc/index.html
@@ -0,0 +1,125 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+<title>PTPd - Precision Time Protocol daemon</title>
+<link href="stylesheet.css" rel="stylesheet" type="text/css" />
+</head>
+
+<body>
+
+<!-- begin header -->
+<table width="100%" border="0" cellpadding="4" cellspacing="0" class="info_table">
+<tr>
+<td>
+<center><p class="info_text">
+Last site update: 22 November 2013 | Latest PTPd version: <a class="info_text" href="http://sourceforge.net/projects/ptpd/files/ptpd/2.3.0/ptpd-2.3.0.tar.gz/download">2.3.0</a>
+| PTPd is hosted by
+<a href="http://sourceforge.net"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=139814&type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" align="absmiddle"/></a>
+</p>
+</center>
+</td>
+</tr>
+</table>
+
+<table width="100%" border="0" cellpadding="20" cellspacing="0">
+<tr>
+<td>
+<center><font class="title_text">PTPd</font></center>
+</td>
+</tr>
+</table>
+<!-- end header -->
+
+<table width="100%" border="0" cellpadding="0" cellspacing="0">
+<tr>
+
+<td valign="top">
+
+<!-- begin sidebar -->
+<table border="0" cellpadding="0" cellspacing="0" class="sidebar_table">
+<tr>
+<td class="sidebar_heading">PTPd</td>
+</tr>
+<tr>
+<td><a class="sidebar_link" href="http://sourceforge.net/projects/ptpd/">Project Page</a></td>
+</tr>
+<tr>
+<td><a class="sidebar_link" href="http://sourceforge.net/project/showfiles.php?group_id=139814">Download</a></td>
+</tr>
+<tr>
+<td><a class="sidebar_link" href="http://sourceforge.net/p/ptpd/code/">Browse Source</a></td>
+</tr>
+<tr>
+<td><a class="sidebar_link" href="https://sourceforge.net/p/ptpd/wiki/Home/">Wiki</a></td>
+</tr>
+<tr><td> </td></tr>
+<tr>
+<td class="sidebar_heading">Related Projects</td>
+</tr>
+<tr>
+<td><a class="sidebar_link" href="http://ieee1588.nist.gov/">IEEE 1588</a></td>
+</tr>
+<tr>
+<td><a class="sidebar_link" href="http://www.ntp.org/">NTP</a></td>
+</tr>
+</table>
+<!-- end sidebar -->
+
+</td>
+
+<td class="vline"></td>
+<td valign="top">
+
+<!-- begin body -->
+<table border="0" cellpadding="0" cellspacing="0" class="content_table">
+<tr><td>
+
+<p> The PTP daemon (PTPd) implements the Precision Time protocol (PTP)
+as defined by the relevant IEEE 1588 standard. PTP Version 2 implements
+IEEE-1588-2008. PTP was developed to provide very precise time
+coordination of LAN connected computers. </p>
+
+<p>Documentation is now being added to the
+project <a href="https://sourceforge.net/p/ptpd/wiki/Home/">Wiki </a>.</p>
+
+<p> PTPd is a complete implementation of the IEEE 1588 specification
+for a standard (non-boundary) clock. PTPd has been tested with and is
+known to work properly with other IEEE 1588 implementations. The
+source code for PTPd is freely available under a BSD-style
+license. Thanks to contributions from users, PTPd is becoming an
+increasingly portable, interoperable, and stable IEEE 1588
+implementation. </p>
+
+<p> PTPd can run on most 32-bit or 64-bit, little- or big-endian processors. It
+does not require an FPU, so it is great for embedded processors. PTPd
+currently runs on Linux, uClinux, FreeBSD, and NetBSD. It should also
+be easy to port to other platforms. </p>
+
+<p>PTPd is free. Everyone is welcome to use and contribute to PTPd. </p>
+
+<p>The PTPd project would like to thank the following for their kind
+ donations of equipment and time:
+
+ <dl>
+ <dt><a href="http://www.meinbergglobal.com/english" >Meinberg Corp </a>
+ <dd>Donation of an m600 PTPv2 GPS Grandmaster Clock
+ <dt><a href="https://ssl.sentex.ca/" >Sentex Data Communications</a>
+ <dd>Hosting the FreeBSD Network Test Cluster
+ <dt><a href="http://www.freebsdfoundation.org/" >The FreeBSD Foundation </a>
+ <dd>Providing testing resources for the PTPd Project
+ </dl>
+</td></tr></table>
+
+<p>PTPd version 1 implements IEEE-1588-2002 and is considered a legacy
+protocol. <a
+href="http://sourceforge.net/projects/ptpd/files/ptpd/1.1.0/ptpd-1.1.0.tar.gz/download">1.1.0</a>
+<!-- end body -->
+
+</td>
+
+</tr></table>
+
+</body>
+</html>
+
diff --git a/rtemsbsd/ptpd/doc/ptpd-2.3.0-migration-guide.html b/rtemsbsd/ptpd/doc/ptpd-2.3.0-migration-guide.html
new file mode 100644
index 00000000..d1dfea21
--- /dev/null
+++ b/rtemsbsd/ptpd/doc/ptpd-2.3.0-migration-guide.html
@@ -0,0 +1,243 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content="HTML Tidy for Linux (vers 25 March 2009), see www.w3.org" />
+<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=us-ascii" />
+<title xml:lang="en-US" lang="en-US">PTPd</title>
+
+<style type="text/css">
+/*<![CDATA[*/
+ th {text-align: left;}
+ table {border-collapse:collapse;}
+ table, th, td, tr {border: 1px solid black;}
+ td.c1 {font-family: monospace;}
+ td.c2 {font-family: monospace;}
+/*]]>*/
+</style>
+</head>
+<body>
+<h1>PTPd 2.2.x → 2.3.0 migration guide</h1>
+<table>
+<tr>
+<th>Old option (2.2.2)</th>
+<th>New option (2.3.0)</th>
+<th>Meaning</th>
+</tr>
+<tr>
+<td class="c1">-H</td>
+<td class="c2">-H --long-help</td>
+<td class="c3">show detailed help page</td>
+</tr>
+<tr>
+<td class="c1">-g</td>
+<td class="c2">-s --slaveonly</td>
+<td class="c3">run as slave only</td>
+</tr>
+<tr>
+<td class="c1">-G</td>
+<td class="c2">-M --masteronly</td>
+<td class="c3">Master, passive when not best GM</td>
+</tr>
+<tr>
+<td class="c1">-W</td>
+<td class="c2">-m --masterslave</td>
+<td class="c3">Master, slave when not best GM</td>
+</tr>
+<tr>
+<td class="c1">-b NAME</td>
+<td class="c2">-i --interface</td>
+<td class="c3">Interface to use</td>
+</tr>
+<tr>
+<td class="c1">-c</td>
+<td class="c2">-C --foreground</td>
+<td class="c3">Don't run in background</td>
+</tr>
+<tr>
+<td class="c1">-C</td>
+<td class="c2">-V --verbose</td>
+<td class="c3">Run in verbose foreground mode</td>
+</tr>
+<tr>
+<td class="c1">-f FILE</td>
+<td class="c2">-f --log-file<br />
+-S --statistics-file</td>
+<td class="c3">Log file Statistics file</td>
+</tr>
+<tr>
+<td class="c1">-S</td>
+<td class="c2">--global:use_syslog=N</td>
+<td class="c3">DON'T send messages to syslog</td>
+</tr>
+<tr>
+<td class="c1">-T NUM</td>
+<td class="c2">--ptpengine:multicast_ttl</td>
+<td class="c3">set multicast time to live</td>
+</tr>
+<tr>
+<td class="c1">-D</td>
+<td class="c2">(always in .csv format)</td>
+<td class="c3">display stats in .csv format</td>
+</tr>
+<tr>
+<td class="c1">-P</td>
+<td class="c2"> </td>
+<td class="c3">display each received packet in detail</td>
+</tr>
+<tr>
+<td class="c1">-R FILE</td>
+<td class="c2">--global:quality_file</td>
+<td class="c3">record data about sync packets in a seperate file</td>
+</tr>
+<tr>
+<td class="c1">-x</td>
+<td class="c2">(no alternative)</td>
+<td class="c3">do not reset the clock if off by more than one second</td>
+</tr>
+<tr>
+<td class="c1">-O NUM</td>
+<td class="c2">--servo:max_offset<br />
+see also ptpengine:sync_outlier_filter_*</td>
+<td class="c3">do not reset the clock if offset is more than NUMBER nanoseconds</td>
+</tr>
+<tr>
+<td class="c1">-t</td>
+<td class="c2">-n --noadjust</td>
+<td class="c3">Do not adjust the clock</td>
+</tr>
+<tr>
+<td class="c1">-M NUM</td>
+<td class="c2">setup by ptpengine:delay_outlier_filter_* options</td>
+<td class="c3">do not accept delay values of more than NUMBER nanoseconds</td>
+</tr>
+<tr>
+<td class="c1">-a NUM,NUM</td>
+<td class="c2">--servo:kp --servo:ki</td>
+<td class="c3">specify clock servo Proportional and Integral components → converted from attenuations to gains</td>
+</tr>
+<tr>
+<td class="c1">-w NUM</td>
+<td class="c2">--servo:delayfilter_stiffness</td>
+<td class="c3">specify one way delay filter stiffness</td>
+</tr>
+<tr>
+<td class="c1">-u</td>
+<td class="c2">-u --unicast</td>
+<td class="c3">Unicast mode</td>
+</tr>
+<tr>
+<td class="c1">-U</td>
+<td class="c2">-y --hybrid</td>
+<td class="c3">Hybrid mode</td>
+</tr>
+<tr>
+<td class="c1">-e</td>
+<td class="c2">--ptpengine:transport=ethernet</td>
+<td class="c3">run in ethernet mode</td>
+</tr>
+<tr>
+<td class="c1">-h</td>
+<td class="c2">-E --e2e</td>
+<td class="c3">run in End to End mode</td>
+</tr>
+<tr>
+<td class="c1">-z</td>
+<td class="c2">-P --p2p</td>
+<td class="c3">run in Peer-delay mode</td>
+</tr>
+<tr>
+<td class="c1">-l NUM,NUM</td>
+<td class="c2">--ptpengine:inbound_latency<br />
+--ptpengine:outbound_latency</td>
+<td class="c3">inbound, outbound latency in nsec.</td>
+</tr>
+<tr>
+<td class="c1">-o NUM</td>
+<td class="c2">--ptpengine:utc_offset</td>
+<td class="c3">current UTC offset</td>
+</tr>
+<tr>
+<td class="c1">-i NUM</td>
+<td class="c2">-d --domain</td>
+<td class="c3">PTP domain number (between 0-3)</td>
+</tr>
+<tr>
+<td class="c1">-n NUM</td>
+<td class="c2">--ptpengine:log_announce_interval</td>
+<td class="c3">announce interval</td>
+</tr>
+<tr>
+<td class="c1">-N NUM</td>
+<td class="c2">--ptpengine:announce_receipt_timeout</td>
+<td class="c3">announce receipt timeout</td>
+</tr>
+<tr>
+<td class="c1">-y NUM</td>
+<td class="c2">--ptpengine:log_sync_interval</td>
+<td class="c3">sync interval</td>
+</tr>
+<tr>
+<td class="c1">-m NUM</td>
+<td class="c2">--ptpengine:foreignrecord_capacity</td>
+<td class="c3">max number of foreign master records</td>
+</tr>
+<tr>
+<td class="c1">-v NUM</td>
+<td class="c2">--ptpengine:ptp_allan_variance</td>
+<td class="c3">Master mode: specify system clock Allen variance</td>
+</tr>
+<tr>
+<td class="c1">-r NUM</td>
+<td class="c2">--ptpengine:ptp_clock_accuracy</td>
+<td class="c3">Master mode: specify system clock accuracy</td>
+</tr>
+<tr>
+<td class="c1">-s NUM</td>
+<td class="c2">--ptpengine:clock_class</td>
+<td class="c3">Master mode: specify system clock class</td>
+</tr>
+<tr>
+<td class="c1">-p NUM</td>
+<td class="c2">--ptpengine:priority1</td>
+<td class="c3">Master mode: specify priority1 attribute</td>
+</tr>
+<tr>
+<td class="c1">-q NUM</td>
+<td class="c2">--ptpengine:priority2</td>
+<td class="c3">Master mode: specify priority2 attribute</td>
+</tr>
+<tr>
+<td class="c1">-Y 0[,0]</td>
+<td class="c2">-a --delay-override<br />
+-r --delay-interval</td>
+<td class="c3">Initial and Master_Overide delayreq intervals</td>
+</tr>
+<tr>
+<td class="c1">-B</td>
+<td class="c2">-D<DD...> --debug</td>
+<td class="c3">Enable debug messages</td>
+</tr>
+<tr>
+<td class="c1">-j</td>
+<td class="c2">--ptpengine:igmp_refresh=N</td>
+<td class="c3">Do not refresh the IGMP Multicast menbership at each protol reset</td>
+</tr>
+<tr>
+<td class="c1">-L</td>
+<td class="c2">-L --ignore-lock<br />
+see --ptpengine:pid_as_clock_idendity<br />
+see --auto-lock
+</td>
+<td class="c3">Allow multiple instances</td>
+</tr>
+<tr>
+<td class="c1">-V 0</td>
+<td class="c2">--global:status_update_interval<br />
+--global:statistics_log_interval</td>
+<td class="c3">Seconds between log messages</td>
+</tr>
+</table>
+</body>
+</html>
diff --git a/rtemsbsd/ptpd/doc/ptpd_2005_1588_conference_paper.pdf b/rtemsbsd/ptpd/doc/ptpd_2005_1588_conference_paper.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..3d4f80dd4e00f98bb6ca2a50744a79c39b5c1f3a
GIT binary patch
literal 323600
zcmb at t1y~&05-y6nYw*EcW^jVLyA#~q-61$6NFYFPcL?rIu;37!;2K<mgbT^eIs5Fr
z&pYpZ_sx7gU0qA6daYWuYW}XKR1_6w1TwK9Q?74}O(7$IxBx5wdm}4eUS>%hE_Mz8
zh>Js)S=Q3d)X>S&%hUt_WEKN(a<Z|rGs^;4L984sEX?u%4rT=aE09_Bi38&1X65Hc
zHnlT(>IE6__YX3Hhm)z<Pe~vyWCWH!K0pq at PYPi7n+F84f&R*4c~<GK^+2G%)dRAz
z0RL8xosI3UJP?SB<!?MrPS(HixL7#<%422W{99XAAlu*cV`FFin_PA-uD{9P1hW5~
z$Ii|ESGk;A?0=KX4P^W4{Q_B7L4TDC1hRAdwLc(`ljU#qK-`?%f06qnf%C~Yo<IMZ
zBOnJC+h50pgB8g7R~eifY=0XoP7Y4izs?&c2RG<%Z8<@}Ki#j3lcA-JsS`57lZC20
zStNj+gIUqZ)a|F~ae<hX?d at FvpeN-4%FI%BX7&KklY-B*s17fu8Ia2q#KCQ9%4Wg=
zG+_mD0YNOL?5ynU#+=4LkP$yG3&+#Mu(7Zia<hX##%xC1Ko$_U;Zqe8E)Y9An-Rb6
zlWik7yBIpT{IX&eWCTh|F$HmCgg;w4G6L(9%73gQSyMZ67YhK0g`HX4(#FNqiCNsn
z(8W~L)Y#s{^l24=Ky1t^03a&|Kfkk!lc}NY(+XKIGB7ZJ$-zb{LqbwcK?*{G0}OOR
zV3``O&W(3=j<*YGY+HmX5M9sWA|cT$qy^Bd!A-lqtodBbI&8DEtU`;=r_-j)R#5J(
zYh3-gc*A7?l+|TM1TYnZOn%3P|6&e={{rA69sz?z9w-<dxOEE+V1(~=GC-$<0S}Ep
zYLdNsp#bTrfLE*8rkSW!!7^&lID8hLpJe({glbqWNssB6 at gTYb#A}#5cJg5jlnczI
z?-BaM61uGDAI at 5}#0Qa=5-^7k=meuq;&w(xcyb1R+}R&);phDP at -m8s=BCbo=UIQ6
zBvnrbQ)UrE7egC+bLQs~SwkaJo2TNZh4EA at XX;{T@>B`{JuNbT^1s~->yz>SG1?;b
zb}puNF3viv&z?*f!1~h^0Bq0mp$uUA*%9E`5j<c0lnQt@;peNL4Rn7g at htpF1E;61
zek-SJ at A5Ql0G1~;Oe_tB?LBm!`{V$E0G!+)U1kv{dxyU<l}(-PU7d`d?(o^x|3l^H
z>t914VsB&bq~c&`Z2Dx=BF-Fu=k at bkms$MjW}bMeszAWAUwIxORS<yn*J!A!vH_kw
z%TF!`;MoEEY^3O9Z>(bKqQm^;+L={NJzR8|#XMXjRGzhe>P$if2w?qr$NW#9-;Vlc
zpTE at kISJ2_nZ@j$Ow`iOoLR%tPRP#L@?Y1&hR&uxYlzA#$}5P|t6JKcI?J27E8E)|
z+9|5YsXke{le3G6g`v~ajWf#{{y~FSS(#N_ja;6mS=Gta^k1SxO`VOMEFE0zpX&d*
zQ4LEI7Yk<{(6g`m<IDA=B<HgadKv-Np9lMsFJk9ldzztNyyr9Msm4$KbJ_nmp6fmz
zzk2y4hx2JS;o#u>dHke-KmhyGuJcSkA3tStasQ+_p31oXpr6a0>phvoQ|o8yx%77{
z?kAJ`P5;vM_wTb*wx`l(UH^Q3mh)Tx-+De at Khw{n at Vo5y`L|xr<-d79b^2orp8NiF
zhwML1<@c!m_w(;jes1}z^>e at fH(&Ojlly<u!#}tE_r3kQ{E7GMtpD&hKNrgXcl-W}
zVKWNb+nD at U>jts||EG1c|8jKyVBP=L{F(Y?_rI<G-{bz9`hEU$tLNE%w!PnW_DugO
zXMM6!mOs|czbx^|PM$6FxyDax{a5Wj_xX!{miAodd7OWr|7D*~{AZi{dHo0dyi%X*
z{ciWKHcxu|uJ>H_tm&^+`cE_XcRl`z^V|>cDJc2#WIqq`(`4%~ONod)`zjLv+s|!4
z_h07lyiWgO4*&K+KmG30UTylcqMt=7|6~Y1dv`{54lV%WQ|R}!pZ^lfsr$23-q6;R
z`M<kE8$<J_J>Y4hcQLh92Ry|oB8Co<rk3UwE&z_F2uAY{nuS@&+4y<S;o=4|KP&W1
zGlGB|%u-LAqNTBrow?2P7WC|V#SLvOZ9HjynkeAO`2v1=U)rB#Do=rgi?Ic>yuFjH
zq0LX0#xH4XEG&Oo70(9pr(yWlKEbRa1TZtSaW-XEQ2c{Xkpr+Wv4EKE9sX4!2LS%5
zuV**<tFJ#I9^g}e^$#J>zr!5Kh%)s>yG3y{-&u{%8`V``UNKd^jWEWto1iL6sTZ6G
zL$0e&R?L%82E9YI^lxz1l8y|{Vh#bfW at a@vA6 at czcxVgkx4dlD)17sNfY}dAjBrg8
zjP|dt{y1X^Uq8(eqh!?3+ at 9iOL6sC?XmyK?@Cj$%3|r)(RT(DdmRzU+93&ugHq at KK
zBuz6Zx&u#I0S_333w$4HxN=kM`)$o^x6o}vg0rD9D9rHn-I`$mb at Ys*ULM at 5j_ihD
zp82!ZTM at pNiJrywm)*-p#pf+FQ|jOS#Peuoit)X82WOzr9<&~-v^97bWudKejja*8
zTUl(hvJHD6KkJ~nH#9UDR9C-t&$S;QhALWr^%-3vK7npAZvj3%pn2I-6 at NP$uQ{a5
z{w!0(P&pk_=`>~YJt1s5)DTuA*M!y(!i9?yRXvuO-^IfxS0bk<Bnm4+-&x(RG23%^
zJ#xzjGaTu5+azGfr}t+=yC8u+7H*})A(0O8167Uv>j8`0BMTL}BHx<BT-O3t_5p=3
zGgT{(cgv>;IX-bHK`|&ok#)fOcK}mt*bE;wYLcl}8SQ)bUG5cMUv(|o5+t5!2OYoa
zy1Ub$hV39 at iBYlDb4t#vi8`H8u&r at z51!3Eh-=vJ4*~Y1Jw`GInr=sH`g(giE39;!
zD%7+GeJC0aaYFYhr&7S}5Iex|J#k{Yqv`*uM0l`<EAagk5O&gTkE*|<@Cy9U2Wuy8
z1q1QKKL=yD at H5K@JX3Lrij-cSwa at o2!$=j$<p?$?o at Oo>&B%r2Y`C_zIA{Aauw at 1p
z0 at cZ6&aa7^%|bh}SL&T$6c|=nPRru!NU{$-T~EyWt|mqd5$$?rNv at eYgBQrzdgQA_
zgO1SZ_CmKYN#HQo&6?4yA`N`CLqA=x+;%AP8z&<fHx-=idQJ^&!^vNL7y#fQnC;`8
zclw!PDaPZYFn;1x`XEE-5EW}58-sQHo&byK1e%r`3HT|FaSZEPjV!2lg4yORS&UN0
z#gI0=A7wuL{Ks?o>8rJwblfTP*mLrvs%Fuvm-Ch&dJTvEmr4lrDys<WqqgKlU9UK+
zPc_Cz(R?6J%?(02vq?<XLSCFvXpEs3SQKarr8XdbTnuRqatiP*?|7+zfuM`AxcUt<
zL0WE!eUT&9TIq3>--Y-}Wl><=!~hFl!SsbZt}{hMf)nl4)V&~u%twVHLGm0#e`X2j
zW<|PGTqT_7SBO7wmAY(7g=GfiiV`EjmX#KYZD^1@;Y7To-eP-|8L1<%fBSA8T6GPe
z^NxJg7ozt15i0<J9EOWnOmZ}idA2T-JV9}j(+6fE)k%gWon}a`s at pJO5SYkP8#1Z+
z#b+MIJ2Qyb8%qzbqk at mL+eEmhDzl~j8guL@#z!Zs7=@TZvnYu)X2&69#b%NYjr(>u
z`|BBJh>yHMmRH012TB(312VQ at cVucoC9I$uCT*3Y>0Zx|->TLp80A*9px4HMnB=xa
z)aAwHbul*A>4><iW@&J(-{$E|%JL}uoeo2rVQ+~kI1N9ZIJ9TGQ4K4A+7#H)U&LS%
z;Nu~GSR)~G;gOc4mR7wVS|H8pfno6$K-lUA-M-uDgC4$mO&hx;3*K?X>#jP&NOVZ;
zAxj%wBhYB4Am?rP(x9E;b^g*B$OLSMWkEc9LyfvMEHp%lL<up=r}sy|#VM)JDv6a*
zp_>o at LP+0_R~5)UBNg%;$!V$agW)O^>Lq22^d#?E;W+M%MgTaNx=f$NKm33C`KRVT
zeJzNS`+xY_*XnZ4OB_gT$CaIN-$YB=HF!8vik&6-h%Vo40<4?#W?JIK-yJr0w4;J;
z*|A=+Wqb<&M7#}yRXp6$y_;f^UZ)tOv$`XaF%>yHKPqi<=Ahf?E*s;JK5yh;q7ldU
zUrJ31%i<Zwr|3}TB;H-;d2B4A`}WG_UEfGOzAc8i8Rer<F4d^xn6A%f?-<g%wujcc
zCR<kNV!k6~sAQ!efyA at OAtw@Z_PY1FWOxoIjk7=CfzpmeLi{9 at HMOgl6Rz=`z-6D0
z`Ii2V$Q>k)H+>wZE47 at hw?Ui2P*!xzGt-aXg$#mB1O4|y4d~>Q0W%qd%RaXk5tG#w
z*%hA;*KfN}>)+6`#!RE&ekh0~axjG29H^CVE3e8J&G~L-x2U7 at DgV3Kp7rfRIScnj
zd5K)ptmHOygA?mK1XBhY4jqf;w4B(^A+dNdxoDhogwyIoOool=k=`aw)EIU9H2LmQ
znkKLrJJS5cSSQRWxb(?Y;6-vXR?Rs%i5uc<TB2SOZVP9|tz9jR-6hFpWmma~w5PJA
z^zy?xq1HCyNx}fD=)^o$iU$?OuBr2Vx3Pc5Ev$B0o~Mh at 3vY=7G$x*=8=<*H_=QE7
z4To!C$##LZz(y8&DG{GLDy*-WE!5O+l)8p^0TSpu!<KJ0?@QRn<QjM!8#9)w#wnP_
z4YZK=sVd>HK7{d9-FRHssX at INb|L+!jtEgq2A06FLA-*dsg!`9`1Zwnhn2zanygVA
z9ReImSNnWw#|C7`ot&$xDc>3BvEEI&Y|zQPDFD(PoWeWor&`9QXwCQ*UXN+$xELY5
zgsSAcgH`ix;EiB|mZ9DTD$io`c`-RaqH(b^?YvY&EF03JhGak;opMn%qL_?R>Qkrc
zdpXS*w`5{or$j0zs<Soe(=j^~@u7Bp>9yM{>h>Ux<3daBB3>{KMP*!~>QZEjj~S|K
zyM#wz)JAr;x3kp|QzVN8NT#JFu~}yU-kzEY-C>~<!>fxQy&NSzF$D))p-Dx|NYb(r
zPY+SeduMeTy*?9?T$*T=Z96CajwSBRw2`5+wARLxr;{w at hm58`G1m7Uy(o2MT^zXs
zRu>h->?`oOnIAP5fyvMOok at 1wB!%UV^eHiLC}1J1&`PeYlZbOz7%4j|2!^d at E6h5u
z2P at AaZ}8 at Y%ESI7ML#jOe5VwDw2(US<*IYT?t|pahSoH5HQ-Z%{;XCV69Xr6`bda#
z2pj0p=w42Op3wJ>Z!T0t-E74A{1ea2C6QC39znK9 at kaLasp3$uDr^CYaRZ7I48ITn
zUE#K3%9b9uL>DzWDW1CugKI?Mw?c5d*4OQViUem{0LdpoKOjgSQ%j|%a_k|$(cy8h
zd{R5&glBP+m1S?HqSzRH+9{{A>X;BSzd;nz<(@HtT<@iCt%$p)RR7lM?ueMSxuX%?
zDqV0l4!x>idnMvb(A@(-u(YN?RS;P;(@9fPx at 5=W+a)d4xZNMeNz|U52g|MIk3(dZ
zlicO9 at liKLA9mtv)Pcbfa#rqvImPgq(;Y`FGy1f&j#kI`RdTg;VZ4R8XWmvYKox!B
zi(HZ}YO at uq-fOAZOHe1v-Esl~3WLIk5bh!Xp;S(=i&!mnM=HcrEi!Zqy{-O~Zvmo-
zN)3);Ic#TWY at qrjs=qNpI?vp~hpo;S$)4~CH&r!ek+dJ^24L$AmE8Z}M1Mj;e{rHu
zX!hSRx<3c3 at 6-qF7P(%u at 6dWs1WKaSAv)8saOV|5uy2FXA;~A|@k7`sxI^zQ&h6z7
zbNDY}lkcxDb2&0eY~qqa>gZHHbdba|jDU1X<Q`-5n{4UpxcTl&`wikjjCI=AJ}3K~
zVGo{o-kh`v(_T*I0;L1d<x-KgO<!0ZZTST#aNQ&1YI63+X}_1v6*8uV`oeUik{w<Q
zsus^UhGxF%_#wtt$7!l<>0r9Hvnhwz>OKZ~OEtAWGDirNJ{X8GN>M?jd?mV=k_Vo8
z5YljR+%4a>%bfONg3xSw|A!i5e+KW-d<Up*<)s_Hq$C!bw>eDzfY<=dUF+Qd|8V}g
z=@&h^_FK;bua0DVhWvd at ukY30bHw3B3hMY}S15M3kiX4pGmRG!2EAJ5`2^PIUW$*4
zy^bJ6g5s$6kfk?Ta7T%=2URCfLny2EW%9??>fMCg2C8DHXjsArzSQ)GFJr7|vqeh~
zhPB$<b-jmPPH1lB=9#@L>7<uO?!^f$ua>s75 at e2&r6E0CEwfS6W=%ki9yZ#vge1vg
zO~Uhxg>Un)bkwZt=FgXzi4U$Mu1YeuaTw^Zz8%tBtCe#Kd41z4>#uH(>RT^lW)J;3
zed{%4qbK$CI=72-5l@?m4Y54k(n%+kx^%4f+$96K- at NNXkaOXNdO4cPUG`^_l>!{t
z1oy#xjp}oYEJQSNkZ6d=?qHW*j0q!4R7{m0;Xo(4*##B<_L1EZD%TJ7$<ztTZ!dP2
zQ{Wc|Wv8|JT}f5<(A1>ym$hL at Lrhj7*-2-sIPIe~(ur}uDTELi1a+dr$-vT&VC4Ah
z88B at I7s=U~TE<~I^L-WX;5Cx06n;|(cIRN<VM8ic=`>$?jB$wYvCv{yMZpkf93jo>
zk*T4x<E3k|A-Q1 at I3frl6>8QN{J|t8E!>-i&**AkTkK0s*}k#KN4#eB{k4Vc8^<{s
z3m!puOqTko##9(;NQw_FI<||$>iRr4M2MPB@#e++m3kv;b(+2f%BW>5#juP_*?sC@
zM-G at egiHow<ip%71SzLZxz){nXCR33_DSMRgDT$P#umu{HDE3K74B~Hf*yqJ&=n*N
z_-)ltzIgcSIC~ci!;jsf!TzDS!btrRrW6=#(8Qu(&U#i&w^frwxFr)lX5U{VU5t_&
zS*Mg%mvR%H7)PP#XCkjkOL2jt6B3HT>_Rm<iBYNs%o~{rqO9|MK23TN2SkC$7ih}Z
zbu`8-zTNkR!x4e at gXe?a|Nea;LWP-C54{axB=q=5s^@!WgOJ|?9t3pChQp?@f at Vx%
zK;`)P)D&j at 7^vJzhaYfAz at PaQNrIMqB+VS@Y=|QhZcfnDqgCT%`)tH0|MAp95mO-O
z<T`+zZg-CvPY5xcp^C50ERcN4OnZ7opkx?qei$r~hm>k~itrS1{DdNg4VVNwunxAi
zesX^TD_Q)Vck=NCjeoU|Ovkk-JQnVVX<X(b5IY9F966A?e|FD(#h6X&q%b!BoAthc
zeG+1={;_Q4-o}<VsyU=VXkLA<`h#fNTmp(uK*ZTg&lnn>n~)iIQN$pcH&BohFg*%j
z5r>tz!Amob5J+IA0ja(J<n{gymH!KU{eJ=U{uK!O1xNgMC=B$!p|Iztdr#2He}%$+
z!;il}pXaMzaM({!=^2pv%kf<AH}5}zqGy=v=kW(P_WSx50QKDSulmoh)i2&JVC?z#
zXOB-%)^m+t<^S3DFPZ-d-ToPZeWrhvK2v`NWPj`W49-0tzgs;+VZWfe|Aa7qj at O^S
znr95?FPPOYh>DH-4~Po*^mywD8w0Wa38MP%6ZsRO;^z7TqT&QHak4#OTx|bWNEIXS
z>7m#EKUmezM>l`juAi*z|9QKPRv)(85Jzjhqra7e!0E}p;T4yM at BoJuXZU8C4k;2t
zA<kCpKx2PK at O@p?9o2Ra6 at aTVWuvaqVb%D<345wr)KOWj_QxLGn{_U|$>unHG2w$_
zjjs^ywQ?=C1QI<-N3({+`CSj&`Vw-SY_Luo3_+)!;WYQY4_iYF<TVTkk$4VWT%SIN
zn^uIQ)~76FqvBecK-^0APw*VPO~@T=jxgYHO+dtjzJt3k`eZgV{4Mc}5Kx|kFBp!{
z&w~2KZ3$&V#X|NSwCh(*EAJA^lK%8`vr*Gm1?FjE0M?K^{0PWzb9Hhf-^k>*<F|
z36ds2Rby`F(&~a=r)NzfZJbnSK-KmO4tQ#TQ~n~!L6*$^mi!FKd~$}+VQm=<=A6-n
zIFwzV$ow#(jTRy!GCIo;uxjvN%?VENusk=qA=0CF!R<@~lEQNf$63c`b)eVDa!A(g
zy)UN{w(&a-!=-%V9hEHON5(mc2Htl?4DO^_(>4{P65Gu7Vj-6~`C*vd&W6GhOP+u_
zX|8&;Qt{*usf7p_eNk)hOt2b)i7Sea`e0*n<yVx59|EZqBndso5mn(uD2|<*hofA^
z+^IfLFWn^K$&+uFb;%TQIw;u at _^L8^#_2;`-HxGVP2pO+_4Oto4UWWH3D!m*w;hN&
zw{8?HRqxo_+tL3aQA_gWAPuagExME-)?&3a@<I9G#f0u5)yp$=HQGqk>lat845#PW
z3G*eHKq^Aq at d#lJr6lUaqc1uTnnz?~HSUP%cX?5h#{;&NLkY0gv&q)MlG#Ec$tW^V
zB>=0<-Eb at CDR3>xz4KMPM4{oum^21CZ$!`eg$ZeuY`BG`;=Uro at fXPeyu8Zm+h)EG
zU@(<u7rGPiS{(7+!!VyB`pr!oWjrbA176`XI`^^8&WzoHMWvsPx~+UvdK;WUcrK-h
z=wxunm*mcOcr{=j`qAGT%)a<8-G9?qn(%S|Dk%5bp`zUfx|FjfEulm}DX{l#EG<SQ
z1RvCUSlENXQV!`Gkvz69-kH}*hrzcf4Dej8PB}4QRYq!K)U{OoWQ7am$y?olU!*pN
zbuLczjV56kXt8&!8%OHW(j-WVudcGc3f*zf!7f(nUT>u0Z)k`~e^Rl~{4DRqv;Jc<
zQjND%J77EOZC-u|+~HpLBw1s90<3!<Ztf9adDmf+hp>ojX4x)zLpoVK=;T0r`XZP^
zSI_O^Dei at FRdb6c61ldFZ(!9sPwm0 at ui46`4#6Kfddk;KGB>>v2h9}5T~r<<%p)js
z>@ME$#$1|kySLNA))D1sF|}e)z9?SDZ1TWP6pI)2jOfn1TrV$3XWnaSRMnds8uDK)
zBQuHM_PZ&xH;Fe>YN;6=+)^1%Y^d?d;Y7F&^kCBGCut2pPYn<OoA{z_h}V;REd at 0b
zGHLiR%jmO-n+o}Hw(CNyGC>fLFDkU5b*Lk`TpV+qg4?;6QBCcIdy<$zl3>DT{O~av
zLky-HCclsLxL!yX2k=VL7xYx$4Xf$8-y5SK3xyY+6XPGfHFU*te=v0_)0Kx-SA7^`
zZu?xiYwW?8(2GGU2gWTo9mRb;0>_70+YgVa-Y&^bl^-5;%@<!>_z3j^jJW81?LY0|
z&rkCH(-i`NtpCFmzEhWbyU2mocBgS734uLq9|7)VB at c}N6=T=JN5hfsdQjJpRE_u;
z_hV%=%w)JlI+da`6>BhM?E~jq<61{x<dvx_by#{yYsFS>wYs;~y9udCZ|)+2#jom8
zVd5`t6oNNhY(zgVKuz#hL|e?>?3hxfTRm!PLAqITzEz$^zGvq-bF+<>auFw3G`aGx
ztW{K-)~ZJpvvFIrRbq#7TF>lh4p%Vspxpvnj3+d}6f;Y&FZi^&a9z6psb0pWnVHU#
z9+e<_+z&QbMe9A$3%kuJK<KjZUZ3MI3_U at er4Kn>Y0O)^!}xhGHfz})H-QXnGB-^`
zFywBq at iLU^<Cd{}*!KZnQQS1=S(72<h*h`HR`(S9!#qBw(Ge4t`Y8mX<RG1S^5n%Y
zy|aWybwY}z!8WMZ8c3?q@<zu`$LLmgVd_6PpYG#Q62yo`1%6BCJxcJ|nQcLDj38=<
z)bbe0j8J~IPDI=UQ34I9T`0yXF$n>$yef9z&_&d63M11i-7b{0EPn6b1 at oRgmJ~W!
zcg#pKjYkJlu8%o3y5UXm?C1#LSh(%`a3Y8_8CLt<ZZTdlKgO4L?h{(QWem643^@T}
zxMuAYk}Fc>Hm{*f!InZ_Vb*rS5r}~0a1ojqgl#k9*|GXHX-pMF+!I@|drGGaf7dCd
zFkvMpNKG~qi(?jBE2f#$#L9VzJz*?H?J1b!P5Y*m1m2EP^*bhWCY0uoNyN6QwBJI%
z`TWIR(_#%RT at b}$2w6Bl!1XmEC8C<YlzK*@kiU98DXvxp?mV`2A^6#AXxEVj5}81>
zl2_v~$c88YXVN!>NQTP1+-aGr*e>r{TpPY-hHRwlDj_X~IhMmxpohx`W{dwA$wVCU
z3Y7M!$=1Mz;Gm&^LKuouP^CGkYCX=7z|5oK+`d}Yx*+;MV7!=1r_)644Ey5>FW at b?
z>54=5ybO499G1dXtxAC5yZ#tq>)4WfhVM{rn7fkYw!vHH8%|rK62uGvOCyY}7z~YK
z8?raVsU7NNZrMph;pCISW&%Z;fJ)?7O%<*`>Wvt=QP at jvXMk-*M at iHCYvdtIZu)EN
zR_GSP>@ZG*OvmY_I%vU|Z3PloMLhyDG_*3g-m%0!iENJs5wc{Mx<mHk=HGL%?w9JN
z0=^#Ff?g2%lWIB;E>zC(Qjn|`93#fxygeIxKZ&}24WdK!gekSNbJfej-25{Ck+M4q
zD$M0!+z(BYb52r}fhE{!>KyAX@?wb5Rq0K05tmga4#|Y at +Yi|fTp1jQmnzGEugP9E
zqmqb0qNltz9?`al(^%@j3lWJQjayW22-0OFTdzW<F>-q#eQ at lRRRVH@PkS`*J1jbz
z!};Zpm)WiT_9h~!_P&8{!cj!yS~)@(s_Lh9WJ1-OmYIU-#w;VViEXhd!ORgH+^DNl
z7p;zvsUR^9kuAY>X3L}I%Npf|HEgyhyuUO~p#Ons04B0ykN!{Y>5s>+Pst{~p9BIq
z|5v1z_9yoQ1C&w1qQu!{EPREZN9U!vlQmbZQO@`>$qn+^KeewZT6-Tt$5#aAb8UWh
z;9ns>c*|O;ONr!n+Os8YE|2wPZ!*e at lQl<gvg4R_PxaxNfpuZ7F|*@U#zAXP&}<TR
zEwD;ZdqLF`Faz79s6G9*Tv4xrC-FeKVz#`*h3c(sM>Gj9w*Dfp!e7IAKywZ;iBGto
z&89C)I$fk^a$TDHFi%l`|7{JoJT}N4nj^I};2O2YlUI3EVhFVHT~e-c8%156H{7_z
zrUg<#9O%~KhUT47F~b|Cf116_L4Dfv at M)czSQ{Eu4<&bwwsA}kN+frinB>EOZw^84
zO|ps69LLz6H$RpC`HQFsp*aVXZh2336+QQntZj8%Cspx!31#MdK}l%kN#$l~DRVu5
zY_+HEVJls&#A);t6%MnPkea>~|J-t0$qx7}PGkrZ6BetoXt_Uj9vgFAovEW%<eVI+
zLQm}dUbkm|)I~!MQ6sVzi-NoWTO}neCc`#G9Hn(p2hnncPh_B<S4SKKMi0FGX
z?LOXDAj0xJ4uvpEM%}$hfAF5wLU-}|I_q2Jsk)$h!&7)DWR-{wok}u4@`}co7#Bl$
zXFNWMAHqwxh60dEp;cr#1w|@2a(geak3;^sk at j4X!UUWuz?zgG5xkZf+txocTsvO$
zYlb#J^Nr(sFiDD75#7(jV52ar)^Enk;K*z<>yL8FGj8z%-IS8eauJwe-i#q<8UVUI
z_gfn4&1~zNRfuyArCtfzA|wS>M=;q$(pKXH@}fv&_=eERunEe0hM=HrN*6&uI%);P
zHX;!$)|)qBBf~MAOwcz)nu!v__JB_NY31+ocXMKXd_fe$*kom{>SN4|AK)N*@iOiq
zS)YU+V^ctqY0=YUS-n+=s~`;yHY?oIpm|7$UR2|Jz$4!#^kY7Vn7pgdl?6j-Y&=g6
zHp%`aRZ~VGK`htYH4Ckh)Ab;fmd?N}igObkm>1l3p~W!ti4YuV_fnhOD=m0~rW60<
z(soE^8Q<5WG8&t2S#XaG4VJWCF864Bb{&hNue-+8%ZgLmNy~DfPV_dZ)6fbT)&5kw
z!UaQ(-v5f*Z^Rr`xfRo&A52(%;abqiuDv%xU6svx-8N#ysD#^}QBbrIl;`HCS;V at v
z3hns8p~CF*Ewk$(bZezO-awx9N17-XM!#`%{PpX0Xa>-$qB!y&kqM!8vdrKQIl at U1
z6{(W)5lE|p)rGgZ2t3?Xc&22ZP=h`7vYZ8GGJC7%nKq0&vo|DiDl*J-h{S8Xh3?G;
zfNKUe<XeZH!#s679dMnor`TO at L*k1S2|e~+WXE5`mnpe~kFXiSF8iJNzg=#nrB-h7
zN(L+IC15xpb8Ux8QZZQm0o`=kH;vST$r(4J7D&*O7LmIe?HPOx-Ju!|1 at RH$)&#A`
zvcbCFxB)9{<Hxv7D)0<fp1)4C+EYYouNlZMQ>QsmYUA-%!Z6V4h&B^%s|Jtubph=B
zB>lG(+y!z4PD^R;D{C(7M|c)6K^8lMe{yX9kTvpe>_p1sDOtnP#gkEz`9JdG<o+o?
z?q6AOPpNP$e`I=?7(OL+JpEaA)_G0 at 1}HzJBHF9jSw5$U0@!~h`u#iG;%Cm{e};hn
zoM8HA at Y>}Gyx!n?3IXXoD1dR|c~{`u<DF@@-xl*ws~M=R2IBjbF>a1`W^#JpB!GP!
zFk-A!ce-=l at O>4ACTmpxXmc`U7tclOV^(kf!T<1VDJ2(D!HnR2Z+`mwBSJ<MHq;8T
zb!&5cja35|C97*QVnHL}%^b8soYte+s~swBWHEmE$yO?7bXiI%QgIQr9ixl6SnQhP
z&~4C%db~xuWMm<&S9lur?n*eYh_B$?h)S<%vEHRL3t9D))YFNTr=Wy2>3pux323Xb
zhhmwlAn5B0(k+`8n~L_%ZB%1jLvekm=)id3XJ%7}gV&^`_&84nRp*O?$TwyFCbP*v
zg|-+of-h|&<ji@%bIm7h(bF!2$JwZ<W>_#q9vLETu~2+dbg`F^{89M?3b_<57nR8f
z&R#KoImkNYaK5qIFQ{8c@~9 at ss+gi(`C37Dk>s{s>(-LZoG$1yar5~%r`KN?5-h(^
zg5X7_=EUqrLXy&~&MmHOkb|%>QeNn7N at 3>`PGMb!DsxL;@O_{jzH-Tsx4ZKWwvvPx
z9;8FPmzv_K&|<lbt$lmMEFv6NxzP%DDd&|E{MMdOA{->mK{WX_gUhbVOz}Nf8S};&
z=A6Bw8Epv)7b!l!!>tjr!~VS7IVbGyiV4hPg<Q2qdOIu}GWILgTwHU@#IdU=3Z{~=
ztJxpm at yg#GPxE^y`DcHCjtSrBdub=84Y|vd-X<A;u*~Ie77(#X8ymt8^s5EG)L!7)
z8OznVs}|J1{l?@Nw94;oLSH7th*_?^K{h9K_}252;=m!|rKETbrHJ*Wn-A!GYnM*u
zkw_a76M2(0mD at Bl$r5I8Yn?yBXM<)%VU_ at c9GtB`LrZKCOu45NQN<h=tOu=-sSq;4
zt<1zH%Wb|5Eku2z*1%={fT=B2i67HlDY3Y55s4Rm+>IB~tk&0x<a6ny_m5?ADnSfL
zRE!g2oE38X=E<)p1H1|d_)_R{a_F{>h_T~)NQPs<2+O6xRx<)0+m#41rI6`cVZ at yM
zHy<b*aG=0~ZLbd{&+6ni6}?a0a-wqTUw;40RI~hDjf035d6VuCsinjRckY at k|6PLf
z at G$h~PU#nQwp}|xp-m1Pu|>5Dqm^630D5Kw at P>R#HIr9{VqHulji~@W@$8FMYqP11
zIqK*Orm1zGibUP=M8+E>E;D>r%8ewnp-$Ti1LjQFu&QfmRz!V6W*=ahk~_(*X3s^*
zqBA7w-b!bkEVlre0-=6$+Kd)5{-vB|ZbpqeKFmHi4qB;G4rmB%3UXs{>7Z6xrY)Kh
z+9mMNWa8xzj_ at Gf-Ve<ED4#hGJJ at rd7J(B!SH-WFX7c<mGG6nRM2O8CCh8=ArdWi$
z6la?K%Ge*Uc(1!7_NHB?mj8WiI`G3vrVx6*DYyPQb5JjElKv%@<#*sci^zmA+Yjl8
zu>RPsYl*=_?Mb-29HO0&hFy!7L(2MiqKHeal4wHf*O~iZ6`v7aQiW)6_(gUOno)pL
zl`BIdhFIfuO~e-*Q>D+IjnSpMKrrsWD)y;QOtt7)T$F4UQlOGXW0 at vxkT*1y6(6Ii
zXXw*h at tx~0F`svUL4w`7$9(!HFZ}mJGa%@HB|kg^ftws?tz#M&aZu_A#SLc=)#11&
z5K_^C4e5j?5gSTw49FCHkdKn>?xj-Fxn+l9OvG_HCAUg5E|-plxh%{zud&oRjv%n<
z@|{Xji87S)i<`gk!<Q`9ACMFksPGN*eh!I~z8GO;@8L={I||L%4&2nA>DR3S!0 at O#
zYU?=~hn1eQIX0e8jOR}COy?xg$@WqNBMH<*SDAh?AP(A76~Mz6uSSj6hXrkm1m{EQ
zBmp+%?k9#YUuJa1i{@mQu_{~3U$Tz$dleKM5jdxYLuv&#I1u`%F0A6In4 at 5R7e%Sb
z$RIIg3LwnI7mCjL3_{NpMQZX$l1h#ssS at g6GcrN{v at Qj~hHkeu)|Ts)syLx~Z`@%p
zfTEv0H)7y^8)0azN`dphP=}G$*}N5Q5fiG at bH|?)ZR}WFjEg+cK1L_)D_>|C3vMK?
zzVs$F7z%dzyuy)w|CU<s_I+W+mRcjF7J~UHo_1{6j}>1HHN3!!6KMuIo#4})7I2eT
zZ0O0m4Tt2<vLv6@;{1Z4G?|J#sB8onfmOMT^{8E2dm*@#akx;iu4T%39vaJWDcdOA
zuk))st>0@?nL+Yxr=9t1UR$u0QzO^eRqB{%ZWk44W7QO~NUN?%qQbJ`@5UUB_y8HU
zlrQ8TQ)c`JZxv2^ZpX6xOYW{pWTHBXKb(P>Yb%p$q at D5c2YnxTvxAT{h*~0e3eO}w
z4B)xs)S52k>LIpK91aAdHp at 2T>}{yEXW at B}HrQ38?n6Ur-|*e5l~1V(yx at 5YhzOZ_
zm~YCjxAQzfRA*m=+z>Zg&qrO6vdd&7AlcYpqKT2mt~z4ZNoQI!X%{FOw;0czm>Vsy
zxLYOJACv)vK;0M2P+ZGIQ93GAA5Mt-(Ntv;!BWt{xDY2yrpEZkh0QusT^XU*x#fha
z8n+c$*Uh|@COPtV8`YkEykwhJ4>MXBJ}!p`VyIRO<MQh=?&C#OG5kmvdL*MyQZ+tc
z15PQN!1H%6F*zv4`AQmfCWOvA6|GPBzWQWsG%ijN9G|$h5Oy8+u*63x1#bl=YG?wU
zMEj*IAtr3JqteM7zD945)6R8i$1#?Y$Hy=I<yd#^P2}Qo3Xg2UbQHk~q!=+yQ1}%@
z=*qAdwdD&j&)agPV!iMIs%xpGm2UF5M&vz|D|%5%Pp`(EBwdKQX7Y}6G<xVZ38s)6
zOm{O<JA3|C_QDSCoJwKg;iw!J+8-D}a1eg_oSBYciR*?Y$KqE^QN5s+l@}v8Q~E-d
z at 9e2qO<t#%+Js)=Y_u%05h+XdZTclkMy>qFFR398;#NY&y0;_T9x48GghJgSW@~$I
znL)-obLE4mS>eWf3XR75e$()5=(1@^#V&y15ks~oklDh=qgbMS8xb8T9#-_NP{^Tq
z3!vSZ%cy{>h4T|La)pQ9xgz~*@1t*Dvr+fH*p5S=4~%wD%L>YHr4i}NTxawWwKd8t
zh^=8X6}w%CJ>_aKM9Mb)le>D(JpPYJ4aCm*kFNsB16Y}bo?j5MdwNsjDJFEYG&WV1
z5dL!nmzLzIs^X5*?{7bt5t~@mV~ng5Pz4jqZetin1f>Kg1qY5HEI9`)BnpisCi=Q0
z5%k5 at MO;BFovFgO9OaAe{v_lj%l<A`EzazlbX+cpbb0fFsbiAkYwm0J<KyM8ZP)4N
zU|H1pC3Q0_h+r{avc{yN`*5(ajPl^YAQ1o%=wKkvawf)?q_C^;pJb}Em*DTCGW1|$
zE!)UshdNnK9wQiM_mIH^@wi}V-qks!y>zT7;oOzI!b|haPoxS}!aO*Ia;#_jXqC~n
z<Fn1Z(2EeG9#}(bo-Ulra%!2TF^iYTMPAJ1K2w}{$)(>npFdQjv~j<LRjM>(1unQp
z)gna8+&_<M-po%e6Lby%HsCVz^3^R0!8>G_seFo=X7M&{HM`EGE#hPvHq~fK+Jn+}
zFu^P$RF&`cu#@w&a}z8Hz>_=9)3^#2j6C9k#j?A)g#Ae-UWVLOe&|pvSQ(v4_}r8s
zbdP9nE3l)%hY;X_Q#o?mP(R5C7F`L|dA6<dVfYS=<+Pos{btnnrofYy^M#9Hd;8_(
zCBIevQorgr=WQlRhv0GRSij5Chcg4mAyCT4p at 5X9)T at dTj{T3?>9Q5QuLX$K^@(oI
zJw426hu?YS<GdZ{DU>Z&TRZ$hHx1)jhofTssDkNlI;X%UFx%K at TnSz|2V2E-26onf
z2(~o|rd*5sHE`vEF)<z%bi)31ZRLJx4F2|7WsQF-SG_znD#2vSz$v%g&Ts5O3j!hw
zf@&3<-T*Z%@Fg0AKN>{05L-CxAb at By0HO+OK!|%BjIRimF^kA0khqH22BOE{<qOD`
zE<Xpf=-{_q at Fx-u#6Iv9fjR~lvcX{nFuc%}05~!sq;NznA(UA9P3Z3;Ml=|aVanMI
zMPPJ+s<AjAVa9QUBG~<~&}^b{yWQ(?rClf&lx8SjXgML&tmCZrED%$M#5S-kUF}sb
z-UO<w5_-Uw1zE3BHDc<)qITt;@ZYfbK=7mL2UD+woiIS4;de_AqLY$|Q%LeChrc8B
z%ER6hhlx`%5pY7{j(R0T6(=!(_%7T at 2?;xA%>pAeYG?w#7W+^vY(jU}@Pf??I$hX&
zd=?6(D``~>j$9Hx3XV>gFFT4wa!%?d+#%E<hDFXpngZjyC^+zSvM2*U9qmIzTW@(U
zWba0=Om9oC7Dj}L*cq8v?4%SZK5$xWL3lypHrgeHP0mZcAdYC@$0n2$sg}$U05$$P
zR)A8U>MMCiP8ChXI~HjkF(J#m3#Csq{WQ3=Q|~lXIn)GWvbGiJiw>tr_x<+q_HFku
z at ng0{5%Wzar)#%rNf(YSGN%eGn0E=Asda^!a|rehc1b4RPIcr67_z*ui?$Fa?djC)
zpBv%oyGcD=iniW1reaq~u1kqYCgNzZxk{EYYz7%8%cRJC7TPA=7TYGr3EBD-Uu9CJ
z{sr!U<^aYzX~Z;YZqEj}<$YV2?;GDtk at ZH$b#4TC>K({Y+EHDmZr5$;MyDf$qZijm
z?x_-+ at E*!~(3N4=IxA52pVEwpEi%kk4ET+HM0}?pS!+4Xzo+vfc?3N2Jm!a_i$#Y_
z8pZVaMGfPOG1xNXGA1T1C&4mQYPHvPm#vr6m2+su=q!H}FXt;~Ek7u4(8~PiXVOyx
z-w?OxvPf9lWQn-wwO}^SvFK;u-R1UWbcJa`-n^mj=-o28ce`(`Z^%QyL*W%FVIxW$
zid7hR*cjoD$P{CsCN{_G^iO7lwnPbpF>hWHQWNTP!>2i=^Q570Ejt$QM5Pa>!KNwj
zj5TsK<~7weesgWwlXk>zNouik<2$(hEVr6;Tt8&HblBdh48UfCze2EUVO`2yvR;x{
zuJl6V>m^Pk*5PZvc+D5gJIp7+%k0&q*QIAwS+4iK6 at DdqMf+Ikm_WFhMO>45i9YRY
z?Jn=u<tF-$_saE@@nU(^`^$Bmeyo1t*W|B^=o0T3_R)wieRq!cn6F7rJMcT<B;hO&
zxiC&_vox|m4mzuu!Hq(eLZw0SFc0Kh6q+Qh0yje8GLljQ-lUwpypExb8uy+wq--3u
zj29c at xQ1PZp;f_#{;0U28llJV9UX#If=vcCUCVHVa9X`@!UrW8swvJM1_qyOP;1&^
znsZ`wB62r<;@XJUh|cJ$kN3 at G)J(r0)}1T{l@=8 at aab?qC)a+tUgve^jj)Xlc?W4l
z%5QSXQb^J^^1K<MlG0KOu`VK3)QK|gGUo9j@$V@!<+BS-3XuwZ682L{6!?;03MmV1
zWn0?DU9;WRJa{DE7ud^oB#;gi4c>@Q0a?kNDbAU-a>|m*YUY=|t$*X2S8z6RzIC$O
zYTm(jR at erfN1nOu@D0BmJ|4?_wb^1ZpK}iV1$sY-JS4BXwL3keCTauMUYl1JOLbb!
z#W%u0swduAru at A+<3nDu76U!MsxZwU^SsJgFoP}!-G?U=d`fgCe?P!EOhT7~4H*eL
z1ZUcDrl%#U<v;JM_S!0UU&vGSyTNzzI#Ir|AN6F#M&5ljLl;A+^!{Z-l{{Ib*3xVe
zb%eJ0S1xtFt|FI|>F+CKIJGd<Yl_XpGjqObA<i%r^BJwK38>&kAAL&vRNOq!9M`;a
z>3JE3KZm>!iWrj-V~u~zPRaIAfM=V;nQ7x_V`-nFjZ<SIK|ig%;8i`+R9<82<+*;Q
zdpLbHbS2<=<9gxl(?RCn_ffuObSvFmgQ{X_(ez9Haq~wypP8$fU8~Fe2zGGm`ZCYu
z^4%Xf{gvibJ7IdB$GFyF)?r_MG*;Lwi|RXkt-tKOT)3R&6t$h389TDy?`Q$qZmfaZ
zWO=XN$@P60MTSAK`zpI^v&75&?p6UT4_xO*a$^cbN^Ht~szvI|2jLHuX at qGJY1io{
z>600P85NmCnNgXyS(aII*%I0HIaE1GxiGnIx!ZZ_dHwlZ`NakJ1yKbLg?5FjMao6J
z#hk^(C4?n!OCd^KOZUoNm5rB+mN!-~R%BP=R7O>SRk>E}e>C{`rCO%CyN0W#qL#8Y
ztq!v;vL3wNv;L&PqG7dBt8t=9x~aSQWpiB%Q%hkhY3qkJ?6$Y<2<@-iA3MA{E;}7M
zzjc{+ZFK8*FZO8mO!q4Fj`qp)4SW*+)YC82-!Z^9&^pL7*fhj7)G*93T>qK<bKMC0
zNZlyMX#E)HSmQYNc=H7BMEj(`WY?7F)Te2w>ERjqnTaoIU*=|C&92Uw%<azG%%3i}
zE!-^zEI}_tE~70ctq`u{e5L(bxyrHHwkEpvd0l0Fal?4yVAFZ?e(Uu%@^;b=$xiVu
zXt#Awd~bYTXMgL!;o$CD=pp)H+7ZoB?XlqT=M&A7ty8Dd$Ft~jyz{~fwu|md#mki|
z+pD|l$Q!(yqVHVa`)@UFckbNpVegY4Xdjw?$oyD(w0(R|?z(<`euwZ6!RF7`)&3*c
zWa9v_{L`c1KOW6Iy`jp=#liLG7;{?PQxkI*rz7EYRm8VQG>17lnSc)i?ZX8$Xs at f_
ztl)uyv9WCM)#_5BuZjZTG|LdkhR76DtF)E6u$8I;25Jn-8A~IB#>x1#c+hQ1kK3*~
z8kX)xIGw+6n~xr+HGw at v%20b?sARklZ5k+i^Gv;5Jzy5wlMswW(hxQ1V6~GhdMpV&
ztCZTIyAUQ5RA3GY$vUrhxterd555GCZO875+I}UF9pgDI00kC at LG+%G-E7JqBR~*N
zFkcoa>f_#`!{H&A&|b2@!Hei at P!0$@z>z?lpx5EGl=%|Qa at TI=$7_N<$ETO-g)(8l
zRO(;IZL`FCAcRlSeqO<tlGwl_RJV#nBmu}&d4!kqRtSb~fwL@#knNmmwem0CgTQzt
z!DvdPDURzmQP@;Iw%~g-ut6)94N?xJnp6z=lm|#|NcX0qzp&;*5u!m{gUI=u21QK?
zZ2+Y-`Ys#|?ISV@={qPvf5{(^VX7qw@(^#1Igmvmz*mE~Iw6F=IVIVI`|ZBXy6}it
zJzpu|@Zy+!e_2hLs!OKc0 at cSgXd14f-ImvLg?P#{d5RpQFvH2q1B2}sg-L3#7XC&t
zN!XT*KO9ilOq1Ma4qKhOp~NE~1Q31D>=tG6XEg<?zNZ-|W%8*M6mQ!|*~S8~rC{zp
z+6``3jSG?n_Zas}<OVYQz~M*;fpKUOj-<>Rwft~9p-6I!ZMVX|a^bp*;O?&A-&_cb
z$8LnbIG;t*4!O^uUR4vQjz#Cw(2YYcC?grjF}<bkrtW6=cn2RI`FJyeP8)!G*w=n%
zmnI2y^dUDFXlcUgjJ}6-YSVP at X&MrH)ENffHZm+X>Lx!Bs`Ex*Bt1 at d{7WwOmE6H<
zZv|@@fjd6`Bd at 9y+6R|w*2_JeY|Ky8BAhrF1tVmMB4T+jGq(iB5$in0bEpyxeSsY%
zqwbee?@_$`5t$ZBylI$f9506;Ke|$vk;o3FE?3svA%yjVJ)l#ZXugWh3K=by^Q6;B
zOsu}t=CM|j>j~q%-As7%&X$Pz{r16qtz8~vuDg4?!dnvKy!vnD?-M%meSpj!WQ!(?
z>)sEaLrv;yJMUcrCo=T)RMp&UED~FCDLyz?)_*EG5wCU>U&)=1ny at RC-=w=_XQ4uc
zzB;zYZIqQKDKJ%qty7?9O;MEX6axA=!xM+CI!J?P(=L|=9%`dF3zq#=r?~DP;f#=K
zR7(!S0QEnRdnu1M67C9bwpkM%8&bFhTezX=())Jc6z#B50S4Jfr1<1mXHZu-I1v#M
z+d3 at 9;AS?Xn@$WjyrC3|lGzNwp%})kC8L?<sD0rp{aiTYkzgCcYrSv~_$n`6W at a+>
zv)0M7<Gr04E46-avc)sQJUpye<K4cI;YbX$Qp3xrcMn(R*RJ`vY$P?+uCenb?`W#+
z+NFNh-le1fZ~-x81Mu$F%uRfM=>H&Q*k(P3-9y at tRS`jC+}l+~hG9xrIk)FL(773x
zr2fX^{z9_JzEb;pLRv*ZvUT*hwBHWJE3v!LpkdLDMPG?DFWV8y+z(Ih5P*NQu6yr0
zNPv5wp6S)BqlrN8N|jq_N!fNwP`GwMH8RVY6eT)B1#RD8Tl+Po^vje{1`9{EXJ;{J
zJ(56KtQ4WrVImi+w5S7}a0cBRT{iOx_vhl4c^E<bl1zcl`da?ZX at H*gSbHyd7Xm9i
zxIz at qCPzj}%J`%g406pIpDYC?KF5H44Fuha8b!9I3HKVwFbHhONNhsAFBMD>b&y=z
zAt;__Si+=27(~s7{+XREQ2dx^BUUU=pp-*3(E~`q5a4tycLGw>>sF;eJK-|g=tzbz
z+h8+;(#pNVh3pGhfF84E7CUY%cqHqWS<{jGO21)AOOt2SYqB+?5+#1E at 1RJ{B;)!C
zT`0v)FSKN7g!xWa#7-IyWoDYDU;XOkl5dGXoPBE-yT!f7>e}L$D>nwbz^v#6C6(oI
ziY=}KZXe&G#jFS7^LIAi7c*<U3N;4$aEw$0q3 at y|jO`F;f=U6y6OQklTF#-IZ(wKN
zhJn2`LWtSH7aB_+mf5b!HGYlpE*I-%8M*JgEWbu)3`Wk;EB>*x97rkWp?wnRMxW5w
zk(k2W?tJH%?9-N4An<W)`0f`>nT)a-kL*e`XbYd+*|C+2hH1|?{Yi5=tR|(Gwn|&U
z!+-*ex$BTp1Zy>uLv5j%@}pQcgf9i08?jvFb#!jkEajnGom&<QHs8<ar0y*nkD;2z
zJ4LeQSlQdjM@%12y7`oaZz5x_Sg=I#c(80I<ZyU9^#nO*XTG|`f9G7!NTGp0uZHAp
z?|v|%SSIiSx3>tVkSK;l=E753wPA2AOCW;GI7|H%cjK6<ap#^)fwpK7R;lJ9mBG%z
zS)9)jWBDDjP)1$L6v4N!z3+a71;Z*_!X5ZANy1v>x at hqPUG>HKxnzegcnN;gGf2yC
zi-yr~nQU5z#YBllygrJcFPua)lW(v)pqt&SbZ>RGRNYP{E~7lMs1U<cbH8W#nuo3E
zLKrDcxhnye6RhY0(4}pS;q2Zfnb2=hJ1n!m`nH^V|LF73>mTUYnimQ!au(y=k7}XW
z))?@DS5}o+pG1g}pUi(&%j#=In%N!r^2dqThfQZndV7gCvJY*2U8H4`siPUd)kd}k
zRlX$YyNv4DV$BF#9f4foDFaUVx~6Nou<Tm%xMFrS8M_Y)FH&_4iPYweiw1bFQsupJ
z7nkz+i4}>C<r6b-m@~e;eWRlJ((${R;M7(ZfdfMRz|kZV%a;q^teBTS=pNwPneKO3
zzkWsW96Yl_$nw_><5W%=ImAyKocCJm97*8VY=74t*}K9>nZ@{J%PQ<LgPLBIuFR;;
z$h*w%8wL6;9WQ)CPtxM2yLc?JvEf8N$4BtxzS2Z2m!R2PqYh_(VMk91cn<8;k2PH>
z-)n@*e9+6$c`KBUd~WD3O^H!wZu?K?Ex(!OB}a?1-8hbBC4Vy}5C%!on)JSk`6zoY
ziLat`bRis7ZRQe3Kq(WKUlG1&(qy@&yh)~ww){c(2v2({Y4*6|>+$UOowS|r{A1i>
z&KYCZ4-bALQ)4s85Bdh+0bF2)T_++{oM1vUfw~qDj^DF-c~_N^P2o5-9Pj|tR^iM&
zUN at nz!oc^Cz7jaGIIfY5n2X=W3_b?qk*J40d>Lx5lT%v1 at 8gIF*ze|0XR2q(YD9%b
zR+{@NS{G0xbQ4ddOqLoxHbrp_7_C!*i2WkKEumDBIlVlrt>BW at +C!4o#<dteuAyX&
zk99Gg*-yq*ztb|PgSDwE8Om%dGyENE5rMqWz>AMY%22MM7LL=DPM2o)TeSvfsRb5Q
z{?!zfW;%Is!P{}$jqi2`QLet)jqOPg_{)0Yr;#GFr)&nLPp>t4N?i5~?@-pH(f4rD
zDEl!ftiC5?a&%F5j4vJExRO?SVN*)=pnbQ2$Nv3>(;_DUm3FUwySMA2cHMb-F1GFZ
zk}>|g;+xf(a(CuT!e|z`MryizMe9Q^R9`k7bsGmh9es5a%H+Eh_-pn^xD0ka#%;hO
zh8=5E at a6oEBi;bw)e51`ICIaFy75QFJ8d5^Ka%jcJ_b*Hzx7o`DzLDToev(C?;xZz
zHr4W7n#AmDc@~|&9(3vOAS>Tm2V#>@50ERymsYW8%n=u<=65sTP+~a{2%7PS1@`PH
zT7bbXx5Ka=Fh1<z{2u^0K*qoOXR at x@MMhrWcMYv*^eA6hTVCaj5Lo|U<oTvbBlBO5
z*)zEVhKYuZ)t0?gx|(fQ?qhB})4bNV@=#2-i at 3?F)g5=Qvku4Mth^Zq7!8Fdv=h6N
z*W1fn8NTbA6cO>bYt5Y6!IMW(?(`D^Wr``UW;8E>ugrId@~)u0-9ctjROrQf9D5m0
z0OM{3&1!nOLD at 2ND96NlGzfO4$g=jTT$%g8^u1ioxK0(PT(>%nq>%jF-AZ%xL|#pt
zL%!hBuIR$$1wzbq+h_S at BOuPzL=$ywF5Ir?M-A$X-|(nf!nV3~zYT+blDl^FMnzu*
zikR{C(|oyWN=NCQ01qt#!R++{%&@*<a#Wb;(qH_u(4rFOdx3_Ceq_LaYybcT2Kt5~
z`sqRNGl0q+Q4@$0{I at qk{+1gdzwxsFm`44aR%>)lQR9`CiNr>;m`I%hVa0EJsd at _M
z!SRZ|90xPc9j(^R=~1MYwA(9fN8RehO_vg;+$Ki7JNj;=)&P&*ZE4mZw;!SpkEh61
zPiobz7(`}B#N#JRjQEwa5_scq^&$_gTcliEG7r}udDeV0^2e<DkgF;q`hQIt9CLCz
zF!eidnefO1Q?b<&^T<;;(VJh3vkhIbhImKF&R7xE`@^=E5$Gt=%nkQjNmk;A+!BS&
zGByrwLVjwbeZp{|1*U0EY9!FZFYE@>qnz4CrT{%kBloE4no;jgcCQXl`&GAH|Bs<*
z7g~I!ar+wY`_HqHz6pha=1o)m$e}j$LK?+`n?rxZc>bvHG9o`;C?vj$!cR;IYQE`0
z7%$iD$hmxB{|@!i%7o1%Ag)kkC!*p6KwLKWtK1QAe)?bV^2-FnUbIvd6cw-l%yLX}
zHpCmfFX{ZkcI&V{yl1}h3JGG0dUZp>*o-}XoN!%{yNJ96@)RA&@R^s-Bhi33yNo_F
z(lh>0Qa<`^L0Ri;0j2rs;FG_Q)2`hGX at xl%>oSqgc(%%_werFz0p?4Z6jBgwtyIhM
z7NzYalVp_1(m{NISV7X+SZlWkrPPxt%<+eYlXgeOLd>spSGaMnBO7!sukFF|q?@RJ
zMB8nz#2h(hzB{<FXDWS6qD~dd*QHdvC}<5$2qZo(8&KR=X$#NOYS<zyJ+j+-wnMvt
zT}dpJnlqk|v*t`J!K5iZWM7YmD=x)rDO_|P^VbtR4RCtpz&k%8(*u9o>$88C?RKGd
z$nCvOU6{&XzT~@|t1RCfkI6lc&2A^kUU1y2k-}E53w)bzc82(@IjcGW<e`ZW7F7^X
zg&SA#-GJ{O at Rr1gFB~fZI at O%GaX|iK%v=ix#gNcZL*qn|^!@2CL4uMP?E4`II0{<*
zr<MdL4J@|eAk}`Hhys_QWT!DDs~SizunJYDWv6a%K_GU~a7i!(2+pzMTEgfsCX5C1
zhlRrkFem693Eelt*ifG at EEsyWfu4h59O%g%vW7w9nDadZm>x_A2EYJ;I#=zz*drq&
zKNCAADuf*n<{J_i^?BNe;=y6Q1kQ=MwrzcaYLlho`}kPT1CklBL>0?7l$KtpJ2n?B
zug%1eR2hZ at nPY$Ed5-Z8NBJ8m1xQO8m3a~K(v^ko?<zJ;R<3F&RyDCMa+t_QP9aQR
zG*Ffgl}<$;@#;^6+ik#BhX=*Kv*Y(Aw>|G1U<P at ZKdm*sSUl!eIeMWlBDx)Ys!NAA
z*OmTEDfm8qQ%qA#?{1&NCN+^+N8NMNW}~S8_TM$BT5 at zt#i^Wj{EIL~>$WD7j`fG?
ze$qKpiI=_9O3T<cnKxE~JD{1%3PlnMPi4IRHG<Wb)t7?F94n at u=?o5aO};zVct5^p
zHpb}{hxFoHsqeb84;o|DUv~(2h>ZXbVfX3eFdz?M1a%qAE4>T?AE+|VNl3K{?*3s+
zk-7-<usz`%f|EZdk{uQVE(dT6vX%ZYK$}1d_R|5i0nG&i at zYEu3sNY@mlYzCsH=Zi
z1k=xd9&eyNKwcQF<>XA*NVBk{ZKTmX2^LhU^(MNtmA1$gAe{d58wgqPXqFdy=W*w&
z4YSn8@`_oX>D&`gTr6mbj}0+6r00(oG$<p=5kLSmB!l33vfkD&^n9wvwx#=*QXl*J
z2(DVSTIN^uWJe(l;a>HZz4flHYir=>x{%gBeD}b<zUn5^iwfUd+wrH_h<D*w%5>t+
zl;@9q+}GI*v(aPSst!AO&u at kX%s?sVzDu-Tu^O&-6?cj8Ua)kPp|hEXdma3-jC1DJ
zZNFIDtM*MDZ-z}b<vcnww!tx$`(%xa-cuvI=~l&Jtgv&cWS~fzo6Pu|>nZ`S=}g1#
z_y6bI8?i4D#mJ2-i!4nLTPUgIMu^y##88P^5)7ebO0}V>@+9_A%ZznA5v0S7md4T%
zo-%_fYBwdc)Y75GXdg4U|Gc^F%+t<&pL6ba&iVfHzu)(N=l#yPRCmP4xhd;bHve5f
zS at DfW|BjCe*f_DNZ^X9#Uv<11<m<90BQmkow^QO=8;_Pf{qB>C=NFD_w60=J#)kNW
zgX?xx>^#`%+7{27nN#QAp6vbTL|%iag4XTR0;g{;Ea^Kv at ov5I$45;an7Lu>uCY(s
zf3>y}%W|vwh5TXLJV11NSqdAle{Z+rVc3uVdLzcE24XAa(6+^1Z2`09&+#i00;+cz
zQzlPA5?tn)z0j8`?VO>qKM7)tslIA2=GF%x+?wkZXa<-Y+-`Ea<Lk{a6XEtvFxPcM
zI{)dX4G6<ZY(OhKlr}hwO7zASyo~->fu1;pov4GEasn3INWoTwNeC46CYY<a2A45{
zYI6(M5s7qMlLowx2l;4>&gL%n=~!U)(kqZ389T5~CdnYg(N0q_63v;Hm`VgA(wsDx
zc(e|;q?IX08m(_o9}y_PGBm&h95>I%v=u*AN at cd(MJTdSY$aGlW+J*^H_l5stqjbx
zFV)zMNgTCVf~C?_ncMiYCDg<Myn_W~ybG1;MV)MKq&9}*C8}u-ufB=P(oovyBokqJ
zm^D0GibpD19o0_qh(<4r!Xj*9HZI^MzL%QPUe-w&aYi24m&h%hpHj0hhs?Kd><8E@
zZKRC~R>90xFj`{>uPs3d=l>9=C0(+lQV!`7JKopbG%`WvHrFr;nPj+u-+4FjSmNo!
z8FWkiy4AvZ-ERAQE+ZU+wK$D4<Z+F8{~k{yinyWPRt095*=lb2Yb6AoFaX2Z17@&S
z{E at 4A7@y)F@>JEJ=PB!mJ<EP*mNU2E=t1uN=sk${F6JujLIrVwQPvk1qv#}kWuT0g
zC9+Z~<g#2=4i&1Vs(ae2Pv|eL4z_I)d8fu`!Fk;nhFq?|+syWI#=8|qa7=<ETw)o~
z1&;qgbx|qAW_3zk)ARHa>zVzY at 4D|>-&0crC#$R{^DrITnT7A9F*&rB3G$kJP3BA0
zN4k!#r`@`}epwIJS$ct9q5rBsx29QT))l*#onx0db9|G1XH2?zm$?u)$SVRdNI*x{
z&p1}oMDoigreQYbq6mvwFUzqJWsLhEj^RA6;_qAmNGLhyarWP^>gLH}VvT$thvbMH
zlN<6wHB?I?Qgu+>RkBJ|<JCN3g*vS+s5`p3epMIfImCLsM_;yJS(dTe5@~j^UFtaD
zjC7_sM+bcN?Edqp=ULCM`QU5n`?YVS?~w1d8ER&de;8uf!)IeYS*~FXmJr)niF<Gq
zpW&jP>myNOv*MeGo7E7*RqHN2r5BMQ1BfBSFd0td$Y{wW3S^GFBZcy=ERy&ASk2f<
zWSi_EK9+sNd8v|b<eofIEMlcu&0#7+#i`DWG+Fgj{nS8WyqZGft7&QmSEy8dsP?K0
zx}gr!u{uXj)ob()eORB<|FV=7W5rort)bR<tI#@Sov|)iPwf!fW9QoI?Zb{{js(XL
z#{|b}$4<u`$1|tH+21+ZIoo;8X#&EiR)1mKyYcg1oa2<dW;e3lP*r>`n&^Ccz6 at c;
z9BQzhq!;Ti>~Zo?yX1-#={!BrY}TpjiJl at u)j?^gL+nI74hvzVOx;kA)om+C2CKUg
zX)TkF)fAnq9DZFrYXw<__8mZ7M5202D%BBPs0+;}NVL~WmAzh_fy=tC8ls9%^?bF8
zgFaVzsu-D8g8dYE%=<R`4d%O>S|CySoV6aev|IgC9?D9p#!2aIwNkIB&QeCjeJ(9<
zU#22o-ba$`m#b0%k*&H^`lwo5Ew8FA9R)b4L*<+fL{>FN30FbVUp-Vq^a00d_7|Z_
ze1Tb_C7!kO-=B}~gyXP6MNrc`RFkvP7EQ2<djHsWpjxr*OLj5qY?F at R`(->vsuM`$
zvvG^a#CvFqeXPL+h*zsI+su(M)cp+Vr$U8HK%CU1S_YF_0sBQ`)sk8`inBhU{vW66
zr^^HUR$P3tDiLW_Us-5*s7%?^-(q46M)GJamOFOaXVFiB0n6oE&no{CudwHQ&H0<6
zEBOt_CM$-lT~y|&9I at 7y#=e+Dh9^YfEpqP0=e)m_M&(^;CNRQ0_NYGWiN}y<Rw0>d
zH_#NCVvI7I%m|FfAhVVFJHzZk2h6v#)KELxN}$FblTX=?{wBrL?KE7W9)(F0+#`07
ze>ZzSimZ#&&hBQRIgcRbqa`ymnqBlJ-_9Q}Z)v&`?R<Szxk=Ue>`+x0U`kDh1R~c=
zqVgU<iPNU`%s~sggtd#L9zzBvb?^34*Di^jJ9X^PKA|07x#C`ojfswG9T^cG*2>*7
z)D_a=SIwI>eW6Kk<3<e|)UQ{!PVHJX18W309k!*FAjacP&31Xiv%OZhJ1s4?`q-Vr
z6FEOSk?nQyRO&B%ysm7&o9maJN%S1|f1XJ{dM5qPQ|h_8qHAo7%j0%=Po}tC6*7E4
zChbKj?kt!0zTeL9+e`g+ZQ7xs9N_Xa$xU&2CEMlkrq0MM@?@v*cI7n#likT<17l-Q
z9$1rRO<LYycYe79cN4#%f<1}l3IVlA%G=bP;_<%VPN`<#)nT5TG2Z?IGCe8HLPN7+
zW4x05s(Z8-?jGK{(S8>s`={|blD$s<G_JhrXi)4bkEtwLSWyq7v!iQ`agWIvk at +9i
zs|GfT>j=-j-P^kmjL$#(`3$*ZIa~h2{FI+F#ayuOVlWLcHs;QCnlqRPaGOx^6VXP&
zDB>qaXiM5g1QCQ#n<CtXwMkl=O30`}D6ME(RR$%YZAwC0+5{p5tss)O- at Ls&&Ic{D
zD&q6JnR)Zxym>Qk_Dr=S&U at rlVBdOk{5L+HaEHa>`E{{fnOdKkj7(VPXEvGK<u?yE
z#WJ-4{u_^D4O0Z8iDVQTzl;zzHv6%c*b$GJ$_{Mk=X3Cx?9;6=A}mR)_nQwHi;T6&
z^$A=|QPQO4TLXPXMd1PKLn?~+lPxhLU at r6-@s9f9-a<+)-+E$V*gtWkrmC{nn`ei$
zcT8?-Fgho5uqLgOBRQQ%jmy(<RoEkAG19^GulB=}n1M6R<3DTWQF8S>OvAsp0xPo<
z_p#1=s4kK8)<EuMd(##48vf)>!u=bcelk+hk&@^^?@i(%XGuEIpm)Y*MTJ>j&I#hK
z!;QknTFK9 at s@&2qOk;!B4}$Q~6Nqg`yrvq#4+Qvn_w<Kp6&SO-DQ5G2TIK7baCJr8
z6baT0I-0R7Skvuj(&h;RdHfdoHPKkpQ<DCl>m64RSzBW&<NkwsjjeBNHX57SV*W at n
zk&10&%LvZa&r9o4BgIxA;HDNtq!t at Uz2$8&7T~`t7&Rj65)CLd_-Gc?#Z;e&+auvq
z<x<Gs_Vmik#76U4T2Pny-`Vf>Ac3Vs at kdQ>qQU+f9~B5Zpi#f|8Cywm$1HUwv!-Gs
zKYs*2!oAT+6%K19qOqkdnH)7j7sX?hOhyfVG?_?t^jqDl48PY%4yZ9TmfR3YIM?5A
zo!sLyqc6vCh_y-$Qd`iXUPIa4)EicIH at C$GyhQ%pEwMhKh`Pk0c<)r`Vgr7>&r&K_
z%0g!R%%H!aD*A*+&i4(3iMpj$lLE=F?pH|4JdRAE)&0Vjc^#R6OtWQSDdT_qQq;A?
zGIKx{R=i4n01&wY!x38PrO!7Fmw2Tl^Pis7LrO7^gadqGtM#-4KQn{CSM_7mpoegZ
zuTv9rEx<I$_h~Ou5c8h_-we7>gouBdE&)CRDuHIe53B-Wz*69Opb2wL;9I<g19dO8
zyT3vmuCwHIwbB$|3C4!@5tVBjDS&YU^Vn~eI*H0Lo&sH&dlKfJwJx(B^QOpot+3lj
z-O$&9e;CMj?<F5-E>Hk at 5!N}%K7%%@r}?bbHH^2wkHr{&4jP58^`J{3Uxx7lAO|*I
z6d`Lh#(5YoKy30b9t}ibcZ+Q>KL<W`LSG18 at LcSh1M1_sShGyMp!k#n_@=r*y;=(u
zLO(_V=jJmzXJY@^r+<Se`<DsZKMD3#-0|%$h%Bs8XUV(To;vG6agH{qhpp=v8+su{
z814%+8Rz>12x*-((LKrfD|}q+dW&X*_W%<lc+G>fQ at uf9=qmJAkvE-GD`r43+qx~j
zNt5&-J%;ll-c$<T<D7fQg{hd=EbTjKvUZt at Fb*?3M1M}l39-T5H-gq7?pHlzeTp^e
z7+7ZjI1T&p*uR=%!1u1S4)<X073iJ=HX=tR0^_0IBXbxwY>#~&<vr|sAg|0pfO8L+
z=^%AK4jy(O<9|$I955c33-G?L0zU^H2foL1u<kg_pA0`<;GE&y<ow{=kok{%k~z!w
zxDm189J6cYhvI444HN>E_))Smg>uZ5^}_eaK65Sb+MHXQGY*Bm#Lj!=8lRJMDuWuX
zO1ZbJLC&oVTE at B0R1M2Bl!-x_!#Qim+M&{i2-hRmghL&FxISf#fT~?oz_H_dcc>GK
zG(Ai~=$5+PqsO!vv{F5T{BOtj3DCKS&mmcZ*R=1`$6^Nw_aIf`F1dDJ%cA=k?jdD8
z)*D1DOSE(JnxsQwiZ-OUTs_tmSC4qXhBMBj*-!>;9aBc8?!T4)FA(QlJ at hoje{&64
zmNrDM;H>2SyD}Z{I~4Lhpc^RnR4DsBT}r=u1$mJ-H-KlgFn+&=X|6U%3$?Mb?t+l7
zz}nSX7tM#AieG9ksVnG+-a}tdhj5SBOPr?{8N5E|hIAff=iJ at 7B&n0H_n=$@IS!RI
z6|(*y>nddZQPyP08n!8gxHs_oEc*k=w>i(r*K}UKP9^HijQq^X(~SI_4?AymUS`lS
zOjErj$Ej1;jN at Lz=a=tetE?ya&Oz7b%+IE2`(xBEj#(ebclaE&Idu)p0D{o}BK3~o
z9maig&)jRZ>(5#3>JqCR=lrR at 3-r2mLX=v)>7E*-nW?v|$myjVC)YVDPWRCuEla&;
zLGGv8QS at -Tj~2-O`4^htx-Q?gnbKdbL$1MUynChS<NvU3Yx(qy`Vx_fx at 0+Wq=|K!
zhsLUZ!kfE<HmQfK->LiL8x&E8DXvzahGC1CO>hY+cGXiOY)G!hb3j?f{_7g at liz{{
z at VG1IUFN&jZ&MCX>bi>es1<XL$+HIKJHDT$avY at XX7m-T<*uN7O;AN at PEgu>4c`kg
z9(V&YVv*|2wY;XjT;^=9w3%hy_T*ED;d+DSVE>@ptAT1fAu4gTT36+FMLs>Qo~7w(
z13iLqk<9yD=uu_pmksETfcg<IjNJ6vyzJGITDN4s-zNL{Xje6 at l;0Dq(`m9^M$;IY
z0bPQs)Hkrsv&gI47{6&*^84WTl*fBR9!-6p at OOfc_3~TTy+c)8W9)<MH|);=<lDRS
z2=_$y5yWs5*Px;x-o>dtm=9inemeu-Ny&<BsszM))GBo?^r1K<j*C-P7k>w;@6%K2
z4{(3SDWJBYf1g2boR1#91hM)J#nfM8JO%PYz!rS_ZK7N)mpau;m^Tx;4X`_hHI6{f
z*a at 49pl{Lw^_R3x9mMy+CH_XBfVLU*NuZwUlpj-<xJ6z19Q5S**6VV8#wP2_5=YQC
zFQsf#kiI&&m(N at AZFdiUrLUQOvXA%hkJsRJr5)yI8s!pM7lEKnhnvJ+>H!Xm_c8ZN
zv{gB3om37|RJjZsOyMWgAZahqgc_TzJP%CMX4B7r?HE^ro&t{BJRJZ&0Cr&Q)1W7G
z{8V6wMaXAR$PNMffnPbgOz>X!l4ru at vrdlS-$t(ilp8q1jgdOJ=XRQd{pM&3tdk7&
z3VH~jZ=*u at 7AjOrA)gGpEZ*hAJNGtCy`Mk#gYqs-moc;vevmUhD7bdHKmWhhdr;th
zw*gN|AFkp(mN`UYl=Id_&{pL<`u1kLr2zOU at C8mhoqK}(yHcKYf5;V5$@*uuJe$wH
z*Zc9}1U;1rClAv3 at Cwy3w1t=tWb>YPsFtChf$q%Ry!NB}pbg(B2iPa%VCmhwzKlv4
zVk&$sVw-<)U#+oG9anhf&fUAaEZ4g%h6o!oHtPi&FR at ulOki8zB__lV#imdmu9T%b
zg2Y2Mg*K{c$D}PSq at i{L4FnqOP~1kfT`zYrSspExswPU5fGbsPRjFWFs{W{|IuEF5
zZQO5W?=@x#a-61rs?~SCIp at r4=g!PIgEI$=>Q`{D0-~;LuOnUp^6Uc28N^~>)V~OK
zJ}_oo9}(-yj-~we_p!fA`uD;74D~WlhkW)jDFLqnM{OS6-`0I`&z at hI+V7*fxJ&+$
zR_E at SxO+BmRvy&n{}XU_-Ui+T-uPdSMG5vQ-~(iix|A%}79sasL-1T3eut2uOz>31
z1)RL00hE7(@<yNt<=0_53f>RKRWo!4_7TJp20o!?A%7=iKY%{&G^E-N-vXXV^)Y;b
zkbnQ)!k$$7p%;M1;X`INbOv}0_!&@+Hg-SXhrSE^OXw?7eGQ>Jd>!}>Pz4-G at zC>t
z at Vha_??gWG5%17Xl^^u}__%DnGa@$M_lB4VFa212&zr;k{^POzmiP#k>tM}a8#A8=
zy>aeg>>u}7AeRn^oKob`BA0a_e-`=UD2sR76OyM|{n+dkF;L{rB7b!(M9!Rg=M{~4
z4=%Uk#@M at +en?ILJ|NrT?Wl7bZ=K7CO<vroH}U^Y3CP$c;u?VYUXlLaj$9IT!0$rO
z0pGM^ntiw4cgKzs(>`r|qA~Il#MH2bSQ;K=x3Snlg+O{No+!qP-Jdv?KOjbrjnn_F
zZpV2W<K?CI<pJa9<MlUDJw5N3u%4Ee*m^qd^QZ4UJD<rSS))ihcN2wpTd2KvbANjd
z>F0R#yxDJrX2jGnz&%iam{kaz#C=f$<N$u)McAHo4Uw>`n}nhF0%u|SJ9xA3f!pXo
zTyD2UhM|80x=(#q)?H^|^LYC?ecy`wT;_*ZGkFiTiusdbU at 71SP6IpbZ!O-Q82hiv
zMMA_oSFIblsa^)sa=>wU5!nTFL-#`Wo+qnE`q=fv^5tP82`+kAvRP0VJ|`Sz*39tF
z*>$Ch%q1L#Yv%N9>A7kyTV at p=S)3{)=FJaZi%w!!$v*&vU1is>=(1I!;NkFKREL4G
zXK_<g!bv;(BZ&hFG1wmx`E$Z;m)JY-zs=qvn`N`NOnpii9lpt4Cw{`&S=MWLdJ`E_
z!X&yIA%&7Y$bMh|7z9*O&wfIhffk?#P>C0k1I0j%u&_?niFtOQy%(|=s0UgAm8 at nz
zg?+n_>@~KH%tqT6*kLja{9SfP at +oi@_$2JJz^_6V+-B*=!NPycvcC*{I{2u?M_|te
zzXV<I4=nww*w&)m9*f)AZgUp*MQ6dE3zPs1rNby4#?l>zMj$EsCfgxLIt>nkccvK2
z)nw)c<Tq+cJTxQRjwNlv(l=q{ny_+BM1`x-uG5&RGoLl0P9y3xqD~`LrG)Lqc)LZi
z0O<pA0fu$ojdd6HIAkBtFYVugYz5kd#-71C7GhQh*fz6(W0P%7JZgl?F0iMu)CPMx
zF(W73a#wRt5*rbmVX;@#-6FkP67I>ux+Rg7lVa4`9?f8z$kzb{SK}s<4}1nF2UNDn
z%rEBW*^^|aix?TaS!rg?YO_NvDX0EREKF*VNps?7^GO613VAF-OE<V1+<V>3=gxJP
zxD9u$+fmP&Sqo#F6|*u{!(z;l9O*NiOBRBR721-8t)6yI+|%dj_c-EOpVqGpXoH#~
zS1ZvBtybHhHE4UaR;^ugw`#3UWrL@|v)99Xo?K6f$MDp89NbCUqx;xK{7nMs0~&x<
zK*buzV9#Yw0Wo}y7?$EG*a;+o<^%ds9soO_c~Q at cdR|z)uzF!7kit_7YycW8uQuwj
z+lcyu!UN!0z%pRXz}gN#4vG>`2~CHlL(@^KUm3y-eUQ08Ex at F00Kl$+w0$L(e*>UN
z|Ddd6`wY=m88W^!x37 at K3u${HZ7rlmq%0aXW<&V>{#YOuj1|N>)cQbuu)d(aL#+wa
z1ZxUvI at Gd2S+K02tV1mh6bFk7iaS&u;6Yx%JJgoyp6W~0SE|)mb$xYnH7muZOPEC^
zVac<Df_u#wS>aM|bg9yVzY~LO1Fiv#aL8hy45$ZGr3aEL-LQ7U+D&SJ7~nvQZgIUp
za?2;|ZPF)7!mr%pXZYh?=8}aq(MnvoF`x}#=(`L4E?GNO>XG(1<bbr-SoPbbokQAf
z7%sRvdCk?~_|=gzAO<u54swO9CD#D-49S58pa)P{9e!)sTBRGmE~Sfw41K{gPNq*M
zg!repd{Lh=8GArSa24{Xln10NlQQ4PsMK#&>c6Pezg?-%ML{VbQ8*4unP+&S`kAO+
z6V(f&I(mDE<Plw&CZ#4M{hO3eN*OXT^Yq(!`UiRXdwKdRdHSwA{j+(Z-AtTaUCETv
zBP4xE%1SBc7#^;_!S%IVFXehvrza>zC(ERqC1tjd^!+p5axZaTpzo7%^hC`_Ax|nq
z5*;xkQP3STvH~=0MoxhKWkwG1i}W^?A&uTL^KbCzH2NW}P=)@1#qZGy(g_{}Zw3FD
zL}(Cv%8WcG>i-Dsk3m04X1hfFS4pjG*G4O({S~X-56#d<jC$M*eGQ`?BOy8J5i at iH
z)<b6K0O&<Cv;)*)2E_!onUQ%sI)!c}`HHB!i3AlflWJ?kC(!c_ at QPI9<z`5<Di;GJ
z>0 at SK0cft6<wY7GwQ at La2IQa2ApzONOcIcj&L%<08PqE$uanu5U1s1p^rD>!-r)a?
zTo8Yp+ at xOf1po6z{J%9&|3oXyPX6n2V!2FyC6uJWv;1=40{>NhlCCj%UnuE<<5DQ8
z&|ZETiyTK?g`VX-p{=|-AiW&{c=4&*BJ=t2KplTM2;Jn*g)WL|ke&F)Yv5iV`W&y0
zbn?f8NeZ_S!C=NDz9jHv{wU0gle8ky$rt1&#Z*erN9S2S4<pVA$f<mx^t`f&IO$i6
zkaLf7qjQaOrE{rsp>w`7*O}v->CANbT|QTaYqD#S%jMEss!MSZS7vf#z$hX(g_)X9
zkfsW$%Cb)p5<=Yi3Uw(sJMpQkN~v14jK=*{q-yoDcxh2p(mC?^_~N3fxU2Sy>rPYp
zz4g%I%KjuJtJh(J3e&#qxc{+r=Ln_6`wnIcZrpcp{d!sz at 7qMGHs;1}t-}A9w6ZSl
z2>gfjstsx?JHzMPo80it4J08U2_!cpECetlkbneo^9dNf1a~D=6hu^HDzG8$qPXa~
zYQ<XD?JQ+FJGIkwQFKQWu?o5sN4wqDnGT(H?T;O2yZxB{*p}IL%Gep#VBd2=U8nx(
zOmFV{k at G$|@0)X;bMCnwB~t8hDP0*g(#nAE1*&URo1a}u^EWT0HaZb(Ymc7ShN4#D
zJl2MS(b_iMruI2%h#K(w=BPnpLhW;SFE!+ECGB{xFZ2|KIBF1J$Vm)=%^;4z5Qk?3
zSc3oqnsR<$jN?{7w{blI0ex- at pdBlH<v|MQtA!YVQ8_42=qr!PAu!;>N at wZc>?{T0
zbfL5K6eM(}A)uJi2#_Kpfy5j}0K|+AA$;Lk*kD{KCxnbb9wQD3c`$y4d1(a$ELg=*
z34k&GKd)ZT{{Y4_-S at Zk_<IfhE`z at pq$_%S&rW6Zz*e0uw(UL%=%Tc#YirL=V!L~z
z_YJ+i=r)5-7whhMLAZy6yA8e=>hW)AkM;1qzRTTwx8Kn13(ZW8d4td8I`%Y|ckBh7
zjgij0B-d2%1>qnGPmx?fk}F7ZP4QDgu3)PN2V2@>3CI)jZd$Q3RB{rmhc2}~<WaDL
zHNxVk(ktIkUl$>GY$cmR(G-IxnhHWf#qMG^3Bl?lp)_btUkxeWsM4#i<8!Ma7Mi67
z4>BXAzt8vd4u`|TL?RJ0=;4S`Xd8w_t8WWN1M555qt2+)ALYAzAxx$tvU+*jd3N5p
z;G_neW6qP#)6PpyaU>Fg=8So6fupu?1Kb#Qk~_^^;usRx)P9wBp60&d=m at Y055wj6
z2{|KR;U-^)BVlqONDu~DNoS6jz3pxe^}uNt!^ylDWr7$$Y#?nQVsr=e??C<x at -Gk(
zIt2RrAn$|BkQTa_F7_+?d?aJYOg0 at QZJn`{TPtS4c5h#?Z5=Dt-?U<#ZmSaPWtT0<
zoel?YjIM*e3-TGrmmm*8#I%*R3Y|w*SQ`qXuo*-72)+yx6*doJGdP$G_i)&3Mnnj~
zVFmzd#?SK};cx`OP{4$M0g%udCO?Z1`wR$sum&NqhMbm>1U1Gee1p0P$5aV5e;J8I
zv((KiG)j^X=cgE{5*YD3#3 at AM0+fjR@n)nnvk#ncXA`^RY>YdR3w-tws8Wkws+Sr;
zVT}kq($Tjb at nZBf(ur=tpw~VACDSABhpbX~<t#;R?B@;epJ-~DnM#>vr;SrZ)0R2r
zYRVitXS`Z8XGwV{fu^WgI-i%pqXiz%n97wSy2h>!BxMAWW^w4Mh!!Dh05<&)pS?Cv
zSV+M+fX2!B6*Uj-6)h#0E#XV3l3D6=KGh?s$bBfUNK<(>bSTYGd}I8<BKx3o2n=$$
zSSNfI<BO~l%p$vpr5Tl#M2?o2_p?7QwJ5zK{GixfX0(`$M&49mDl$5atVwUmQYaNF
z1<j-wwMJX1U5ng#HCkM02Cu3ByiBQFi#Tzv7H21er%42_e2rC$T_#YD;<Z{-{Sqiy
zdWIJ37*L8-wHD<hfI?3<wr5jV_|!XyI&mk)2CTO#<TA#<nM~znc6%1b$YoiIEQQVL
zu$PzRo3K^!d|V;`o`l7NkMctF{YNh!efQj+=C_(#jt6Yb3Td7^*TPv#b at X)YiN^E8
z^~ai<kJVZX#U-4jB+p{ei~ApTir at d@)Ww?{KkVy^ZLE0x*715rPDWYdm79&W`0kDm
zcf at XVobB(s_)57waA&60=BU5?QP2hq2tNL!_-o)oDXK>GFn1;t{31xPLCmBjs*`n!
zG__9UW at -xu)bQ$K8}RH^Ga at SvlD8O{qKsI+C at rI`fVbsag5LbCk}geGL6^0+deAyt
z{h2N$CDokC)L7jGnv_(kkYQ$V1JBiHWHmJ!T2xe0Y*{10HccT at WUk3{CnlCoB5D%&
zHA}C*QV}rFiL=xgpUl=SC=_gRDXf at TY?(oR6Z11Tyw>1PkO~!0zqqDx<p#5|vQuaA
zAwE-;Yr>|3UFEp^MpbMecQBWNV_`9`_Bpqx22^9Jld99IOR9O*g6a>duT)7Y)jD{R
z9-7e;GMbx~IvW?4I?4Si8*lRW`u^I;E-jM#0W2=E##rE_YjH`i8CjJW<Y<~eO~TYC
zWMzL<YQdeb%!hy(uMT0ooM9-50;byT$YKZs@&yeva1b&$>^6Zq5=E9kg?ytOtjRza
zq{uW8G&t~UksP1d*cRO#`fjbiOJ+2l`F88{w$V?9KDd4HA8WWbw(fsz;?(Tecr-^*
zu>9uezK~~Qi2L<h+pAw68IQOkbe~b;TE0ELvn^P!K5-<pYxm31y(9lTxbtxJg^s|9
z9lL(K<xd~|_U)29wK%zIVpHAbk+r2Gan+Tneg3KL{-0RM;c>(AI%-t>J(P*o@`efN
zSt)fS<(QO8noN|UNt_9%%cR6}X<Q3~nFF#7o5?2Cxfpi}qX#2bY7rKT+>~#k%30(f
z0|Sd7hoVM*(EIj#*!p1KkDBz^>-H}X7#p^q!sDM}JAS;o(D(P{iI0DOY5eRDp at tHu
z;T55VN<ObZRG3gFrXjf$ipYf1Xkro+pkpiy&(O>PdHb3FE&_LEmcz=-kh4fqZntN^
zpu+|=*?aQ6<%NImJKd;P1xLl(3WM9<S$^%_^4(>;+vxur?*I5+bbN|baEfZ72cd#i
z^d)~nzTt5Cx9oFi^Vu_jYpn~}cLJYhe;P<|h^x{n(${8JnaUmZfUPw_re!(SDRav_
zGH;Q($X~6l_BW}U{9Dvp{3FV+ChQ%l`;PL6=CJoz-K26t^R9QY?!0nJ^MUt5{aw>t
zhi-knw^C$nC=1v{+2;H_Z5AuaO-e=icB?4KEXsA2jB at a%ag8@*lr at h^5SmQACojvr
zmp^I0=W^*9EDZ|{ziALP9B$jSR~Ql}8;{4A;;`>7EybOG0S0HSQdqE0Tw%_JI{(9T
zwa2z~mGSeP`||a at _VwGoPVCt6BM--qG>%heUUHH?Sdw<>TD0{s6E{pv2BVp?Zekl%
zlBuIqYMX#oI%sK9pcPw-`l79y2o*|!Rr{lrKtdCsM&O~s2$a!LN*kAR9e0o@=YBc9
zy4UCYzTe|SfPzLlw$iGpw$!6i>)S`tMw^%ZQ?AQ*r`oj7A2qx7tKCw0jsyP2x at wz)
zHL+H{t!ZU<tv}kmYi&!i at 72Dj*W1(4 at Hn;y6ICIvJtlLHHT8tt)wPL+hN at i0W2wH^
zJ<+Jw- at Bc)ty(!4qHkIjJ-RZmtUnyB3AKC7jw++0yUyFx*wY+IuXrU=-{!H$61!WL
z)g{(DOtEUU%_7?jYL())`{Py7#xbqCLuennh5Z?l&@^$!ah&n#d?Fi1KF-I-r4ckD
z at j2SrG*vu>1f~w@=~bffrVe3RA*7>~$UJpHl2DEV?#FbJW;tzS>MlY~2f8_4(yY|Y
z<>>3?d^mv#J-*z)oDTzjR^GV8nEahAb at E~s#TSaEVwQU1k__EhEM}J|6f*l;{pJ?4
z-|jbKXUPa3uY)@!c^~|9J^Z7jtS=d~^=vPlfVa_m$cZ+Px=nbK^QwapojLV}%7}mj
zmQ{2XbiqPoiEUTxHTH2kV=q7>J;Evj3RYCt;qRACI=gl;OWS0jXi39TOHdN$t*xoD
zTm@<$0ld@@+lTU^AV?vLt+~A~)jc#>I?+ at +xz1`3ZDM;%^XiudhR$f;ZUc<tjo3+@
zsFV0GH|}{ZHO7H<Og+so2%BIX<X{SB;0Ewi{FCR=II9UYRpYG2<@f>}%Svmd+pqz1
zi}$hfp!VdiKP<usx<avukZMGDi6J6sZxu;r>wpN;;wce}Z^}zF$d5)M+S-~Ep-28r
zR{|(bx?-`et}Ep+7AM+snTJK(L3z)h^+ZJ6`!z!;DmWUT2F%znreB--l!OXULu@`K
z!Q_}1nHgrD;h8!3A%34Nz{r`K+FLb~p?=bt+09jwO+<?jxT9oW2On|!|MN8WE`1j2
zeRzwx$PFVC3ZmJwTXnty at Mk&Bu3ZcUcL5Y)5#1<E!kC0};hFG!m<^k?Y2yGIL1SnV
z&CuAVhGxM_x3+ at j!jq<K{$FbsDso6O)fxx}g0(@+V_*RDLa4gRQ{}B<cxzY*Nnw}j
zR59;o&4Z|hcMpQiNGpd-t04FWp-M*wi^)ESR6QL&jY^H>zsAN1Yto{boYP^p;j%Qe
znVe-iOqn&mE4L8t-MeFR?(kO*eeG9+S0=u6byYf_+To2Qg6W3#m90-C at sV55zrJhw
z=cQABEnPV9;ZN_DZk;)>b at U{pZykCm;eUKX=`h9UHYFTScg%qf5*u;lTr;kD7mHlP
zg<nBmMcCM7h3BD*rsx@(fSGcwqji7+{BNYd^T<JG5PVFGpeR_xz==8;GiVm>(%XB8
z#b{KBxiz7TDO1Xf!YZnB76+kN+1yBGt%<TWQ%2XU5hjg3S$F`SL?RU>wT4>EHiyG$
z_qTT7R&4{c_1}hGzcur03FnqO^g_rT>Slj=^#1PA<z5^LVNdgG_;+tL`n)w-A2!i7
zo}g>=!Z3M5a7k&WtNQVzi_k&U2F2 at eGzb}?M|elziEj(LS+~Wx+4V);4)YGnVd*=@
zZ<$X at Cym#*YtHvvA2>g7&HL`L_ndY+cvzLIva1f&>2V37Q*ue3r1}|kk8{!|xKxat
zZdF!!1EXS&cWFW|SXmmqhKVQ=o7_1rf>>Z$h-~8AlPYLo3#+pXO`EXq9AMd7fPKV(
z_&@Yp2dpF3F)M2=fIzH-F3OF3#5e9^a=sZK_NjAlkMhg_gxCh~2p+?e_%gnZZ{ojW
z9ad{*VgLX7xtLk%=<HfbE0ad7g~e<>)47;G!{Zg7-FR_Qgv;V}5u<E=Q>0iixboow
zo0e|*(N1-rN<ZIZ%<MLCJFglkB_ThWrHE+<wg at o(R)m&PnGmRWAGJ0t_?J<aV@?b_
zn1`+4JF at +`>2OHBe(2cm6TQdpb->^Y&#ZO>SGpg9Zg~5hiQ_NlFa6 at zQ$s`FJzu)L
z+|(S^l#sIgX^Q7ESaS*KAI_hZ)1nryGjh61T%}(v_0_W1MQCVP-awMM<n`oy@~&P$
zN$3*C0<Xp14PFY)#;(O~25yFa8~aP`pF?twuAu;X&(+tPPyrXu-AF*9z$DKzoXG)>
z0!*LxkVrh~DZq+zCPPF091J5H67e62w2oqcr^*;mB+s3eK`y`)osW)3 at l<psilcP?
z{D3e<*IvLM5<N-6O!6m5oTR$fagkUrTd`Gb(FEt#rvR1FS<sA)4n=xqrP32w80}oh
zE?Cm>B at If&;@+ at cVfk8rz#sI7{4CFfjN!1Jic6f04uV&qm0zkG1icvJ6N6A=@Mvmd
z%2ZBfjT7{z%&}3Fk3_7gvgS~n9A&Wl6_cWArK~>XtWC)m(0HgJD-5 at v+4a3=x at UKe
zzxa*PM|(aW_p5I6mz|-;Z3hGHn#ebw^z~0aF_Ako%=Yd%@P+=(ZyjmA at cQYAAFQnN
zH0iibUOMtZf8TOXeV14Nxn2E3W5+cO_ffW9qUh?80sV&5I}D(pRR*FkM1e*b>;e^B
zU__1wR+bEil?^N}8z`%)iAB)a1VN``Sb>*ysKx+>IXH}XB*8Q>aKMW?p4V}#EVFaa
zL;0eEZA219g at I}26oZ)p+$ApPEYnMY9M!9Ng;97SfS?-xk3;!P8KDd%5H0^?TBHDV
zrsFhuXlI?p(Tq85E)!;VEW%cfJBp%EMH{7QBtHuFfH^=j3AE4)FqbYITfBl_-hQkU
zggg67--d1D%o`8(;-iZLntlz^et(r)gZ$wAFV*EA6vcVQ@%P;ycei)9?A{&s`)==+
zWAD5JIq*2F$i$UoG!|46t3V48u at M9_3Puo=dLdX8w9vG%$uPB-Mr+i`6vQG at YsMM1
z#+Z(6r&XJ2hcHYdZOLD>6HUNAyLVB;&Aab=@7>(IJkR(2Jl{ut_5_8^)xvh+0DVAn
zz<Y!`>ea at HxVVwr=&ux;J#BP{r<dq+^?BYU-(!-2$pD7}nBz at mi`8b=WkTg8l~9 at d
zB9vUpFS^_wQtO~-*!{(*-!EDwfFusmB125^6e6ZX5nR~>eg?Vm>fa=^Nkzx_zo6p)
zmIHMGA^;cU!5B{@MJy^mA~SxOPx1us%$mS&;u$q!)3tzkzE+8jD(&gV-JvRIpuR$x
zUR_=e4dFt{n-pkdwiUM$t^RJ at O?3M;KpX`$z?o-js$@emwbT54VHds2OV?I|$<m5i
zO7+v4r#x0#yo!^?WXHON#%jzA9jxqmVRuL8&b)vt6fIl3XXJSQE0-`um!BC69X~YQ
zJeCM8-LS-y!;6I at 2loA?c(In?6m?cXE=E9|4zvh;B}eu!ZTj|%ZswjeBWW}w?jk#>
zPTN5{Riay at p~--AkyAtZH-nW0c8vLhI2h!>oE;r?AX+geqa2GNxU!NX*=%s3h#Vp0
zNL?funT${oXDaUS5wh?Wza`%yTLvvji`L={Kd~rZf#IA^dlVHF0Cb3I?QAQA5kHCL
zd4tBSA=D58KXN at 1WA=Ik51}zhY*1nXKnUJlkHidEW$T0W*kktBqbwsl(AOSCfrg3<
zY|S9GUvpjYD5gSlc2Nv#teKA^4)7<B^_+UQIeYNO{kIzS-|F9T`3=l{(L8_4yn0n^
zP37T3UBODaQDWEs^7n_gP7a?ra;9b!d<e3sv*a^=_#9v^09+t+NmPa0|*GO_L|#
z78uZ#i->wOIcjk~Lyte2lyhL=)M1*8Fr`Rl>)0fF6MsnD#dnEG7D9>*7_l--5;O&l
z at +Y#3Br-{oph%XMSI6lotbs{0g+b=y`0cTz0UMk~dV;u*Na8cuh$x<tsR}Ad(bOg4
zpU9YwJFm!C#r2$0L38-ooRmN9&FH$C7BBNE{i>#i?$J<b;X;LMg*bu(2m305jnjty
zK-^g<*@fR+=x at pYPRvV}2OnOhuDSE-3;;s3AN1A-dUK*6is4Rqq8ei+=8Hu_J7b+$
z2}8n|a3$Q`QX<$FJMB2>Iwg%7&$})Je;c}{zh=0 at +O$ZIH7wy`LN?a!l2|FD3?IR-
zvj;Oyqm1RK7?+_kyk^m6{Jl_3tO+&Y?L<ScDbyI-hxdota`(pur~x{mP3XE!-R6PJ
z0oz;DG2Ks1$IL^vUj^R}y&oH=#&k1=e;H>oW<oPd!djLI6{BVNTTAHYbjaumQR;)Y
zt6p3~FIFZC>s`qJ*BMa0GV)=~1B-bSm*a#SuZt(+lW{5 at xC9@Na0eoA2lV-N**<6|
z?asmp{COHIianoGp)xx?mvZQe;IYzmfykHSM1?GqjnY{qkq&?z*Lv%5Zf2w&<(Z)(
zW>HWg1;vi%*cR8LsA+LZ>S?Vj^-+OMi;}hAla at _uv!{BWkg_KgrM0do-=a~bbhX5X
z->JQI`oxVLXNQ-S|83;Tj!hl7V6WV^ZCfH<P_&`q=qo$AgR6<NJ;_ZyADn9~Ki+(3
z-L{s2k2<#O{_e<McXX}a-rlypura#u*{YLuuMKrpKEJF981w?%g!e!bb`-)!Ikqo!
zkG_+2FGMv`9dwtjlW8~ZWjieG{(jx-7CoaI2ouY7bjTrw95g9N6w=ZYcq?*X`FyAX
zDjSeVW}?zg37j($l=NiKz^EhV?RLaE6au+0cLAAsv)@db$MMTRqp%!Kgh at GE7fyyJ
z!&De63=`pNvi<`-p?5C&hM$>D8JhVN^;e|f#Lq#hRPL+RQq5Pyovkw&gS_Mk27*3T
ztVdo>X`6Jg@*4!`olNk^Vx*^$q$EnM3HEqVk$Gu~{7chjh5$NYB|)hSsc72KJ^9D*
z&kqdT+P3fdsrEPias8dkM9f^;v9|h$)hjpW{lFs;dvNxzU;g`rbN#3L&VD(y(D7Om
zaj|>dmQVL4kN>%SQ!Zp-_|Y^TB!_`Vc2qh-I>)gjd)bDfL1z-0KN)F_0CY~ak}^{`
zXiZuP>m at 8f-S`tmDSFJQ-dk-cacvG>l9H67D9cl8Eat6TnFk{`y0TQ6$zk;^jCo6G
zYE`OKrXMYw#pC1%oF{;`%5G7*VrnS5&RxwSjI7&Y<r(9c!>F7qbDUf!`vlox087RA
z7=kHs^TnL#6B8mS-f%m!n>y9Rt(}MV04yk4F%PGV^3!Ub)KuY?ZycD67pa0*8q>#h
z7e2+V^x<D>XkoNkgJjMuC at w21Z5SX&<kZ>ahYxeJ1}ZEplgSjv3Rdmj+%N+Fjf6j;
zYRKI{KrPxoqS20%p)q<rwV5KQ%Varxfh0pv6wsrDyu27%jRYbp;E_bP(IldgMnlsC
z!DD<I<9saRBqkB&u^*GThS5?KNunBE)k{hqN>tbOzgn<m>JqJ}ZOJK_F9CZuUs8|{
zs8(MRwe-sml|k-)aqdMp0FMwoOgAGB@)3>^Lgl>~`vgMpBB-j!>%&l6GcS`<$PPmb
zLr+f0b{*jnNKWUmd691yPGACK9Y^R;bcJ#)KEC~NG^*SQes=cr|KMoqkMHWedX<M!
zknfh=x(tqEdA-2+DnyNyv+ypH%kB0!yc!YOpmS1OnMU)g3e`C$Po2+&Q``PvYTG5G
zw(aWnIjcI!KjSThEN6fpU&ei#Tg9&t){E6#CBHGVN@(I5_(q|PPf)!XeOxc!Yd$O-
z@(poA{M)7>;UagD|DEfi at DX>Dzu~<pe8S!3|IOX!?+XvOFZc)E2SP5xmAMHYR8I&J
z@(O~7$<VtQo5$|9=?JY`XEkNIt^X&xYG9i<&+vVB_TAZu?K{W6clMpncV}NX$98gd
z0$!F}C?HBllddF#2n%SSOara4L;34i!qgH#s)CA!fT^q%L!*_YcB7#NHnvt%B|2#n
zf-zt}Fm0qT$i%jcN-(?oz6-QW_0RkH-jglsectDNo_DL at VPcFBiNsmP#}2cAWjt<o
z6+dUPxZuNaL5v>5u-U>@!KB$pGakYp2+*{Hj#R;Y)8T>N!H>C3wu)y@^a?<z;&)7U
z%yjp;@45;1k7FD5TKo%hu)jkbvZT{!Fu^B`Gti{gI>+6!PISEJPKWeyxZ&y{jF|?^
zFTWhenH|6Cq&nftvZnf at 2Pi{NDp8g!_@#V%M|%e_Kw$O)(WL<=-m3k3W&A78)%*Ly
zMi=;#3@*<rK3lu>%slPeQ+L2emwJ<8hEi2eD7)Xb^1;E~&)8MfmX=bhfD2b at e?$%;
zj$yVb=!*nQ$L8WY%<LdGDD1&@3ww-1-0R5-S4rOD{+|3K(>NE~p4?^}ZaZWgQ{FZ%
zbC;8sHK(mm#XmXWS>IkjT at i`r&}shFAIur1l!75F<l0PGgMp9c7AOnUJ=_&=Nx5p=
zP$`=NR972A_(`7g2>~Ub`LpS^h02oLH^B1nVDbRYG8j|n2ZPG$!f;`%@J4~=vRqpq
zMlcko2wFH}BXL3q3cbc|<*;(apkf8H&{r73hltho)#Pewb#^m3!j165!X{-Txm|mi
z+{Nz_CXBJd$C;~{+sb_<yn^<KyhDmJA|H at sL%|4J3d`%FLd2WrrVJt-*YbHspjitB
z19)0Pow5%)an$yOymib+$JmMPo*cS7 at x&tQG<`7twN(+|6tWRK(r?qnxv5rkgISpO
zn$QvA7!22KgbgK~E;fhRK+Fc9%{*_aDYB^v?@u*)JSe(cFvejodl<am6GNAtBl8MB
z1LrXbTLVH+6ZEHNbiH%wH0+%koI%CX6U$FwtwhT$-a4jN=uBrP>d^AE)nMgWIfZW)
zEm3f2puxe;T7F%(1hprVleLflD2@;FI7ucHDDy at _3ndJYp;`?f3kieBfmVY^^34Xw
z+S3N6M&kx1v=Mm&I$fqy?|e*Z(E<SFwpaq?av3X^KDSF46ksrGj7V9^8*Lr!c@}Ac
zoR^>yqr4hG>O1R}DE8r!fOMH6e){U7HDlLq)W(c{H5g4UHSi at r8ai;~JGGb8Rr6lk
z|MZ8a*Y<5HO`aY2aH4BDkH06(U-`<qQ~heYTqZVtS4ycN<^3(szv-c<?(Z$#ay;<h
z1^(#P-q)7d>==}ZC7=Dp?t!yf0k}EeA!I-X&kz~$faj2K)N|B(((}HzffnGuhYQ?G
z{;h#miM at f>i341PI72uZ3AYW8E+$qG_6*Ii3J=!=`y|c-a2l%;Po0b%wrddrRPk$*
zto|+om at 3gTIpO-D3wKqCjG6H{@CpV1Xk#kx06;*$zpy}LyIGv&OxS>q&R7U|LShIH
zS?v*eQe8V_DX(5EFP(-P-Mw-N+S?LR;L=QSW~zJo&I}wm$nl=D;EnltvXN4`L_;E=
zlDwk at YxKiFhwUxE8FaNEmjoX7NM*RWHOW at dalg-tFz5)9HaUi at tXDyv2_e^EJ1>e|
zQ*Vx6-LZN4;L9Ix6Q8f&JyU<@)ZUYz`{(;7=6HD@*I?gJH-39^PyOPxYW-jPO2>VZ
z$M2v1 at 2|nKGm8UFd=|M98LH$qIGqB}9TMh>2EHM>i}^nDJI215+3Xu<4mKU~pX1L(
zFEVt9_4=X$LHWTrw_Ct9niP2q?nol<lH{Ni7B#oqg@?630HY(Fy<RYDNwZ$lYxh<^
z`}0XuL+_I^;$l~K-jqR12E+0j@^zVzrJ%*epvA_ZRqh}(D~$|fBWWc`4kgJW at ih-I
zKwPX at 5xUp1e$N8s3(C!VM6n1bc>Ij&O9-CG0O0&EL|JhF at J-=?N9EtS6GMzCmW=-=
zM$G2*GZZN$Vbw7>D<LmrV?YT+5Cb%5UB3E3<%9Z*e;6IO3EJwvxjQ(bc1R<{#?e?x
z-CI9<p?>4+#pfbm5eR}XSP(_MJ_qW-d$8gLXg9mf{QAh2$Pcn_g(}%I+3R_FAUsSC
zQ={~#V~iZ5Cg=%=LlOC?6jw!Fmt@*R1)(LkTXgU=g-R)*3?<>XNb*#K;c*~C{}DB?
zV|p5EVOsE(Dt-Y<MM{Sabu7x?j6|Zeqe9bUrJEY1aExN8UW$OfHD&f$zqL7CN$Fxs
z27YxTSBXJKbB!nXXZ!NQaP1 at V7-K=jSg0 at -R7_k|ET|MKsaQ}cM{?IsfpN=BBbY3#
zAZLoxGsVBvAY8 at iPR0W44pek_)GcX;qY at fBWM!G at I~emH9Xvb{oN$B{0V^Q}7_6K~
zLZV2l4~{Lw6PP1r2iq3_A}+yCpaSM>O6Evab-TS^@2_8Cw7F9w-^_OPXfHmvoz3d8
zAg3(L+WelR-)Pg;+40&<IlZZ_4Mk+F-ZPjC#xh+y>J>G}m_tNqr=Y3z-*4#idk~@$
zL{x-`wtzW5(K1yam>ufcc8AU3d^bZJ)KBXl=~svg`Yqcn=L6dVr(@VYOpZc~$LwR|
z1jLx8oQ^p-B{eoyLBe#=JQWrBpcE$|s3_BH=SjDvor1_GBw0^sPP)+s4K$Ep`GYN(
zoWL|j!!?AonoK6}K#)%AS_NwcSaTLGgTrtK+((ik1$rs)3u~#IG}D;dLeg!4=C+V@
z$AzecWYkJVEhM8y(qAIojOEAg6-(CE2+{jT at pJI3wb9xd^@r%y9-R<EIN_jr0v16A
zK}_Sa%)-474mQL8m at YlS58ekyKke^zsVYb=T=1#O8B1kbYp1iz5+RpUgiS&G(<O5Y
z*L at 3ucKfLp>iOO!YJFh66!wNxwKcY#*f{&SereST4dJyIs?FO_ZE~P!F8d$hRRi0^
zb;sX(XFKPw^Z6sbce#-Bont$R6Z>#3rb!%fAq3O0m29QZtj!>5#^?$n$@ogq)<|o|
zU~EHLp-Q!8XlpA~qg%>XUDkoOwUtmo%cPC26BC(4Hwr~Vl(k)llfCyG8mLxH_T9a^
zdybUf|NsAexNOT(NKRDMArXr*ie#<qy6g^q2oK3S8usCR at +6*=-A(Cj?p<yUXH!+Z
z*`e%S+l#j8tj)uH$6d^F%Xv~4{JtKbRg0QTe~vKyfk%h~Tw!%De7aG>Sa;MlazdT%
zgh{o=Ly at mxAlEo3$Thm(-y7H$K)%4r0HWOaSYS3_3)pBi0)#*>9%TetKp$CdSFSe>
zeQ`C8h!MJGiS*CJMfyH|cgx_OxmpoIBy*NdvQn|CU1WilR!O|2RC6q8Mv at gGAFE5$
zCmIr*!$XXSPxH6Kh${LklCCD;jgpfX5!!(>;b?;Hn9u~u)`B{7YCOskmSi(Ztp^n{
zQpE_h*kvX2Q#;Y3Rni<<=_fvf$r>44nnxwJ#r7^K`>N5CGo at X_2i_lBvA0X@`V#WW
z_bdo~V|w?@_A}$F*B{>h=I2H?-xUlh9Fe$H2fw)W?2G^W?M!ihJO;nJzEg|G&Df at y
zfyHe%fBoT+XMgwb8eCr)Z6=kcYWED8)E>CKoCsgeTNE at 9E6jdyj8-IO7G`f+0lG9<
z at 0+YdW{_;Kg7k3^>a2D;n-{$>d&y!0qqB{ARhLMT<N|Uuk|Rxy6!4I<lN_Do$dQcc
z?f|v5sScE?b|KK2X36p_36$@>$4)R!C8>BeNw0rR78e>x#Tp*UAIwkXr}MU8exlmS
z_L3zc9p~0`T~(?z-Bgv7ruV4IVogV?;?kl)RZ+Ak5soG;+M+5QT9hCVt*uoScXYVj
zE|h9&s;*Y}Ku|}P4ln31q8s{<eo&v%r*(&3KoP4-%&*HA^IRkk^F1-WrPo|%B6DKd
zz<Heff_P(ukU>ry9${oq%FU-V at +ninEmumSSAP=Hz%@VHtPMyXe0un`&!&U9pTOtP
zHo^l*!$3<Jc at rXNG>p=##=UX5w1>S~So+l*MUXeBhk!_o+*|PR4P|1G#IC`c`{!8#
zJ~Q*sZJhDQZQCekG!wJ5gLIAxeq(Lcm<nsGbu_E4Mb-xVgIQixkfvi*323^kDo~3$
zRmHDG1Aao(JeCpd8kwhEBHQRsW!*W%kMYwyH_JnV at 8#F=+*-cKPw^aYquY4afqY^1
zqoee<WHDncU<P0yGNg at Z(;8=Jz1ljBD{503qD8rfRD#ho8T#RoIj&{|U9JK>$36qs
zTa`dNJ_Q{Cl|dT2gHP%D=96sAe9i3NI6pxs3f{HadjfE6aBT<;N<-2Uo}c at QF?#?*
zBWA@=6<-Ew0fQ)vtHrR4ATmN$2=#_wsKB|7$#q`mg6hKT$L#+miyKEN9&}-86q9P*
z=`?tY{~`Yq{+GP{6n}xAC8(h}m at Zo(22+^fR~5TJT>o@!tbi6C(WZY&libCAW=i^p
ziF=&G=<3z};m#b3XP<aSu}X1ysyx;0(Ct#(ayuY8mSa3OLgLhLa2`2AW;)}tTTFHf
z-7^e8-2Lp9uWXdH#z=EQs#VeqPkVxaSbBK;<oEk?3uU$8f$Wl1+{7oC>V&WUOfc<6
zlU8A;a8f`U0elQ>MLUIU#%Rl<*;B3)UVamR0b5Usv|tw9hrWu&&~9rV8n=#kj|wN6
zPjvsu`&Pyqa6?W&4rI at K5A4cJfJ5-0@P`cVCdmgNyGM1_cpE?rrk$P6mCn834fAbq
z-Sj!#vfF?ysM%U#_4Ygue}<m3j-%tQ$t7pOc`yb4h|Y0Wz!i86{>ydEbKQ%>&0*8b
z80IQC4xaQL$m}<rFF7dFT3Xtt*W_w?mR5pF0|^G;5;!a?*n#=_xRPj3AW9#l*V)M*
zkXnaXA=0IsVzoNF6^etZtfr?`rB2gxT^&l!ZnG(NA5&dbRT5gX-E33ZAppAI4UsU?
z1wa9vwpJNt$S_O*y=Ira#{gZXt<4LO$L(@fR0u=DDFF%b3R^`*I4mE+x%T$Dx<p4?
zTYY`}Py$Pm!(mS#JD>ZJO)!kKZOjgb?9g67cUd0G+v`PRUO4H6-U9l!)tL4%N9AK$
z;A0Bm(}~#AZk!W)Hq$i|OM700Ibt0LpbTh3-e#?csTwJf&ZI0V62<Nm%ga(uod1+>
z2txg7f))8fa_kaP$#06Ukqf3*3`C;wMWWF3a>q>{A!HJF!0<@-{gyM`+|-p`($sD1
z at 9R%?KX||8%HpuM(-jG|W(u>@$Hi7l6j~wCy2C;%AWsgnyW(My-W6XWmom*0H_%68
zEb(|S9F_>tkU=ox3HX00-Je|q7SX-znSd%B6=)FN*!hb!rJZff!IqguHZrNw$=j%D
zNh;l_;?P!DzgTI=sPKB@@(rI4A4At>d^`Jy*-u~`H{tJRR@~tRbzEk4Yj9>DxDkpE
z)YV8);x5CT%R;Y^sjDY`{$(<C5in^fA`oE&M4+XwL0?1L(Vob7<b}v95f9W0 at CR12
zFqpj$Jy?SX*K?X4UZnUtbeF1#S~Q|YfB`JPfqzu{MN}O{98bn>BPyWRtaSK)1zxAq
z#ahM1p1N48xF)oL{(19VW~&=FXmFxI_hLUKE2^&}L|>uAX?G^r%J`hbvW%3q9niM?
z=kLv{W0fqzuHU#OB6>34e(1;F+5op#%<PNZ71_c)LgQ-;Hdv!K58bO)hEiM0T;#Y;
zP%+?X>yi&c0q~L_d+U7lz6P675$J#&={~#}Zor$;qxdQKbox#F7kmj`!Co(hF3Djm
zGq^0y8hOlxjRcMxoCDhpN#c^Ap4_#AHmMc2$Sp=^W at Tmrcob~Iqw*GG57>>LFvh`C
z#tYzi<6veob4EIii<$G%+juH-Rl1BX%hQ<;z{k>uMr=8}TUwU>GVGI9r at t=kkl(<s
z8|Uyj;~o5tArPi;YI;QdFW*%I+r)Lp-~XK*+evJn&vyKA{yN{;KHKs665A=40KSS9
z$N~#mBff at 35mQC?(Ijr56owiKYk*Cq)7HV51gvYjAdqOGK%h*d)2OC(Q=@6p(lUuw
z+NN%;2a{O$LE`MabJ9UI&63~y`_4Mq at BjP%KKP&zR}2;*KFKPGtdXq*gF&cGN8myJ
z0D^$uPw8W+n)j%FpPKjQAy0n0FBk~;u+3^kNL5p^Rs9n227$a1kHv(O!m9$+;`fCn
z;ke!joq*{h?5s29bP+B%-eHI|=_PeetJ%sY6<QG0$=69k=ZCvtpvXwOv at xraHPex{
zl98r`|G$2UDEu&);0hgBKF at ku%g|U@&F^<<ewNda)vx&`=B6(AG at t6x$arQ^<Glfh
zfC<K2-anPppab0F2=2KJ%<|bgd}57Sm({gWo5#`jFpR>- at CO*puanwBi8cAzNp+pr
zHv4ze_L&`fqM1aZ7^^ZnHp<~t;_eNT at p0zRr#BAW{T9)=xj)YRg(R|7C<SloeTO*U
z4g+AlzjPP_7simnIrk#>PWMrK6VG9bJ09m)>Tu%%b+~bc_Eq%IzKR^@0*vEa+{1Bk
zGNCW(&eR!bw%GvlK`UotjDECo54&73R%LaTVF{hR?jp@$7i<PCF4ECskEaQ{>1?^A
zry-Vxlj--<IPLb(M|cH6jl*P|qyfYI8xbr{!&tMC3j*?%7Z;|s;WB8uhpSn`c98l{
z)GwDCy{^^98f`|L2Aa`g3%j^OMHNcVvW*LHX(%I&>qnpGHlvN)Ao>coi~Av*f;Zp-
z_cr_+xLX)#^4frgvoK5`aT(#c@$(U`jLCmEPuQG9$PX_PQ0XC!mgnX--q<b%G~!Wd
zIjuW6jcemH%zDW?ppob^eXd0#kC|$0|GePQu<lymd`kMpi=qlu7y_!|eXdgcEXtCU
z`gs^)mQcjNG{urM^9-NtC%~dW>RHkg?lG^Lu`rGWsN6kh`rOP<KLyU&huYmXgwe{m
z56nBwUq!8mN3ZD}N4RmzspeCx=}CCXG6n}Mre#*Uj2N%nWb-eMGI<6ghGk-m%IJ)l
z=?&BV2Fs;bSP$c{YcXq!*|5_VwP9Ot=*wG;23D%8YpdDe>?gD!HnChYtYIhc&O}Qx
zm~<x{t*#v6!Jh+<g;Z^3QnAhU90V{~IIA~@d?vEodsx}MWPpjXxL736^?@!QIV=}T
zy(8yhQ%PI{E9|T9s()1fu)gp2*Zy(w at q^EO{rt6m9ekXG-og5B>u=Y$!E>+}mi_9r
z-m&re)%xq_4?qSUgiWUp5K5wqZOR&%r5$!&Mhf|>-|a5tmF at l!ekAk_c}RI7WO>Se
zNxCB6;BSQ9lbQl4R*{ohLQD0?s<JWlSZYWaRa)Lc5DcY5eWBk4Zt&)D8Gb3f>w8an
zH#IGPBsGO}F)UjhluF|e<t>6ph}J8JC>(3ggypi>FJe)&cr$Wan-^OxRt^Q(pc>SJ
zLqT(}SD`IfS}q|4bmdhApHL>1DTPtm!4OC=q!0{>*gTOq;{F=rHuraXhC?z?0{N
zEn7e1qvq$WvU(XAh9r~wGjD9Q4MrYfYs5U|D>dRVxrTz0riP at nFXT_i$+Rz704YS4
zR3KA;1TPlQeDn71MZE(g8bk>Fh&`rwB-TS(7)3xyn+(BT!vIyGO2;w#KNyydMkhH7
z4~-0|HX|$)3Gc=&@KPwbx;Xm=(Ps}&^cntk at i))k_(ezc!O|DQ+m5W<KTueMch|R%
zMu|QzijFW}qgCIzU9U_zRx~&NXmtINJ~!#SNOHqA(s>ybG1kvXetKL9<Iu+9z<o1$
zUA&%SdZnM_us`Zk9+emy*b>Ra3bY;u at u0LDcH<|aPsDb_cP0+OSnO!-G at MRcN?y&)
zN#3T|e)zVuKXpuc8D7G#NN01`a?|Rq++5Dip*9F&PA1*$=vKPbN2SN|%^52Wg}@u-
zokAQXWFC?H=OD>1%7+9|$L$G8ievC#4AL1K!<J0?WedfGkN#W>YguD?#KJspIcdQb
z#GeTjC*V7}vr`U-L)htXkoaQd1nNcCm*}QH)L%lvSpoMGF~-6LwhQ#Gp{}VermJW*
z5M?#a!D=AN8gKI&i1Hd2y#}JZ$4gr-8 at 7DGbQy4BE7d!d;cS+oDc?X-ejZKpqFt-8
z1WuJ|K1<zsAjsAhoH%e=&_8dzvtz7z&(AwlKgC<FBO*o<IWb>=jtE(lczXefk{atQ
z0Ll_Pfq^)v5=>PMslSX8#DBL^|9$(MN0X^P_ZUP?%G(!MO=X>gnxVnSMCP+uK>!14
z{||~5>er!j9*hKw=56)w*Gq+%J;H{Pt4ana4bupJo&NRVAD;&QBZmfOmbgQ<pItw(
zuX`)rg#qe2?uTai)b>3S$@=bR*SF&D!T2*zpK#M$J39A)$xK*#5niVUxEI>N2~OO?
zAg4)2X>+z8`mxQ`Jps%0sjfv`L54SN_HPbs4sPa~%yzQ_WhT2#BP}EL5yuYaP-G}N
zlpj(LTAythvyVCUJIAu)rtt#H*$eify%a8li{TQnZa8O(MPku(I#*Z<OL5tx2Goce
zRfQ$RC8d at 2m6?H-wf1%F+Vt9NI0{kB7osJ;YrsDc7zl3Y+*H_9+*I1swebN5)6$Z5
zxA1APCDz at OR=cZQ)qPMpYB`$!fjXX_l;6y}m7VOq-R=2;brFvSF at F}`0Sr$Az!fyX
z^y&7}v5pWQ9*pvl$dxd?QVbmPWC+Gu9iG-!N47QNFePoq)+E9#ao%zVBg!5dJ_C9r
zUId6HAvpn}&gNa$T==F7Vy?5Un=ZyRfyXXI&qT5;nP~cj=m`a`DYuk4LaF+SlCHc%
zJ}^j$DJmgalX4Z7BMp{=zXA73B|A)Hu6n07OH*i7%V!&!XYeJpr1LS;(a4t{-}w*S
zRT>+`b;oCB=brWM?9A@$G3%L~UC*&zo0+wD!DC!JZ9+ at 16HIFf+hhTWBPJ&05Tzh$
zKuuCe8mU}u6RK83ZOYL!!7&g+q9$#Gs;E>V?HAM!t*I!XN>PbQqf)Tl_uiUAt-7Oq
z at 6DS-yYKyf7bxi)AdSFL4Hq|qF=EoWO&%jVme9hrW5p;WQyDaj2Tu9ca62Z1TS~SF
zs5KfXHk%TlSJtIr&6g03qNc8up!z}W*I-UvEv##JN?cunITZ)oj%~(}`wa`Js84!P
zzuk)>TQZ2p3Ui1;o=(8rsS${4p|{P`Od-~;5%n^+ttQK=R_3<1LD`)<FZ450s=fZ?
z at V?T5zy9LeJ%4|9entNvlWt^8Gc#~z!{~u|o%Zxk4)sp{e8Y~8WX9lu5|{QKU9o%V
zg7%)#b<Z4LdgM<W)vdYsM+XnAdTyv~U5oa?)|dJZ{=S;lU0C at GfF_?pn*3?dIfM at p
zLybe)2tGoLG>&MjYjhh+jF+kJtH-IIstkcQY6{Gfc at DB?fk_oGoe-m}F+sc^$Q)k6
zf at Gvyih|emVQ*q2HbFQ+hUE~?afs45#B*FespusQ@?!)(foY<)Qah at V+Ihmkl$pN;
z0%So2 at t*?TKAu|xLZ=AK>a7|iJPi=6P=zmeONds$0++6eUG*r6b_NTCD)`>JbPY-D
zR2Ps;@k8-Lm{x!q$^{GVLE*o62)abDEi(CP)DmR<ss8tX6LhD332otzS2~r&j3rXN
z)4%F(>YR7y);$YIh9mNZC-DVvRfU;JY7DIE9Q at gHm<Q}`vs>|??aeizKIm7n6^HJm
zd+8lf(kz&ETcOQfSXgMkW-~?Gw~0P)tFSYA#D35Imqm9)fJ6~SUf0#Mk#E*j+$hMp
znlcI?5(1qfOs9cs2HD`#({R<m;2H`JXaHBR2or+HacmIw*#Ou})?*14#EKvmk3;Q2
zIz+>V;p|x?NE!4?AMLJ+9=^#t>Yef?J<`*2XftzYGjnJ&b9qVHE#n(yTt?a at N8mMC
zgV*G=d+R~&fH?zg(i6Z6q2y7jA!CFlA+C{h)dOYE(j8;-SO8tiaQIl>C>R7CHEpvg
zl554pm=!Lz;sS4oW-I0hCUmnn0xEd88DbP*5!(zQi{E$taT at 841+?bjKoCdxn^mCD
zNBCrWUn#Nl#>GEg^KuIxZzmR4`kT^?z26yr{&$Z9#i4A|d_><o^@odBUOn>7!G99c
z=o6+{ZQ3$5w&dcL#aqu at Cd}Qr7ElKgNCR&`bP>{NK1%Dv7(qPJKX6*XHL+nL^yjxD
zI-xKTU?kjb$?q1YE?s&bcgvm%5}|0|3M=?06dbvU6mk>e?bQnETU^u!&*lo1e at J)q
zn?~q-@?7ej%qinGL!C&!o_Uu#OP^yvj6Fu5U`{5EDbx>`{n7o>5oNzYtxv2;ZY6i{
zdkktwS(fZG*3#=4YB at 8=F6UQ71`||Z^kMy>Wz<(_DrZ#4j>H$S$0C$T7a50j5{^Ov
zi)nbqD&vwtjnVK=1z9X&<ai~cG%F#6X at GxGjQ|~ISUo~O{ME#%3l}Z`ms<m@?fYsF
z$0%IIq6txrMp$sOp43!*VrFj;Qy4nOG7NA-ao`3hnuY~jRg&N=sYij$!U%@u?j-T#
z$DR@>`<0stsa*FGL832lDseMG<r1qBn-Y5xWMYE&<ZRCPo-y)b3X(@HeXDl0hNWhc
zZr5I_t{tFHp+O0#2((52+YSQS*j#(?L5>DE>MfA+IX)%%(LnM^m|jJnWn~}O#>;1A
zpLb+9dwDGC-%DGAzz5+8ngPpGz#$3QpcrYW4me&H0=r7RvB<0zr)_hZw8ive3y9_w
z9dqzOJa9V~QempsY%t1e at 9ZKE48`>VWt!ZarnaZ={v{OK+S*tZ04YEwQfIC(qo4x)
zkXEm~vmD-0wBj*PeL+<637g1ro=OT5A+VSW$}s77qe8r2ifN8+V8SfVF+A_lJ|jg^
zvL6Ot6&51PRY1Q7_F2#e*TG;eS9KS2gX92rjH4~O#I^`d*pZz~QEhhY@?6 at _RJ<?I
zk1{>NBDJ3$U<TMhejq%M8Swhc>*+Pj24Ofetd6vABe&7pm~H%aVP|+}X1h9?-JbiJ
z`#kv)`(k#VyU%-}e2{rXI3yoRy^?uZJ?wnfJ?tH4PjV-PlbPe{iR{V7W9~RJ&c4l0
zWKMg(_HMJcg}aTnbBl-Fwcc>~1&-`cH)tF6ueOqFnYHXN7wX}9^+isPOAe~b+@)Tq
zkLhEFgb>MKJg`|=#cj?O^)llN+-%i1V$%Fh)#I|H5Ua1ODzS`!3#@NTu<SwIccH?E
zrH>2?Ed3TPn`JqU&jR12X)HzqAjp}xDm!jbb)+zOi>+Cz?Uy at Le`03SxGM0uiJ6T-
z++&$sSP=3mxLeI+vl_?q5FH6M3nsE|Bg^JJH|}|EnPwPxCF_;JX<3$R#{uCIBLtpj
zS%#bcBl@*6*t55SY8g^YC+Y&L)vI{rJ>~u7(30}X@~ZMCbTV1KS!T=E*^jv|3+kDS
zaGuCv8T{XY5QO`}m%^d&vCjDu#M9$-pnX=maWySoO^H*tP)I3FUAv!4P*+FZ-pFVj
zc at GBc=tEfi|6qK$WyDAq3l2u?f*8GrPr(0x{Gn at u0F672($KBJCg*`hPf0>Ix&elg
zK|GP4h2tzD>+*mgDciPHKOUN##c83+j8?y(#Y at xQa;ASX{oAJLXIjJYh4b;xQq_(Y
zT)5)oK#G&oX}L&<O&yh1oWzNiM#cIZK(JLQJa^}v(CWKCB-iaqTBd1w`NFO#hS<Ah
zd7C9SNGuJeitW3nbmEh5cu9wiAXG;&jCvEGx;xY|t0*y&NvQzSr|HgljtRjnS}_Bg
zLt5z$!XnZv&0^i)14|IejCl7xz{MRgydlk5o%nX_>jotO3Z4uiQQsBgzWDi|^dH8n
zHnyqj44-rEm%oUwZ{m;GPJDfxYdg8I?@b&hzK)5nn^4FCA%jAJHIS(k22~*m8!B{N
z%^DVEY#KkRt(%ZQtI9yCR0dI&Hj@@<?bK>QLK8^ak1{4r`?Y9H%BHoN&E9hzD%Sm(
z<#XP9&pp0=&pGe&zRzP-m<kPyAH))Ur_sy4v&yt~HuDqZ$BC=>s(3wqUAdmPnz^p8
zBv#aw<okuC@<-x_kq_mM%+<<0>7M+l`(Y(2>7pzta)*}A>iTBUkPLY+-k&nGt!R^k
zDssiB+^Mj?$>6VNzNw$r7Ib!4+wa*QXPk-FL|~{~84C}mc&R0eJF+jt-;TeXWj7G0
zu;Gf;=}Tsb55===M5S913G-nG-HJ%EkfboQA-G}aCOob|V_8|pnYgTriceIeGQt_D
z%!@*V56keDR3;4|EEgjthuH|%Dup7gNrk>uABYq(iYO{L(}HmZGN(krHYDpUvaDz0
zKE&D`R?I>H0*HiLTX~*ye65Vj8p0qkWo+Q*@D!fLH}EQcfFEKPUM8Pe-Vu4Xd{}1W
zUZh-6h_Xz6y<}B>_!wC0lOTnlggu56?d{Cc5JEPRnl>Qyf5GR;-U}bV5Sj!QeG_&D
zfI*>zXNLDJ>9|wThZxX0lwlu>AJ$IlQx&RxpayElMw$0W>R5qvg#cg)ag#})7+TF9
zGZAu|S!juE0W)a?==kbVz*N!!S{WbQ445romfU7Yl<32UmRmA24#`wx$t=K2ZhFmB
z!$?p`!N?7xHO$a|m)Flf$nNb`Am}jA2cY@=13>nG>!lb>YwqV`0~kM1iJO}nvl=5*
z5-=k;apoHbYVQ}L{)kgB{!;&QM(W>QkLA>%>227m|Kp`MpC_j#28}y^?`rmVv)k~W
z%#Qv8yU5?_<BNx&Mq{^AZ4L%~Tk&)CH;O60+{LIWCx-WZ1;2skFFy}=Og5 at +t$&Pr
z`qO^D==Wh5@&?C`QQS8J?yuXr;@%>Muw*mzYvsIU91I<7oiNz$;48s1$up at 7!G#px
z%C+(Y8GgZ^mW>I6<2YDF+E3Uxl5vNSPIaW!tYJKjE#u30pKzdkUwXng#h(&RrB8KD
z8&zE8=Y+X*wX14e?s^NqMXnehwB2iaV94kBd0{@yV1Yz%LlDN2awLY**$8S#!}d^H
zq`e~<3I;*uwm|p`4hMyYl1jr}I+P6RX~A%$g;X-c#YBuyEY?n?WH7M&=(9yCG2mj|
zmNE3g^4dyJu^dif%Sageg{>stkyCURq)mA_Wu&asL~1%!O<hb0sb%u!EuDg<Ra~Fc
z!k{rq;n0TCcoH`>5zsczZcssXqx at jW+yIdF1VkFwjpVafOcqlFLdz~YGcyQ82cE?g
z3hyC~rcnR_PSZ+5yATaw59Ni_(18BA$D}bz1D8 at lyShLX`hp^*VfjC`2c*g!{D*M(
z at UBw*y=Zb*cWsp_)cW_H(!bP_BqQzm&doT2U8T1EeyBCsy<e@>>hC<3tGGf6hkFw)
zRqgKXI9MOUKRuX at cDK?fyH-C=rnnc8fX-PV%ZZWTWLPfA5K-Wh5OvOAFw9WGVC9(x
zZ(u0%OAO>iMrNj&D#KQpiwwEKU}m1<Z(uw at rU-!|<Xpx*x8lD$0C{YDeTM4360n0=
zSp4{w7e**U*inO%sg>+Ap?iwszPKM6nW}HY%k}$MsUH{c&QC9apJVl7gt7gcvpOv2
z6(^Z;V(^p~k{F2`n<R`>c4F!-#^eUiHJqi!Y6oXInVaUS99!isa^wn!xp at P<iwFWg
ze~Eig96bYJX}dbPqv0laHZD?6C!cV&;pxm|Gq{<DHU at rf13&L`FaE2(lRpfeo at V{{
zSKJB2ph2r;n!CdhgTr2UaF6j4Oy=-1t}Q|7L6=x=nK*C7FMZP3q8&9z^m=Ww at dvr)
z#OXgCPjDydKSSW>=%Y1efq578qRq@`g9li0*`lZ at TNFY5h>%qsZa3MZdg!Q%Jh?`0
zmB^l4fKGw?`-`GzU#wex3ay+yH)jgA6*5`6U$PT^lFgxZwzJ#l^H@%JsmE$-qrDFv
zd6pmDwc6<<kB5CNghTdd$es}r)pnuO%_4oRyat?ZQZjX#mT&7dddzpV+gOJiJ5{f)
zexhj~h<9)I7 at 8KbPP(Jt%@JvLKbGW}SuJ04F1Z*<lU_xy=Dve2xG&`SHYrdP%hfXL
zjBe+)^IPOC%J!mFUTAZ;ngkgsqj=0U>K at IH4Gb4YH$Uq>>OSwBbIrNE&jel%kXZRp
znM^rysIQdm%=E1Q4fh}q6ep+YNxMxCU3<8g7lGpvN_3|@jBK0J9 at bL=JpbP6belUv
zhe9Vqj2?O|L_*(!bVgTfl&lhgWlv|S8IsAvzAZCPTRzsEUCrRkl!|hjJf6Nj*!X|L
z3GjPzEBGktKq~#mCKIVKwW?lJSxbGWlB$YTkxo)q$S?%FA0jzs`j_!ht39HdJ%ZI_
z%EE+D6&O*#4+T6SU}1P at _(Vt>7isLyYCG1}*EF%F(LgTMv<(t2{&Nzt;W~twxHeN>
zn+0m2`OF3;^+t}n$#^hixHUMd1}cZoSX=Xh(FE5#Q0OlZ-sx~T2p?Buh46W|DI;H7
za}-I<-q at xnRuY4p8ASz0pN#W;ZYe58agzdFkuODQ>H{Yr{Xqv`t*fi++_`g*K_Qdk
znOQ_xNLjM$w}xgRt1kAyO0$$Qh_+d3G6&=)$_8jgl__&|1B;N|W)NDGMtj(8y5J}W
z($u=Zy1+WYI!*Kiy09PCsX~!VD9L;uB?ORW6N<q5gDs7*{#<V`5DfTzEr9?{%>#Zq
zp7K$807N3!OSXQ$qkr?ESK2#&``2f8m(?WEld68>^4amhsN at QIMUTHUeWa&|f7HEm
zWN%^n+)KXJZyz7-8F_hc$HEas=`Ln_`!ahkcE+C4&euPFeX!*}R9Am&6W1Mo at 9uo>
zY at hA(uZtc3y2IG{;Uo?vff(p=EGc2>Sh9uCq}0U%)b5W?jYEe<)e0lVP at pZ@s-dnb
z0y^4lAdM6VkZRN1l>%c#%DSne)ezalet|Nj8cawm%f8>|w5XQu-g|fVeDA&c{Cdo_
zs_kIst2DnV+)&7^-2}{^zdwIdoUpw at bh4O!SYiG<aT_vNLoV9_DhWA|y&(Yb-?*Eq
z2s6CIE68&dpLgN&sv4>yl3 at 4azQG%??C at A1NFph_(;O5SX$Ri6U(d{Nx8yv%o+)b=
zfwZuFSV-Yu^+bfffPUBq`^Igybc%#PM&$mGfat~H+&hD9sQl|3UQ*R`%F6`-h+oFq
z<ih(V at pZUA*`cNBG5M at K&Tb{ZhdNyOWk&kj;&$nK;$G=(@r1&<7+s+RYFw-R at jz#&
zN+nW6Cz?q=AIXvgYJ36I{-jN`-BxuF_Qk5Ixq4kgu2Gj%g438wBreTmxSF9_c1^jM
z3)p{dRjaFz`t_rnNJt*;T0oq+{O}AP>2SNZDtCA0zw|DrMs3&-oh)sLaf4PnLQxV9
zIn}5Ft%PJ6H0+E-NlcCyq~iNXLa6N81%L;PLwXn-qyRHL7+ at 7_q0=v7HL0auFM at 8_
z0w(FoJ+Hs~;hXzU^uD#hV}zphZt8Db`mNl?Lx;Auw$=*&nfUZ~cYat}u|gbwV{NsT
z9Ga`0`_n^9FaK)nmyrNi%^JW^H at JsM@19a5`k8+S)v}71kX6Kp%soKXQyx1jm_w#u
z!jg<5p3E31{uzHj=!eRcv*;+XB_c>X$af~+eqn|KW%2?d#Fs=F-B#b&LX!CPs$7rR
zgs6YL)Mx9H*Rz4hK$Jag+iok7l6eL;j#I`o`K!&ooOaO-q5kNmWFb_DZVwGd_xWD&
zk9tQ#Z_{JK$>eGJ9=*gah5xDCjNUTt(2y*2`yTV{PwY2J$=M|H8uS<Q)5L%y0fIqd
z1O>Pma8WQzra(;1G|XNT=QU~`F~`iZIb}|pv!=^@Dt5y|J(u*9oyBm65y)X-wfS-|
zNvC-=q0%1pfGVh2jWiRB6vz-6C1a#arimRd2;}&->fO~sZ#6w!O{>pS)$+~Cl*pQF
z$j!1P+vJW#9TUR$2?y42aotRQxHwnL-z;)iWistEGes`lH+>5fbM(cw#J0u6S7V^Z
z#X;akTU#4#gJgiPAt2c(kVFgRBA|O`{W+Va<tT<84VAI{j#d#ongNIvVe6AzB($~=
zjy0&)@Q{gW7pQ2P*qyqz`;C84dS>s>mo%)5d!5PTm$p9q^`j%3zqY)EJ~942m2X_5
z?gQ)6*>rGwBHq3E=&}19O*>%ro%1&(8z at wQGzyO{U at M)q5LtC{h=WS00E~l;7%`m-
zmhLoAmb{23Lq(t&{Asv)&*G3YLfFHIzAOHP#L%dr6-)R~x-`FKcMJXg07=1zYiJO8
z;y~rf!a)}j#&yUSWe!fr7 at x`4H+;~G7*0{duF;_wwPJ;s5KB1WZ=5>UFkM2yf)NI=
zG$fA)&H~;sjBHaKe}+#^?w93k6IZp#Ohw!><w at 9$;$ZUm$@UpgHV`=A#staEmrr+f
zwPcYZU(PhO6tXW!FWB};rR>RUIm at hUDJzhyULVZ#+xnIDnS+e2Wz@(ncXT;6I9`)}
zQhy}N%GueBU>L+O-vzWg!37^#W%L+N8c#W%F`hRLlf%YwHo-2|JJX84MqTZT`#XcN
z8hv#%9_vg%H>cDP<g1owpbZTP(U~AlQ#CMP_<{w!q at UEqgg&YZ`t7=28OD0Lwy6cn
zb6u at +M^neniatWhm>bSRD#jmVWDv(0lt+#0BhennWOX_tDK)9IQfH8i1gBb-GHAW6
zfeRz8NMgPX5f1n+(&1tr{2AQ2V#Gc$;?@VsSh3?OTe8*LBz%rx;ZjGb`{4Aw_jmMw
zLRM#7)Z6GW^+==hf3r>Usx8 at pM>dW<v+?OQ58wab1KPFjXNR~@-oJjdE9y-ae?zZz
z4&{2jdHKp80nuLtz3damNPxt|7Z(s+tLWf&ss|uQ+`Mpe!E^_kEkX^D27wR_POwm1
zE3smEy<VshXC&oiglSA*cpG~&uCDMLlg`h7#Ct&H$~k0%w8ZJ;!hi`H5QM18=kpxa
zV8_|X@&hxD1xw at z$dPDpbrLIssJv0ZmspDt!WuK!7!wIAKyW<5B=!U8P3e>*;;R@;
z4VjomI0piWILs5PFk65<&K3&T-NGfeI}!h)g=Z$G(8co?^7+g|e5^21gu4~?Z3^YX
z1yTrnB-+A86k=L5r(4ln0!QWO=x$LGXb}kxvDy}Xxv##dB_fCI1O6xVO;wMFHdZq#
z+GS=}R9o;X@`&(~yjR_)?TP(ZI1w86|3UcJ^NDsx__yfy6<9$Tg4vDO-(#0OvkaV$
zxxOoicI1f+Jn3F$Un6wcdlKt~_4dueurT5u3IE7{%zn&qUKzKKIW7r*7pB!aj({@7
zC}C4fC}N3gHwue3#$@)Q6d=ud5Qpmb<$RlhJA;RVH-b_y82P<K;R{ZIfk+rp{a9SH
z*7|a|#!o~jei3$6(Q6|)k4~S}cj^apQNI%iloZ;mj4DF2azMGEh?-(4FcW1=nO0=w
zxH~A35yX|)VELNemb=$261V0y+~Ta8x^X0SSW9<DykjA`z!qTLToGeSF%QKI<a`Z{
zW*8xr8TP^_f~@^)5VAIIbHRDQdT?W#Cv9z{n5P{BXJkSJp*YAl2l(TOe1b4|n=_eH
zt;U=S4h0>jHpeOjYQ(8Xr4^~%TWC2dEk~tg=Pk=^&jqz`E^K&nE;zUpd?BR|4i5U|
zD$GI4s}>y0hYpr9!M#8Z<WK0<t$QEe)0hZedF`#+pPqgF;@n>PQ=1mvvaD~nu=472
z&u#fmVC2t~etes<-yUBvu(-|I1$n545b=52OC%#G3+zrca=&V{&~_RvBz7c2HMdL^
zcO6x*O?|M|w=5qL)a~Qc=O*Qr(Vpz!Ne*RkDqcm1r_OVpMo#%;g=E`j%KvA&T4UR`
zt}w4CQ4*~O^{`CYv?SW39+qj#wkXSwMD!zGD#x*tIB^oqiIEl<+F`J4AlZ-~i`T5%
zx&pJ1p)Fhtae!eNu>EN2DveSsjW-wtI>1e^AHlF8Z8ik`6%RpI7{gXOJLgI+BAGmg
zhxcCb(&szh`Gob{=9aKkC8c$2r#FO;@w<V-caN5CGj2MEfvHAwf>U56TY^v^5)Kha
zXkSeq<F7^?MI(*F_1|cJ?Q&`Ddld8#Pw?}bo7lG^9QVHwS*c%%vZL&%<6!VL_BDs)
z9W&|FF3BaXWL>qb+OG;%JvTJFz(Jil+jcf-l5DPW$abZLltb1EYcs;Fq1DjckSXL*
z)CQ8A5{Qs$OL#q;&B_aq;1zQ8{bdlm71Q<_X-ig!U^wFm;&pq3D{ePYFj?Q9n at dyM
z-(R)4T-7RF)EWUvPM2H+Q~a!}=vsH(cX6&@`-d#YTB{w at Y&9t-wjgz>vK<8T#q8rn
zV4)n4(az#vZU>Ytya>uaUNz<qMAiDJ8i-3w%&$nK#yUtf3>?rTdHj%RJ at v8<>6Xzw
zt$GhgK&k<0$5d^YK>5}8)RXIyy8qbD!$f>IxVCok=EBP-`_rL at o_shQ?J%S#?4h0O
zOD*k+5+6NhI(=~P^^eYvYT1x3f6wRZ`p$#lgOGmAzTGkQ at 6eMEFfTBt*td;Wy at 9E>
zqbnJf(S(BOLfeI7CZ>(+;Eui#G3R<G3-i6_qs79qdD(Kg;m6{#{`38p4=j)WWb)01
zH^r67iutys+)x%jNq;iFUbtU)RCrowYK+wP2)aKLE?9nR%V%;;On}YE`6ebf;`In_
zm(x+l+wDG|-)>t{iC3*W`>gB*9#Zk&;dkWF8g<^f+^YJndRJxD3b}goWO50_77R6<
zIM%x=zboIBS-CccT9_ikSVJu3N#1~H-hfcP9ifw-@)LiB*bLvija;&U<AG&tI=3=H
zMk;KV;RxpW&LEizE(J}&JEp%d9Dr+r8H8IrXAK at 9N7~!n6CbfD;JXl<`<MwfWrT$k
znNKaJR#R+BM1D;<5E3c9uY+BhCeye#C!iIaKbHkRbv`TO5OAIvyc2*lt%l<yPIIuK
zF})lolksAFJ$^rKj=OLq-1 at 4FkO${qh8GJ){CuR4Dj0=p at Xjp-oGR5h(goM at TVrHQ
zz)53WkpOWAih+Aj>?_Z{G(0#j;K0TpK%X&CG2JnID>;(uO0iR{X^JHbE3hUOUuCd4
zO)ad-;?HB7jLt3GBm45{!iVGspzHYeUKf+eZG;8X@~y=k>TD$!9}CHaZF-Sh#Oj}1
z5FSJC27*@Ds*%FZ7f2B~VQUdp?<^P`4}*av%J<~YWfSnk;#XVHlqE5uey&1f5rOA{
z<`yar9t17?QN&#wpXooK=u&foNGwscyQ`-w-NkZ4(aC6s+7>;bPD?}@2uaMiJ`rJt
zNiM?dv*aXZN}G_Fqsi$A85PGQay&L8krOk`{Y@~uX at EJ@m5-3|yq+;kBN3?M`^|$A
zIox?fVvZ$_M3 at 5&BN9UyL>Nrs<L#X&+Q0d<0r2rd7H5&{7AUnDe1`xzstew}4oHai
zyi`A(B2j2Su?hh1U?s2u54F}b6o;K>1O3uFD68}|h^b63okdz;0HrTI9g7m~`75~A
zr%!KQyFB-&q>JS&tULMr?8a|KzttX=Q&REMeY5kg{OW&yd~LkWqg&6WlYOK<|I%oB
z>d?6ZJ-h$uO!dEXr~Lb#^gI6`hZDa%^|KAb!r2=dc?);2xb%TP+UNH~tY+3?cNUK>
zym)1%J0ptfu>HkwSGe_z>8BSiUY!|UytsOL`0F3_oK#cF{!0hbfq)rEpK(HQ`ZuV#
zjA^;Xjm=pDfh+JHo)RH1DtIk6;)MuejbZ|vM+Q}Rq6?E$j3QfxF%Xs0dQ2m-+2Jr9
zm+2a^CgK`e<=KDB7zJm$j5oABjDhpSa8pvI>uCg3*bom?>xDywBMwK5Ndxh^b;AyG
z^$Zj9G`E|rkk*}@s0D#_pFDx2tm#48v<e#^cMBWIYGe}x;>L5@@MPMHkksiJeleDY
zMR7?_jHmR=Q_AHjE%PD`i8LhAkSJz*iA=*X4a+nv!)~6^Y6H%fWxN5-*SGMVre%9;
ztW8O~=5J!t1-k$lx#^*}f+TJ-I<rQb&S&SKZ+5Hh=u)<nHQ>XYXg(BuA#QbJ-$>
zj4X-R#6-wbVcmwOMN5QY`4&DA67sEbA{4E#E~BGWk97>CL;5I*#4-%sCA3u at kH7~-
z#a`mc4W799BEQPt<IOx4DOF=+r6a6OX>(dpGcRc+&2&Q}z*yS4c3(4VbG_F=9SYmn
zP-17fQ?<a$h(y at Spr@~o3PXJPv{d*TC5w$yo1&5>C=siz(b|mk44|b1y|Bp45-46t
zq-f-0gduWupvKj`K-N?oa#ku0A==%W*$aYVBop(m4jnEw`CNR;*xg at mbn|R at G}ZM=
zzP at j4w|`%&Uv!5X>pNY<Yx%{_xr+x*eB1cl?jL3%qNFIXsBoB!zICQEJ-I8L=?E)|
zkI$Z9_f^#+18w;roUDMCI;O?+LKQK$83l--8PnP8r0MRIMa*Pb#LSj`B5MZ*p|u>G
zM>O&6r~>geIG=uiqwP*{ui}IA&vFfJkM`j9;3h>|qyh+Nn3U(`OF+Xd^MK(wj&QV-
z#g6<IF2J>LKIjG?KvCbE6&_ZlBqUk&43L1DncTn}+e3mgLNOrIGp<t}AFnw>LsiEJ
z_V#ke4URC}HO_=T$}kbR#p=WDZ5tBKwA+<dC&h!)glX at jc)*=i at gO4bDSjYws|q2d
z^*IbwH6}d!!_D00EY%}5C<IFinNx~NNx7yxRV)!@N--IDQjjpayVKNW`>R$<Rc)(E
zt<e!|OanlC`4(p)<ONv7f<uvzJn9HKd?naQAH%dbtUfPavJ-nBa`4)Sj>d53boRRr
zhcoC at L?hWJVyrRKpDu}HN+fe)Q7nnq#HXS~Tx(tXHH8B{_7-9QsCTQ{rULWA)`S`u
z=$7CEsXj{<A=kTWZ71-E at 0+})X&u|k>qOhYKwI13zz_e=cG-ruk(_aSXJzf$tC4os
zlC^rVwJS at uB#&<;CzfT!y3;Cg<XkKzZerP1iyL2?Hif>#Hnbch<=hV?P)ees9ED3E
z`Bd&(bGclqqm(ltn0{zVO5aN<M|-4sGZ#v(9M?qdGqX}&jtXh_+1Z(qwDX_;^Lv70
zrPjWEX1GU;g!+>n<a3^-Hwq&oQ?2M at V<8Vh4i#$Xl{ZF>pd4T2_?<7e%IKW)9H=&h
zuD5;OmGrVA$g9)!dw7K!Gz;4D{GcPxKU-c~pSHFo+U(YwJ1sT at g8OU;1b<^gbULJm
z4$(Owm7sdXNoW9kO8LXTQMmN>jWl)ZKi+7|l8}xt$rjWuSAP;hLJ$%9q>H(%n_LF_
zv&bB2kheT<4O}FG0T&y at p+L(`;s|vo6GkZ!NlpjpUlMBGP|rv(Y^1R0Zz7N7RyYoe
z at XhiCFY-3g39B$-FS9k1b(locLa at HJYP*<fyP0a^y52C5Y at ik6qVd3R7zXWPG<QCR
ziV->(mq*BNqjb&5p@)pb3RQ)oSmBVe|0~nCst;lH0k$AN6^r&hyCmEQUtxONUau=)
zka?A%R4>VMOdHH3mHvoF9vJGE^?nrbgem2qPW=LD0t%SV2&Ns;^2NXVGrCEVLV<uh
zHhy*S3rl0sP*{B>8ZiA&)pL&B9wCL6@#w4fKHM9N?Q)BYLyNETKm0L?G6f+{<+v^k
zzL#^n-_{QavGgBgM*~v{sO*T?5n8AM{PYNHaGPm_1{Sd)VIpDbsEr3*%qRs03?|F}
zrk{$->~MnF;RKU9DV+d<mZh*J1*v-1u%v~ClI)8pvi|~TVVKK;_If86EllLN;UH!|
z4TB`F_Fy-Yx8omgO9HGA*(Pm>cuY1oH!12qgArtN3tSWQ30s%5w=VUh_vE~u=X(VO
z!TU!&Z%7SS1D}`QRL}Rmsb9#yBT4ySK6FeurXJJ3p`27t>gV~Mdy#FO-{|?CXUnlA
z-{J4b+v-QX-9 at z+DC(0&F<+L~rEkmKX?{d8hK%8KeiBV8VxLk&N0fP^E2b=>MfsNU
zvEqDLJ)mFfxhDNna%w$(MIX at h{rp~8aH+D_9l`^0#G?yG9ks5b&bl(M&U=NRJTMT^
zkMdn at +fPpfn5L29kP=yVMyD~tr(kbLLNI|be07^{FryoI=(m`UQ)p&B4#B6)$4xUo
z`IKuI)=-|g0SSm9jmD5BY}FJQ at oKNvs|58>BxnMvBnBnECn8Z&NyH`+=~6Z_G0mkp
z7x+%x(0vBt4IS7pgLof8JTf>#_oA)@FH4FNka8TSHPL;m62QN9xg-IU9}EU0R|aoj
z{sBfe at dub+#haMNX-&HjK!K2+M|nUSE}l+vrea<)H_e--bIwE?X2ayomC1Y)z4}h{
zr{7{*du at FgHa5`ZA!YqPG<>)W#PbAm3iMX75Tt*Z7DXT&rBFEIF$2W&UFG_j?smXI
zhd?_6DBDQcw2x=#z3ZY_hv&1tzP7f^Ew7_x_TtvKH7Hiz;}qC7KB!mpBvfAT8L&Wj
zNjcBcJmt!}=t<4X_Nm7{VK}`F?n1 at dk!&4-b*Qvv$&L>vve7<45JfMmX=uiopv4NJ
z`A4(4D)%f{9lmoAcSncO*(2X9-Mjat!Av~(QfuFEINADF(5$x1a;(oKdyG)uh>8^F
z*+;8?nC`{cH^3VPZ%+NC^`}$Ov`31^QJ+`ajZU;~)+Ylf9#>sj^mC5A7iPk0j5^JW
zKv*(3O&@x_!&e&M1>-9p7LbUTXCUSoh<OIW6tk&qKya5;7`TH_n1U8H3<&=IE**oN
zzXWsWhL6a3!5~~-A9Dy_A2d)4*hLEo)TtN<tr?W9XR3yTmr;_>$8rq}<2Vs9&_Qgo
zAeKbvkFuc$+vddr8+tI-{Lg`Z5%!49IX`b`o7w}-p;3A%mdEMY+LNCeN7`lg at rgMN
zS=yYoqOEET?ILs(aVjDn97L&zkcjyb?vgj+n}(kx3KECnZrq**Gi=FDjW;lw!)OJs
z;s(BmA7CfG?0*JZY<w>ko`T9UT0_+B7^ghXCLLv*3XadTibXTz(F38Rid5&>|CSae
z2N;Jqtn)KAJ+mw#WE?-^79GE9XA!N=s?EAZ*Gf~_(yFyersMY+x}bFVXp1G&HO>&$
zi;U&UonGZmFWO>7X|Hl=ro{S{m{3YgD5Z*zep{t8uF}abmCghZd};-0--<-1sSq}t
zu;C;J3uw7$Lr$T?;QIHLi;m0n(~05wuBFqiIiB6;=^Qzg&7rb6RD<R~Hiwa6iL$wK
z&7Q*;p>x3XUo98yYVhs*k3gd^kKqrdckMqwfy$VfuUWKDx`qzdPS#$lIcf{S%m8OV
zn7>$HXw)SZMx7#Mj_owfXv at n0R*yI7tCP^45Z7~ITk3EGy%TocVpRfpqWl6(53{>O
zr#N3*5CdZ~DpR~_bg?u-1S)gF+DNX%u1f5xRDu6-mx<pnmf$&k!nOtrXfFu<&F<!M
zOBI^qX=A0cgCO{X-K|vWOYM!LJ~=Az%h?D10>}RIjbf3SGN|{Gdv<>5eXhK7o7)ec
zG<-5Ux8Dr~f`LF_(tg#$R{ywov;L9au>lgVuK;cmH)_<8(QTw6fhPax%3v-P8H2!b
z4OUW-nS+CBDxx(Vo+~kuie#D&_mx;F6)8hty%eh at s-^izZMr*^t6KR~vYQi!W)>Fd
z!VHaK$t4P1PI0C at mI-K59q3b0<I#+PR*g#rZ!}TXl5;7O#3wU3w3 at q=<8!plUtKK4
zD;2#uSLHXVjVjMol`3C_eSOvEAFr+~)tmh1A!of7XrklaXGq#6Qm8{9c>JM#DjcG0
z3Y(n1itJrw35+2RJY255_X(@L07rd;aV)!sV#9G9?MEJY&@<EzmY@`f4W-;NLBOPS
z1o;9O)UVk`f1m2pY`fUq$t4MJ3`jZJQy+QKEDGZPuY7i+x#PVrd~Nr_>An+ZXJ1~6
z`rXpRi>-opDym6c;lx7r>lL2w+f#0hRq`%pboB5<_UH at 2vDwyCaaV|OZbC*r!rwnG
z504x__Ui0xZO`e}cNPr49*=8EOr1kNST(Ke0T*e_e$j+R$Vx|`ZOj at N&9(kVcKOFP
zah`Gf-n+~B{3GY{ugm!y`_A!?^Vvxp+i}1IH3y*ul0nk3mIMf-2(?;a%MU~ujFN~I
z)^42|?T<2P6)<gdQ>QLL8Y^@w%i0dD+ETKqn$-PK?T<<mEAFJJ(#BfCp69h4s2cfR
zzkc8M-n?gjp6~O0Lc4k*$z-JW5wiDed&3WV7>4@~C|QYJRkAWwvM%a}3$&F)uaZDX
z=)_NnXwnj?B#BfHM5+g(lEm8-4JQ;2#<|jvC+K`YRS7yD+i6^`Fj0DQlx`HITSP4t
zx3H*jwi*W6s#!GJsfnD0+UCMLET)xBcA=0s3$!vT6q~O>LX_cZgDZtINpG^~(Art5
zCHXw$#9OyyFvx(&=dJO5`XQr$V1uf$DjR$A3Pv%0HNqnW+NTF7)b{r#MVdWDsuUts
z4N+lnNTEZDh(n4}?q;HNWt0v?>132XD2`eUxv)@7aD`=+yYHFOnk!w+xp{ajbfy*R
zOwrD-E8E$!SjPD8Di4>(%ai4~a#IISY}%iNPYY#Xp}bsX7s_ND1{TX)R8*{(_X_8=
zeQlN%OAfS&R!ka5M6Fl?1j6o26wK^~u2^AHE0ZXe=o2Rs39najwJ1r)oJbZ#;uR;w
z^WqJW7q75y*b%D~O=fj#*c!JcEq>OTvlc9lv1E(2FpoPS;H~j)2z5x?G|C~y at 6_HK
zt`q~hQdtdU8pwV%%n5ud%(b+VCP8h|8et4kJT?U{W{iLbz)1c=0N1 at BMCAR0_e?;E
z4F2f*gNF=7au+tP^akvbi{G&A=u>V912One!K*jou=?iUqw7zs9NVdfTa!sM?HwXV
zpPo9k5*<^b5NVqakgr_aqG6n|Ale^tS0T>4OqBK1Bdrx|Aa!&eYUgS-o$Pi)tMM2+
zIEfuQfP*|;#<!$Ix13^XnUV6lM8O5$)!@{L^SDmKn<L1S#s?{JL61xwoJ{Q-PkjJ(
zc|PXx=rII~rXqw3auNL}u(AE1e>OyZs{Bg%J$c9ZcJzIx;Qxn<Y;iuQ><pbI&pV&<
zz8`VucCpOs{eazhom>ySqp`M5wu+6 at _<8)%bKw7n07afGV>`@`^OO7>zrYLpEsqV8
zw&y&QIQ_ByK{dBs{$x70tvZIDZg3&9V{l>k;a!(Jv8|VMe(RTajl98l?k+MsT>9Nb
zOo9C)zhN}4$nZ>%E8diEM(+Cv*<rN)Kmbt>iTYDgnoYH)T`3{$^9BtjN;HEgPUxu)
z=mnZ(gG4xJLhcrW2{%DgD?nENAZ-xzn?N1uAF+K$*dxMm*Kz5%{~O8?^+>BYHVTCh
z6hF4JRrXaPaE0JYzvQmqPNRTm5%>%WLZZz~m%Dp<TG|956!asLkU3ap`Q*W;Zp_>`
z{>0ON+EG5Z@%*XBPJVSWH-GNi^WXez_TqP6J$d`+hQ4zr{;=}Fg||L=ejJL9ySG;c
zxYvO-lc}(6b=It28&$RF%Ho?#K$Ki+Aj}w?6`&d%FcfOeKwN*>@M~0e4TPwf8apnN
z^YfCRy$;&if+YvokIt0TGb%Vts=^FaVTJ&mps?WFs#I`kO3v4Wc5zX at 4#JwJ#5Sb$
zDpS1s*<}Q==t3q{?7CdN>)>>h+W^(bfKkf<0v_sPJ3`Hx0jn~CWHKZy!C~BZ8ax30
z(tUDG8A(ksK|Ggh;<=PVs!zIlk=Ba5Ro*Q>=i|?`liv2e-of_W?N9oiY(Fd>^Brq{
zM!e{_Dc*KAclC~xM!OGn^L8)Ei(JO?2f$W^&$I==T$u^Rj1QSHW)thrWjMZ5?jd**
z)`4dVt5UI`yXIW%xNFum at 8Vn^8EoJRdBTnu#&CQx&d$aO6PM!)@x}OZyeU4u?#;n^
zF|%H#syJQ6f~E?OHSKGu$aO{JBpLk=CECcB9nE5 at JMBq#rOJ+?LGsPel$<>VDY!cg
zhFL`ygmP+njF}n(1i4fxgiZ%JbWje>hJh(5Ywi_DO*O#;3skNf9hPY6%`XgnXV28L
zldlZ)WQr}7!4)IC#tekyL`+SQZm0CM9S0tGc#l2Ol}~b&>AxO(?9emsRev}W@^-A;
z+*^vJQbchV4siQNyHsgr<&}xVx{>XVU;WF}cGV9oGn-Zhcm`OGGCA^Ion<Xer7|5-
z at GT?|M~qN}iG($Y4^4zbeTYPT2#o%nYAJO7dl`|Invh at -h?N~t!5i}@QmSB$`rQtx
z#tk3?&bwN-2TQp{N^A|9#Yh$<G?GOQBUxln^J=k2WsY=E%WbIUa0hF5%y#@FbD at Lp
z(z at b(*|oVL+1Bj%P<Bgh#5=5w#)jj&viouq at _uc9d?I^7p3-JwQ}LPHY3+Ntm%J}(
zFU4Mr{~&uV_cP^X?dPqp=B_H&-~@lm-O7EM%NiYrQ-?Dz1kMJ|1{XUVI|8Ill&qM;
zY$H}oFcT5g8`HUjW)Xa>L at KH}9D)>yFuE=wc6ml8bA%lyvt*udM8u~cAEgU&XgI|F
zHgqHOuMiiKaWvH5-akW?I5#!Dt$HUnhUpoD2zgvz?Yo0K`YZL^tR^#omSjuXU@`${
zQcA)gX3#Qf?4ivDJp}mH=9p<zK(fkOk+OzL2tvqE|GkE*u?LC>;Pr3^)zZL9F|alo
zRChnS_4J=f at U6;tdR_Tj=7GM+3qL;GyN8?q^zo5mYbqtXD`0&O4gLF%H%ZDcT9bG3
z<QI^yul?@oVu=Cg+YETU24rQ(<vLfHEY(OsZ}FMboSLdme3Y!uRla(|*Xj*Ns-qhD
z5HUT7^y$>X>a^me3yI9BO8D0Royw#EJ88(AFlS88%s5mJ2V%8^6>b&k+b_6M`R(fs
zC)&vG3EVCX_f9xxoU9WzPz5+C6-pniVDXIqqe2Otf6^ipJFnrKu4k=#W)k*i at _kE7
zW2+uAVoyN%<}I>Cug%)tQ at q2@l6_gCqsq~G at l3)rjScCTxruSPv%a7q6JDKl5@$vB
z5YH&bF%GB*_X)%nh|sBLNtW>?bzL{etTAV>j3GmXxM(aJO~$zO^4BR`t2N&Ahf~v(
zEV(*e9rM*{xeC(|Jn*>F;O`)DhS1zW>I7cuRaf25HHx&lqa$BBd~D5@?qp(T$RFzH
z3N&wgU?ulZTiDgqoX~XBMMB*CyYKe5|DW&bk8R_s<M-}-_WiJZzij7z*v>h&b9Q{O
zlQ?d0RhBC>{khU25N!rtvSpJf43q}OhSq}lgN=%b23iTS0In(_6(O-MtgW{xTRKHB
z)P+`}E#r?egrcfQ^%zs>A5>}izGtV~{vq;v-*@-!-g~+CdEd|H<7^m<<R8Af<3R_+
zTP8;Q)YOSj#<73{ygYVqmA(Ra8T1Wz at ZyvSUdkR;XEf6B{v#dlH<B!qbAtH3lakic
zkA#VI2=_~a0nU+9KDEH2Z?otL7O~k9LZ~lbJe5RKNtDdmCYm&-P1;mNYIM0)0}WJy
z2%!dKI*K6zx^Lz3ihQ}vr1#M=l#(3I*B48wMV9Ux477(a)Lkt4D*Fsev)R7DmLz&C
z`OPGq%&H>7EBwGSF<K<4JY;fV;$j>X2gfN7br^HG9hX5v)oQrN%geP<d0DMO2P_Ih
zC(pZvORH+p6RU1ctm={R;6uIh^6?BS1caQBpDa(8>t(-GZX(-z4$|d?(1q}FW;y$p
z^wrE?-Rr*V>Fb$)x}rMj*4(f6A9jzTqx4ax9<7^os~$hnf3z5qkVFd%AN0os_pP2k
zrnxv1i>UEf!pK|h96u+_+sD$!GNM`t<=ma_p7KQb>HO2~v*A<e7t1%8>+xWo8%$D*
zbP{!=5~5KP6&9$)ViTF3(x)elMJs7`n at Bco_;R?@Sd8J$9#zexL!wV|NMMub4^*+#
zH%L+Vqnh6_486&0^&+tn{;>4hDnjbT)VryFrI=KciFo3)geIkFX;xySCfeW`j%gIT
zImEdQ2TeNDPTgT_XTYJISCLH(A^W9W>xW}^YvvX)M{BiD?_ao=LbdT~33TPcJp at hA
z2CY at -2kOGMX;p4ZE?j{s6~L!uLSiHo5}ykf3*p1^^0-dPH*d9SGe~aTYPH*>*#^<e
zMVk)|Q-yJo)8n~(w=FCFK(~^LBY&QY!x2u#sX&g6qxHm&y%)^I-wE87Z!33lzS=mN
zq2Q>(O{0Mtbc1P#FN9{Jv*xTd8=vd>X}ZxL1bJLQ2Qd}EV`3>?%KXrs&&<1QZ5)ec
zC1)E|K4(;sCsb)D7FdV{p=x3$VF=YC+;U0bt3f%bj)rYqfrMPJsw5cI42XgSt(tC!
zAcV78)z!83SZaGLNrfp?m_n_(wu;|*-;*SGOsX<+2<8dl0p9o25X=*TXQ1c`DOBPA
z{lT{xC+SK_cbKdz*45R{TSP^rl`>XUAgX#Z!~$R?hJ7kMo9cb)OIyEScTfD}!s3&k
ze<Bs_3Z+u<Up_jv?~%KI>+hd`X2b5XBCA2>#k+4E`^wJ#Pvr8&ZBr)?&m{#DZF~Ml
zpRJBPJiD>F at 8Hk6q_7S-EOPJP^d{eV%0g?M9F|RZD&(+)hZB?-)G>PnqZ(p0($ol%
zX`q`3;c1xph*`kjB8cD7M9!6B5nmHo3l#DLldWA^DYce69Ci)Ve(B at +$><7V8j6uR
z`jIy5$BmbXoLX-fSkFapYg$C2WTEJR2-*=rWI7L^2y=@TVu?9mIZSgbVL=ua_#PPo
zuaxi~H2!;zSP3l=|B(L*msYSRT)R}OEy*kLat#QEtQ at x0dW8zX0yYP$6KH~_M-y|(
zobg8VjabvTVFVfpbi_n^f_p*}!HLj+bk?s&bw_7nQQa^ZgsX^k0x{8n&T5$fnnwO$
z1ur^wG5RjiSdT}nw<)oSZhEc_<XJ2wPABLDg^<t3W+HnvRM!xt$=YdcNxP)|UGr;`
z@!uR-w+3tYv~QAYw?I3zAWv+fM%Pv`b;>R1T17yg6j5h`10eQ^=AS8GFqWffg=ngg
zGErQ;6(wES0K7SZc3!<&&ZQnuoOFFlaev>BN50YDmG_;$`{%7|za4)dpL=wwJTXOo
zITbswz4vj<jP$)#W{o*cW$A$qs$!0Z(Zh9QxoGF`rF~xy*~yN>T=g_!EKTyDsmAeh
z>N?!ibuS8yTW9ctYG&PfhV`spw8OeT;fBQk2WR*Uww|0om97<5U`N{${$?A8D+Lmk
zt`$D2qxJ<nZjzhk7)}svQ4eRbT`+2UNU@`d1&kd;K!t46hp$W`u}lHqGgXf3wF&ZU
zKQX3uFRb<hf;|=ML)?WM1beFL^meeP;2J7<1ujUngn>8;AS6Nz^xz6+)CzL2tFRr+
z52qd9usG7a(ca#@-DXWr+k>5FYELrjq&Wv|4kWoPwwO(DO*H0d0+r1ICE@=kEQo?A
zrfjVJ!_;YnBs7g0=pyo=CcWroRm04v>RxSDqw7$e)))*ry8}A_UGJOU{kTp8DgyWc
z`Y4RI(KZlUc;)w%8c>I9#U&*!nQ=;1EIAPe8z*l9wg3-MBS4H0OWDPyE9*D}-4>{%
zI_QKer@~B0saUrYzWZMPw+ at Z%KIq0rwxiACqlK^Ssy at UVU%S%S7FW^-->iRX{P{YX
z+dO2U?Am;N?}i8Iz-LD2EMQatjJ5!yHhsR0(X%{HnW{hXI^aoxVnd;sf4oFN3Tw4)
z-W)9fBa~nRI#?Oh1>WL#z9$9miIEuI--zZ{i2qYmKTYny`L;>J#^bCMKK!Xf$Cj?G
z$k)iW^6KY={rVRT26Egx3u2`Qv(O{aO2mkmX}(8DDYlx?ZNoM<@>OA at TGcDYMsp{(
zgWn>I>0`zY^8ojAZjS$j`GVEx`89Ql`xSrEIBA};Ugys6XN9x+IpbCHHEXHoihf(T
zt=}>G8$80Z{BCG+n8d<hJ0|n(xNTcII!-&L)9t7zBzm3^AC`I!Q8Q?Uo at Ni(-($b8
z9PQyZa>K%~UbTMjUrN1W27VwM(VsJz5p}yxYkDN9QI?&gR6$9?DR|a%dDF0U!x-R&
z2+s?aX=Zp1T3o=-`g|NnGED{9MEOla)SC$CX+l7<kP#ZfS>dw43Ws?MgIM<br4!sM
z+ at Ij|9_EcF&DW4c*%S}!mDFJ#uiHqFxG+?~;aspn at k{?>yV_#oxXSRHy?8ErnKOHv
znO*Phtas<;*`0Cr7LR4LgIdyrG>WO3K!R4O)D{wi)Y>8>sjzGbQ9BRF61249B^3|k
z0kuuyG;y%1x{{Id639y<8mTG|ZfQjfCJHDLvBQ66?2-brI_E#<%x&kK^L_vKfn8cc
z?_I$|Xm%)=4Y-QLD}1_LA;c<U3yki)KH-b}ru3-{dtSPiUng-&S`Vy=zt;)7&INL6
zE^14D!vbBkj_}H(HDnY}?UOr-z~A~7ngV_E4yWW2Js_{ou0mvMpqIfcfJ)&YcwE|L
z6p$%UP>_*`m^gAIUC9PQBAo`+po6UFRI&r$fKU}_ny%>rdV5eevme|Nnb=SpS?x at 9
zaBJDzx|TD_LNok|swvfxt$|Qtw3uZ1n5ssE(w+x at 4M)2UW*I<B;^A8KGN7d%zOjuK
ztx^(_q52YGUW(NuCSs`N0e*mN^Ip$!L>F(~gk$9q%v_^1$excA-jfD>OX1hxlK>!0
zP)iycp$z}wXX>a<om7z;J82*z7CgOPZ&WIFgFpwklDw<e at AdIVeg9>G?>hWhEnH05
zWRni|IyoH-rl9JIeat?=zG#20es0`X?;CM)!KHM^zwUaW+^*QR`CPXs%jH59+YwGH
zYIV)k4vFW*^U`^ZiK)A5yY++Aljs1wk9nfDM?YX3Fz4x6JS+TEoj2yqS^Jmx6>_n<
zjIXKJjQ8y8>UHBo^+V&PU8bTDdLSDSS(VmVW5Dc)kKxCJiRe at GVd*LJY-|C~OK0V?
z)p>PZo3+I`_GNKS3n$nk=oCIBL;w at 8nyShn8c=~np at fxcrKC`1y+rX`vc#9=QmG7}
zT4W3zlIgVPNoq}DXojV0rjapC19pR|k1%Y8VOUTna<;~)8IDt{wc3azXCz5BwW=(M
z9N>?Gby!As07NBpXR(ZUf%Gty1Z at IY5aV)LQK%3ZL{uFv0gy_|=zEk(G3eJGZ+P%$
zYc(ULJm8<_z*Akkyh1&1R+kXtWxawkA)_~B^sf8|`D0+cpVb_Icj2nSs|drQ6DY>w
zZFL!8N~5xX at VLi0$28<=vsy?4fpnRj)*a at z0CNW96sJ%Inl(0zkU?@k+-kf*6JjV#
zm}u5Sl!;Bn^vnz9ih0wd&1V{4sHXLMR)12?uWzn_>6zTdHVoxq08UG5d5}`15U>-d
z=ZT)`jT4DAzWoc@>w(V#^am`E1QuX+=zw{2#QJ~4 at V_;UnQ>;^CyYtd2d0=J7NTzv
zuF!BM-Xq3j5n`I~M9~!=2^kVLE{U#6Le}qH%m&m!j(|A^0^-mKPXx5FLnPaMR1F6_
zl0dVNhF5>rmh{{>y1ciPVQ#*k(Op#ern&Wd^OLQAs9U#-qvOCQk<wtfzWI0b=G?fL
z46ABb#MMl8^Dg>VS4o#bs+u_Q;4h(ln^(i3eXRu1j0K7c{}~u&G<<hkGsZPeYHN`O
z1&?xoXD&C=I5Y~4D^#N-1VrOFgk at Iz$~U-#Y`nSD*EqD0SV%4ibJ|?{R_vB|Tfg06
zd0XSuSS>!qos50jqzA`r{%AL1_o6-A6GpY3;kHM{_Qek3gTkKDKJ7_kf7=^7EFV at U
z#!k{Ru^Bug%;aXoSLt*3oN!)R)=J4Jk9mQwmvLDr*G<k8oiPrN9b%7mC&nV%N}&e-
z`^+dBB_Hxb2RXLZmbeIIlW!>5#iDE5W9|;2IZm%fzQkv=m7pfy^eYXpDduu|yWQbp
z at pud52TjXbyVGuU)YL-G5s=UUS(=L%r{#$fDmm&4)tPFjx==+`S+#Aq)wpY#dTRn^
zd%A-<(I~CTG+pbcnNCNI=X8Cf70<L<aaehYjf*W^m1CohCUN0-o9^&~Xs`?`;n-vu
zAb|@4$;x)5ff|icsmR4aV!rc24&`ih2_+X58OelK<GAC=7vw+58*+pUkZde3hq_dY
zqS1FRb!;8 at V3BH})^g~5%B99a2No+g0Gif6`|J1C at ut<cCfC8w1la5EWLuD$euc;T
zL`eC at hCnZATXV_j0BEFvq?9}DNO$mBpUm-^KhY_- at AP2=`(6B_JJ6yTJf0k%OXBhA
z)m0K!nN=DB1EWU(fm8iHN!63oB!KT46$4ChZVar+Rn`@iQmO~-&lVwOiAiM#g<ird
z;GRTAp+lmV7L&cGmx}dh392q>jS(pV(KLASw2dpgt13K6_<y{_yEFkZ?=~S^Nx+GO
zKjQHzS5rt4U`!zQfn at YW!lhIYgg`PB5-x at b5TJ-&%Eg#>1t at hd>!t%%$OR!qQeoE3
zL(fZR-7b at LjggFNLJ^pp%lZ$?xuz#T$+}Ha;LRd=I}~#JMd3f(^5uj2uRi+H37>|1
z{gM;K-Gq2{^b4KN<-}~I(<Gz1PN>Q66I;5Q=$rz2+pJV$xv%cuJEWoRuG+3c(`!%f
zb+;xOa at u?O=Z`lUTOZU4+R>G_zW$A`05J`UQWFmyKmKe!TLfB?hNjMMEuGmFuGKP0
zQS7g-ep`_AP^}it6i+>T at crF@mH5`4 at I7FrW@u=enLwRd!(r;Qj`Sip28kHa3?U}L
zZwjOt@|z*jZ2HaS(vF$1)~!46>p3_2JCwD}6eZT8iWwpFQWG_)6wpKUCGrkFlW9>@
zyY+?W`KZ4NzR>3e;;(j%T)_LE{?@xx;o*Hsez-y9f#^9r3dfOx_Zt?&_DWi>nxS;t
z&OO)tar7qxA(oA%7 at 5hlRwl2pwNx#yS)-_%>J*+x9cPboC*<$spDi4(pJvW*XXI1)
z9~4g4&vIww*QwXp*YdxxmZ_WVPX?->Ni3^AJj@|qsmny(*PGk&USkwFpC1|KGH^lN
zvV3804Z|(Nc{ai^b%-)34W_y+ at pVENN%*(AQ+11cTNLv$Q456y4t>mRaG__o7rDQ2
zVQ#v|9%PTPVRjmfZPF`Rw|E8d$~h(UZ}zJ_rmgFYf9F2Hulq25TnvVb!RFxyHW<SI
zgKq+)<N;xMLWY~94W)UE2AV`uwr(^lNw#z}?fOSlWr8&gRkUU4l*A)j{^-_J>ZVPr
zrf$llKBkWDQI%+$STt?1*>|qpCXt$H+BB_y_~PHapAYvOf8Te0=X+E<wLM6}!9<V=
zx}r_zJagEk2Kup5?^{TYFQk`}OPER1{XHYyzk at _ydLfVvCv+q9@L{+kbK{eSmU|}Z
z+sT=X)6fvOS1GR7;(9w9Yif=*Dtaf|LaOzoYa!oqghHNoer7E+R0fFOS6gg!Wll6-
z_;mZamWZdd)>M9<cSYuVlE)=0QRHw<Sxrx-k^H@`w#aA}uvjnlh>2TcQ^&i5{-{&x
z9GYP+s`XVC*@En?!@Rl=+3O(Zv~ZDuE9Pdn8DT~|$IWwvGYS!ugF-{|01Qe44z`q2
zM4NO!w?TS`dtEBbv9gx1ij$QXk+-bn$>(`8!0+H0K4K~8mdF8#Y?mI97%9S-Ad$u(
zso!Ws&UU!1*xJHPSzh5N2lG7fYK=U9$y9FUL`m|poSkJk*357cv51O5e}x<12_F$G
z1 at d-@ND;!Aq#rPyAOgp9YC)FN&!AcL0|JQ%ny_DBg_5w6P}VD~VyQRBfFX>_seCh=
zLi(4+`<52^<>XByz*2HS#y`v>>4~_0`%p)!CIWl6V~<@cCh{Vdxqr<)^w19z0c_Ir
zxGyfht{E}Pvk|;;l&}H{l19IIovqUR@|!7VD_3i$;`NlhmD^uL#gnO`R<77V#XqDR
zC`!8cUFq(ii4k##u$~z4R8rPdwRjxF(?|mzJ8Zsno!OCjV`tn}%GDOIkbaKz?_R6O
zX5z~H-pl%3RgG&hzFTip1uI5DugRymt;`%KFs7B6SY9e&9a0 at zBek(H>5$E6lsN}S
zEJBlGo~=o7UoaAAGNPze%5dr=nI%kwXoLYGlM2F;o(QIN%PFC!c<^mnIWJPTCNVLG
z7nkB#952O*m9}4a5~4cUYpU at q6jn9sl;@E1oIk`pIa0spt>;#(+F=hC93KwWKTJ0E
ztbK?%bL8vlHf_t!V^4|YPw#-`Wmvtu$bN+%W2&JWjVXjKm<9l!rZ8G!e_}GhLiUj;
zclHJtvf>JJ9@&n$B{?3W^Vcmeup9OHz54vG!4aJA(kx8ELp;_-I!V1fDMmf^0q0<1
z{1~v%s!2#CBH`dK86E=>%#-wG_6f$t@$-!FqUXIAbbj?M<$_7LZ!wvT1*FQx#4^9G
z+|7?=UL- at f?(z-tDraRxT(|5BFz8_uKnYJqx%+z2lkfG*H|czw?TL9f>p$M&s>rLW
zfq^b|6m8fGP6)vfJcuB2#fq;~t*taz&9z!(O0jC at 6eujPVboFbslG`ci}jaZlS<oW
z(XLk{Q57deR-|J_uI+-I*j at GEd2-;QXVbQ9Z0uW1<4)o&7ZwwJxSRO7;}B=l49Rv2
zG<-!Hoeq00qWM04n5-IHXEF*xu&AzMO-t9lDdsaHn%QhI2c2~teV<yn=P19ou6|d0
zl^{wT!AQ at 6&AZN5SGNs!x<yfL4>a^nY~FpI?qtpjgoSGW+!;Wd_cu&7ATb#~OLs3x
z_wEWiLf%JTmB3cb1K5f(1WX13oXi{Snveq4O<g3b@}$hl=#I;Lo8l^UUnPF<z%NJ#
zWH<D-ANrOSvsnS;AL~1t>yFUSmaL_PSyNk@$<>E^r53YfDk`<scB+Ba?mdI;{N6xk
ztR_|=NrlFEDC(}B*mP*8Ms at um>iQR5*Y#SN(S^B>myLEv37TEkl5dj~Rhh(?hI(F<
zKSs-Ra`AQ&%WV?X(MF3Snt7@!KT<BI>WxufiP<8Wi%O`9{<f~YL+$KHxFc2_Q?n{I
zx~s=^6_I(FYwSjN4P!?bPG}vcOHbFI3(tpN3;!`(a9BL3d{vxMxMG*P1_&p441P;-
z%CC9NkSfwF=7xm3Z6HLXkQ$l{u_2v~vslTz!nH{b)iLQ{9n>z#6=}GWh}5W?$#imJ
z5rg#N0)D7fck*wX+IP2kIm+?(7=LsmVKNIQr_<?=_qD9tdyG7|wa;X-2u{U{v~2F)
zH<h{OZ%uX}=?sQ=AksUrb at z9?{?JH!l_(etoq@=zgGfxeYX>r`xYd|T>)?iFd)z?W
zVCNW%R9;r*z+dutX)-7>3r${cWo3=IB*>V-?F;L}g6e7dl6TholJ{jVr;pf-^hK59
zofwEE$yTnGxv`l at 2|~eUw^yBCepmC#a=~V`nvPVcL{j&wGwRP(PE{RFyVFRAonEZs
zZ(XAAq3~zTE=UEN2W({4_Oguun`}F2V{Kuqcmh~-Vwn>SQ!!0NYcw1U>#mK4vBg at H
z<LL7Q9Sr1Jtc11InkAJFU0Oql|KZt2idmypyOW*-UBG9Q{)@SO`RJB4?uxcRu&Jc{
zth&B&XnM{Y=(c-p=hr?s_oJ1u;pC{NKEA5Kd;h}^uqV<Nwrz?XX1<?ZJ=it%+Y!iJ
zhyFDfjO1J at 3%(6;6`=500K+Z-BZC-5Tmmpv1DFQ^EO7wQ0wBE)Am0KgIu2mR_w0WI
zP;k8X8B7e7h(Bdf^32^RD*>qJ1n|rQRQUnCxJLE20cz&}{J#MR;##3C0Fj>pG`x$Y
zI=<WdIe-?_sdYMwcs)R(7C`$Vz{;ZlJqCc at w*l7u3SfOahlMN#rtbiL--PdNMg4{*
z0fsjK>}Ujd0Bs-b2iSw_eYOeU5%kNWsLMh0#bI3ku^RxB+W@|dKA6IJ$6f+>@&Lf}
zPXL}C19<i{z^M$tH%<Y}qVCUcxEp8We***ez!U!my!pw(3-&C|B6rSW&_DYDz})nI
zh8LgDBab}t$m4&7bA~+f$Rm$D^2j5PJo3mRk390oBab{jX#fTY(8~@im6&oUU`%u%
zJN#c=>_?ITjCePL1q2Y`pX63MY*R at TJ#kEuZ7>i42 at rG(2rMMd2Q6%tkBD#r0h}1e
z;kyVu;&J>I+b??WIT2gzRtO1gvw)z20tpEUkkBq5s8*EpAj(LoXc1Hh%$e~J3Sc2b
zG+^t^oH=uUb7!t*VpxEMScFD;U5q7IiY7E;8J1%OTCkFmV)a&GHCnL-Yw;u2VLjT=
zjyO8di7s?w10{aKMr^{*=)o`8j4fog@!M|o6X-)fc48L>up4`@7fB3a2r2AC8pFt7
z1p9FS2XP38aRk5OsMT~nYlXLQ2mjz-qFZxGRHS7t+8_P>_8v1nEBz?`W822vh;rwI
zJFyvCnY%;G)KUD7KX4Z3ae-Nw!c%FKCTW%x(o(5F at 8D|g+E(;3&sy4{^xru5A?eEW
z`+x4dV0lfxa;9>6>hjbj{x42lob{os{iH+;_h;l@&*;?S2}f_e{aN$a;#xFCMl6mJ
z7cH(MK4oz|3Xw~?nxgv5zpnkk;u>^D>n)C8esq(?wdDIOjuMwGt|Pu`G2 at 9o(m1B=
z=jPqu$vw&4RLC*2qwEFO`DKtdI0+9Wexa2T?BK{J)~!R{H4;LmK>avT!tl-<Ep^O9
zCaf=yTzN(&AUD9i>aUA6+rvt3x2W5&z0 at eMPg7@%F@#1dO?v at 99}{e|^x>jtIAJ|2
zOd1)FoJ0FJP5e5#Eax=m;{-X6+4}O830cLMTzU;nEIJb5Sj&Xg>IhdYtP!oRD at OZE
zvK^xDoLQlcQ0pmL4NUBLYb~BRIj(ZfsFLfF$ty{EB`+i;J3N2f^PSN3N-|y;jZ4A_
zolgjLXk#+$70Q|kWS~U7uAZLGb~fE{=`9o_?T+QcK+-Bu{)EaV22+DW{p&MsQ3Zo)
zGVK+eQffGvnJsS!AtQ#mCK=z!s-okc5HI(?L5r`(+#poG%8FZ(kfI~Ql5#?6l}u8G
za=ExTrL3qzpeFMai+|^Zkl^u8nJcZI*W<ggs20V$l1MRzB<IuS2$yMD7sYI~zqb50
zTGrg_>MHd~D05bi_Hwk7Pmk(qblfgiqnNX at nW;v#i at 1*#T|Qp4|7rx)m|AUijx$#t
zy}&%$>dxR9-$4_+Cn9`jb?|+Ic at JZaygjOKJ>GB6yZ78Z7TfZop}E0aSy!f89_aqw
zquXm=hOhowfQI=T>O0HaX>SA=0QAK;PzntSWo~41baG{3Z4G5^WN%_>4K*<!Fd%PY
zYCJqIOl59obZ8(kGcq+WFGgu>bY*fcMr>hpWkh9TZ)9aJOl59obZ9XkGcYkZHa<Rc
zbaG{3Z4C-YiPTnkTocz8zLSKomt7G84qIgja3{*X3WB0Q5W*rTCBy(hAZZdrfhviJ
z3l$In3spfxw(>*;s}%)Y5d^D<JPX#^f`BV1uZT~UPFULNd$0ZPy}r4>`OTbj&pC7M
zobP=10t5h{0MY;vxULBBS~>J+hY;7a1E7^d53r;u3(SWB&|Z)0f>@h{=qR;Egv<2+
zNMDcQMaN$$*TEwgqyj+3qT{wkg*)!K3_yS~07zC0o4N7ZFr6I$BwoP%tz&S7>agk<
z0467K*)S$vxaE}Ff;L?4!oO!6$7L~r3?Ko(D8XfFJaY?A5m7#Y`yayX(FA5ZyKPB-
z8~_P;e&h}wpUvyH%_{@IeF6X_HQWZ78ImW^O2MD%VCK>iK(Q8=ENhyvD&vhJB26d}
zYenFy^#laLK7tBlnb{eFloWswWH~c3LPEqg1cX%L5B(tZcN$8mZW;wR&I~$;1OmVX
zaexhk`0Xqi5Pd%$N%`R-!DoHdw;5STf9A{fXikY({Q?vdB)1VkdG(@B+t0=8D91Ml
zKGE3l;22c=HBW?urz1LoErvQ|VjxLQZ9x#5&k5i}C!h>|qCklHvV}=p{w8c8)Q}j+
zt9>+YKt;V0SQZ!+n$Aub{N~AtXQKf^W;~CR5RC?~`I|W`HtNsi3bAF7HfwF>OGmxE
zmb-a*dok9b%iY~Q{1_f9sHic^#LfnN{~J(up`x7)w8Cg`<_cY?2$w+&+R&C-*;(0z
z{`D6iD*pXHN2CBzv=9I9dk7*CxNCuq#H3QqEku-4<kJ=MXH^t~djjq!_O&iGJ>4}X
z8 at 6opNzRn4!tGw|&~G{h$9A7O+`P-^*=Hfjg4iuLKh>DL9x`U~RY-UNY0^AWB}7DN
z`!w&Keu(A0c6F(A>+^Zl^**bgymZk2TTtOPy+d&s&8t@)jIA%XzBeIjez)H4O9BxG
z^aqe2;(0LxKXOiK$yME^qMW7Q-c_e&7B;k^Uw+GxvTdUk(tO>Y)(y9(_wC8^?)7C3
z*Ok5Vbn;s!U(5>{%QD@*@YZ8i(^ihOz~W at S@!p?bR9AIw=#Xnumd(CZS8H5wb*o8w
z?t|&`(Qd2CbCmm=nB#{69v`?Pa2cOGOi!-~yfdl7VnH#90L8>jB}4*3ASlZu$FJu`
z9-yDUI_dtSy5`~kM-bzfrO|BP2eI|9ATF8DPwrpxn_HRue_WXZw3{`cG1&Zg0m_X+
z69sIPDMVw0LY~0h(lRM2$zn6^EWn*DSloC^K94C$6k^yCXe4nas_Fjm>Y*6X{}9Jw
zL<!(}If58LKyxy4*R#_VM3%Oh7V}}+Sq+_%%p9kNCAsP9cF%U+GRY-JttunZ_YKi2
zMry<FST1VW|LV+<l>K_Qo=o at oj!e|%hn!=X4`hv*Xm;?j{I0wxXmIUqG#YXX>>k_s
znmm|UNWn&lN?qQE3{4+sYiEl`PYzf3^r=@o%I<E7xy%fy;&+Y1RrK#Hxh(X%J#FHY
z%WEzeUlYX)4v1ZAJnc)~x>$F3H!$2nli4X#>HRG$88zQHJx^=VdUJKDNYrYY|89G@
z{jMDK-gb+`*Tb6zoT?%k-H>W}by%f~K31%%@m6qDSDZ;Qd+JH>j?Z4~XOX|gbPG9M
z=(InZx)PgdrDLLVP;>^ml4Wp4my(jk;fVtV!g(1Y8HSRg;5Fg01ibf)BGFh1giK+g
z0LtP;(NV*j0KgTJSCr%gC3TjM$XJDBjC&w>XlM$J=K~Xv1(ZLkHdK>DF(N5Ixp^0&
z43w8Bl}IEaf!zFWSipRg&WfOF_WW$9<X at -otnNbe&dUZbvON{Q)$(Z^O-)82O*1Al
zw}DnTa^ReU26;(}Gl2}s^m5bH<gR`3*fsc?l+jRatx9awV~3kh494<IHn;|k9uGKt
z!`j}CEh7+=(K>5unp&hBr#+ru8R{{-bBlFCw&LAS+I^GD<zfqP7Er}^v4768fD*U_
zG94XvtK4xOV?F=D^+OWiVP?=|wh{E*xxB4>PIQb=f^rnI!#mQ4!{T!V+$bUH&gJtg
zFkMKQHP(24hRbIPIot$H4=$c<49(x>B at BQrPZY**`5fWaxvdGqAhen5Y8u37OR+ie
ze~{6iHE9*$;w9dI!)sq{<Klx`!r{}>s(nVEj8ElzA3HmBxD<6xU0eEPX?6r{(;c^s
zTVK{}Ze{emes(B>l3ko0Rd;Pua->1Gj^jfmBzLIbYLj_XQBlmtg}3dgO$uj1KEC8R
zDCcZjK&>*it9<UY!>un}si7z?kXa*6JsM%2<o%?uexpN?9|e;!R4=X?+;65i;PeGc
zJt9PkU94l{zw1rKa30~h_T8pHkGef+P4>?jdA=tmE0W`dzO|a|1+vC^AUG$2W7Duk
zRoXFVI_zCpl$=a?rzj|R_-lvo1)?NU&sgJ$wEU@)9oxGrwD{{C+b)dA9Mgw&<ejZ`
zXp-8_ALg305{iyPQK<y|2uV}~MF-QA!*272Is79AYqzPN_Q{&Qd6fU(ub=o&Fiw=z
zFn>tCdEe+kO{*8 at 5X1XPDx>QoXvIh5Z#qlu-<{oRKcM$|B=`XJOv%a{k;4;zZ+CDA
zt+HltrVQg<THC81N~L~}?Q<$t=EXKlsnRt$%@eoX`&2?v`qRji+Ui9&%xsLz8`(!y
z_Zlg&j=f<}-s-h>YmEBWB)HS0C&dfLA4kV2t{uDh%D?5};8i$*VzRq+@=dgS?&%PY
zze;;VtPlIgsqb$Dzhry0_%pt)CmO3x=X8(AWN$ln at Y+{4)c)lD%A~%{CE#|f%cV~1
zy^ofwR$9es$M#zN*hL}rS9*|cgf6vB at S!M1o|P;8{O&!5i)RNVu$<ScYQO71VsS-h
z3C;?w_}Spp+}R*jzL4Gw9#*S#-FGdjY#jJY&;rDH0H+1p`OV%6!|(&_tO!NrnEVXE
zlgPhNU+(cxZEYN%o1t+pBVjTc(copzpqR*I+=h at 0Qi{olIdY$PNg7j8nm$M=*NhHw
zqJ*iDOC1rX$>pRAhjKZHE{M)auJ0bYI9)#PfA9Ng|Fid6&sux0wb$?YKF_lX=ITON
z1;`2;VyjT at v~Yhg<}$(W$N*BpPSI7O+kNSQ!9nzoD?@#6W#8MTFme8Bn>avqX&Z{m
zEp#yMgF!<W_P3GEL26*XrirAGCSr>;$vcX6=@18`^v^Dkl<FpR4+d_+IZQs_49;Dh
zYk=vis5K69yQ!~YVN9FLUBn&oa?W_eH?d_E9Fh&s$T5goN01-|&NMd*wq+?@DeUO9
zQ#a)`2$ItJDHDMmBWYWniL{OvxNa)Z^=@X-EVvY^owULY`_9yKV5iLidPRHH)9aTd
zvYq(hn-xgER3vapkC!?)sW{5=^jJNTBzmxAqkfHDYG!o-=Di5c(Hoz1UvuJUMtaGm
zE>y_D$NJ{U`8W18dMaTq>Pcl%;fki`llk|bn;@8E<)n8;L*>P`(E*xLd!aO=h at iPG
z=SA)1-axJA(2!o6Kb!_c23YH1Q20eQbOZQ5)V>v8;XejqWRNR9l%HrQm<d23i}@x-
z3L%e>!+TaLU2oPKt;+E@^ltE;PQ9f524Kyn334!m#99=@1#$o^Rml+ZWeEk;8jxPh
zVW7|jga`l!Z{LEM)sFrQ^TV^*w`?OLuW&kt)-<?RDArU|dcn9>KN)$vuhhu;mqX5M
z-hg4EA$^@iPo=JA>y5|AdoML_^(r2V>2ek`dAqLt;qAcVacD~~`Vo44(HZT;uH;R2
z(l>@5(vl(~`a0Jys2x3f3gJdQ!&N42VONjlCvcQG_EBCu%Jd#0gQP+&IpEMke-0RT
z(J7WQGxf?Ap+mVQR-*xs3&j>SH)YG+24)`RX~uP^IOe(5Y9xge<n32lpeC}b<LnE#
z*Y-shtu7QGZ`-^qC?6r9T}(6)w-&-}yH9G6XMKi8)e?5qZv1_i at E5UjPft|zO`w<&
zZ#Tn{y`^-D>H)Atq(PXyTY?#iCMrvY2rvERKTAYd;`}Jk5Ymqb7!VBrz`#J?P)I*L
z5Pk+=xg%@>aoqp)Mu^{YBlve-_FvPeU(#!gE-7fd)iM#^Y8n%%Qz+p5&XKGmF`uOJ
zdUNefJ@&L%Ib}rQ-%#!=+Kjr^iEUYlopPNR^=j|EmsSlto8Qr-L2Nrl9Uf1SshZTP
zT{8&J6pzPDmKt&RbK)4|aP?w$t$X-9bTS*=A9>MiJN#sJUC2$95&gd>4Ng0{9+~<B
zxQ>11j^ypMKtA&jO!O4gplw3etj9eN1ZS>^>iKEcn+TC8{OldKdr6jJ$6ONyP14r(
zt^!V4q+P;rkvXz)L2|_3-6!l0!@YvsN+bc at N+XZR>Y7pWXS>$<sr{zgrvK;Aj58&k
zZ``)t>+!1`I4z;b->h+}A3oG7vXn-#p=RJ8F<v|>jI_|tmkNn*qVO|Q0-EkRW5+9W
z+jFm9IlM=`jGwR_55yG-?Kngn7l_MYeUm#}=Fk5RUVfcm*a?@af*=FtfN8Et?&f%-
z4<(&X*nXYBkMCKmyg~vP!d~5wuweQDA7;3&&|QSy0(y!LX!yd*7m;Xyn_ESl9qAr_
zDj^p=U0B{SS4e8QIr#i<_>4<eVR}(+=Ef}eOSX-&YK@%Wxu4mJM)?$&YYW+;qFHHo
z=_C<hvT_hpC|a0wG1kg8LMiQR3S<1K;iT<}u at JKxT|8ITZFs%T^$p!9wp1he&uH7-
zeB_DKX8VI0x~DS6#A{Vi99<Icbzw_rf<LaWd_dt4-v*kmRlh?}c4EKRT)TFCFdtVY
zxnMjYYsH*cflOC;8hkq*s<0BHC4bdz%vX>5!q4%oJ^S*AbT at Rm$NTU;o81EKkoyN6
zJCXb#j>P=lwHEJFWAZMea at ue*R~!y%qy($i`cD^_UckL<%C1Tvutjhn7FA$ig&W8F
zVZiqfcuOF~mW~w$o@#E~I3V{aW^RBIMB(63gX4se^uy_|fD<H;s1HM6Pz1R8&n<C+
z6tLWi237lMB7(3IAv1$4U0aWTjgqfCFEe$AMF3)#4VNH<071EyEDHz~!hi%qd?Dcw
zEW{DKM}qh5kYKQn1_=V6t-<FY2orpA1Fd1;IL2ZR7NQ5yfdCMItIksUAoj?}$S=gs
zj0y?%3!{bjM}3(#BG^y}4a+>U(7LOaTV=9x{16A_aYQ0B7N=tIp48kU`M~<B#jV*G
zyei!gD}DN}e2+2q at hD#-B|j+%qjFDNeulEZZJxJnlE1dTMAgKq*nT1hJ_XzIx}LOp
zsB9|wglB&u)OItfDm);5-j>sw-1 at 3xfDzzs_M*o4YRQ-nfAmUkM0A_TxlSGSLTAQH
zrJ%={Z8428J^Q_nn^Z?;pK{AfpNk^@+jrljYU!yRymPs2nAc(SmR*e|?VFC({;YF>
zkCD07LdiTdnLk#FKBAcy7z)Q0oy&apTSQ=Qc5ezYYpjHNp(7~NCHdh(!{hkwxfsW{
zO#JIhWwebKpESg(zia2RVOs$<%=YuiAqi}l5!hw0sPu9Wd<2z6PJ*hHck>Nn2-St9
zh8+lJVjX>%k-=dBgw+6gNw!iS2WVp{K|VSJZ9sF$fcrd?5eO=jNec`SO4P+SEP~<V
zyNEY}K0sU=t>x&1-AXaHplqd3J+S6vveh=Km8G`O6+oQ+^BXW3vFJe0;Jv9%H|yuf
zeR2xfU+CNeP*^T#@lOp=D5&R877>E9Fh?K&1VbW$u!*R*^D8}{>$Yj``nAm4t}cS5
zR;8Bp4L#YC at I&ag{pHhMRkf}4OkHREhNtsS?CL7-GCax9?KSOxnT~iBjit;a?oD~s
z=k2!9dN^2QtV`8?FZ<QqP`_C)1wC|)(j!{U@~UL5Fg{2qTWjcK>h4wx?UQ!O+PUjj
z^G8**wDBg$Ok?h|3uBudVp-4EJL|nL!fe^8P=XS4OqC84OR<va9nzk=<1Dk&lvBkn
zpX{5M1~kQT0~F^y|HE~)K-Y9;;cx%{IrkEe65^&9xp5^zlEx!mp`?-<A>#c=43(%y
zf+4g_sW#eFV<p~8kr~gi;*}0JS{jdzu*wXosJAJhr5+t>jCPrY`_Gr#dFb44opbj&
zXa8q^|Ni&+_BmSzeADSlh`;OZ^r(au-%pKmuRl`u<cH5LoLexe;hKt)^mT3G53Jc)
zvExAJtDC%UWlSr)HO2SI@!a6n`K>yn2F=)3P}*-s!kv2Oj*Xr+C}Z8Yo#UQ#_-1t_
zmgZFT5BvSrd4T8+vII6@|ITjD!-${%^?Hm~!D1`r(6+^1Z2_|j=LVDsHL7>%Q>RQt
zBHZSwy}+L$?VaJWFA-vlslIA2;?@^o+?wj;Xabn)+-`8Y?eD`eli=}BG}m+kI{)$C
z8xVmNScevPAZ>97mFR;_cm)Hn9KCQ7J5U$1<TxyNkc=&ekT59fjf<;Uf=d`lwYiCF
zh(a2!N-*!^MIIWVi at C#n8s?il^a|of#&+zL$ubymv{O}#L^JYYQwbNM%n5UmM{96X
zT9|UA()ted(GvMsieOB{G4qs6TkvCLv&@k at 2uBu*t$3@*OhQ-e!Z}H!m5y2V#hSY?
znWHv}ODav3xrIMjLQTxadzeqgJ5i}#(n<DuYGWi`rkZB+>RY%Z4WzA3G%Za}Q^K>G
z at lZvpBiczG(ddoQScnbG#(CVpk5WrI$QmglPRo7!BDtmUQ))KmlKCc%{Rn%ct+Z7x
z<zlv6XoaD?wiu<1|1nNUnq*3)9Mq+D8-EYe(1e&<T*KDLAj5V1&bxs}(uO{apquMA
ztY+35cDrZqa)x8E8mDlYJgzeDKjN{pCa$Y at RK6K*wwRlNS_wmE48#cbfSK$Sf8eSf
z!sqzAJW)02dD1#;&$b_!Wz20PdXjs8dJpEki at 8cWQ9+#NEbEJ#v*;}SWROgd#j-*w
z<dR%c4i&DZsk_>zkL$0kj<#(Qc&A2a#<(5~M-Eru9cFtO=eq at maa2MiQeruy^Bn(^
z>Z+27jq0Sjs^{s&)>Hcf|26;j{wJmgPF7hj=3xf5F$+IPBXVdZ6XkXJmdux^Pjp>f
zPkVF+{fZu<GxdDET>nLXX-&7vtjl(9JKHXEX8Wi3Pn$IJK64>(kXK8 at ARe7qKjT?V
zlgKZRn2tGk7e!dadRd0`DC4{j;3&@F3jW3wfP|BCE~Ed3RX0x-5he1G9F)UyRIbZU
z)lkieDAiH*P)RC9O;GcQ<?56=uWsw6`Zb-e=Mro6ZhgsuWm(2<N2J=t_GZU%XOuJ5
zIi|)JPwzcz{Vem at RX_ZV{lE6F at E`QwGQ-R)@{d3)d-xm_l4S{Nu$0)wO5BYj_yQLK
zTpx-On-w1_9#%sPSFMNilHNqR3?zmU!({}KEn_5y$d|eDo)pOYvQRz at U=`<9DqCeY
z at u}=3&PkPgCwJwcVi7CNYK~AXRh;U=nI@@Ts=pdUOi)vaJT+a-<O*$8AFDm;yl$W)
zbga(S({zd6t`F(6`k$7vVyrl;n>EauU=>&=t<%;;>xms^d+i*1t$oPR#1Zcp>X_(Q
z<=El4?Re^RI0rb#IOjOeI!%oTs at 0!4?_GHQ7w0%BuiFi+H&qp%i%^|s7s^m(%%O(p
z$$F9g${sHdv|BDqk<QhV%toD}9_y(xOdXKsI?PVc<FNon%G7oBNZqnRWQe*WQPxuV
zR87 at M$`R1jGggRIVBZGR1th4qrBWT%1-ig|h6H=9RM~6QX}GOxssXC_R2Qn19Q38i
zRmI4#;>5jUbY$Q5HX7UL*jC53opfy5?%1|%+qP|Y%#NLkb at My_GtPPMecwCo$NQzm
zs<~Iy#Cq1=t47Ud&P$3hlkL`ZJEonjWiN~BSxqYkCC?@PL;tEg%d9v)RoI7$Gh!3v
zO|u2KCu8ipLt)YhzII}Cq_qNeX+4$9%D+l3MTtNZ&TU!Cf at 4)jl=0Le5M_{-W!u*r
z!=J>;;k67}O-%-u at 5jf-+<R`cYINlmx)tMC7NNhYinjUhu4`4T-wJf5xHj^Po_*FJ
z3YQ#6Z`Rr*jdwwt26b0<y_ys%8Ahj)>z6*9&q{@YWkj-;3N$;$=yE>_;w|OqaHq>+
z%fCj;UM(C6dePZ)tE5CUmeqcIr)VhscxX+-j8x#!j5dkUdBg%>VCc7hSZVy#J5YJd
zvcu|S?1<livaT~6TbDJ-89}c6D8GmdH+IuT+a^E0@|uBEFIV11HzhVpo5WvE<-MhJ
zoR%VWxP(XtQHq*34a5qE#<Xtv<!hFaeG(?btpwj#SnXzcq41LRaZ;-&vn4lda;6Xz
zwEwd{`@M7H?U%7m-tR{S*eT8+27>AIe8!6c_UGR|x$e at -$>!JBr4zCdGe)5m0|Jd}
zL6S(0Q^wTSK1p0Qmb2O{jnW#O5*M7#5{9u?^seU_bHz at jF?jnM_sKL@#edSiOIH?X
ziitJdupqjIvc9P-l1J;oxv*OPA#C&M#yDFk*P$shg)bd!X+jxt<E7HD)iX|+jQ|vt
zjHnWLHn<bNwWDhAvC9{~FN_|~z_hjdo~BF8-hLgYKR24~`|=j&bmDXjkF<5;w_b}h
zjOq}PK$y<yrd^CXzg>Y}0Q|a1^;h%p at MnN+?abvGHYhY~Yw1l)<>J|C(pzb2(UT4(
zu`$qa0`KxPS*+&o+Iign?w|bx7H;o?{tLr;7ME08PqB9HIiILK-B(J|wPxfE3SsJ&
zm>CpmHZt-|ddfPBNx;MFO)mne9ft;W;6(b8<<D at ndA64KM+aX?qbaWdf5cdH75^M7
zlb30*w2`^eXerio(>T}aF?#L`@9wBy5-WIC&!!;qbJ%0KPs{;tZ>_b{6(iu*Ad^3r
z$d%S?(l;%4-M%CZ_E86}glJ at Bvt7=~FC3^98l4~65-p0@$1$j-$VLQ75UQDKz(yTs
zVO?2B3|+`V)ItR-DX4{t%EkgU45=wVV%z8byEqr0dA<sA?(y_g=F`!1rh6ksdfUvb
zHB<Yf9lmXhi6!VeJN_tk36vP>I3=48I+3;Itr2}a2|jZ&-k-VI<6Y*pQY|A9^#t{6
zb^^v9*j9<>sBLKjMUs;(5U((>gP<#+%9ytlT1H}h+PlpAG&m6+u$1lA=i!nDUq$g5
z6A`-C>fBY`Mvh8>^PK5{tQOh0<EK29nkg}r1Nc~xaR?>8%>+3^a9I{VzciRr6Dp4l
zu|J2=)zh=vVqK0%9>R-Se>l=_WO~e5Jtww8KiPTR-Aas4Bn_bhw#o4wQ;JaGJ<P0A
zQZr3VvIws{HYvTT61`kZhEseDuyMM4kb+`!gsjy at tSXV-1^jAV<$cps{TVwmjo7YB
zReJbf)RISK<MpTW3b9(VShr%xZE_gmMk(fN!$;C{_5oko*U#@`>zYbfUj$25*4|2y
z^Glof_u>iR%GQ~h;`S{*wwkHfR4pLD?9GJ4&0_**bXKwTKwl5~OJP;gECoxuh3R?(
zO5Vh_u|e|5(nP-83pC8AIMY at Zf~iwqGd``5(6nR`{FWZ5(T_XNWdgfI(XZ9=Yf=fb
z(~4$HR+Hj)_hL6Bn>g9Zdv&U`8JZi3;|e6s3OuUg#!ENiiQ$J6K9!!ydr#Qoz^o-B
z2{^>i$FFbIN}gV(fMpJZdew4KOIXc6R+OEYo`VxtDUW~rQpb8bSyw2~6mr!uud^X^
ztovf0G^D_#HO8WHu>kDKqPaI?FFpPY3av|)ii9uAA25>Xqw!Qt9eRJ5YDrMzELv!3
zP2OYmB3 at V2O%cWhBGe`jfrwr8IfQ7s0Ba?ChP6q?pH5^tM8#p^1He6X%Mk1Sx#@zt
zl{H37Cop?xdZnf(n*OJ8*NeI(gh%|4J>br{P{>~_I&fYv%0P5}1nS^Jeq?=-0?bDl
zeeCY=kY~DQ_P)WYXUAiet at FzIj3XhB$jKHsRDKR5_kZkKxY8#Wa$t1IJ(-Sj8fzNx
z?@K1!ZR$28w&qs-ebL68uE_|@f5suY6RmJg at O`(BHhZpGfsbnKixtK6g}@H3h0l{F
z)Qh<FB7|hqLnE?c9Wi3e`A)JKc4I;E-@<F at f5(0P1MLm$*6&nrhfo%KY&fm>kg%_W
zu$y^?@?cr54D^8sos~Izkrn=7miq}`aG^(T{YCmm?Y7<SNx;}3noDbY`Ef}DG2*^S
zkk@)^=#M at 6ub{mi5heScSpT>c$;8dX>fV7-(O1a|h-+YSshbQ)i&Brsfw|;PUGZ&a
z#q2a_(PkGluR!w7007ubq?;17SFF<&5k`rDZe-JS5!Jd1^$^F at 4blfE9>oxZ>kgP^
zY at 6Ow+ynGVnVEhz@Gjg1h5i?H4De3p0^4R_`)Zyi at SiZ;bTAn|pV%pbKXts1?paTD
z*Zy5hkiav%`YC}+9~^E={@+K@@Y_BxeT2TZ9)x!|H<ag}v;7?hjJ`1MZsATLPKoz{
zPQpk0calf6I|pJzAx={@=>%bB7VN)(ly}9)XV4}X)K*}3NZzKJ`>dR79cDPtUJbdg
zlX1M{X3F6#PtYChGy=1<u$q#ba?7z(%qY<Vl?OSkM%VKfLZMbkR)d7`eB1a;qWvm#
zl4k1<JYCCBBUyN51W!^<uIEwLWi<11L_b~i-_cHRhmPZ_`f=8uPUH<Cp`Qp<c7L}2
z-We-+!@43e;a$NIp^a?52ZR&}8&<Ftop{8$$X+AUui1~Jv*1kRRU1u=Eq%1!eSz;6
zYzxmCzO#FeH8c@<z?+dh?IdIQ?c~Qj0dEPUmujzFpDMm?O^!!)wS)822H(E1$~ZL%
zYUV{Jx?lu(ajw)XbeQ8`D8DOuit5OTYx4rYU7()Ax`#wP()-T?Z6%Hse$Q-WN+)o=
zco0mm#vT09P$D{iYN!!fAuyH=sWQk^4D$r-oo(Qc^U56MddZlXWG`3m&W_L3aL<U(
zz1KgUo|%N>3bCkYJ>+nK$-LdPdVat2woZH?-QhfW&3aj5vGI+19cDZSeL=`A{asq)
zpDuv(np?V!+%?!{ca`p0jj#%<8rt#atdG-oe;wRok&8&GYTH>JexYhfOZ5bx7?bFe
z6S3gUA|b7Gtr7QD{u_;V^Q!Qr!S^$m{EYqY2AlNjq{|qqda!FPnD;xuiVj_Dj*=dc
zqCzUIcnnLU-2EKKL#&$<#v=Jc+=G@&u2&%O8N=|h;}U~4a$|T$9&xvShQ6>PZw}}L
z)8kGt5&sLFIaq9`<F-kU=c>*GuoU3jEq0yZIE}~ZSI at zl1@@@mx~-UAzvh+W#3ER7
z=@D4*^y*DF;ekIp1Idr_ZS7I3j+F7N>14LX>dqV<7)C&A8N?gpU)^Zsb7+P7ZBw1p
z9dvg<kw=VaSu_B1B$(;iHD5frK##3}ALLBDkHK-#>b^lu+Hx!Y!zc6NxbT5mlc<+M
z{kVXtMloY%4(<d+xx6RXB}ZI$Yxvn_GGPa at x3Ip8{cwKi3EGzeN`t=Ff2&h at oOOh6
z%xCZuSD;5HZ?uh~BbG3vunhto{Pw5RLqE?A2tW(#`L~p+u at V{Kb|G82RWahQg at blh
zOUE<!FYnwOh2GdVZaLs)ypK0pj{_dS&2ZRqEmg at U%t3h^0Z7a4h8^l(%l4&m-Tp3(
zIh*gIF;xy=9wTypv?07Rk1BifT+Rr45NmGOLBN})^W?u-^tJC{9Yc6$j>~xzvyIzH
z=ge#xdMO+MJ?>;CnE`<1{tvJ12)oX=XBzKmK%#e7tZx at YEzKo^_#$QMSO*-K4c8{^
zqavccdRJ__UP1MQ!g-QoT)~9Ou09NZiI}9Pzv+^C^BpmRPGw0h$OdB#5HCD#t2-Bv
z7tlrDx|x7{+l`Vx(GTKWK_YIj3rL^S6Q+L8E%_YiEwgOw{@y at cP|8;O5G at iH$JtBe
zzz at r&mldiI4|rZMdz(U#rytb6qzQ9DI%##E_Od-gOL)g}AqCq7n2q6Vf(VYH5MgU{
zu4cb#AMk&)?b!4!E`)wV#spy=^KXF*R+DUdSWh>W9TiXyidPS+{BaK9S4@)G7J*~1
za>Hjj-Rx20^@Cs+hv2O7z<8iDUzeB*oDJ|1=vvpYPK8n1jF9W2uI}3&=v<6l66gi0
zW8I<dc5{*xrXjW$oiWZ5Zrl<5t`4|cP1CGkFPQNq$UdnDW88|`!89b(zUH9A2gcJf
zKJKzg=xqD()s{|5rcR0g5m8pqw2)8=Ijx~v)z%h^z<nAy)Lj_!)>z at xO&VDLZ`?!(
zWwfR=^}1hmq at o3QiD`d-=1&TbM;UQnHX)hex?~{->_3Mdr1A9$qVBuSY{L at u6_q~_
zxE{ZGA3t%#dS103zawy`+-Y5pde9f{AdX-t(1Ws$nb9W_-%#5x?RTDUY|>w8-P0(Y
zf7JYZOz4e at Xn&I3d}F(*=Bb$eqS)PVhjfE=yZ7C~AXBgOgN)-mQ*4mY3fpvo?{{%I
z(Gh}J>`!#~xjTt-A^yTnX$J)J%LlZjaQ8#snl;c>pJ0S at ppRk+?(jP30&;F7`&D@>
zU#suFoGs)h(%tpWciW8${%7JkTs~vdKr_Ep|6JhWGNa~u?-TC+-k=`h<rTsS;Z=V-
zQ1rfdG(;arBp-^`=j<_Yd`H9&<rjsg*x2uOjU>jmpsr!JHw&@DPg|T-Py9c$T*E`_
zvLKz_*TZgk&SIPhj6OKTLE4+-50i*vf3#P at zch{!TsO3}@{+kXti=r5IzU8hY8=+K
zfRCjsJ#_^jKlPiev@<%|6%)jx`azDdxZ at bw9@IEtnI+<Qp2_m;PyxHQgge3axz$R1
z*@e=FW8QeqHTJPrGf%H;5&SV4<yhpi@$ND^q~K5rBsVO)q3jG6C7%Ej%N;%#RyTX1
z;F~0H5R2t0{nFavI;M+CPUVY(jLMH)-gbMKcOPwgP;d_~jvWF0oVS{tFk_M!(TRG!
z8i93a{pES}_j3hP%y-nam;F2EI*8c?tPb&TZ)yTk&ajsfqydQj2F0451443cUNm6N
z)334a1FuW+xvv^P$kwVgC_LATuvOSYVYHh5hh at bBzuQ*S=jXlfRQe{Q*3h#gQ61V1
zNU1;GO6Lz(dBBI-Ut*&DoXQor9c6mZLdQNvccL|DXF*qC*IbIqP|vl8p$3zF2^8&{
zHe(Wcem6Oes3@%d+{SY$igaNPxYelg^aZD7N-9R!T%9Cp(xVwz-AFlowQ+u+U>%cH
zhSLUB;_KU80p(Et28=o-r2$EtH<EBrnD9D&Qb#jLE&6)bJ>Sr7S+;RYJDk at dLTs<d
zZo#gg#>T2q0DyFX99+^FBL+>%gXqoAM<3{yB;Fge1xSX!tpJ&=1Ogg}%D at 1ZI^@V&
z%&YB~P#%IggmOd12il{JP{Zn)Dif at Wo^j|n+KuZP(RgVe)^1|mELbmuj(uf00pxd%
z_~IuFpnSKGoWP$K^_|JNpP%93xUNdHdyBlaUS~w+D*U^WF%BgNrc|f`S5!GST(p4V
zBYca_qcb)x*tG;>N#*?5?YQewnwzyp9lX+dSh?NM6szG8b~ub$Ao^llMEWQ;(xP7)
z#1M<VYQ&$PfL3Wz*f at Mb9R0K>oita2Sm8$y?}bm<u^)M|<`?c$Mtou}em1Ok96#1l
zhV~^Gy!?41%VX-yP!}5mrk_}j!RnFvEyz1XFHBFjvh3y1R;I(Fs?bO*wOFEUNcBL`
ztfO%xf8Jo_q5WDDBWaDxHIq-oLqL%{kd;EUQp~irRG at m$H562gmdQY+&lDGe8au8>
zqN{OP;eO&sA5Ficbrj25meazsGMtWbP#qhniWp9#<J`3RlS&IDs%kF%qp59O+d-|i
za at Hi$s#`O6r54`2#kyJ|(tKI7TDA6aUbBg9daa=OPqL<3w^eMUyY^ZQvD%QzWrG8^
zP2`mk&i9>e0dxtJ&M$&9raxyo2iVXJhf!vjxeiqVWEvEZzlqo)=tMFT&B0z}tG-o-
zBC%HvVd?L&Sh at 9}?jrK;cLUV+qmD&=k)ib?4zvI)Nq8~;bk14ifhrI)ACd{D3?i-R
z0aTqeE~R!1unLj^KpexI#6WgQ?jK7%0vs*6@!3q4(!H>tz=#LNYar>?vL8jh7`fN~
zZk|y)uAQv3Qmgb*`K;)w_|19&`!?vl_q%yc?Sy`c##*gC_RSsNPA!wBlZIYJw_MpM
z*(F&vxeBxPrvVxoIN9tS>=k2)8Cc}je6bWeJ_W=Yj5a9dUR<yMW*Mk*F-`)FPTlYR
zb<qkxOnu?#pY`1Wj*Ao*)e96-1GSsSW^OlfM&kh$=A12?m~$`;O<T~urBQOp9mTu|
zYxI{Yre2v%P7;*}gAi&Yt?k%eKCGm0Lw+88Vl{QmhuD58M4Z16ok6K9`+e!ODxLRs
z%AFeoL~{9q6U&vLiMAyX<d+I%hOp-x+F;z3CqNZLil?HBlFwTw9`usVcc at p7^YRYk
zDRD!Kr^4F`Uen8T(U<QFximea4v}=yiHpfeKQfc#rLn?_j!#;h0EkY2CntcK6Cj)O
z_1ICQ`};`aLZ%~?8V4|;nk>%3b8z^Ot*QO%!MIw;#Va_^O%?B0l9VpAfH>18y*1?S
z?;{{JkHRYS!c&eU>HG&uRM0hQ4rVS6nG5(e6`5;@eqloy-vW>l?-?jwIu~o6P7Oem
z^~p_%N4NoUgMA()75Ugz5;Lz2`$9e=2=qyqatwQ3m1wS7m6|O2P-)yojP8Q~cx*y>
zp?Bkx0I!`Rlh}i<2#f6kosf~#eYe?o9tLW~&Tr at 83b!R{j|kh6c#pu8EM7ZMGI!^8
z6Ro&oA|bR)jzCBNNDi_Su!u+95kJIRuHmtJ-4Qj0$+IyAJ|E^WhwC|FyG-_<8>bkD
z?rlV>HZgzVyyxb_?)Q(&p0UZ@@aqlTSNp$GXqr0T=;fj=n%vO%lRo7BP4&iju60aw
zj_&sam4w7i1(6JXyje^;<S<0{NO6U1x%rt3XnETmuC#@pJ{a|8eMjea4!nlfMjIsL
zz8SLG)g^EXPz&LDcJxgcz=O_j90ThadNS$3JQ9`EOgY)TcS<y*6g4kApv{fMH+wt$
z)$_|sS3}|gKojrh-Rd3a9qnE0UGDAgox{74W0Gqi at 9Kt!FP>pkU=%R8H-}px?0_Wh
z;n>EnC~$*bVudf5L0v>Wi7zSvRew&il)t+XE^mr1HC%y${iGTfu}pTm9}rRG{d|8b
z3Xbj|{(!NXT>|ht&mT_ykd#ub9Rxuy at lJQRPCTDMmV$l4n?l>HyWV|$Xd>S3j90cE
zi`mf(K2ED^Ah*3wmLXBfg_Vc(%uQ=@b3YXpsiuOx`Y1t<dYAp?)pn}jY^<n+>^SxA
z`~2eG3e{e)rC4>ra!f3-oe%rn%vHZt<0dD35x~nz+xjSLG#sw9bFNz5RE&}V1Fk>{
zhQePWB|O1~v?PZq71<<b;`=A}LjW|0`qt=OsFFJRF29<N=lD6nRuP<<z_-LR;kfJC
zedqhdbCz at UxTQChCTX32Q`MFt`0mN(ulAdOKLM4FVhl~R^-)3tp-8)Pc(!Vc8AVWb
z_Ds^MxiK|tUOZV)UUeLmxp4!!q^IVBz09<AiBX2mg39k-arRy{QVaFARhJU}>IXu8
zB|JhKCRLXcBmp7!bMQYt&Fi9v2AI^{NXGTGz{^o%0~?rsQf7}H5IXP2N<YP3br{{+
za+*X;@;IobxMOxsx>rs1Q{8Y%L1hhnHE<R_37XP8Hd~UN4>c&&SAohj_c|Douk$%2
z-OD{>*x$5t7 at e7$h)HZ%=tXj-|7K0u!|QZJbQNv8R4)-uwys|)FUp!~$Zlw4<W|zv
z(3CmmOF2KcYF~(af at +}*C>E_L-sIin+|xyAcO+JmJJgh-8`o|RS4hF3cM)RIlp{Eu
zKsOom=t5NT{`fl|N&+iCk$MpRd2;Wa$~w^8Or&6I7Zu&Y4-U23H`W(szjujNBg+u#
ztp<3ZczyfXsOF^xcC`J6l8iE=+GOSPn=J;WSq<h<i=NDeNN?jyw7UH at Y$jz>X=Acc
zUM`a!erf(frz0g=qi&}qHK^G%qlHlwnX+w}Y_RP$?6sks!-{iM;J)3Zuge0z$D#PD
zG0~yF&9MAK4)uPn0V}-`JlvNNpjl%c1om at IHr1SLp%^)l`gz^tnTa3|(hsu^k`jpP
z1LULD&()tF7~32D!_|)fh-RPwp$v4*oQ{Mk1$AGXqJms4gfI2|ow|s3D>>M#gNb`3
zc}0Q=roGE<_BEb?uM0~@?CZx3_wIKm3cnii<s!K4vz+w?Y1 at E&uYo?)?93Z=5E#_S
z%l2?E=rGV2JRsJ7KA)@bcDeyYePKd at 1XVQ|D`@xBu$|p%kyq8*W*Oe3Ys!n`iSuWf
zONAQR#F}dA+Arne2VNof==qemd_8|XqGa!sm3oaw^ayfd-+EguqxekK&lb_QJaOCn
z4c?`ljU62E(H2YR$V$6SsZ#)v!zKKbJc`BjdE=PnoM$;e>DA&R>(g6ju`qtx6Mep%
zn;meq{JZ9S3r9^JOON@}(i|t5PgG-;Z(6R=>B3HCC-3Q$OlR3;)1#^4o#~-2pWhr4
z^E||~Vj{4j!aT&XuUUr3TV-AekxFKG)&G at g^+ywP2KI_#MnQGMUuE at DJATUxChU;{
zhwVAW)9xkV*t3f`ABC at OAK}#*0R?B?rN&~{`<t5agXzq~`ExB|b_ybo(9qk%)hC at +
z;uQ^V24s()cjU$_4s3wU%+An1q#4>b<Gj|0Z*WGECKJjzT at GfCGZ=t1#2!!ek3l-?
zA^weUAYc$MC?vu at 3aksQ{}~GS{ag00!6pt`$>fO&%4N(&@V0R8aF{|XU4!0Rhm^wK
zx>~EbW3Mg0&w$#eUpnpzBJ(T#Ohq;KF2QW&gFQ6^K7FgKClub_6PTPK at E*!~Z(WuO
zyGa0bVf at u!X%Ws5KgRMc8p=V&PgU|Hm=$N00*`=a%^akRmV$nI;5=p++pC|Y at k7y(
zTt50 at S*&V)7Zbl(DsLWfFyMw-n{Levxn&mt?MX>mL7plTmeZeMT-qJiKn*(vk>&F5
ziNIyeNMw^UilZPa1<s6q=8u1fJgc~Y)=D%ru+ne-yr(#*<0>l;smiGmRUTmcfjb-P
zfhj+2p*?flBz!bk$RD)1cn2*SY?u>RR#W9Y63eE})4VIQfgajB5y^}&%YKA|FT=BC
z{*xu6Ma^pJKG?$~&dhrDlf6iX7?YmmjLyK}rQdyOgO`Vg#qTHQnE06sp8_}Ace%F#
zhwLNh(hMvdg2`)nG%KeV?xU<M^QpD_gJ7>)pUZ_lgS`a+3_aUr1+y8unY+=e-jC_b
znfi=#PTA54tNTsR2Ydh64g8({NOvD|50AKwdzGsto#R6|^FWRkBlRmr+B+X9uS0$3
z*x at Z<#+c~wL4G3k`ru)V(S0AYuilq_s}E?e>Uyp%-XB)00cue>BPT%KYrfbDuJG~)
z`n{S9i`8dTCmJGE3UWu+s&myAp}kz<^{Vj3f`jnJS%pvI0|Ae_BjNf7Jt9TD=srNL
zp~QBSn=FtdW_oUH)*qZrWTdrKsoPt>RmP;RyB`09ZeQAoQ8qyfJNS-sUlM=<d0yvA
z{~u{VO#h(;DZAU7(2LpGI*XV%897?mJKKHJg1+B=GlWc-2>-<&Qv8=dNXX7ji;k6z
zgOHAyft`?vfq{XLk%56-=U-Rl3~Ws3MdW_T$%@e^TiBR5$(gt++SwS`{!)@v{)cB|
z=0wOwujFiEqe{rZL@#V$FJWR~X6{VL#>z&o{_imZy`YnkiLEms2PY%_Klc1Hrek7c
zqnC6xu(mJ~v^BFfA^c`9{bQejjfJ&4<-c?hek&vVmo}<@T~qqzKsg(k)63a8+89{>
zOQiNc+E^GE`1t-qj$-~#6x6g>8Q8VhSeSKm=>HQDdLaWRlYeymzdNSnV(9!&0EHz*
z{+amCN57qu6j5?_`gT>)*3|Bsc_r^?Y~pBPYep$){Oyp1vpW^NqKTQsH^a)EQt+Fm
zWkN--<YI4cZDR9}zuyM_%i{m82O;Y}%cV#!qV`X~|5pcC{^P*E*<Js{>VM~U{jY2;
z<zMx-K at 4zP_vrYZ9Yjbf>_jQ&19Q46Z5zV-a}s3XGMLYA<jC5~{VneorBBvAm)l%s
zBscPR6{AoiT(WJ?$!G&%i7vlj=TwKgX*8ypfaDK-w1{W2nej71ZfjYziEVC};2zq!
zfP3S`8f>M;D%<!`7HWy!ccXp^f!n}h!yl}=V(}eD^}*gE;<oq^O6gs%>mHyK2_ at JH
znOCewT`J6-e%%MMVn5J@<825jr=}EN;>~t>EiQ&?h9?X=x-llusfW()fDtrkB0SRc
z0jVxnW&9s8&ibF={AWe~8`FP<_}`Fb{l7z+iS2)Z^#23PbV7F4#{U5{Gt0jb&B)C5
z{~M&)|5rr(m+mM04aaXdIT8K?a>f4v^)CZ66Z&5Uj{m at n>7Vc@(kt3Ie^UUz_mr%O
zv4z1uTb$ur&c?_@$j-^6Loe)TXaBzm6-}J%TpW!|oCyDg%l~eu|K3%E?X2w_mFx|S
zOuq3i?8HXM{4aI?hW58iS(%BD`QN4~vk|iVWB9j~${d7 at 9RCczXZ~`uGg2~f)}sF<
zB1W%l;^wSFFY4wjuJn(=-`^mv^vzXd|DU=4HQ~Qo`rkW(g at uis@&98_6sc{*Y>Xj$
z=hmL2(q{BY9A)ntt%Msv+UpyekotEc;x>@YCrG5rMyq~Cee5eQQ(3Y+n%^TqA-yT8
zY^ms^D%E~IN*k|yULF74s{G5u7OzorRbY=lLS955CYd(17%chYo)kXZ(D&)Ac~<r^
zn4G2O>MD#uW~!kFcKXiQ%m|gXcXN8*D<{>$7H0itM6kxEC=|)BnZcmp>?=Nv)#{DB
zEQ}T>+qwDmX9cm*5pdXzXh9)VJ8HNotJlL87{l}Im(+Xp-@<WgGw(`l+17UxMhuZ|
zt6p#m?iSoKS_bKlCvpBED at Is%Ga|X_i~JKU&^|h!+0LXP3_m9$g*eeB8oYpmsh#vi
z6*o|QZE^UB$rvjMjU1Nu7{;JBdJQs9p9GWC6&agtT~lNa+&|b+ryMUH@$BrzP_}U^
z{3+xExNo$#n8$Oju<m)yc&1>q0b_9WWT+U%sRne7`Id*Chn{p*>RDA5UFVuukAw%8
zW7rG9(GkP+{j}sd+VCZuTa|V6YnS6%LmFdWj?X)dR+A=!;|2o~mv8r3m-Xs%(9#|r
z3!RMDI&q8R$)zMtp17OaE+le3j#mUbKSM6cc3D$o6ADSqoP5-M{D|O|9{o?W2=YIH
z3SH)$DZb3+*sta<$2`ZCA%B(UC1=dg?$rZS(TjHR;B<PLt}ReB)ZABy!wg$`@SP4U
z2H`A84RVB{XMevedKyYz9``*nM>Cy^_sC57pv^P-;7^@6Yy+ILa{>CMyXURV_eYH`
z0KebUb{T6g-O!G2f3C#}L9`j9#|6dM-F!ee4^*RyUPB_sXR2lgZ2yEybPEEs<1og%
zuGYrNdG$L`H6zSgd}$I9xbY1+vWPjVf+W3&t_61aA)i9<mv9BvBcH_?1Plw+ZXsyZ
zw-VLjX&vg(d0zt3dhu^B&zuA#nYyv;VO2UPnJ9Ihycn)B*GiJrt~qv3Be9^k+y<mh
zhA8Wqu#oKPj;2`YDWG(-+^bYO?B at bkAQses!GDBTST2x|cpEoo4`e)Ly&4v#7RpFh
zJ{{If%|iz!#A%FI7o_I+{I&_<QQ6_?vkURdF7q9#vbiC9Q!BY*T)RdVO>=optA{tm
zO834tMO3dSgQ`lh%=2Ey3jN)!8b78SxJUv=r4oPb>w0{iRmiEc=>Cz6<@CEw`JK!-
z)pE4ux=tuQsmt2`JOX1>)FIipAi!DO7Pm^Gnf at 2WAs2<!2aFzq$v6Q%xJAY9{ZpB0
zQLC+dj~a$*8oJERbHfncYJVDQyyB6S3)!h&_lj`(&fgW`ZG6O8Ll#ogic=z at v+g2u
znnJP%$*vNWh-fOoItif0Ka8S#cDl<sofOH-$VOVkq&TASMdkR}uSH`&H9+*+bz&XJ
zq7YZh4`{odj@%cB=7qS*PBIaA$ml0@=Q<z6y6A%GOH;IKJ|MoNIHwa)Z5i^FM$Ui_
zvYE4M8u~F!&>n|+gkKS~{vHiWcKEn{V1r6=SP$P;9k&RI%ywnkHX3RqEs86P(z3N?
z at kG%Ft9&^6QgJ0{m-%_H&%{9TfAL$n<%3hpmms-KGvYcOCarW8``!`2OoS!Q&YkQ!
zYwX#ZNU%@duHOa#ytd9;%%#8)lu06E$a5q`BA4Y#rHW{Mq0mfc1KOqzN(p0_9Sp=^
zm_;y3>33>Jlvvv|VwVwevw^~xMH$r$7ep-4{Smp at uf-ZG&*-4a!LpN<ow<HdWE8dG
zuu|jz$rkKpC_-##Pz(K}60U=Lq4ek9wm95rYS^jQOYmo3_5;Megp<RT0|%jSnoDut
zqi~M~)*bXhKIEA}XG<|u=Xm|$?Sv|gLX*e|V;oU=jp at 3AS4ok<fLPw+gR=9Jdb9vP
zFu=ErNf?!6dBWvg#*oJft)!;k&p$~DI)|Z9?r<GqhS5rIxL-VntNBrZ59Jz1x}*={
ze-yFtW=R*OfiQnhlf4DvFpm}!{6mUsu{jn2!${nzjHl$1vX|q5S&%Z2<cpI#UM55~
zk+R^(I>Ad)KiC7Dh)rscw$SO&YA|KFy4AWMKfzMYbNyzD+<hlh1Xp%9Jy2L+3qoPI
zoC39u4ZZ|#R2-MhY{zkZT at tILNsJSJycTjz>V7j=`Y#7S3I#vRp=85wz$HDvrdnLi
z)&Ji7CtRCccHB>DD3V3*mei&x8YlGrxF<wSDypCiIImT`T`jmx7^rlb7CmWLc9@(s
z{S7R_57+CdL6^~CI}+aHI^|t<Ur|Vb*}J8ogs5_Q!U at Fv5IcXkFA}6_uLxiCyIFs)
zBiv}{F<<1^9IjRbx`~hEqT;r at C9n@nyIdr>tm`1-;LfbMh20D}0elRXQ{3~*EWF*V
zrFRxRW4{zBEn=V>8k*baCd3MnO-&qyIXs-wI>UWwa4dq&IS at WJ!q5(OqTG}+j#>2T
zR!vpQny}(s9NMU|R;)8Rjdn#DYz1&uy?WO!4h;?iwS8;xiLb&2M14rxF(f*sKUVpJ
z^{5Z-w%d;sPhsVY#-6^YElKNWDGr}WFw^CChCfpj7wFnEqAGvmI>0l{m4mxcX)xlc
zoM>UsqyRD|bOu*jV0xsv_i_}u5^-0f4sAZZB8q93)iVafa`bpZ at ila78E1b_H~I~v
zU?flFbNQL1WflXv=@q~)YPVU``ng3 at I+SWDRJb`UUAx_0w^gi=;&acZ$*&-v0`I#4
zss_5`N>{bm?$CpBagvToc}gIY#=qsM0I(tNK7sCik60bex_TxQUCV!{-(ES at Xb_kE
zPtR;#hFi~|v at uV_(vUqclPrB{vi7A}rI+3grwqBAsNsz3aZQp-Sy66Oxde1iuwZq`
zo~TL-;UqxG(=$k at dTq9ig+whL!L}8ttr-If9<aT at nzT#xXJqqYlJuzHaH5o^O6+9@
z;vzhlleZhje5<+?vS_};b4vc#%K%ZFZP-99dWrEQLp=MB@`2KWN&Jp@^nO+ut#YC4
zgPUYBv at rPw^bv~ra2+GKVY8#xKqw^D$x4uI7hyGi2(KJ#V!Y}l6fhmT%xSSvqEgvC
z6f!l{=*iHce((ecDDNHTtJVV&#pr9MowE67v>|J9l7)wR5n6!PXIMX|;PcIgmbGso
zYPI{vUSaA&gR&tId{n5Pp=b4hB|Y!eHB0d%62TA at 7ufipt93hcCXWCS4Z=GZ;Ai%`
zKSI~J=ozE<n^y^{9BLxI-~Em9nbgQ<!GX}U8`eU5uz-i_r!lmg^nv6Mg;tJBscu;6
zhwr*Y0`sSq`6VziPe(||qun_gGk at BKits!z?=4Y9onW?;#mSSQKxPwxKYwy8&EVFv
zX;h3hWm1~0uMIuE$Z?-#-5l3!8D2D*-kcopZ-2ZkMAFSg4v%L|Wf7Lm$&#n8Br=%I
zj^g!L?H0HzDBc98ilOtK(Uw at prwk*WQg?OfmT$X?IGCb~S9JMqJFxvt_G}3<juunP
zQeHwx%o&KL6()L65p7atqPaK4K%;~T2PiVj5ho<L_o$Ng7bF*<dwet4jtk}1Z+nx}
zCT-8hkUx at j75)Aq?-)10Tpfadas);ilV at _kW>}6lnJcEsuCLfl9R at 0?lxCFB!X{di
zK6ZBEA7^zOsl>7?oEYr!J-=SAzAZ<eLJI4F=qa}}Uo^|*-07cN-kqon4KFP8kmrAN
z%{7O_Q0%o%1c|O*m at K+pj$dT318m8jUQ4v})2zLagYbvhB*QN&1s8cyZ<T=b+L^SS
zg4ZN~m3K0FuYSvJ3B<)WaHmW0w>}r%By^m#W6`WAn&c^DfOE}9t(SOCF0ofytseHr
zUB#*v-iwpCdbR+Cgtb$@Y0JZ$2Y(7Gm$G_p73FKG-G>+#uj@^)dUW7X-)Is)K7cJv
zt*YKb^}Vn%Bn%2aerD$@D|Zf~n7FPC$YerpEw1f}LIz0=&gvU6Q{Xv<C(sEu=<sYO
z`et~_J{?qxguH|%v5^M-eR3IA!AK7ihz!g<TKvK|z5E-3hYr3cwi^vp9<PG;gZrN7
zc-F2n31HEa_ffW+fZ8N~R*j}gBE0MjUES|jf{YnwRCm+CkX%rRhBYQ6efU~WFc%rF
z6{s3J&z-d(yWrJHHdl&41_VG;k%#6VUnP019*Amlj;!W`Z*E%JbZCpQH7_=%hOPm4
zS!*2}EvkAx5i4Vq;;U(9+)R4HJ+f{GG>gfXN&HpadK`o>1SuS?Ln_%tG3pun at ewWP
zbcWlgb7xV;2yhFaj?h*Lx0;6Ja^VLkQ4k7quvBf;S75oFn2zSJ%0tfYmmxQ}*}bWm
z4V at bKl9@d8)k(0r`kKKH0!#VDB{q(O>X7g at 4?{hDh>O8Pa4C=+ at VG6#pdSV9p at ui)
z4YN$CH})UsYvZ0E#6@)ZEoX>C<2SNfo2QTsy_}Fp%C+a;+k|Fwv`p@*qJ0ub`FBBK
z1n=u_grw7%@OxOgMx(qT<5jcfjJyD&ab(+!lT;8&P{T>R=rUCa_20hhpg#*sfT6Z>
zFdG8Wt-DNlINUXE+piS2Fhh!eU|X_;+{9ET0Wl8reMo-7X`<AT?oP+Z$s^rr_<`?G
z=&0w4<fltzXwkYSJfLDDbQ%nRzG|K}91e>p`fGX9F(Y&&A4a30-17of)Q_~ANAYS7
zA={ixE#ifc-c`Z)O)S9kX9v_zvRM>Tv?X%WgBpw6Tzi>7F^8E`@O~dEQn;Lg<KgjO
zFxnz^47J=x31XS9zcuSR%<!lhRP(>roRWL>RNt+koDXx)e5Y*C%<w$<orpzBsa4Q~
zsZR{AAWzqqk at JONc757fZT$Km{Q*ao!I>6q6mwh^C4xxb%7ucB+=|$ea%i}wHn&{e
z)G}4zti2eYiiJclZ!kUk15bHVgcdS%Fu5%!7j=X&=U%B40m=WP7$pm*<%sY3rIh}O
zrV$T&or@$-=U(T+zA#x6czH0J6tsIuI)O=Bs{2pwF*<{|ZGXIUhTRZGFR^Lq<lvgI
zb9m5>q#91ifzZwPqZy<0I at fKpWvk7l0F*4ktbP_)6nmC?!0QmWOy`-OHtz29tWcw)
zVi^Ze9C}cwKs0A1mQ+7-uDWIpe&sWwQZh7d1MbsD&{ubhsEMhQ%7bnpQlqG}QOG1r
za1e#))}L+2r%J>GGJ}?JKIBaU?21)`A`~&h?}SN-xWQ<{4AQxr!dqe1^bUu0A&xRz
zO;m5|GO+HF at j`|Lmb-;U+tNmB(UMt-J at 7Lr;v3%VZh*~3gpdmt>fYS*$6CwWbLYsU
zLxeR%0Q=VGUpUiE+GP0r5+Cm>@Ds6kwN~2j1}pzMDptq at k!s&M8S|h#U*t0*@WrNO
zfr1cVT;sF|$T-!YF3B?Vq>HpyJQBS<zoj9v;wNSIBPtZ8!{Qo(@v?-#s#VGw)>c3i
z>?m0dR3q~!nPnrGs2RR_Eyv-^Y<gHKmRbF-pD&wZmAYK at O1?gT<}g-WhL(puo_TvU
ztlU|PDGkCZglAhoyiN}J!Mus9pdHcdO^3vnph~m`p|VA*WLjOhE#g8%gd>u9FBcsX
z9JH3FkC~vkP|w24F|tfcgfDUVo*Geo1ibn%-rlPB?_ciEa+>Yn-q)CB3P#l<_U4cW
z%<hy>NYChbZ$NVSei0=IZ}6w<b7JBNL3Iza`5mt`)QmbfMN$|6Q}T}8S>4d7Vm(Sb
za-#<SuhUb)Uxqv&Fyioq*c0-X&IW-mc(7arEqE~6x8mg3EH6{BV_nAI)XsdtbE#il
z%pdaz%jU8wtB)M2h?nCG`^mu4$N?6Te0NGY+4-ka`Nbte<_P-ca#lS%p2UHV!TY><
zOlzeB5A1v|fT+mB`wI=R3L!EdBV$S0hSaK+sG~3h*mlmi6YmfNXIe?G-C0zz?|((?
zs2*B)v1?gG<lXjFu~?Xz*DsyXm?GE~e0y)hYZ9y?MichKOK6%kVK#0$Qcc6rJ;GgV
zdy4mp{CGf^s<_uXi1u8Vl-tW8-FD|y7u@{$xYFr&e2Sk`q|t`~IZE$UgNOokQ+TtP
z3MGbV`y?_02`iCV=zIK}69$w1c#DrHt3h3WLm{L9?oP>)o_tcGXFW2cKBnem&R)q}
zqg|(NnH_`CRjJ1UxLQWkLRO6~{lC2z7l*E~(O4md3Y=|eU_-3*lqcQN=qJ~R2|-cG
zXs5yCM1Ed|m_Deicaw`kQFDQjhV6t|=R&MhW5j at 9Kfy9&_(ji$#vh`t0)fPZE3?gn
z;EryQHKovBwq7kh2k7T$)oSYbqZ+UC%h-VqnkUVe8|?)dsunkonP8E^%gK3X6Q_Js
zt4#XXjBaEf7i#>3yJ=wJv`w>$j_QI^C2}e8&m+zQI>4NqvHXl<g5YFtZUw>w5Wu2I
zF}sp96wIIchZ&hFoQL6%B4v79d46GGSWN2RYplZJxSJ0=szw)mJEYiGeh4gR;7mS;
zxdgv_yzuH4e2(R)JxnZG{~Q6nu4Gkaz5Q5sRAnGtxz0mt-Tc at dVgu~qZ9L*v{cx9$
z9I|NYV5rgh#9?wwcPv5?7-nw2BqP|^USgMFJFtmN_^<7X{IZ4^*dc|2H!(<pRV_Df
zVbBVsWLG~x)>2<+((D4S{K41D7%Qq(Lq3tzkJS_vMmu`-TkVGA3b<l~zl_i4-)TPC
zEuzHNVg{(BX3T5TOss>ps`9WVSjM&2kBip|Huk{owOuHfb7GjiSKFNR2)3mm_)bY4
zEsGRuW{5As{i=-bj8u-rEaI1m`?VgWL-`6q#rU{n&^KvC3!y)qa&!B1JeA_}ir^wZ
zrZ%fbha;{U(RZ#+uq^B$5jnWK#st31(#gu#rDti_e2C`ER(dsTmJE=2dBPPF(OXGt
z?qBve at pDWhUw>+vSMtPxH2rED2V%?@5IO05Ra02ao=<nI<~b3S2_8UM;xg>4Nc7$E
zs!I#hS<7>d>yMZ at M&+y-Y(fpS@=<8gCEqBp{@8Idv<T)v<bAk;;5mH@`l{hOWiDzT
zM$bKT{C*;~AR|4>pUqMD&rD_(zntkF^XUhQX{cZ?x78sMJ*-I$G3nms=iInsdW5cT
z#Vl028;lFp-|36SE4Fb;yD0$C6ZgY1aXcVciK7nej%h$>VDEfCJ?Zb6v*puG(C8v4
z!8~f-)fq0o4}3^(X;NZbOi*M&24IU(#i{n;WgW_h?KfpYf^Cw68uj at gE^+wq-3WN^
z1#r*9vPL`BynCM3O^HQ(3T;=vq%|Bm@=rF;GGd8?C>iPLMaE1+!t3)#Unv)xBN-z3
zknf2&r_PZ_!TrsL!;6Ex%0-FySE7jTgm~SHd%~6Zm8pGNlhm&Mcp?}#pX%e#Cr+0Y
zUJx%c$yeNLQ7&i{N2$GEJ!03p&mwt^Tt~1*V-bJ7=&e^8x5sb7Y(Jp86yeHt)Pe3L
zR-21YxmkEFF=9+YW)x7$HHyMZ%EtN(>Rouj!sPvUJB%Ef5HfJ5*XtQlB#A<3CN;zU
zdn7uyNXB`o$GUwdr3B4q-jSCO`rs%&;}zYRc!a^d&1W5QaF3jyac at fxdwXK~ICw0-
z%AVTmS;(KA?50Xj{MUtkxP`CknuCW}0FvnVbuj(J;c1;G90>O?cjXBi{#dXF at 008+
zSNTdeVqGIXm+OHW*9}sh=e6E<HlP<>ynDai1qHh!-eiZFpq6s<LfnA3_B%=B>P2~R
z%b`fuRoiw2>eI5 at n@93P7{AL^mO-3(y5jOh0$l_jJ3B==m<x0H?F at E%m#yFX?FPW1
z(kb_X-x`gTsj=Tlm>xIwIXXpMoLn~e3T{ulAQpF12X63)n7GQK46^a6 at j;}uxFd_7
zg-J*o0jTK{Q=VLtP3~bo at U8bLF%KTvfBi1;Ib>hSZ?04aGI?y^O-h%aNufG`E+6_+
z9+ch0M?X3xSKP6J#kRIq_3cA2beso~%b(UkbCp}pU~AHGbUysY;FA|6U%dCuk;>le
z@)56)<4VPiQxEXMN_)CBhw12IU5DP)@AFXEa|&yrk<`!0A`~lkuKK^mA-fMNG!7!0
zO}M#8vXfcdOh-)O2CyoA-kK}*JZpoYljW`}bX}Q@$J({kCaS1)yz%C(JU+{D`)T`!
zoS!G&4}++|hQ)>WF&i1rgCf(!)Es10nc0_ at oGLL)zY9^>bo;J<6ZB98?pOYGEIBsn
zwOk_>Hv1#`DO6wlhP3txzTB4y!pv658rKWrAZF=$K-io0AWUh1oR6$WHrw;NM#1yN
zmH+3^AH9#4Mf8LeQ{RF@?Sn9j$J>O4uff<{<J;L at _hwQLn~Un*^rW(8y5*%0A4ad&
z;3DS_8 at KuLQ8o5}btBmp9lOTEtWOEAfSUwm;k1=Bp*RwDHs_y5dKq-=p=79Nd%}*t
zhBwb`UpjpWAQ5<R at 6J$~$L5d!C~a)tW5#rE0`V6X*_vQA;ZN|v7rq=sUI27zf1m6&
z)m)WL?jH5cpfV?Rw`lVj{C3?B75uymwiz^-jKxun@=Qj-a_cg)pY`nxS1n-qsUhf0
z3ENK>tA0m)SN)EUpL$moXmK%#SCPU)U6rZj5W99g<4Ozhb7qEIr&o0A=Wv+XiIo4{
zEqDlC^vEQd1>2?HO0Hr{>YjNr&df?8+_pu6e#osq5AVI-p8Dy{A4E|Ar#0X$M;o4=
z(q`>US==4osRk#Fz;%ET^X<Jx`}+rb(@058w%9 at fvT{j_6SLM9T}UiHKix7(r9vd`
zL?%|z?$O;*0#8F<YnA`2Uy9%lII(MIPQ)qr&r;YWg-$a*9(XS_bN1%8XWIy|zqKws
zuTgjiJpAD-)0_f?i}4+|m)NBSSusHX|9K7!z25-)n2+8psiEn6el7x<odEi#!DZc`
z8^RCBoQ%*@Fzv0Ss}l<n&Gy7({w}q!o^@<B&A>&Ef_qfgkBh6TwiFB{vb6NB0<PIc
zF<2 at Jk|9E~DeNSTK>L8w(T(CB!D7rFteaT)*+<gzE4LsS=RXd=b6!3mC-Ac<(mPNO
z7Ujz4dB(Kw?WD0jdu^fe^iEGwi<7ZR2r{VHXufOQb%)qIG~~S#AG1^VdSzVM?xIZq
zdbZyw^*5ro9|<Q$J-em6ghlSE66n;<MKf<SL9SI|Gt90x*F-0YvsPzcorXJ#-pF1{
zCwy*>F at V2UkEtdQKiB!@7IbpiyLpXq_>UguK@;#_{<Hzoy(x3*f)XL>JS^*+3l*t~
z{zoH~?!UIh?0q`PK<wO4Qv?w0INO!8N5y}i at OC7e<sY!ub@>6U@@podZF;|^6ZYX<
zH}D+kfcy8B3RX=-gTEfPJbT3CO`KN5TG#pKc){o#5zpN*5BG8^&<m*KL3cbQ{1gba
zShw_NJRHkQ4u9gcR9G-#b4xM6%~<na<Q}e`)_=xr)1kpDopv$9y+dhtcl8=b+T;OD
zlYie)r?#w at S6sP<{;lYcvTT?d*s@=*>49Q7?(xj4oVt6YDBimTsCcw2{4QunDnN at V
zjvT9gUvHS_tO;6*1-Y_>mKl)i#^7ZQ&x|wN1E15|89RWrd%C?JwyTYCXzMVQ=R{al
z^BsP>zCAAmw&8By!^t|jN!=s(!VTSkZL8ZGa3Aw#3Y at fMJYw<czTu?LLPTxIM6$aF
zxw3V2 at V>nVf86Vq>_T#>iTkbsDo=A))36>a at 30q$b+oOYjmh9!=N2iHO_*_geTjNJ
zIY7am7lM)Vn0QCLB96Tw_5+IUXN1ckfagebqePZbT<s~nVNup7ldT^+59kPZ4G>&n
zKWD)Y+`tMt7=gOJKW(qgE5TzP%69WPKwR7>-}!h*RN#g{(j$VV$2)mqTe3C0^<{Hp
z<vug!<j>Q8X7s5319+>MxDz^mNJMM^>Zj%t#De#Op(z*SB1zKxOE>{Ow`A=L^8AWl
zAlwZi$^C3+Fen|l`KlmCB^63^6zAl|ueY1O0J{7PsRAiUPz4H81Xv<0e_ at Vki6|pd
zW3hmAq$W_Ih{*^{X{gPh+OZQ{5-KQ=a3&V!3t^}TzwD?WeYEfuCPlB03GN~n5x6Xs
zA;k>8qIzOd3m*Wi@(<v)!@~g}^m4-f at wh`8LsUBSA4Unq_KhM)C%u!Z at o6X|3Qd07
zr)p##qGIw;M~%VAZc7Q6^UQhd?mlvbxSMp&0`r&cqLfVrP4r5kf99DCT`bx-60vUi
zr~^2dQ2<b;3K4`&;DR8@;KLo1`Vzl at 4k@9W1^o=GK}b=f>fl%`fKjT6KmW!dZ&)t2
zf&6W#(F#W at 9F+kfl~vxNC;FLuB~ue8_jpWBdN?;6f|Tfl!LK$)AWL8*#A8=i;vW~U
zwkpytbOELF6Z%$WJ;P2Y>XdR1Mut&MxEP*DkRSU2Zp`%L&vC at Eh)M`bg5d!JFJvxv
z2I{_$8Z?*$wN*0`Q^tY{+PI=zO(mC*;*E|83QFN=MQU7}joMz+pM`QDlEU+(05XPD
zN!fKwA?uT4BI?7&W~kd0*Eb^Ltd#;@RPOzzAK8&e+$C2RL*P2v1Hyi*790U!`bu;o
zB0-p1C?{*OjHNsZE0KtF$zUSc$_=tmRAxqCAN1uc3rI*xCvKu)O8c-+P>eG!fFfRF
zvXozGahTNQ;YUGkTe(RfL at TTNd5(KP#atm$5drEl7ZWj{)uC|<Nyb4n9-Ic{wf1se
z4%sja`<Ze#jpZZ~QkbVhCDFflGfK)LmQ~9OR(>mf_sMICFydA~pZk4A-9X2r5~c-E
zuGI2Vnbtz6*ly)n&Y5%6coUm+y`+mqZT at 0eU&3E5Q+ZP_)Vp+v&TqzJ`>~qBdE51S
zxg5pvz@}L-^Ilo5*|#;KlD9|2<N>(au~N-^S?5DU$?uFaL#hivW?V6}lxXE&Z{*C>
z1I;d+U?Wa8^uR_WQ8o6s#%{R40AT_XCyPCX9_dt*(PS;iZN7txfQ>vOjA%lw$J}KT
z7Z`d&NQ-qQ<Sq~($d0o+foN4#b<H`D!m|S_z at N5CUqcU)u6L%xAs_Y*O{5w#s3I&t
zsv(Y~e}TS-unz*pMZG_+3`CV5Q%~ZJ{z5GfX7=chUj|kxflcL{E-XIVhPT%)fJiOW
zsv{(Y0%n%?drx7Kjb at +(Ib<191KM!X$o{p+uh;_UkdOeYUeUg<pIX1H*`xN7mef)r
z82M)*5KgXzCq95(6j{0kMIy%fz(~y8eEXT;v66JO1_$EGJAzM8_})GSx8GiMOM~MH
zQgK*t|Lt)_;0HYYdes-Hf^q%-1zkX*zer)S7KaChlv)H?4y27+H_&}Z90oq-3<*T4
zs1F2q at MyAKZZMiXQt&9K>P^+ at 1<24u0m*VU_+NDu+7jniJpEGDoe?A^0Z3lzG1w~p
zZJT*|-$+>(q-rwzl5Q=?GC=QeKf!HUdu%gAk-||O*Xq%mj$75}aL1+6j+X+G$*g<v
zj!UH#gVUkbErhQp;arF?ki at b*Nuc93rimwtb0?Kpz4s&CKq?<BhY?slM~$RuY?6Ui
zjm48A^MGU)&mkuk4_4geFOVXi9FAkZ#cggK0BO6$S^<(d=y^5qNKS&c-hd3o`_?3o
zMq1svZKTYOwcU!oRd4;%b#$np5vDVyBJq>_KFed5^nQ-kgOW6r;7lQ5qQ3>XZ!_Mh
zOlCY>y5LH+#=t=2cHJ>}Kyu$Pzy)fXdQ3v8(mX?+3I;VLk>?UfnK9c?Z|#|f5G7fs
zJ)8kTZ4120kdyr29*|<uQhMfv?RF$^X8&?Z3&}I|B~}6KrfKp*$$YzvSWFpi?iC9T
zNH00mB!IF<s9FGNKMFgOG$j^4kkv*M2q4oHLnMTNDTc|+`NWmz?&4~7PjW at O-ni;r
zhg})(om at Tdy<K5%Y+QtI{9LF{gk8#S5?t<YqP!*CTzcEM`S#9oIpN*s^2Qs~<(4<G
zOF!>(m!jSaFO9ulUaEUfz2)G2_m+%z=37?Yy-($NgTKY;L%^xuGbfx9KeL3ZfKMAP
z6EE`!=c%t5<!eUyno+)Hl&=}(YexB+QNCuB|KE)A at 3;KMfB$~}=Uc at k3RI=Snehg1
zOD=CnN_qSF1H%kIL;T8a_BnKJozFjCmVX?Z%1eJMs;)`fecI>y$9^22{`3idh0Alz
z7Li4c(N8}W5ql=4M0TAzspWi-k_G9~xrxlC$Zn2|rix#?Bi(wyl(t5suzKNNKdVyL
zUb1Tu1UaQppx at Nw$Y at m%Y0LyjLbfPv-9)l%<1AuBTWbk2L>Gv3mI9EyiXkjO`MY!#
ze_MidY3Q=PE3(Qqq%wdwGwH at q;ywhZQXA4Mmm*yq1X6O>UYzwC<S1)*UUv9+M?58~
zNra0?DvD&gM3*|2NbYG!NmY=l{xu4r%mKrJD*iDadT~fw)ivl5hV-P2f;bwI)_8n_
zK03-{A81J3kF&s(+ at m*Ha}^v$aqa%N6vDKg7f-lUj=_N%Eb7D!AdpsT3^I@;4xU6n
zRc7iTSCpBNV>*GP8Z!=Qn)X;QRsd-wN9F*@HIfSox5kJB1R|NbBn47svT_~+$fHH7
zQ#xgvQw~MiTBsNRqjo`Z&xW-1s52n!LAMksa&yLVrEJSscS at QFXM<bzMDjx#h)CtB
zc*JjfGQIm?4tqJP2%V!p-j<HbS=vFo6)2GA<K`&;9Akszo=qftAc8cq6JY?Q at 0bLn
zeOt^eQH4#o$7BL2xrKb=&sr@~f_-f6NE`gszUs`vZd;k}<U8%~@DoTgW*LWpQ1S!M
zfu!clms%*CaJ;6ZUKk=h+-nL*&t6kfcZ{@N!fTV+7(JFRGd3S34kXF2a%6x=i%LNt
z*Qh3?N3{vRn<CvURI|b%rfR_vQ at -G=sbjd1DQLK~scZ<0DRBs%sr3!8EBZ}>i~dcN
zH-?)`Zyq<{-e4{#ya`?2c%!=9@@97F=MC>t)O+HkvG>hOb?>dG8czE?1#?>TDYDbX
zPa&RGe`@uN1E-$P$Z%@jhKi#KFJlMCo=B6=Yb9J;hL-fke=>q?1ZnGYf&)@Zyot=Y
zR0K>sn~`}UafwV>oQ80;{K6?~GKPp4Hp|lC9N}(Fn!$68IL$GB>7$jNWV{NSK5M78
z$%7y|+EL;X-2>dgBrCf%-3Bte)fFuO1>|1Zxn9=DK6njHNrXAm>1~hB9OO9jSo4Sr
zX7?KvR1`YdBPt67ct%BD9e;o8)Lk%*z7;xx_Y$%VnkTA|JOvEFiw}gA$^W_N<2RGH
z)Q>F6dOwkBw?B^UlxaOnSAVt7_mBOQf`9geNa90qyx=+pi%*eKzMc3m?WSo>9<331
zt`e^o)Cm8t3zc2C(ZU-Om0cH>ZAm<Snh0w|9*g2WB<R?>kuKwVamriEw~okjl`sIT
z;)_FQJISNop>T-^DA4r<3tihSA}_1dC%*O&V90ZoS~nn*g~L3mRJsR3b$5jJ=1 at Yr
zg6r@&E%jx*eb*aMKj_KO-#cppc4M``CckNe>$YMwMG6NB*n8*+nH#WcQ%W`t&az<Z
z<N;*jc}cra|6n)DQm_jyqbn3yN&)V<)NaV2xPY-+sF%Ar#bLdrDbu1{b7!$9{~`=6
zUz;m14{`c2<Rc&&?~m66#7_`4uvO_M(~lvVfmO>v=t?G!=zJYwbNr`GK~U$L0<)>_
zy{o<X_t=O^J-0Q)x9mw{J|K~@VJaGNUr-HLTeF(S(j=*ii9aKSsVXgS2_tBdB$MDn
zZH_0rHcv7;#M6*pgC&&SAPHwE3n at uCIGbTK!Ho0s2fvvpvpGfZ$(K(-IA9PNRSFMC
zhkry0BLEv+Sv8f+fXxfi9$P{%F^VKb7;Ko-5Sq2CUDCC=03)^NnSX)VN;)ErCXHdI
z-o#$0Ag(~K8QL?mIECI&vUV=g_t-3mZYvz=tSRXx+6Y*rFc+hobPUo3GC%@pd;q|S
zER|s8PA4VicpI)^*BDj5*k#EVQWzY8x8D1j8mGS2<Ab%$<`~MzN(TA*lm&>7{@yk4
zopUG5!r_Uj1GRJpnf871?ydctshN7I!~)=v)G+SBiFI<WRJ$rY>Eza?+E}5J)2{kJ
z-QcC_wKWRcw$LvSm2GiM8aI(>MjAq{E2^btYuHLzV#>4`?7XrqP=vJAFKSl1PPN-s
zTIb;_#<W2|V%0 at 5g;C9}aV)(x`sCyHfp?ay&l-ifly9kfoOT_0rrd%Z%%(c_@|RNk
zZsoU|axl+j<>v^Q-X;}IgID>~g3XtJkT7fTmUu!$c#N&u44eYp=ZdUbNPQ1qz-~=h
zwnP)bwr&|MLZg+>3O)^JTe{@rYnJo9)#996e$TCFB=}b4cF7_&rn$6cwdiJX!Ly}p
z3@&CBl<o`i(DXE^G(Kla5D>j}lo<52s4z>!N)<-qTz55v85Y+h$kHM_nOQP3zobqV
z^qwEdbOeE*F7VK1G~MdNvMsYVe0o`prpy8CvTC5=y_XY}@)d~8$mDK2G_h;8BRq6$
zV3G}ubkf|&bL-e!SghYWGK$irr>4cqd58yiInUwA$;#1c6kKDQAUqh^vt6OA8h>XD
zL@!Q^bo*^_)+o|}^%mnkN_%_FgA^Jlx09A{v-wUD)oN*>ctqN<zW{tID&k5ey<$~#
z>>4TwLvyG-hN7aD=HrX4(zs;;Y^ZLv*V2iv+4KNJKWHZLGppUL2l+sQHkLHFZ4CKh
zOqBy_D(z|aw7y29ENcN(zo=U!hixU;-R2EPG<qL`<hFi`U{&ug$lT*xR`SM{13OLi
zoQ0<6B<m%rGmMtqC291Q{a)vZZ}#StH3NFJMlpohEL^m8LALPd7exzAnv>eoxLTTC
zzR2_<Zn9aE7JHcb^@m;SwiSxp;5_N4FAnw#4dEWmpHoGPy|rRv9bYPfh<E5ZHds-T
zV0M#9J9)fUlSTVIyCbX&*vZtR*<_A3s2_BhpD`-hhAc6$i!PWZyqMHXe}*oY-K3nf
z4R+JEXo&JN+oWHurTvOn>D@&y0G at -ZT4-uxW#BPe*8 at 3=G{V0H{N%gliD<K`FGvx=
zgWc7ZPD^{Lil^=KdEMQH1NorIbPH3u8SH5iZB>s%TIRPM(f{MST9R$aQRKW=kt=A^
z2avR$w3^+V^!^70xD%ncFRf<>4um8=j&n${6P2BPtn<k5SRx;bU8G!)`n$RSf}Z7H
zAPCJBA;01`WUpdAniy8eK<43&wF!z?sf6gi!Paum<wvnWnd*g!K6=iJqHs88Toy5m
znAwRPRe0d3t(O#aFpEAzlp{!R<{`VEn`aWa=d&0Zk+v+f(?brQMWVdwS+jF8#qA;x
zWb18Xm+XglCv`>#Wmt?{J7DM9zA`Atqj-Vjn?eAIyU%^|j!Tmy(&bfe2 at -i?Z?v04
zp7!iAadj7$TnRYN;MONat}(qWh7S{YNu<kDYE?<(a7}q6xVyZ`Ba%g4 at _0O;A&+!<
z&Gqug_+slfk93XLdU>SFV~v+b#x482c%(mL6~IP`x-9EjRr&CXM|x;%>t*53IWLdg
zvBK<|N4m!LetG1R9Pr3r*~1B4^v>s_A&-nR$s3bLp4d?L%_HNMTwij$o%R5GvZ?D`
z<dN}mvyTO7WzxtK6HIR&>9Op^BI7srtz?mp{IJMNlPoeaan8#k^ZVmwk#Pa?%E=lO
zlZPc<B?%VECqDlp0%Vbl(8VI37?MR^QBM~6D+>vrXC`T=?(>6AbC?#VYHj}YfB#7v
zNl^d)ZFWLqmVhF|j%bG~(oUESI>8yzv)H{YdSx)e;fSWlSG3jN`BkfFpCR??uz6ML
z%9q}?ymIrqEgFYP7V~+B42#({BAoY`X!Ghv;x*RNM?T5@;8B`2^)GyZnK(E?nY76}
z6>{&1J4sFTECYYVun!dN98Ir9zy(>lWZ;?=65$9BEh}j>gjPHE*{|5W)8X)Flm;oa
z?7o0+bZ{eZSx%eM{6K?-wRlWCk_|hvSeK6uE`kgj015LP9(J#JIoHRhfp-?_Yw at Ok
zGc4K{Sx9-<VudZl?Vu`W%*tO?%6AD~9 at +YbmAoI#4>Sgwox%057JVL&jlu7$>Q*>=
zB>x}mThyRG^pX)8v;MJ=>huA5h>^ngswsm-UdeL%$}7h5 at atL8>Ke}TqDoXe#3<y&
zZ~mc}k7kn at 1yfQduZ`PCFr|?Dw2LW)X4F)*G??6wqOwEQe(#r|S=~Msr=N!@X|UsQ
zbEUKx{o+db4aPsXQpkmz!GuB<W)NH{@1^@=F{IcQ<SrLg%9qdeP$kXx&4(%}g7`9}
z44ukDl~Nx4p-Q)$!1OAL82!p%FeSgUSF&G2oH)#=-_Qigm*(fv_%1s1mnOJFiYbjC
z+?t#v;XsfYl(CRj?!G1*+!U^6bah5ywiJu*73F+b9w*hk2L978(ff&W+2^oaEf
zmq<qBazb9n3Xi1Nl;NTBF_1xnAt}$sBhBcLtTr_MPsnY-LT5b7a<Rx~%1V#WY-lo2
zE-yTME}^WSP=y8_BREycI3qgumV-Q$$NA*TSEW4lPtBXbSJNkMrZ$*EoU6*4&dzgG
zCX;wzF@#B6Da)BmqS7NkCv&)A&^vR8$LAVx<<JM?wkwC0>%yBN^MJ2U4%GvETc|h5
zU=lUuH8;<sgAHD)!$4WA8gt!cX)M)5_*e5r)A`K#H-+Z)elDLsHIg#`8Xb84aUz~K
zQ#|e*UOk3&d_%P!8<gEOy(33WaBlLl4A*>Q1T at g}GElmwU>$!pp>a~_xB!B`JMOm^
zg1;A&BZvLa1P*(nAF4kN<)|IJpwmOL>f#D)%C6T1qiOr+1v_r{8pJ4E_ at n`OCR?tM
zriw7~{=%Dpu3Yly<#7XGf}#a at zZrH=G!UmZ<*#D<lc{JE8uQ1y92J at Jl$7nDj8=|}
z)C`=m;%zSTBEf1x>MXl0v=_k_4d!^di#|d_9*el$Ivskp)9??kv}nOdLyo;hPZFk*
zBNzuu?q#7`vtXwzZSWC7k0HRCU~_ts!uUNs!S<1-3_6rX$4ZNl at 0{MDluwGmyx@=-
zpH3k0Majs!d?4FIV+En%BZzD~;3yGuAx^!J?<nImS=mt(nHVCW{q<y8@}YnK&!w2X
z-?iY7(U9pq2$UNKF3v7ZUW(e_bfX8yU+gV;K7rBpqJy4JSYvT;pyoCbr7;Pm`n-up
zEL*G&0%LS@*2a8fcr?zUv{0_0`Ay`0kAl|2K-(?=@2r-=iHS_{RjsOP)(|HKmd4w8
z2sL8V2rF<aku=3s{eSACLAMhj1|HTXWu~w510mm`c`agq;R^_^O#<&3l`k7a9cFj|
zXj{h at j-g}@YsA@|ZbD;x)9K%UypBNTB&!rV=8%3UJqC*)7t%`}XpCPdbrs^w8UD|$
zI9+M6IyJG5#oP1d({T)CGIQd(yzA{_(8AymQAt_YUWvFf#|1O8aK=gs<2hSw>50Nx
z4PT6<g at rWnjVSX>`hkTU{m65bM`=JgPwo?mJA-Eqe6f)!#PUU;+AQx0xQ(1;oe*!&
z)8~WWR~B>AW8HP*XUzT}IyD;}i`domf)eEC?;JIOa|B2)+kZZqoboOk=|*gQA#0b<
zAPHa8L9~pEZEeDS)S4UWdE{`2UtbI9g#C!O%flYjhr=_AP at 8At#DId3qsnn5mL?Q4
zS?f<~PMaUi at UTJC1(!fe`1J!mT%HVGYm2v#gL at tAX<;>casrNQu9D$#I1^!kvGjgn
zCgSHAy+S!UFR{~L);zP180?%5HQ+YBI0ZSD*w*5NjT00!D>PN?wCF6FDCxYj!NXWq
zuBj2pNbQL;Ob_18;*Svu4->Z7frmm|LAxT5d$J>|B8I(V7YS$AvqBnCATo~>A8u%k
zh_(+Gh^h(7^HfbxA%{g&%sXHrWbR|eGVM9yF~e}}#!0>fau<@nKI<7|_h;i7b8wnW
z6t-wh6RlR*7pIO`j(H6SkM%mA at iB9UdM!ME90t9!WCmI9uA-7P-kD~T;N^0g>~P*%
z2R%xo-k3koEcvBTBds|35vg&jfQ?a<f$bBG#%Llor>Js*mA|ysv0YtnXMZe|JY})&
zgiLC+s??(6RjIQh=c at Z7i&XqeGs}ZH9R?3+Mc5w{DE>yAP=iH6e`K%;3$svwgfNnx
z59Q9-bRiI&)lY{uMPyo`1{b5&nDJ8-wDL7K+e1(T${x?%DY8c_TUJ%H?iPMQUkNn#
zSiq>zq=(@D81KG97Uj?K_9c^qAS+S5^?;Ao5N+3R2<jPWF$P8&deCc_BTtEMlC=A^
z at LUr?!BwMsjnHrO5GBSYoeBUTgl50Ivdlq`Nn}_Pv%zy&wfajFox7-k*S~3ka6p5l
z?B6uyRbl;8bDs{cKQ%LB{nf}me?cZ`DY-MHy&|>y=1de=OvpX=H6hIe6R2!}o;5`*
zkJS8{nZMRJ0hx`UtfVzrXr5sy+GD5yn~%&InI8jwP$@0`yQ-vAnC^oT(zAIbl$eo-
zjdDPAq51ivuz#}HxAz!2CZZO_Zk}CxG(R7WSsZ0fV>oMr6ozb^#h|9H(FTi|^w)@J
zGtnhvLwGyk$$T8(2w;c#;goaRFdsc6X9Ufas|+F*7VCe>)=swQ at _6eb!$D^MSZMLR
zM8K7p0h%fFI1`#4dzZ(mal8W&_roX#8H8(CCxi7{n&{g>O(V%$L-O2hM3aSktx;fO
zyoVN;SQ^MEo at spQD<hiQ<g)Pavlwkdo~VP<D*Ux<0Nd5e?!SCjOOhlz3cO06;F&%M
zr1O-`u6i@~Uku=mGNQWQtkRqm3dGmh>>c*PTROAru-WG${DN(d0pwrca4@!Rw}At{
z4P%5cz9R=RergvpU>x6d!pJEE at j6`f_R{ixgG7_ZRBk6VdcroZ!Xx>=^P6^R$XS*{
zNEQbt^2|8 at li3<qD*xuF#9ZV?CJ0Z>>fMxyx-N!m&Ul{iVkm|ns4wkYVtc2C(A~0G
zf$ZZG1v*J<;uT+iyemuJe9scQrvHXytSk9W3EEZ=4S#)L##I+^JtpSsS8Bvgh9GF3
zXz1}8QTJ_7E)2tbUF_ at mzj8!rKK<n8ROA&37<(>B{(Jltq&f>X3<kvGON~)`oByH&
z_vn9>e1=nL{VBm{hNS$w1>YvKPNwc}Vc^dZT4I|tSz!C=cE6Fnk`IPAdycNpiL0;}
zj+&`FKPJbw;`|`(j`8$~x#QKT at yvqC3?1mt+qJrT(p~EZeYDB1|NbY>$Q^-XN#>;N
zuCrlWGz*TKph=F4M4{^#NZDmcj+e2~ZCtS0hc!WCEPI`3O6C<T$)dOKUk{e#lHF=|
z?Rmn4vp!hT1Cu3P$>ksxOnCiOa+Bj~{Y^qn<kt2j{~{@^a_gUx&(}-qPswFT4 at t75
z#AtQ1F!=&mQc5_xf+Ypsz{!%r_e=`1q#F&gB*$bR!I1(LKi%~O=_EA^Hb_lifxgxZ
zm(tMPUngu8%L3j-0<3J>hrH_`Lk@{tM~a>OAw^#g_g<RxBePKvO8V{ByIX(1B$B2?
zOU6B2M&iM|L(g_WtNf3iZObzg<*~9+I_0Zs!0uwP>CVTA;;nUqm+X|+H-sFgjF5l0
z9>K;7;}JN|jy4vKLtX9%@d3%{0R@;4Q~dn;n8+D at A91JneP*2~H`ERE)8`ZA$TFfF
zT}Hk`0gbNXM{ppKXqO2wAsTEvhH?(BLK|0SXP<dOjRW{R9+eR1$JFQljbDShunT=Y
zAf&Tpf}dmsr+o#VfhoxfvU4wpa>WIQ$+jw28Y$m7uxU)z!FVMX=vY2+KmXsPspo9^
zh;8yZxgApa-hVxvz8KeM5Wcc%jBD=ynIpTC4*R_`S;#{Yjb`rl;r_vH92 at 6(OjEbb
zC5x36UBclwoFf*U?2xMh$72rZ(C375H(1#<oMad|`P|f)B;BYzCj*T}WE!8`5nvP*
zAc%H~Chd|je at F&bv at S4Xl8?4bediNgI2z}>8{WCrESRyi!VKTEYO!O`H~q-##GLxM
z!LiKRBg>iWhp`p~JdB3E;J{3NNan_cORMP!aKi^y6Kcr9iNzCB at XTCr!sHDNon=#p
zf(jHGFVF?&pM~61D?e>+n0nw@?k}iNhYc6ltc(zT(+(Uvx%+Kq$Piq3_N)Mth&Fzr
zcXWYK89_WxDNrE%M4!MQGmed1`=;q2IW~pagMDcW at FY7wy^Ip_ at xwG-LdH4q4Yi^d
zt31`XhjpAdq%BF1!q?|qj9|bOpA+-2!*^zkkW23hpB_qBpy+0fJq)ODTD>Z3OR}(;
zh0d8jB*sl6k{~{UaKzM3E7;OeI$<VzwjxbOC(YZi8dMS(o;m#^LDS%s%sS-~W+<KT
zHb<ATo>xV9?H3j)@0Vnj1|vEY8p>sI^vdk|YG;qIUArFR$7Qk|`Vz?Cl|~C&Y)esC
zhTCp!4ue6h+6DdC8bXIn)9awFPd=X#fg_DgZ5x8S*~CmMfsTHv_tEcHwwU)s_~MjM
zt^%>Kg%Q~?eVqD~<0Ogkd+wRvDVgmf`F=^Z7cC^i&@s1$HBr9AQxbQr#*S$ttcW1^
zD^HLvTIAKt%EO?5OJ`Om_+%c~WNn%ro_P)SqD}=Rb6&x`cl5~zQ{3299!$|CqrXqI
zvF`ku+aF_tkAP8OE$9Et11g+E8G{d=GN at 3JH4G<qzdbiE)Z0IyKt6mLZak|J2PR at Z
zN|Gl&Vlk2NY8hyD-QXuBtLkS_VM`SULf4SXBl}Xt+Ocd#t|Y->zF!h)i1uKYkv)J(
z{gzDLF$TV()L at gy4K!efLX;`fMcFn66QtTm4zq9&+2y+L;wMav8A5)U6&3k0FNVfq
z+piNsUs^PtaP8p@<4~g)r)`}XgA<Bqv9)T`ist%Az8{hx@<Pd(=h5b4FC^8+Ge%?A
zKb5wVR|GT4XSDs8EHiugbM};LJ|~V5r*qx?jq{D|DY606DF|{#ob#=9WQlMtoSP+!
zE6LutbDdgIaD$5d2(cbcD{2^%K;*peU~#;A)iV>_OFpT8za%jWai-sz+DP~!DF+ at d
z*0Xojl(N at uMIx+X>B1r_+$%9+5 at jK8w5uVoB3O4O4hvni+Epi+3L=<cSDcWuUU*t*
zlw{1#UVv}p3=4d4WAHrohFVuP6P)t^LuaPv3q!-$0_((H)Uapf_%~+1eVuH3!@Hy5
zL=e575yD)uf7U5JWf?W)IQ&G`H={FN5?{{Rxmu9uEfu4dkn;vh{LXK6ov$<RuYZR{
zr{ykzh>o;qGvguoeo0iLX*p&*R>ot;d_ at GA6)xcrpP6~vXEo?@@RG#bTsoMIOOzWV
zn8)^JhD>W&>)SJudkFZJOO7#yD^47b15X5Z-RBOuGw}`Qzm*j{u{cUkX8TFHj~ELz
zqB7FXXnXFh#@tOd71-ov=2!Z&vLYnk4 at nGD_%rqo<dzM|?}FcY#0_>SU#9G1C#NVD
zPW^?KERKBTTlHdiXZQX%;Su3pryF>bKU+nh&1Y4aGedL-nfAh4Pyr>0iTMi`fGj3`
zjZYM(W^;8Tj3WS9V2j;Y at b2E{#dx>Ke4Wt48w3&C1`C2T(*KK`I(DF&-?>&jVVkA!
zEC7VZcqW*|Z%T at GBh`vjuZ}$8o~2`#-Q3Mg7%zBYH^6p2nBlVLiG4?e2}*1!pVfQ$
zlHQixS(Gr5B`Wj2;p>lAW%29A0GQ_9ND8b`xbsTBUy|X37Lqxk)!hq$v(N%Zu{+MC
zA?{ddt9qhDyPE1;#xuonCVb#1Bgd*A42>Cu^*GT5-)I)!aJeO%EmO+{7A)H($u|Rc
z7_sMm;GJxb8k9&7tH9wh#}UQAx#>E#Lxrsn#M+rQ#Ns+5nrUK76TKGO<Rdx{%#LS|
z=!$_4G-l8y$FhtX3s)>3;pvx!1JS4g<B`DhY&3>@!hO$AW@~m_CM+7YqgeBshT!G=
z4m@!I!i`?}^+^C}*$2KqY4fM#uW6&;Cy$%k)ygCdNhK0-va>Lr{^~+gn!+E!M)UPX
zg=pZBS9&gne{`PL30a4q1fG*I9i=haK-hy#M-XF=aJ=LN#|;4_Nb<xR*e0XCk*f<V
zE90&yiN!3VF7V!5_ at t>^42|HH&l88)K7-$cr~2WI<*GreB>H{$8lPBe{`wi7Bq!E$
z<H)Ch3aAgNaRuK`K`dbjS(PWV?c{LIq5~1ImI;y0h~5zRzzz)};J6!}%8Yi&zyXO_
z=x|QHEO>z9oCp)O1BE4<zQKv;{D~)SuzfCu2HH`b=$>zQ#*8F!JYLRp0kX!e3>x4#
z8-C#EPL6eTdb3j_=?o>`FG&Xth+8J!-eZ0`o`=2HVAkNnIIF_Y#;zDNARjV-4cPJq
zU_5nZ at _kM;gQYFg!Ut%%6Ewgj`aim>B)g6nhh4=d$kQzHJbAryHhup^69lAR<)l|O
z_$8IZ0R-z3k<MH(OB0dq2Sx!@?Kl=pOnJbeN#iG)ik30}ml!0i(Y9hdJ|u<+zF02a
zUE1UKaLg8G4aRGTHrqUoT3yr(74S&yh}#p6>)B`SGdi7*niK&rWQ|$&iM+*P|8VBS
zA*(_r!L6s*i>^1@>JLkKY8TTlW7um&+f64_7RzZk6anz0e4Vo=3b1e9QT6$G!|$Ju
zN)51Ekg#9sE|BtQhcPh4N&o#%u9-3R)u&DfOPTFS_zz2q9xqE}nY9J4kL=}%L5#rV
zR0P=~)Q|Wi<z8S&=ERk-Nm$s>hNVg_To19EFbW!Yh<Zz at +R)up4?9=jwpV8at`FUT
z9WnUL=Zc<vfiJhjOG2aSQvG_X_gv5=hh|+b3{lAq$KqKJaX at 2cl}&sKta{asY1Asc
z at XXZ3z)oyK+Gd?^xc3`+K9rd=H(hnQoKJab8q<AMM$f9`QO74vyixVc_m5 at Q`pSt&
z&x}vt>_V1fRboBNN7q-KHByRFq)8!6-qKdPia{w;9-09;(uD_4AT>iW+=p!`f~_1O
znbh6s2%i^oLsQ|N6?F-N;P&9$lUMNL?4plmzvyb{pd&`>ysDfeTiO82_sfD at ki}#f
zN<{f;FiCunHHR<VyXuJ9c<;uC&Az at qr9k}h%vrFjcv!BrvGj#@qs1c!s2}k(;mRDT
zn<w;}BW?3e4TKfAi_)s|gl_%{@Jx}JIiejyD%^&@@KSNS+3L+r5~GjI?7>mrd`nv@
zDMoc~2|Z_?tRigZ_2Wpv#T~t9#2Dh1GSyQKzZK(Ks!hhsdG{)+ at X8a*I)!eMhVINH
zD`ac)`NDhO9kQSWCYCnIevWT+iO+fRTMa(7B+^FWXy{d~U#xd8d#&JkeLu4X>gjN<
z9SLV#??;M0iR39oF=C$FX7^pc(o-uu#s0j0js3&mQjT>V`f=4hPT`u9^T&c#BX_MO
zi<k9U^S({C%^A}onpwf9=D6X*(!jxiQ}6|@L7c#N{wM~D`<)vyU&3IZ<iG`*Jn)8N
z<|LH~W5*1awbzOqB!JN<0;99$iOje;vsI=ALTHW at WqFC3(9k~tnQ+(QskgsriNl{3
z&^9d{DHXWpwVa8MYeALZ<#w(yxB<ZeT#XxQ>3k_NN|+mx&F+fujqAuxTwTs()rY0|
z)FnJu^ucg|VV-!fvp+0z at yuR%i|lYXaq%0==J|#mzA-r97KOJu{zo4Jw%YU#Gv3PN
zDl7bk<*nl7RXe6>$D(U&GsLs&0OtW?@(Nz3HZ*<IH=T?6uOSU*Zpi=Z8Vau?G!@cv
zG69gZHFe($&wUd at mX*g3%GkirL3twNePC0TvWJJk(SdOvq;Z9aC$4Zau#fYC#C0(l
zdg1z3>4AZ94ERw7ix#KbQ~gvZbYh$RVx^P&V?_jRY at NVlLCq$nUP84FSyY^GNZ!(h
zs++KPZCHHuqWpzn*bmD at q~N;ElpK}ev72zhoNaMWt?<Fvy1wF&)Ocy at 7b|^sf328f
z=>$3x3Ohe>GPHNaPZ`=ye8&r}cD#QqQya2ukV3;nN=z|ut*T!i?c#?yw4yM=Ynzht
zWW<K(hDAh|Ed;pp8{T<hYp486<E>g5Dowt-qUiuHo*-F=a at T^!6qseJZWLgVn2!^T
z4W*y8`}O4e<@<$ak}tYMG(|{}B;@(;;cA6-_q*m#TFM&N^8K)cEZW!7NF-3!6U8rB
zh3kAa8xRc7gv<fnu5V-~0HrN8im4q7xduz;iW~@l)5C$;dXz|>6vF0Y`8%SUIHQhk
zurJ0A<3M;<;4JmSM94Vyrls}ccGQ|Hx^H#d{lXBu+;J?sc&-?IUAhIHbzbh|4h%q<
z2BtfelQ+aRnKq3h%Y<g1%4XL)nY(><`oWd`dmYaf=0E+2!XpL87f!5!IZiw+-k$k@
z8VmuDc&JLjp}>{D?>6*JH{N0!<L~`&k8ZKK!^9+>*_X^w8Z%@FH>TLq9lW1+O??9;
zlNoV3!Q?kav5UE*zkbhgAy1?y&#Tnqp;~lFNYzQ*lXxg)^r$TY#q`>*<@;d?V&|v@
zi}wfB8x?dm9Htq>QQ~d7cGVeOrGU2aX)h>+SN}Pl`7GF7xG+bMkNiZx at luBxim-D<
zjlaMUK8bIZG*h3A%K-N&@1oU%XOwk{@FHWgm2<~X?@$sQIC!ZaRP`n%OniS{sac%<
z^$aIV%0)fANZCx2#jGjoWW|_TFu0*t)*Lu5-CZ;6r~aH$(HDJN%8)`D3}3IJ?ZXP`
z?UZdyBWql-QgeMU<hFLmGAa|zBn{cYEHgfts2}!B)dQ|ttd=TWS49evb at jws2k7Tj
zwR%4C1?S)OIo83#1+L>Us{D*gHlx*}{hqwGj8*$}IA7tc at s9N)z at FjcCazI(A47|$
zX&k6s7MH|3Yu?|^5o$$KKY@!@g!>F8xh&r=3xWqMzih+- at butJ&=LMooRDh3>KHw%
zCWcu}D!rW at qp62LX#}2C7^NL}De_fS69aBr3KdsC%Cjr+1eZH%<u_)E#LtTPO9<H=
zXlVFpRP<q4U3WkyTu?)DFnAS?YksqlJ5Dyh^8K)Yyt*vAa(Hw|hQO(43p-lgqn+4q
zor>?@@iO1>Y4CXH{xp2LX0G{K(S{@gN)yqPGmFtRJ-FaeNP*>xRqeto{HFCXjMnmZ
z(0VdhXS|D4pHoTV3fWyj_on!Yc^ji;%Pv;5+#V}1adAiMUknaewjVE<6oL)<HPc!n
z)DySs*Jt80HpA^|h(EGCRQCF4=;b`MceTQLf~7eO^B8c^Fo8zGG+h_dH7T$jD{g;h
zZS|_|;7uE<xm1R#PYPj{`gMy{ulW7fyIA%y^{6kEcu{zHMfzb_n0_^JlzVT|o@^`5
zi6FBRQSo0AryB!Yz$xvxm06$wtpLdbA33^s02!E=JBrQ63P at uWL>9V9;G?9N at 6a2f
z0!XC5EGcK3f8a&hK9-a;EL02e+W%?!u&3SO^KT2AY#3bK|1ZPYBd7ku^87Y;{bd2=
zy8p1OOmJZKvdKrgi;FmWq=o!78eLy1t2`PP@$Ay^?@*whTCS*?b}Gi$C5 at e|ytShp
z79N%16|Yw`NQ8g1i at 9SKf328XxD&5=dhof`Zfe?_ at 7Vj?iYaDl$>nUY)3&KKe<XGm
z#BM>_QcOJuz+-`jah1Tmp0?E!*P>~UrMV;QMdg9g47~bhvI^MVv)6`3ua6a|>Ge1u
z7*>Mfd?NU3`r{IzK&7I1Nnt&MJC0>^?#L6sDClG78_CwD9eE3+X#KjEJXCz1 at gZxy
z^RXb^aQ4ejj}OR`_X;21^NR0S$g}<m&NV<vBoy!FC>Gd?Mz3+M$lndA8*mm4HK?BZ
zCSq<l3Ysgh^Kn2S4ShZkP8*M5aF;uV at rED#2~1j0(;+1830bmBpRlVYxx-w=U|+nV
zDxo|cA<Re2vR*6J){5<Nlc at 6JXjSY$HObF;$My9i4!>16hphf at C)q*KA3^&k4Of#;
z;vNecG^_Ms=@pj;R+d0^0(X9R^0g+F5srk)04~BA5{p-#q!`*!ho-Tzb45`busMhY
z+0XW-tW0M9AKO)uElCc-uJTWi&l6zI)7z~3Z`&XdL)G7<NE5aJ3lUq`XB%;Jr-O};
zD=<8C&DRPkg%ASp;MKVYNx=>QQ%-p<=qwzIc6M;%5p--iG`2Hwe6kr8ffU})Kpn$|
z!-^i8c-(^Sfv5T9F<$x7>^r|$9vQqXGlsyxd^9l+B^KquKySygc)5-RgK+P};`Qp3
z4x$WSbF82mN<D2#-FT-^Ka(#RDby2ge<Vmxy!$JKdLq6bE9B!sjh0m-m_2e&1X%J(
zBZZnPwBsJpy8;X8NvG38s&z1&f?E97a!ZHnJO5fBCt=Y^t(-6VoDJlj?K2KfHXpMB
z)H><k5yCDHv7P~Y^ZyC*s{x#})v+9Dw9dJ6bb3VR%~J?g_~0Q0NpP#AumJmlL&GB&
z91nW3^La&+1I8S55Tnj=zRdm at DDvgX>lQ%<&s*#>=uz)2kip`R(smI}m7{n6DVoda
zU_9Y!wf;eaDLZ!0_VA#_9qsm&_sin>u4y5!2Z!t^Jdk$lz~(3>XumK_eADA<2di<9
zNKoJ|)Ez<(StYx%Vo~ZxV+}knsXyp3w%=pt!M%ihXq$tZ-Z;YHIji03C$t{Exko~2
zLZz>Cm?Z|YBOFC%6sT at 5EMwB6ESB^TGXe09Lk4z))@{&Zu}QRzZwi$`H(;B1{A1M~
zd#`J}!bPj|&%~ox(3GKCRAfk2tG0$XIUB|r&gGZI$Fm|zYy{>IsfsN(`<RWzZ)_4L
z9X1tKPE~OTj)PlkQ*{zaOjUsg at j#8qqHLVkt(3&MHcNywDcEE141wX%*_diYlOkGV
z%h}MqGt}nA=93jewqGn6Ud#5bbkZX%--o4NZOU(|7_7s+$*L%`O*XUosPhw(kD- at B
zW(xBu5I<vcuPE4%FPkJ`n^(R_!iyO$+V%);@?b(1$@!%?Q&eBzQB$P}@O~vYXpS<2
z4wIcSOX!A+M|xA$o0y`X|9KT=bLE=N*4H*(hbkqcp-NUsO0BAxY at ThwqdSt^20R*A
zw{pSpFO(TxBRIlSb9f4aeDT+NwF&YxLlJ>HHTz*9k>Uf&;f?|`4zROf^`=}N6wt9R
zBz0H3leRd|qB8mgd0Fx2|I6tucb&xNul0tA5B#0VgCoM<2}ymv=ivg9;%QQUUY2a|
zf!idl&cvx!?)EWrE(0-JF6dN)79wkp99Ij6$2d=@zg7qvj0KAxJhhsf(<82k7Bq83
z+dM?ia`auzptBrdId?r||IJUdW`9{aQ$3Tk&L*4jTsQ_NuD<IO;+2k(3rv`ghEr{~
z3nMm-^@kPnkO)5jI17WrBA8?G44x|%TU5x6CthfK4~r<ixfV-yB7c)(p>CnatP-Ds
zLrqpZ^4AOw8 at f9ztjo<n`82+oj(fAPqHiKnw)n0L8CK;N9AAjmgm)<PnA|mbZ6*T*
z(*zEk&%Tog#)pk at kipz*dgw1Kc9jGz${nC&gW{0xCUy-6+ at i#PXuLQCm24ZpV;RMB
z9SZE!b7htg<@M;SbgAZXzXfVj$0?FCu3m6rz2~c2OopOXob*{Ka+b{C7q#*yRI`IG
zl-*%3Am^E9Rju*f(dECeaFV?(YHXc`!jx5-p~{XJ%{I=-6&~$4s;cy{I&R9I;#%)*
zxA9t`r5O- at p>*0*nS&y{U4^<T#p?xiRP at Ut<-iGZEhjTUoOu;??K#O2YE*aRy0%%B
zRS|om=M8e5^N3?2TaCJi(@*4=bMI)2&l9)qG3EyLP0d;PS{z!VGBP()`FNONJid@#
zaJU{U{x8Z`w-=V=-)IzPFcb~}6W#objuiPjYx at kBoGUErFosR?{zkMV-zIo|<_b0r
zQ+!~EUl<Op`D4ZUu(Lpyu8%rdANN`xQL^pG{}=CpmvUoe!=xoOB*gPNFV%jv#GBo;
z!0R^)h0VeY_M4%67|~xW&%NRGk0sdpMEPyee2Ia<y at E?ShnmHPeJrE16C$c!Izb%R
z^ilwohfN`~GgGZbt$c}c0?sz4Mvt!8wP%ZvRLhBzf==pHFU;bY^R+ at scT8fQs!N?p
z*<`w*eyr80P+m6KOIvxMh`rSPUuFHxQs3t><E(ye7p1Nrx$yUa;}s=h7q^gsft4LN
zchxMnjCYD$1&u|#Q>`n;tJERou?KDj($JGb;-myS4T!NJRoaOfGcd7YtWs^7c<R0|
zJY>pi#TC7zKKpEHS9a+AX_JT8am{@a;glvj at tFNm!|)g~FU$8~i6=X0=^d=$O>$c|
zDm}rm_9&Jy9$fc&sKvNQ>C26)@(V^aqN)Dx6(<4GsU|J)VmRBb1oUE#h6FUu4chmO
zbLtrMD9aisf(}ZyiJUJs24`b9hhU>0Z8OIhB<H}zO9zZfn7;nr!nt$)AUPcHrd9Ae
zyW^6dtEwr8o_O@~c-H+lw{$i)%m!yu3InvugW1wGb+}=8iamRs8O_fJQnT1DeH*KE
zUl<N;?8l1p5=s;>tTu3J^9sB;EUJ8cByef{hi{d4`0uYf<$L#&GO0uV0p=Nw;hToZ
zWAD&?fgAAz4zZ3<#~vO->7Zfff<7pQPT>V^wWT0XA%yq6Vh6o5l9}HiccWNUdWf*X
z;IQ~fO^SAi{0PHxIzaZvOA@|4AL}>EqbJpO{<XB6c3 at dlkZoXZUC^w3 at G?rpX~MH?
zpI7xpJjS(0hU;#Oi02sZ1;gegqmhvE==7MALOa+j at a8o_d{)k5|3Qbl2(m0RX3JoW
znGE<REfU+%@_kvBbz0_zR}gOCk(1By3GzP}L+7lWj|GG2%;=m-JFK*ULt-9m{l?*8
z*|?u8x^s_c;A+WAc`6j7YL-=4EXd|y%l5k>@K3n<rbAgYUV7jp#G}BP|KNi&v17c_
z;IFU<X4}9HJfcA3P<hDH`7wv)v9Th=Q{b5@;&l3*aAAEEG}cRwhyt0Kfk|k=h!Hor
zF&y0dvEoFU8)l=9TsS|-IGt>jMUcH2&oFSu7m#oHK!aQc&BpM3l+C}E-^wPW^Ru*0
zv at Wmdv{WGx&xXSfMr&eli0k0&K}vDi!Sx6(L|;M#Cpi1 at zx2-SP(yPwogmI*8PRbf
z<;z)h34 at Uza9z=-Aqqa-QKKP8^ZThLPDSdfs-CFxkV|<YN7!Vd9wJzUnctL*wc=cv
zRW6DUbnE=0T=|6|8vlW-;h`8~_RLETKGt(siF-__K|NfZbqLG%WeL at l*b%B8g=WF`
z0pkn-Ha7`II5=K$9wOwR1=-=AtYd3z%;(racY&Qa<lw=WYvSHckBSujx6c1N{P*e?
z{x2_C9E26Y)W}wJkHB+{XC#jW^>Qqs1`pOrhKZIJVQZ729Q3dum*xAiOsK*5>c+`~
z&;w^zOJ>8lCLfaVn_b9+p#}3EICq!Lo63a|nZ>%R6 at BoVSrXBi$mr05$D-t83oWS8
zc#+%|TJV7vwBW<CL$u@<2C31p2VbsTCE*|{Z8%hRt0JyQ;2KYAi;LQLtbi;8TPBnS
z@?5hbrF_q0A+lB0LEulUS8Sh!kL*mAxj_)7rQ<81i~@HYqy0~JwPeeYtH61$!Yk;b
zFQE1KbdRnlz5l at jBFIwu`pqgRNhv`Pz`>~{QqZUfSffY{y!6b&s-Sr3cpzM}Uw_ZR
zA~KLzRYE%2!EmL((geOg1{pfh4qmCM(6!%Qm9<}o3qyY-+d0#-(z?~%`QJae#%n;I
z&zfIp>0}>;{<17BOA};ThG8d(5{4|QUvo1h at P6kIYkT(nfhWI_9`;)+`w7|Hq0Hr&
z#{$d1Cvtdnyek82aL1J-0q45f3&-PXH?X0#dqt!)j?AcpyfTZ?tm=Gx6{-<<u?xyl
ze&OTUh#j!3tUrkrI9tFtkvSI8!w#8fQI_G%P@=$FdC9|;X3cuW+E3*1P%vKiOuQzU
zp~4-qz;`&xgiTS`AD?JP-h*40<%}02JkTZ}K=z9}d7F5H;W0AcjK<2{4GcKS#$Y0z
z)p!l6zkh9CtwJ!uhzCdpZQj|*^IfBQ2O~#>%>ll#mOdCeW+g6Hj7~h^%SH*Co#8o~
z*R)B_2nC6x;j_9~1(Z9&^7XQa^A8rCv<wD$QAsz*-x{1JYP@*o8On5+R3z=nr=ADD
zol`5c8qWrr*fD-%J|Tm<WFg3X0`DyGJ5w|9LzFq#3x{k4rm#(gPOQ~mL3WGbo}D{3
zY0%~c{c_KxZoPV|6Wh>>o$J8C!8T)79qJFDf+#gONbzE^4P010Hx~@7A6tPd8WZcM
zz_)F&==7EGJXk(f>4MSHl{6A$WK+3t(5-Z#EY?A^!_Fk at d0=DTIZHuNT%(^tz6 at 3a
z%EHuL5bO82|CWOLrVIMo(sWuN8|~>rZyC9}&=@O=*OsC3`6`==;hsYCBz}$GEk`+w
z&ayP7ew+i#+Ao7XuQktkSkAWBn)h|GW{NY#n;5ry=tnlnp(6XSe7!6thnCv)-SXLW
zBG%>)Jauqz9)_>WaGd<}wdcEqoBsU^BZb`u`wZf>^E$W;?gHyC3x>(WiTW{)P55`5
z8iS(C3(Kr2cPCsr06Xv=HPwtrt*{v8tSG;MC5IHBt2RC%GP$G0%CMYVk-xL1iTusK
z*zUMNd3-Gpo*sR?!<?l8bB+_VCUKwW6djMl`z6Y%6z}Pa>tT6(=sKAm_-6RTruKZd
z_%&tFl9J#S6U#40O$vNA?zN4l_Fhko(KIZ)xsfHkVPuUqrf~WGwIG8O2XMxf&KnG_
z*H!MFKR$%A?DuZmwdinB4lK-ZAS~kd#(-(9=#8FLYftzs=7~WMORtF|S>=|6nPlz4
zIAPj+Fn090t`+XZ1uRJme`5z$D6a%VV6o_ at 0{4 at t(aw0`i30vuVrMFeTjuJ2w0t68
zyZ&U+nUzc8zy5zLwTo{3X83%&_I$T!dirfy3)5dqzxgYpflrKA53T7hy2`Y0;%J#+
z3XcoP6 at JrCEG8^{VueD54o1>3L5H*I6C<<QEghH2E+KHk(6i11Ya-Rxf^3)a0AQk7
zH&SXPa?4#u#vK8_Y=e;@i$8A?mrlM|{Y+H at o>9U4*!&*rv3$KO(lH>)f&ed0fD&vA
z|Lf+&mt`~jEej*$K}66YH0P at UBg<px-1tVu)+fMdI^ge<ILDfF_~MK>Hbc;`0xBz6
z$pf!5AB-Kc&uhiw!FZqJbC693{oL?AjwHyoVR(#j$v$S{x#7HS${kbnv3y+?Pugj;
zjL9%q9?}jL2w#47Db3)$tx;>@pL_?kCODce)dyoo=Kr(8{ZBGVHulXGhQ5;W|3nCz
z^;|F-5+r)w8|zVSfF-IdNbnTRj*#y>(Nc~{ijCAhhHJNJA7zgKo^?yVTXd|~ilfwz
zw$pWe;||eA(*G>w*?<?jL($~N&GvI@!_9w$r*Tj7VE>|JvVj28GUKA6go;Fbgw4UO
zd++rrL5{O%uyE!}bvzh5`X1K`PX}cSK=0b3ccuKV^!J)fLdy8arn{0B&-nT6VMzal
z^2NLVf`vB;%NNH7oV*X_hgA~YI8P|tBfXmS*?Iec_Is>P1CtYmoCmYLU*lTg0{}0P
z!`Wo at wW^gdb{a0eZ08C#V5?&US-8L)i|g0j06A{d<KGNVZK^Hb41Px)G?b66;j79<
z&khG4dhR?^OxKIR?ufOYZ=|EHE{v|cZjty at 3GEC9;N!_vZFh>A$CEX<WAfScg{Czr
zT@(N<LI$3XVzO!ZlE+;>W$4|N$B1?I%UwP*z1s-4*fFY1O*-<M*I%E;Z4L=nBZ0E_
z`+WRkAZJ5kAb1v>56osPtBD7QwNw`(6B2?GG^NHUR?&(}VqrCr#SJnjsSOKuF^r#B
zEbmd~v15u|qU at O}Yyihd+}Z90{)r)P+$)r2HQv1wZ>wi??qJolkR*-4hE-L^3&*^G
z4P{x8PfEd$TA!D!c00Oi<qimhqH}jF?4`19X>`+&gVyUCN5&&Ocqr1M4@*4RvG?8S
zs=cd6NBGnRi%oo^;_WVM2*+g+eQ_e(-m#~ZVB)Js@&97E50V^;EI43PAqbu0624U=
z(2~B$2BqMKjDs`0f%fNJ{DO{2lCV$=e8DHN2V=*eu+ at q_IfHfhvNfC_EI#gkzDvS~
zNgA$51<>p(@XA>c)yMF48`wBTj7voti75KWPDZ#hYxm^68y7Ge21V%%zEdB3d*-u3
zj93vZ>(s$z6|F at j8;tLU?@soZ-ZZf2oQET&)>$#VLuYh05Ghx|b=hh33)ffOTW!Da
z;FVrNb#u_$!P8&Tg(yL)vRGt3A)>k<lD5H-&6d(;abh*D7}$ZTp)5m at HSFNE)U9o{
zZwv7|f?K4QgENK`<;hv==!<fCjfk^jU%`Y6&r!NimTusg at ug@f{hJLZw$iG at iHM@m
zYd}KE4+?HzK6~nG#TM?EH)UepDT){!mE&JfrgIGGw~Qb>Kcm=KdnP-NVe at t(McF;>
z-~R6(U*Zi$&LnPet at NO-?j5~5i-xb4LC2uQ{5gNJ?0n9>>$^o<=O!)FNm-V_LB_;A
zp+_gt2&aWX>V$;_QKl0M_D6f3I5RQt<(U?i>57wBup*|rz=t<jlN-*$h8PDEbHnB*
zj?*!42-44V;OrmE*Jb&*=i}t>mh^H&!tb<Z9;9bxC{`-+YW}VPA4bQtUl#Z8)@Rq(
zw{viI=*cU}(Oir>${&L*O^Xw)Nm^o13EcAx at AKM8Jo`vIvS5d0iKZn+g22)f?gprN
zG4HM>!ecp%gykdw>>*9>mU#A)c&Kv68Y3-H_!4J4yoKxih4o17pykNMia5JTj;i^M
zhKTo5x9UYJ!#CO_X}ny;J8V4=?^d#?79?2$_n9~}4m_<LV=potzU&P<LgynIXX6TO
z<5D3E^RaxrEW7uL*i at 1g4v&r_saX+QN~%`G_CStBN0<>aJT(TgeCmE6eREmTo6HyS
z!W>v8^GC~@Hkg8p(BUWBnHTncx*K<tMGnq94XoITc$ks{MG9LmOu>;6{G?6#!SN)m
zq(~uglv|qO8A+cYqDkUCK}^ejDj&MRZR0bdy9r#57#mnxJ)<4u%i8ee3Cc!xG_XV!
z`O{@E8q#n^BP$k!*9;pb6Y>B`K()VrY*$Nm9XklzidT at MANs5(C$sBG_CI(ORZ^>+
zH?rZP7J>qb5xo5_YiK?{3q5MTuHZ`e33KxMr3sw6Z1IK_!yhtJwZwX%$%%0pXp0rJ
zgOSGi2pNr6A`X!MKL&%1_j~|E+g?|U*!3PKMo`_y$w$UBcp@|<IonxZ=L;_PQJ*_p
zMt9+f at S<cVp7wRTbh%*E=ou3EjCkiUP#n$&6shY9#qv0zSRW_f*-&g(`H`Q{_!+dl
zkZXvBhFcdKGp^{i-JCoynT<iG<{HndDAF8z#cdF!xQ`FcyzBeuc>VIgp*!tzQ1UA`
zThr0#;tPCs^l#iTn9b6woJxx)a$;^upF~J`ULlbOMp`d}Ij{38mI>Z)0$u4sCdzl*
z`_Ao^2N0;%oS_i34T)*luUK30c@!RqT=Nf20klG9YOoiEtl*+?{^ki&a8d4%t6+ at b
z=<--Yh?+Vvcwkh1a$vkbp8P<sN_cKMU>gx~s%^aV5My3bq<!i4&$;PmIOcMqI5?D>
za0Cq+cY`Bz*h1<;o3U+e%@alsmi7Nppw4EMhF|$t<F%KIG|~6nCi<t#tEk+Q9Pj!n
zJ1!oYRNvvbN8)Y>5vX~01OyvA>9SbH6B#jd#H3Z^Ljc<~r7HNaB&H&}c1g8#S=T2J
zfS?bpN|=N11BKq(>o}UvOEUm2$eqZ>#A%hh8m*K}8XC*0g%%d3+Ngm!mzT<u{KGZO
znO6y*<MJ at cg;!3s=d_wmtWGatB()a1Q^<gm%O&+A;{<qPeOVYQIEJ)~fpc8hWIqRe
zDBxc%uO%7Z at zABt&>=j(BbZ6=g64B+X4LH`zPgO8Vb=KQ%o<$usrL=l?R9V{Xc7-p
zg1hT{5n+JLz|3TqMJ33BwoooRlsW4}stim($w9I*A7-t+<khBN6q;x|Bfgo6YO*uo
zMIBuP2VQgmxi%&E20Cc}yA&EZ_+`-%N_ at eXuifevdpv@zQG~p0XrmXdx11H*H~5*I
zQTdQk*_5GSA85R_tGC6VMS8&hqg&ecyH6FQ5|->V5(2d!SsCm7vhZS1gH>?N^emDY
z1hY_MlNDHvSG2+tRX*btzyEpHF8zKc()h|?oc$|d1 at sd8`v*3b4nsj-{0>cCuV%U&
z!9Nsvjx%APnbDgs6Y=~<;hRD$ul}nMerd2;F<WJ2-IVIf#0zgTu%lU;`#NL@(rxnN
zjS(Aj0MByGW-HzduGaY|)|M)RurNGb4k8NKx7UyhBQRIM#5G;p#6%%L){;5_ESF<m
zN7?E?8;Y~hplZV$00ZQtI!{9<xc;FNw6MrpCW19vR%{NhcFh=X3){T|&bZvF=^0rk
z7^ubN5VD3rL94%H&&56=P3&}#8|5*1B8G&^B9yo_vBpxCSD9drwbtd$#B-lI0dBJ4
z&X;b=%UG2pZlB7It`wM?$VP2yaP{kCJ9u`}fvo}G`@A%kne)TE5JbqOZsNjEjFnZ`
z0!dRZF?tyt9P-+i_R>V9`AIX$^tv6UwVzdW#T-o_G06%<?nmZ{EV at Sv58ag934y6z
z1H=n7de_9s01YihCnVZ_7C8$vr5w%Y(&Wu_ at s%WvDam<?3Gu6ggsr6g(WOrm)k9JP
zDN12fgXB9nkA=8aQE(3 at T<auE3~w-DE6F0j&MuOKkSea`Z5_S9<*WsA0et8&`ITK)
zn5Jj|MokC+0+24buDMS<ZR*vP9Jl3eTqvGWWHZ`OIT1CsI89=u0g8&I;@s~pCXG;(
zixx6t;8G#iT4iHmiA>GKlAW1tVzM5G2Jbi(+ePR4B8WQQ(IZwzY-9HXDXQ8|G^|Zw
zaKYw}+o9Pufh4N6ASPoBa%)G{o9B92w4xL90Shju^E7lS7VXEig8s}*?0?oB+w(PD
zF{g3L7uw3n(qeGEHKTP+4t5?VQ{z3;+5!8c5qZTj28DUqzE++e?vNxnXpw+i!HU0w
z`UU9?`E$$FOM?oFR5&&l*`tqc%2^8vf5v4blX>ZiMEaSG7HM#=TIrFwCH4F=sDn3T
zZK$OFSWK1<q?lWkZ5^u{#ntQ)TQqyFkW!d_Mk~xMI3B+?Le_+WCZg*M6cKD{QZ5Hs
zf=A(@{i=J<t1 at WX3>(O~%a(XWG<wL|YB!>twG}>kE?RU`)Z&Sh71#c@*iLk~k3`6a
zz7LRL_@%KqcqVBd+-zssU7jZUQ1n<6J{MU2)%**W!T$7oHMe%~Bh8{0jb21ohS$Mb
zU2bFVw23b-s6f0k at dG6mJ4`e?GGjT1T^1hgWxA5Gb)~cJ=(kD;#2k(MwUB%3FS%Md
z8Kx$6=Bq_UD;p8+2%6)NoExN(S=SiuLdGX!7pqe%=YM4vJn{L}J0CIK(6<m3qipSD
zmk!w4*=3a|YFA=@Ye85>oL6rqT!u0xku;x|CgVJ0)k?y`f&Em^US;n`MK{gNjjC2Y
zGiU_%CyHH>bcGzXS+sIVF1)f2ez2{x?Mz<Uj22i!Lk=W+UW*-Bo1l8owy4II(gGn~
z1Et^5G(pDQOk6Ix1-Xni4|y^@SRZ*}rFAr)mj<JTn!!Vb-{?iXcG9V)XFUsSl_K##
zp7c6qRJ?N?nUU#A2Q9Fi@<tY$r)|eF=e?Y&8+W9sRc?~eJKH6rdG76WYlimM*ke&m
zfz-yos6O&4Kw_%<)us$zjjIK%xLcbawSHOn!OZi<Yz2WryX{p6rYbr!TN~Nv;J0ii
zvN!gb8<Pez<Xak!i7AZmmAay;qvvYd=}D)HDO;s#C=k~xwIeHAv%M at V&C~lLdwa~}
zxg*)=WU%%qM|PoEcw(1puN%nsvpDoS89ci-798|uOj8H$-#=jjo;Ge?;5<jzQ>T9v
zM;4GO$``W6dX0t%CZ%dv446|b&Gk45Id3L`$IM@=3%g;@t(4S)zt7ATo1;-UOD?-y
zhEDXl%b|sZ)r(lnsDhh$Ox$%Fq@){yNQ_^K8#o*6#|cx5nd`EODJuMkv#o7k)IA%2
zS96^-ADTi%#KSMl)U-gz__b;!kfS5GgDgcO&vr#@M at n}*Q5Mle5s}O*N7_%Gg(x5R
zi!3y;A#6=2&n~MsKAM^53kwd*%v_fs#~Y at g?F4q(sV9X4vIPzkDTs~^8b39$_ob<>
zizfgal8snUODO%kOIMW9gpMp(k*MX!JYgbb+oGkaZ}N`9YAm&HwCGtSO~Hj%<URx!
zy(-t8&pM9g^U|32UussC#+2i#?l}nipuuR4smhO9H+Pid>O{TOucr(szk{<a)^50k
z2}_f~G4*iISXK`n%vde5D1=GN^r{`0SfW=IqY|>ZSxx*xFZLtHF<b!?!G_h<=-nlc
zHTQ|9O})F)CZG>CTPd>1y{f2$_{<7uiAb4EWN&U{^q~!65DB)~AfwkB;a<x|MlDs9
z%tlfQJukc=>jU}JEd$;Ne8`PA2BePctz_)pwl$*;wSsXCa=$}+nr)wosaFvsRkW*_
z-el{@JTOvwS<I>v^9BtF?K~Y_q;xT|`Eby?ms~8=un!2{{#DPI`E~`;ok4kHHd}H}
z-Ws=2Yb`cqlhpxC`))e0-x??FYT#o87X0?y%FE{MPYleOK-lUUKJyJOQqdAtk^ktf
zmMlqD9X5Zf=oR$gV}9%DY1aOy&$0wTRMj2jkrM*2Wm|68<%SR-KP=d^dYn}u`~J&<
z4}ZR&#KT>Qc at EyE({djMd1^09_uBMa5xAn+Iz~gO7~wi$3y!;GfPt`-Sg^P}As6cS
z8{f~-f3Vzd6L`WAjtq6faYt2l9HrDThqyY}5#11-0T}FboY4CZPVo@!kj3HwgJY`W
z1nKU$`1n-U@>uR2snBAJPjK1-VsL_s#zt6;4tf6mT!tqYZ_j^To0hk}FM56+104@>
zuq|<mS&0_|F_1O>j!Z!fSEFpZB?=?nk-4Jb_rhbHm&&rgxjNk)jmPoqIdDxUy0V}=
z19uc8$IZwG)Bla?41{TnSVRtlLKOf;y{VGi!jDI{<)^zSKK+$Yn{q006pZn(doz>x
z1ZUTK2};{*jmKuRJ1b<la=>+NPV1WmrRrpb!BNbcjOTmrjUo!#lT>c0^vjwn78rg(
z{3H0t<1UFl6kqrK;*KfiQ?+jFOkYP7$kG}wF*2E6I#r at U_k#I*Lm`z11hRoY<Y14n
z0kP!&O=Dp{acI!w1~!nB7R}q;(8yWDhDW5zZ{$H~S~7M}lfx at x1w@NbcX3Kyxq at rw
zS3cob?W+D2#bfcaF$v-7l`EFwgYgX4K+^LmdC^ibpOOf%Fy^{t#B&dNe5i4doQZ#1
zoY(yR+cA56uXGJ{4MtC`sQf~etAI7d`AxRvb6K93zuEe%iJ;D+adXXlEc8wp&zkTS
z3x{6bgS(DLmTzWMVKfo0SdGN0q3RG+{%>7T&YO9QQO;eJM(p1B;VLcpujY|7s|pRN
zw{kyahIblG3Bvd;p}~0PT{4t!+b^{I^i&j)0cnoX$O8CvSrFmy at W3Ebf3<j#V*{Rk
z&-}ZEU;g|!E6$%@FHmcq^N;16Hqkv^28&W617M3%>>OMH2Pz35JMx6=<rlz$*;qg-
zG`j_^nJ;ea$!@VhOQiM0guoLDYOG`KPeaQ`+|Uavda~nbGjMO^g^fM=vZQ!|e<6CD
z>pS#hT%Zxxv3Fz$bhjh~K3)l2{x13;L52|i3pdV?A7S`hh7*r(zQ5PF>||HQcTt38
zq4TYeV~LA|b0t;b?8<Kpwq;yBgonp5(LFh2g^PCh{laxUkEMm*Y$2K)4jk<;Md$RR
z(uuHoX#y1-&qT^*BPVvR&GL_bnhQ!q*w3yc4TL29Sr5J)O3P1gk<XZ5-hAGcL*L~Y
zxypZ7_O~@R{$inDxeKcNH-kd;Z24w*BL2Q_ykV8U{YhMk278N!g;0(AnaA=xs&5ZJ
zhMk6yw+q6PSr(GYaZCJPHOwtTQE3nh5d2JhLy*>#rgkS$Gc3$%Vg at qOaWOHcjTbi7
z;meYt8Sn);F$7H<S(%wtcj|G88od{|lH3KF!5=y2l+X8u)R-_Cn!$0N+>t;%xM3($
zZ9kjYU%bt8Md(4tjVI2Kx5ayf=@A|M=!q-V)bY(yqCPJw23#wW$`u^KyCijSNJPi`
zjS+V$h6Sk`hM+y-yDJP7gJa7oL3 at 2I^i;x^9G9Yp>%?5PWA=_nUs%F5-pMPgPe#Re
zJVrSfJ5UPjSU#8KdHI{HpVzKV&4A0Nax7$PU(cFguU-?P68=*d2UP<+Y^;Q{as`Ta
z)IF$%2rJt}>e|pE`J8);ac+}cp$5MnyxIC#Mp4F?3L!Vt-~r1ZH{_ud;Ty85+F%5l
zI$=@9ZO_p1(^nilhD4&j)X({PS*&%*+z~jN^bJlnkmYDBWV>5B)LKjwEpY2pIMD-y
zsVFCU+*#pO0qypD!TUJc!*zz2`w;&77XB8LwsXq|PpPbJvA?Bp2ftwFOrGHZSVKE_
z3*T8NoOw&Mo_4*1_}~AYmp&`#iT#D`tLKdE&3%jQ?S0F4Jmg(;&MQLO;$)-1oRx=|
z285CL$#w>WHo0MB&NdcO)k0>ScLmgd(AV=nMA5w+RXx8BaL?JguHa>Tj^~CO-oe?H
z2K974;H5Y>9l>zswkZP_1|^oJ8o~0SnE&v}mh=68oBDrE`Kasr$IUSy;07e|c6-~Q
zy~6M>l4mjbVnA9Ensvwq%ZHXk5`}_voQ*!|y#-GD8-vbtOCk+>Odykr$U>?*nzIOT
z$Q*Jx|7~M`JqNZ$jQ4ZBWqCaI`QSOG(!pLhcWu~)G=`p(%Wnqfho(JDv%gzP%#xqT
zgowls!?VXVLn^;oo};zt7ej9IZLnaPD~%l?IxNdUv$XwQqx)m_gi9I!gpv>cmwGTa
zto&ZF))3wiCe(-t3UJa3 at e7J*dQC-GlVHkCJ8q&y<!(NJ+PcXHOBpx$+%iO#ARln4
z5BVJToo&HAHyr+ed%#Nh5h&Em=APrCa?O8p+aJ$?(<m?QE%%IF+;iOQ;@*Sv_S7~g
zytwh|zgixa_^w^qzgZr#Estvd)pGADWBzKnsoz at v56foYV<82XtFQS`a8Vmx6!hz=
zZUzP6+0q||Ij*W=;vgckUJKgTBvqWP-y94kJQNI~%!h)9T!(_+=tc^<er&9w5G!9V
zOGZvO9ue8XQPOc6%=$5T^Ae+C1OiudoQ?9Lf$}XY--xlpM=UF^6*eR$P at x^yqA%8|
z9U+~zyl|XMw<vVik{mf{Km$gBES8Z$kHtT-wl3l|im`ztbaq at T%DCe+bPZ8%HQplR
zXSArom$rE at eUs|CR=96wzP<p#&n}trcIIrl!;A%16^{ObWj1ZtmOA-GOY;w9l?ASi
zCo~QyI8)DVT9Fr@*%r at 8l{-3xy}xS>UaHGcbMcHGWObY<?fTe`8vJDFb_Bfe1ED|Q
zN8=Sn-IS<0bS%0y^MomeB#Mj0K at NsQ1(V_gk8WedD4P(%b?4VM*kVSmAsIKxxs+Kd
z`eA8OHR-%_E>xe>h)d!I=y+}@lu%0?t&3G5Ri4nYq~&NQ4^(I}SB%N3kt+$f%nlZx
z=DJpRETt at RCSB@OF`JuEt;(Y8q^@wBs`8tcZkw_)V50ocQ0F&GmtKwVYf6RdS`+1@
zHkCJiO|2N6BuxrN6fHb-tL(WGe)RfU(W{~%%C#zGkv2(9?%yP>JHTzeu-SJ1wp8sf
zMYQy(vP6zS at hkszYF33niUckSV4q@T(T-_k4T2so)h)vV at I>JkPj+UF7im`vHOG_`
z at upN*J7C7#T1E;qrS}{Pu&UVTn?xsF6~A5bn~Kcc6hbJs7GrOG)k%Oxf!r>~@_AW$
z$JLST65#L>Fh6UVIq6#y7gRVNc$aASpw&~vHLf*z1?4f^>5k9B=rIkU;^4YOBoD4i
zbmIb+OSCh7p!Iw(H;gh~E2i4p3RYk$)sd=Q&6ALp3{N|*O*mNMzAN-yQ9*6N!EO0m
zmJ)i8mYRlIoW)mi=ZYRr$Ap^IB)e`tPaExj_!3i_?RslFm^*U67F3qD=(5CJ|F6^(
zu{v3oN%$&R>oKk4g{Nihpv5C&fB1oVWu+g%EEbu;CKbt$F at D3O0%wed^?I=4waT?Z
zg)Ryn<5hOu<{%1RCt7+`_Ra0+6*o-vY`DnJ;3Tcde$6jgvWf{m%FzH-Gw!|f9?DVC
zQsxh1bUIxz6QNl?m>aZyFG!}EYRs-(Lwnw?AM&+L<)3cI?ChHG at PvOq9z}sT)KE&)
z_Agq}67S&e;nxx`qXi9bO(Ky<GyYF^m1NhJgRpt6;;-Nzceq(kO_Tjk3n6iv<kct<
z&s^IeK=zYL?qNrpUP)vwNALW*&jsNy|4MS^@597&qkk9k*Pe2qgn`Vev4V2DYfRpF
z^p+m&^;YoP at 3B8x?w3k>eX^`|WHF88-<I at R_n7`+xpz;m-<BScep?1Y2+Zp17gQs!
z87Nb0YBBO0FSRO+LPWku64xsio%5y`Lpd+S+5 at jtxK}dPNtMh~?5!3&_=R)Ug+-8j
zW5fzjNZ5#*TH#qqsV1R)$ic1-TrduRr&M%H2ga9{uV2g#woiXVEoKUr{-!0gOg@^z
zQuK^MC>>8Nz~707n?Av(2rPvvZlb&#OVq6Z)cZhOOjVh`3WR2xCH}Wf3Llak7EDpc
zdlPp=^MRH8+aT8A?5%&a+^lo2V;?P)fwYX>KU$t%mj?V{xvL<(K3S^mAC?jQ{WztS
zfEAF at -%9+E05hs`gf!km)BheOAPSh6DH!n*xL24^=2!k?FG3cNnaPe1f>e1l;icbM
zQ$J`%>Up!9?HUl at w2aH}6^5}=;etplgcNl*ai3L~S8>ZV>1M2GQqhQ&!zvj*m(~~Q
z+4$YXss!=Gh;gj_)u`Pt7Y}WiZjNKBW8xS;Fw|4~-14C1Vv1WhD1TdSR;Jf)i?hku
zP0OsBkAr(J)5~TA+<L8 at k#|k at jl*E{n|l$fd1FPhkJpO3i<|P1r<B9c4hr`;`aQ}y
zQ|{QR9G)Z~F(4!;xwiA+`(+up;T%+|A(LC+)in*N1SayM7)KGflrYw-1uuH6Obm~H
zYelVS=kWn=quX9v_ at hB{frr3wu8%|B7}uZ&EjSE4c1lBS$vq(OmJZcY8=gP+8KwNL
zTU^@kx at WG)IOG>HQuh`a%REHJIuH5I20do&ul%INb=2af#!W`yAx66&ru;-NYL;+x
zT(4t2?iG-6n0q~cw>lo~J#_5j`yRKs9w%^I=6%3~_nmHwp~~U|TSCILgJgqUe95Q6
z;hCmH9Tq+aMxBo<w#V;?fPP1bM2tk~E6k>+VkLt!>ZZ&-vk-6Ac_?z!S&*6iclXzv
zx-27g!NU^4bf|3MsTQ~r_r`3vMO|`Wk~m&l at kjFoR#P- at DD+s(ROS{d=E}CrJ!dAA
z651`vp%pV``!h(`=+Q^JFg=>iohz!^V>}Ayk_zK!fX9+Na0>*E<E0mgVQ4s>$>Xu)
zF^%^a5vvu0Ct|a>$5cDZSS_j~L=&|gn0vICFgLE}+GEaF%3S-IeYD(3ye-kx11pK#
zwy8vVuiB=m<VNG=n`+J=JssD~p~<0+b+u~Xax^P-_o7joIdt8-`Xh()>UYeL>*hIL
zIh>T3j}@k42Y(!o<k6Btzutgcpy`Y{a8!0q=dJa$+ at XYuS|2U<>`kxV7QRhsX;sq~
zw8P29(EQcWm$ay5c<ivdSDk(J`RLvAuQtu*j%Ve1EI^91f>dHt#eCQLluL&hH8sJ#
zND-S#$4Ig&Uwe}hSsy3xc0IRW<9kOt<Ox)jr9Do#Bk<2VpcAauiqj-9a2Ri4MK#AG
zouuKy6uLT`XE^SB<5tt**#kpSSdVfX9TbheXc*(`vV1)(J1F+HfQtcJz=^^EF2swq
z?v{=BPDclwv0|e#93S*W;KAPy`ywp<seYo20oN2>+;V~;jw?$F7H|X>l$14217IC8
zMy)VCdK~*+fkqR!@lYktz_w^Ef+zPVXp4?FQi5Y+Z^yw?T$61P7T$A%1p$c{?19%S
zQjn?)C7NPD?hvy3f$dy7gyqgx{R?UO+i>UaGFa!Sy727Wv^SlSEfJP#${#P@&Q$&u
z at igVn<k5Ol0(Y#a=x~uh-n_Q at N}x-9RK%6Q;@?`2^b`;3c_py0dp=goI5!e#fh{?s
z_5E;%f|=?#CJ#<Mi0cwd*%U9!70cmoO3bkj7JrQDp_*QwER9VOXDK{fkkD}q8{ng}
z8>u$f6mh%F+gas`tq2CuBVKl4MzksiT;Vxc!sJrl496H+A1VcBU{dXHgSJSMIh3H0
zWZjhxZIc#RA3d;FR8b(<7TwbF4lKF8*W-jJ+Wxu&`oQxJou!9QqANPCDtmIGi-};-
z{&P3DP1X6plWp-w3uIgLA=d7qLR&OGDIZmJTbe&3X|X6?w at h5$aT7*;IgT at TOegN`
zmco6a=Q|GV23!NSE&A;thgSK?!u7np!`1 at F90V>5xKE3yDku&lb%ZDmj8qxM$BpR`
zPl(5g-f|%9z;zAXV#p(-)tQngCx`J4c;GdfLY|g`PXlijUeO`T<N&EBuMtnljxowZ
zHDd$nhj<kAxY_a=Mvr*sncjE6C;|jVJ at A-9TZ7(YM&x4URGWY9eD5fOfB$(}mZ$6M
zW!cIPz8-ipKQ=WmM%On?bzh1&veh?@9z#`L_L4`RU0qHjIl_Zq2)P<v at w%DzsOYG{
z67|U_Y;eRb%;4>@UMto$Jjnq&*=dhF#>{?R#hRhvNz&xCO46+6L|03^Eu#%t;wcav
zN>sF^#>!J<FF5&8^&e|Iwgz~-TH>DT!tcnKa@>}%VmpeuFdwVPS0Cy>jP{hw7?ocL
zIfj08u|$KfZGKXzICMW6YIl0$F$Rjn`fNp;<L`l|YUACk-mM7({rg2s405g+TNQ*S
zD`?e8|8_`{APex$iaJLtCP}a-OQ<OwOCmsu6X_V-qlTIpe7XX6=4UK^vCORVQP|(k
z5sk2y<9+Of*GiAHP<-M6%4kIM1wQ=Zuq3z9p>J`!u>T`taV<67JA*|XkSjbiCXe-G
zjc*~n<iR|$Y<igXh^hV~LI1|g at lG_=COr|~FAFFB=0hp^Yzy4>e9aRNY|)g8g7L7K
zn*RuB(F2L$;GcRgs9U{1!VOC0)Y~Fkbg~#pq~|h*H!8{SPR$u7hcrOJe$k>ad0V!!
z9<Ni!tO^g`<Ta|oHzeg2Ifzh%mhoSBsvdI?54d?@IxJ1bT(S5213@@!Q5DK^y3Vj8
z*fpoxd_f3x#XiSeM8<LD8DioZNgmYZD- at O_yD|D(tT~6*UW4UQ#4GHkK{@C#3k$4(
zk+^rQ*C3_Gs4B5TkKhhUFcg>v(o7|plqlM at 9%h)4a<(ldXQ>UKewbobkn{6!#Z+UM
z$qC_gk`pp)Z81}bN1yNyHoddNQ at 2cM&yan7s4i_fh)<Oata+#-vEO$Uhcrt%soh%6
z61!^QDX^D1M-B%Mw%y}Iq&n-h;%FFHC1;98^VrF%rqRoZw$;5HLn))gg}XI*&;kM5
zqFOe6UzV<vfj`)!>aAra=1C!3<#VK}W8~r at shXo-`Bn3Ews-Pqy%ux{^(x}!%c~?)
zQ<o=G%Vx?$k3R4o8UC~^d9Z$*MUx(NrE<TTfFzu{MDJX{&W|Hc(K4GrYC_~ZuGu-`
z2802^!0H-V3cHb)LrvqP$}GzFOck2h3V{U6AOm{QOkB#ZN~KC}`)M_qS;BYI4 at UnO
z3o$BY35CR0K+Jcy^c4PJx8s)2R4{Sk0ZLdR+HNR%798B{_}@bu6?mx3g>lZ~U3<S)
z3^h;+RWNp|@lScQrWPAw3S?6UjcU2^?oA%F;PF<)?7ipzScbQ_ub0;k%P2t^TH<N;
z+j4tdQT=4e-EXg-mK~9PS}<bHA7W{i#9JAy87b+B8O--kOTolz=tyEx24pUn&S8w?
zYB{_*@zVJFkL at bSj%^2FTX`$^#~p6glWDU5=|@Q1CaF;!QQ&LSiYpeoi_{qJaB7mC
z8Tk=;Bg_OzDv=s>2^(?Kt at _H8>Q(VR%)zcpBEjKI9XMJVWEw;A{U@=%BiWl58B3=5
zIY{>E;oX<y`z49R5m?jwU`#k}U9*_f6p~Gig+g%5hPI-+07uCOB{5ly!{<fwaBM1o
zfn~$-)HgeOrc2gQ7>pW6tglCrHh22E at K?OGmHyJ`$@uU-$JX^tPHUIM{c%r>H^*B=
z7flgZoMMtgz at JK%$rOG_u%uD4#DOc~;dl<M7>oBBC)>@`sXbcjBzK9IB-JD?qg|NZ
znGuuJ at CYa+%O);I-nx3$&IZz$U|DByW5OLvBlRVK6|C*t6A0%owH7vS<b($PXiE|1
zkV6hc{Q9}D;-zs-4UbQ28t;r3hi0?%(x!S0<VR{_vOVCqy!y<2M#g3ixfRs_$<o~9
zp;o)-0Yg>zsM2;`TFj^ph9*aKD^qE at F04?Uw}vG>7%Z>8wZ+z&u9UO)rbgAaxm2I3
zSG?lu&%0RpzGu~QUHvB{MT85IZ%VqJ7q1`8rr<updJM)ZzJ#?^F_ at vS4ntNJVI2vn
zZhJld9~w5nk{$OXV;qz&;n;Ht*stSS#dM4(6j1_DT3X~DP`pXwRow?d|0ubKQ(AqJ
zfH_f8m7i_!+g-e`4?aeb?`Py|r&#jf<)<QxkA(B;jpfd_!WpW7=oEvSW#G{FfECSe
zq%E*m_rRAQK_6CS at k|4xhSlj!!QD}qaX%qx*cr->MGm;)BEWtDVE6`b;#eQ0-UkjP
zUaP<xV>)mpM#P0_VYXfx9Z$d|oYVm~bmxMy=(4DVlIGq4jx2QyO8UerX2vC{-kmn0
zYITsCf0T^Kj;Gac35we!8F2Im$!#UKew5_bZ>wJte+69qmTa#MVtQ(MpA0<t?zx)Y
zJy8FU6#8~-V(TNYHJ&)u9gRte_d8&sPDfAu*++h0RerKpUTGbEpcjVX)Nx at Y2zuA0
zfeaF^#8p at 7omX<n;WN822QjAr=O)~Zc&9<8F(luA5}V1sNVVMKdo(5c!qp`Cen>Ep
zPDs+~ujICJNmb4t9egGI(YYabWQ1(~O40~d_Q1DwmBLR*6yFk+;BhtKHUp1tdB#`h
z_lOt9bq|HQRnOf5$6_fA3vaM<Jv6+*UL_yUY+s(G!C*Yg9HG$mxb6%b3nz+qY#EfL
zaK)Y0e at daTjj%JmGe|TfTL!z#(AYzja>k9Tlj*Y4LXufF&op9iG+wHO_v<*iR6nfH
zzWC0$Rm11 at 6CoX2_jvvv+ZiC^GAw&bmA)M+^k}SJ!<8C+FfbA73PwF;L5VZ2#w^17
z?Uw|DDV~+-z at 8GNhMJzlW;(O!RkhHdnsDbWkVlE>mvx>RJ3N|aM)bpzey6k2lbx4Z
zgT8KH-;S-maYjoL#Iy1ny)*6SUi`*n&RI(f^tY=|`6V|Enw%TY5LUR#d1=&h_bEDt
zu#GI7nXONir5S at oEnHoj`ep`rNigF7sT1L!j1-<wGQ6wvKpRSk#WrV)ID!p)UI`(|
z_n-a{J)=1VTsgtfGFl7vb2p<_N$NZRAajs-sP?=H>?yg7u!fEg0&k2coQKT^iHExC
z)4^lzuGg>wy=JBBbYtH4GNki(5?|>vf;j}f_!EM%_a*tJMD=sr%y%7cU`y2 at jFbSb
zQ5`PX#;#C&7X at Cb@#wC=#V$+>Tl=LkZ0egkaMps at k4u`$1<kI3+R$Jl_1oa$hvH!(
zrsQ@!sI+|$JY1Nke3FzELknM~pcTF~8A>~PiTY7b=a^arHX-1t;fZgA^KK|_?)FkR
zJ!4-L4_)%|CcH+8*AOB&q~Y7g6?EJnTFpBPm>AwWT7Rbh5+k|XwJwU%OoF}CJsMu-
zSfyJ}z~G5dWORi~ibfn*mgA{;gk=br5yH at rfz_nAR2W^Oe(@81h(z0xMbhx_-SR#y
z?CAqs`i<#|U7s4$`;U%17I>_wS5#ThQzs at 4a2xaPSPwkUmPJbTW_;S_JSuTZKXG!(
z){y}kIhiNV4EBK9HsYo9A2q+RHK|8 at gS%TykG2E1!2&X?a at F7^Y%zLs at c=t?RfIdJ
zKt|(|%=JtORNZ7iG0dVWJ3qQ-*N{6pV|S&VEY{A^tE6cn{3E|Qa{&Q$LrxrG+FR9_
zt=F?Uv%R*i9MNcQ{6e#JVOoI9JT*!at_;`C9<WW$UQ at TUt)!8L3T`OCXZ8%JJORn~
zLsFA`BgnZ%6{Vt`tI(OMs$<Jj&s3{WJ=7|!7`8m&v|rvy4a at ek7_lXqGRQh3V%cu!
z at iWxm7X0h1bcmoSPj*nAXo+AMj+<I!HA1Ve5W56S7S54DR2b5x*b7fx>ea2@%?Whz
z=arivNz_>yb!`r{A3|fI#107DGcowp)~H^mhH{~PnN0S&GA+(Hs$&LlCt=u!Rm<;}
zt7unYe>+2YBto~b!9DP5*%(q{o0*n-<=C`XXFuo_?vPub+Ct5(l2u>Ta)I^ec3v7|
z%A!9qCYHv1we>0nr#T>s2deiwHeVPw-5c3H*E;k=N!5l0rrF_k?Ni>DfNQR3GoI#>
zG9(G_xfiD<7MP*qecFX_Q>;e)rGZM~d^v%$wSyPZq8>H-=su2<o<{xLc*z@*M7Lm<
z^v%#d3zB8rHoqk;UQLOOPlC%Pefm+7pT3OV0%YJCZ@~tC4 at d{B2re)KH&@exO%}7z
zsg{I1QqJYdj&(;NSAb3hvFJ`0WB*~s<`*)ld*z)lN3SIWfj{TMN{!q?pb^QlbOLeH
zTGM+bQ_DIrreE(W3|~M3Po1#SB2!om{8v&3uj)<g&2KP+SoNy*NXhq0g2oWok}s^l
zO$D~tX&_0CfmYVhe6=Q_DFbV<vr=Ud;|5L#i*#(J_hNK7o|b#tkj2WvFVxuUUJmiH
zQrdG_bV=2+mKv82(Lo(s(JpvoFmv~_6$(q$W|-ZM_Z(NZqs3OP7!&v*sm?hlSwpOG
z$04&&p>*Dg4GoQ^>Cj%)YQV1I9d@(Gc<Ie5>6=;VL&M{tj4bH<9mGJsx|-EiTa8BT
zcvboaPrfj+;w)LD#LoK16PQV8r|}I9UeFh1#T299rA(6=NdSB_hchXfkGlTQaM$d0
z?<#PexL2#`sy%YG`}fT5g>Lb{{qpTI{}~?ZH&gwQgxqvu<HR*IDEH*4eO>E{?k(<A
z3j-uaOgcXoC}4$azBNX+<ii5%BF|WL<w`kw_ob2~Z=GxYdzLI7@%86fQT(;L8<5ZV
z#Y??=pM43wUxH~Vh~#wvmMX_VsU16$3m7HkkV}<I*5U~fL*SUU-P3#qo<<ASwF_+X
z#8IeV(K|Xjf#XpRF_)@}I;H^0?hyN4V603SNl_leFsQ1@)Tj|yKHCD at n0iH(MUPqC
zV-^Ii)Yz*%@bHN3w2%a)7dYNIV)|K3!4cTFRN<?Yk~wv&nBC*QWyEXogkAF+r_mx7
zw|Hp}e6Uo(F~M0JbE$$eC0LB!8ax2K<_@=cN6Bbh5-wE_Nu3PniOKUn-PMw1L8=1h
zZxy|QJ_;(jo|@MF#}9}g(_2+Nx+l13BA)=J7b`w`6#omBs*ofbP47`Sk-MPd(GXG4
z>ddW at Nt0>hk{D|`3*9lSp0r^0=UMzh5_9IrBE}a~?7=u8nMJQgJH#E~Itw=p6m=vG
z-!)>X%A}!2EL9OczDluFg(P@`lJ5_Rp(n>J9k=erAPel+f<%j*ARu<CFqBtpmP6mf
z458Kqpt#F9rLiWlAz)_w2G*Ah*rE{;FwMO&&5ri%xZ?t2mZ(nvFk%HA1vcP06Vl|0
zuL~B8RO%};K-&DkjV66i)h9C at Y*deA<PHLzJvS?Akb3|pvSjK3*2;<;!(1TH7ex)P
zh69~=TCrP<M5tnf?+9!du~J3&SoBZHn&A^VkVS?9jHUB}U5XS8Y<V`4%=qfPp}!Z%
zau-pZ38FH*pyf8?u%UE>Y#W$QCij(wcMo8_vqfuz$b7*k2Ri^iar(_62u!Y1efbV5
zccYmc-kxy2g5}f;36>$v2|w^8?~e?o=g#Tu6Y!O1&*E2RDs<6{<%9HI90xqEG`%#&
z;3r}=a4dZ4nujN<m+Y8f95+Eq_3Ruko=172Bpd%DAI`=HdPC`Te>Fd_Ac=`NO&FWC
zvl8ndPt}1zg=P8wTwo!aFlZex=k8_D7M8owE#%gd4SMp4!1SwrYLpE*|M&Tru<53W
z(Jmy4*cIj2!3~J^OrHO`;=i&Nd|x^9Tk=QJ6bHN)D-Xq|=|5%>EkS3Q+&ldDELx(*
z-qDdpbk`JSRsAiK4n1RC8s4v~bqoY~DQ*zVVi6?R&}A^~*x1kdz>5)hCE7bQ4sbTm
z$TqTsR-P`>WmIhjy=bAs4bA$4@$A0$hlVoKO%EI;#BYpQ3Q2w8Xd at 5&^D{P}0pqx_
zJgFt)WpFM8-?w049KX)|mb{0o<I}30_m-q3Ntu&LoRM5}0*|v3uZ{PclZK{bW=k at k
zs4X$*W+lB9{>pU381rgaW=5<B2^YRFword&GjK_)RDm1xXO{Z%Aa`I{JW-O<jU|9|
zy(P233KXZ~dGOMWuPnhk_9{Epp(F$6b}!hq6?kiZFisp at mxhtk;)Vsxmu*}0W*>)E
z!T+7#JlXa<%mSuuByY=AG$jisWFupv=O!lI$Kb|@HL`cU+ at 6dbKk&Rx)KFL6v-4k)
zpsqW+eoK&Vxl2j&a;P05QqTom_r+G1Ahx}@=Xh=4%*^h8a-l?v+lkryV^03c1DF=0
z!{NXvM$86woFC3di#zVly9sXp1n@}2<CEQF`($Gm3pj3=7tR<Lj<#fVqRkKQ?99!-
zBtc{HL!y1>y+^Q^ND`c5twBim9k8rZUUqlKWU%B+e<!!Z*hl&DK}q(Y!wEkq5hpsQ
z1)Ft31pYSJ^ya#p;ojzQTcmk%?7KDAB#92hr^Yp7cRBwui-V6NCz{|u%#Cl3_$v|e
zNAh^{$+X`Rrdp(<mrbh<F)|-Z5-Y3QCNb2*wiPWxgmXd6TiMuL0 at 959lhn{7Pud4=
zHBf2xDe1Xlyc)61Oy;X3o^|33o){d`Mr at Cc<sboL?+V-xZkW1wq9n&D{5vJWa(pE^
zD9JlUIbC4IVi;E$v0Kf+mI{Zdu?(*1%^nQb2)kuza8|Vjh&@kp6reA4(1wv3Yx5PJ
zV=wl2jb0}x>54lSmzgY)z>OtSpTN)+H3gGUL2!LojLk&1)f)sFFsKh)KWI>!++<Yg
zr8BWEjfvoHE-M30v%?}k)Snv$PAUwpXPQX4e3_QMa0)`;rJ>$Wl<R|^QTdVx?-v2q
zfiTMg4~-{1wQ+ccf*NMOd6*}Yqx5>7pg3U6PV6L{W8=-Miu0`i(xJiaO-Y6axO1}&
zj4&v_Q5s{j0U0wzi(o5owkcs<9uCv<ps at MMNXD=K-4ekJ6-T!HknD_Bcn|Tn$-((a
zH!K!0fxwJ0A at Pm~dT6+F4rbJn2jfKRa%s4;_>dg6!=0PMIVd?CZd`xh{)#a53rj2a
zm!z}MVNsJoIzmcWr8GV`vi-myE^C_%*HILkNiE5zDPfg$qejQ0h2J3Khg%jB+3c_`
zk>{3$fblQ!C~{ecrO|NbX6svm8#w%XWGlXtm6(2c$zL)FKbt|$;u32gXN{gSv&5ro
zV2P(WZlEm9M09gH$$i1wVGt*<nCc`doe&p{bsmfZ($9xRBl$OEE~lYU at 3Nv@%fGEU
z*pUm5TwQpeF=;W*xnzj_hosu)d+MjehiR|9 at 3g#UkNGWmPnApmlGNAVQ$Hmm-TWz$
zEX5dt at XXj{5;B at 5bZb|nZEJ9A=e8G9RN at UsM(h6!#E!uwa1BG2P<zcW^$E3_eHLTO
z1u{9vLvQQBOpd1~3e$(U6Yp9u%cwosh9{nJdUa8x*Wo-8Rr~dk#3A|qmDZSreDY&A
z=4ig(yaoA{U_AG{lAZ0xai45Q2F#p^)#ocuOfC*R7B8t$dgfx2cocb^I?>S!bW3W)
zBAL07b-r`%0_Ap|s9upPq+{VII2%sz!rAH*t<Our{^jwVp!&$bknNJSprEmYIi93=
z$%->?rEDZkd1A05_Jt3Ofmdf<IsEie2%z7R0gO$={d(*Z&+(iXcd&fh9Qv2wjovHS
z8&)-r)#u==jU3Q847sEqMlW8zxSB=u6qub}1+j1m`?Btl8hgLvMq(Ks(eu at E^(%VI
zd97c;ac-IN5x#{P?p4r-%p|ce-h(sXa;C?XnnV8dbY(yjZDYUeaBfK+Uxu~wFTqP{
z?=8P2bLD>Lb~Kj9%F=ba6i-8GyXBKZPcP at _<|t**m~`K1z>5JmtxjwPpfpa|wHAV0
za}isydD`L*6KKf6H_%wkXXOe=rR(e*2?q+I3d9C(*qlLkt3*1C8F_+}7i`=~>Gvyk
z&OWz>F{Zntq&G`$&BYUyisMA$6}`=zcV9fxA74KSFga0sx;cl4m87V?imvlWzCR?x
zosjr&<A}FutfuHw-O^nl;mF^d$pXvZmg5swVCA;Q=ZEC55k=KF9-BQNrX|Od8>L~0
zbjdpGO${2m_Sl0Pj_wD4<y$fSlt$-_<~PJxsGY3l`0&8uKDei)f(h^uO}rcW#{qqW
zMC6fte at LcoO4bIE1^YUj0}9C(j`h^|CA?H7i7xh)d<Vm*@a8EZ#EKT)Qv1-L4I?+A
z<D^Dck&=#65=W{Wc#mfp(xN9ty?7quiIRY@;|6v$B;&DK0#4-w`=V`Sgs;Mm_`Q(f
zT35|Pc!U+<VMKILrK^{tp1lEn+T_((74KZP$Fq0#BRIp at Fh-U`=kV$+B9_Czovm=;
zQf1{UT3gtCsu?3oYV`|NM568oGhAnXXbfS)KPWJ%ZfxaCb`Vi0H1?ck>z}(~;~8H+
z--Xg2nUU`}q%lz1#rcomX{eWEM_%YNlAf~xx%xFwtq$%<9)mMeg9^Ek1gz#MIl8Mp
z4OgRO2PRXFm|-<$SN1;apCM^A3_-)*?ip~s%@z8iyiEnItch;wSmwUFsY|-9x0^a{
zj9~b{*Rb}{O&`hQNJ-QF5Inu}y5+Zo-88<d<L#zl*Rr>p#!LRseYIrQlB2-6S8-SH
zjy?#X_4w4x?m^d}w_gE)GL`n-K=tw9I!N^4BM6F|-PCQBdUsRcVeNQRJv!j|>UvP4
zTf-{=S6CzNJzrgBuUs#L`M&eYV>}Lg^`U3xF&WX;U&j#IHHO-22nCkC#D`GeF6m;H
zu<leMB~e#3y3sXbI?f?8r_c_Q&N;QaNm-a9F<?+9&OsCsDU`${3f!|}#3Xv*YZARA
z*PI7Q+3fF^WQYD<^;2 at qot;5{C%ISAtH1Gm@?P;%a!sL!B&N{7Wacr3I at Z@Qg at z#K
z(n7cH0WYS|&P+O{&<F%W6H}<;HD7xQ1zcnI{+6*W*_dMrb-Z#a^%>K1;T}peIi@`n
zJV~x0)YEIP=sTVEljYme9NTgTb-Z`9z2Y%~K2q9~yuRuStr}FbzW)CF&xaJSK-dZ5
z^T&|%H})ZlV+*PPFp4*|P;-esKU}K~xxUsQOndE(eQ(#UNvQBzJI`nlMx1%=P4xM(
zPbS{?+H0Tp{)A;9Z=iMmd;Q;-^!G=3`JacWFOd7``!>8I39D+(e3X1WBn_SK3#$w)
zvmFvCC5WG&F-K3G5%yw48M^mDdcBi)MYLEoV<dPdnRt^mBr9rLoYm&mNh_NE5YUJ>
z87es3NwxP8H+*BCPCT65a3Eem8=>R<)c=ROeO{UTfUz)m_g=JdZ0f2T8;Mdb#aVXm
z8VJh!M1H;QmrEb__00UYTW}_#IElkX(F?;E?@(TwQ}T64yo5&nfJSs5b`J?^dmPXs
z3tgk)WFM%Qa=wXk at wbv1DHnI~gnuiUk%VEx<Vlf(mjSqno_Jp$L_jcC%ofRQ*zNn0
z_(M>MMmGvg`X%vnkK`Vs<#c$f^q^6vq()!&MY=R~(7E>6+y=$`6x3k0anO96jHg`M
zLvk(pVN=*S at Ob0FE=-oNa^UIK5SVcrRDjCbFpz`3z`6!*&To8!IkHkj1A?U2yguIk
z80eeEV<*Dsc+7cWU*XJS9vTv)&hhfL2;8Ic;>>syE-A`W;|TQh%1bSu8!rrRND!b)
zvcRF`#SK_!hx7Y5f<@i)o=0d5Wte(8uU(>bz^^`JxEUfBrs1X5Lqmf!#tsrtjYqE5
zasfSqz!di_oX<q4W8Kgq=|4YCf#mnL0?X3Glb90&Y#4{+ixT0Qf}{&8#|+OoNd~mh
z(yw#JMySwZ!T8fwhQHAZRxiv2bIpeajRf||6-L+2&`I5HXtpxNG&F=;&XO5+rDz((
z*m`n&Gl$D9`3i~TGc3$mDGa`34mYEt$J{yV%q{v<T+*pvz0x7fJTT2{_E0FPV2vqj
z$_-U^IvEvYFyY2z!nMkrKdkzUw~0q6>#RX3i43bT!41o)@u!5Q)Rc^>p;Nj$6qG<R
zFP#|!&oPLZ$QG11HjTUDQc=ZrVy_%T at 6=zI3&ulZ1fMA(cRZ!%k$XiF17%`jhaCao
zik1$YmS<)Rr|ny=IS&ooT62Z0G>ak9nG1Aqi?MnrjC_7#O5C}i%RWM8zR2{P2!mAC
z2^DTieR<UBX9%O at Dq2;kXIN8m1Q8PL{f&ePCMhvn=~K)nTH&#E#{k&plp{)ztZ|eu
zZ819q)_}cb8bppmAZz9cD-^N<R at 6C%vt~hKNFcGoAewSV|0VqkxC$eLmX- at vqg5iQ
z(U}$={TnmZxuQRO?AMO|`CEcjLmm<Y5?Pj}Mx-ZUn><$5HofKhR)62&s<fogbtS*g
zc{#Tx;UDz=Ojmev0c*7jb3sq%Y>k>#%Oakjz?x5lwHT6_bxVvI&h5i0bch=x^Sk8=
zN^ajgvyT~xde~1E0D;Ma20$|9paH-`4;ld6i=H$9k|GBU&=E84)#QOUKS>&(f{JB_
z272=9Hw^$T>p=q)Q|`KH0HDf211@~gfJ>4zAT~H?KpgR)0gy;JXh2- at d_(91b39(z
z2;zBbpy9Q4(E#=C_Mido+wA*61Kj(1(|`>x8gK|CzmJ0k1b)x}+_{Yh4RFakXn?!g
zZW;hM>_G$E?0L`t7+Kog<UN at 6K?5Ke%^n&If6 at RQSsE@H;0aR?8sL7Dn+80SG_6`E
z4+xEU@&K3An+N!g{ony{;)4hHt=ho at bkmz`^B!$`@BnwjsQ^FO!2{fr>*N8RPxasd
z?)iGtfTvMG0~G$4cYQ0^uq+Tyj0B~>{`*%@1?06eWV0B4zHiCu at c&3swCXzns`{Ks
z4*MI7x at 49MMr7J=3F=HcmONqJ>g(4%zmn!m5A<>OlpI$DsTuX1#MYm}fyb+3doQ`q
zf1btahw0B>$9?{m+^2cDU-C|JoQBBid&zbFkF&lqc4LA~b??^}_9F?9OfDI=BT?oG
zNl^|w)>jO<sw1%Yr~!=gbKr^I-o05nxoP1GX-L$$0(((P_!%SRET=+^65$&ZZ!VZ8
zgFFDYMM~>YrpEAw<r62WhGPp(3{9VhaOsw$xkR~fNpw|Aqze>HdSe+n at F=n3l5{wg
zw<sv}j)kxZq2(}9;8}2%F33?0jagP;QJPh>2KE_~hKtkV?z4}EhLae_D~8MFg=a%w
z(4v&x3O!sL8+1;eBHUoG)k88T_vlhNeU=v2UTX%fy#|JEt{5flEc at x;X@<nvy5qc7
z;hCix&DNE-w8C5U#(<BWjwqP`bzYc`Fqwx&C5ew6m=KmnNY4dDc<rV1wdv#)o6+~G
zG$JPp=BrCGL$r>Fx3wqsi7pqUq%7u`PT^zei${c45X8Q7Y~|yrEd#!or7JuI+QoM<
zM;f7pSvwZ>ElvP#41ZO-Fg@%4of_7ZkFib0B6qOfGMc^dC2#_RL&BAn-SkHZ;PfZa
z0^<E4Vc(a1^tVFm4cYR2NI>0V2-19$PPAv>nIUVWh$x9W(VRU1N at 2{@d!$b=eO{PD
zPOqniMT3qVbjGAsDigJm<@;<Gsn()uIrg_0mn?c<n33s!OC+BCA`5c|`dr}Cp=K1<
zUQv67u}{vdok8h)=$(`GaOob_8f#{WXKcvpEP&Q;%M;U89ZtP9GzviM+39Q at yo03W
zaDPf>)ag55tv;~_kSnbIh-eD>U;tN{^hDz=Yo_lGXIr#$L>YGqmv>S-;z>+D)P5)+
zygUC1hw}LtOR~pXC2ydiVVXD8C-z6p6_lXlq^GA3lK#y15z6!^b*I>5mX?;GX_jR}
zA4%z$X4A}DgKqF0#^I)RIGT=Z!K*Jn`yF3D!ayG8Pl{&tXw&L at XMZEz@!@pPxuzUv
z=Sp7+r^eB<O}Dz|%$Z5JI5y(v$}qt3NI|VJ1KkQGAD^e71(urOm6anPedj62r?ZT9
zw6Y#}u9{DY60dw5Gm9`j95Arsq^suKV`f_{355z`tnzMLTryfu5AMqaYmIDp5_Yy|
zkXcj0?i<taHQJ#8$7!}qFZ1r&Y^j|o;m#NCQAI<;R#u at YftO~p$w93B=~<RA95SB0
z({feD&#Pu^A>5)Y;zcsy*GxxPm=VKbWaXk+`Pnh`+p>p78M%QsT{z0}&UL$5GdLU;
zy)k6oI={KFXYR}uThco<)R=N>5%YB-N~9}(iOkx$gClo^a4Yn#7+f&dcxWt3X4n~=
zD%?SBySimpwnk6(#g15nFXwn{e1DAeZiwH91Wj>n#+}UUhoA)+_~KkznTz8e!fbs5
zkmg(y?%2kT?H%(S?bx<$+s2M<+qSi1+qP}b-2J})zp8tyQkCcQNlqiFq$*FRyTZ85
z{v!D56J0A_nIx#wi{5mOHA1PVoVs9B>W9_onhA}hMSOF+FNtoZBal}7U4ep*+8=Kq
zj6WPe^5{=-D`)_01Q&l;hjm4xNh{6kvNarG{dts<QZH+qUl%HMnQh)EypRsVLqVPj
zUg1X<Kn>)A4+r}sDZ7K4W=Jg_vvSGgS*MXQZ962)Sq at JQ0z6QqRL-K~e~ij5COGUD
ze;zj_psIY%u=@<~lkX}{&8Zv&IL9?>5~2K*Q2BwD3mi_4j=8FmpHR$8jSZZlwH&Gj
z)NHUBR!V at n$AC*ehrK&`5%zQWAsD^_n)oS at NqwrAy&4VsbiZSz=9y*ACx`6mx513r
z1zv+0_E=Gy1b&_tGG6Q}M at jW~E}V1~j#0GV3dl!9<jld~hsxqjhrCfe-oa1hgs7o5
zsL?7Ydb4yk)szuuDR>B3su-1ojHw at 572t^~9s)zsW6svQG^Xz8vh$sY$%)K84Ql0*
z9FUU^GHW8AVdQA+*bWKa`Oqa2gl(T<4WtiIyk{iAA?2;Nvn<q>srng!@G*hXP(ia*
z8<0JiTw+Jd0saJx$$(a3^u9D?)!GzQ%K`MlBMW{l{xP~A1L#t7Fm|yG#c9Bh@<2F9
zE%ERU4MPmWuyY>3czBd&tY;@ITnc<y+zlFXCE)G3bi?8 at SIlQ7gYog}H#N^RrTtyP
z?tz?%F6#891th{9xzV$1XxL}DRB$0pHk27w{F^k>rJCko&WZQnPMy<>mMdYC7XEnN
z7Wd#Tu^db_`pZ+56}6V8Dk>|pjhg**118D`Ewl0Qbm3Lh_0Hnc=@RX)@+ at V<dCpFB
zfb^fUS`u-15z|II)1%DEc&W8_eq<C5vu7UbzfnCz$@ZxZ8|Blsg)0GAsYK_QqS6Tw
z`O;0l&n3pkjUS^XfaMXvq|YC~C}5~oI~7oWCNuGeA>!7l-J8?#a7 at Zu*p?=$a~zy~
zl`CHv^NXWZ!m-zjWrnLC07teRPI at F+rX3j5IT&t8%boi-mrsyuix@<9ar_WFQ=m2e
zG5%$<MB at Y|z3G#y#`joE8huy4!ATtW>QykRWj$*~isxKWS1vKBYmQb_a1$@j)N+-}
zItC+{^w<<$ZY)Y$GYBtC=Ac;-InCf{U97sKT(C=-q%g<U5htno`zA3wlw}c59gEY+
zguD at KrFNmLeGzF{z^jc_>bF)Z+cG_F!lO|7aRnaktRpowZNkqQo0pJYmdqvWiH$Ym
z^px3kC%Y3`TnJf at E49K_ylcM5KZ4Ow`Zp{0>sQfQJNan7ZxxmP00{JFMyHPB4t5Eb
z at 3!^U_-!ZW#@&smvvu#fr0{tKWa}Au_Ef=`47)`e-<Dnahp)}YtLc|#9;#;HkJ_mC
zpHt1y at o1vHA#^b@^$uGUq52<apK%LvZ(yXzh&AF^IqnA$+1L2FnC-`;plUn4J+9xa
z>y=(3l8^pRiW(~P-_7gF@;@%G^0eB@@t5kc^afWby1$yEx?k#i*?aBgttwtD73N&(
zM^90~Ac5s$ay}12<Tw4`Tbhxz>Ii3qhIS&tC|8YE{HR at G0-tB`V_a?x>w8ZkI;h}Q
z>aF)|5K%wbAKw|<--xV!<qY8iTkZ#D_lBQH8;u~)2ikD_5=!`}efM^{aY?r=ygyxs
z%l{ZWtOr^S8`pK!Mf}Dq_(X<`OZ-%1dE0&LjPkh-u}3jph?#wW!iYIWMtJETV#}d-
zGP~Ia_j%VvnUn5pw(5=lZIm2~W`;5TNww;iwD)-{?2G%L8}qZ=P}56{A!BIvt8(vj
z5fSliK4hQgOvZ&JV3>QuyP+cb^T84468}<c;qv(_zYmQKFF|QZ4#?Un0$)8G^6>zd
zZdh|N>V064KuB%dT$zYB*)`do_i=MpM`5VgYw$hw2+|gY8*&$+xHSd`$rt_8!kn*;
zGfG1|26)dcbn1E5F at OWaUP_&;{UiUIeD?J=1&LWFN>u`R^4YhCV;-96A{!d`ld%8J
z&J=NamhtdVF~Fn*JtuE3M{QhTO$Z3y`yKj)tNl+ at OgkZ6EtK&k&4^jQ0Y%>_e@#4J
zlqiRH_nB`Oia17JonaSE6f|B~JIS8npo<hhOSHi;dB2o74noW?fup!tnJ=4%+D~GC
z=WOVwUi@!h?Cpo1gYqDf`2}-lBxSL%FYXna;yC)`N&W4&-<KUv8hFZo#L?H2<GS<H
zktO3UtIGhZJ-_~F at FAz&QKH=X4<qIP<Lt+Gw at eCb$#ake_|Ey#48U<Hj!M+P)#_N|
zl9BAu4n}XOp3xc;NYfMOVBvN2`n>H=j}UmmSbs#mGlgYp0bY^8_*cf!hXF;M`J*&B
zT$4GsUtVkPh58|(vinaB47HPDuO-smPJv7m1lNDE3$|hVGr^lm1RH%Xf{WmxMxUk~
z+=H{#44A}C*Xa5-o8n4hKKxkfv=KC^6l%2)JxthtC(^f%V4~d8Y(@IJz?C7jn^mqt
zBwVkVv1vBri&8X5d2pQiINo6Ra=NLBpi9fUw&A{e10Kzw=W at UTCj<dyRgwLuB2$OX
zHqH8RIGuKL&nfs>aVbi-N!hS?xQvQ+kLHa<%qe`7F6CSdKvrr`EDg`qMCx)`)Nmyv
z6tRha**FrsQZzWwKUBU#8cN}F#9vsyPGWdQ_f$VmQ7cSfW)E at M7We_e-fB(jlZ2Wx
zY;UYIZv=0Gc<rTjzljJ|i=LUv)O7ve`MQ#!cLM|HGMZuhKj|u??+CcBMRXeP4{k0!
zng<Jz4*6eyrkKv|pQ|65eQ?O^zL at bG#P3L>XKYntmzbSp+gzRU1jR-{RS(S2e(}89
zcrnVZ-pT7KI$?cu6NEi*jAFs$I@^>{9rr0KH%L_9z!EPH?VTmY9(foP`fWdU{w&j5
z=Q{u47sQTF{P``IVO=6hH!L)|a5m}XK2G)2N<94R at 8)D&Q?`$RZ`v4y-(9yurCXFd
z720%xt$conjN%+u)Wi5dJPBmTER)jOuD7Pn0M-}H0m7bIKvtYAKxcVVfJ1qE<SJI)
zN4T-z&)gLFTc_z7G4C-+LNI@@(ouo%&sxKxOVC=d=B1Wh5<@!TFY3mha<?_J<JJWo
zzR+3JNyvg*a}w7}Qvg5nZH=@`)aT1#B#m%p%aNfsF%&ymK>=ifH&k_5mJ$Xg#Gg4O
z at B(M4Jy(rbLDW6tW3X+ouQ*0$uhd`5yhV^-afP?KVJu+3l4}JD1buz47c(tZ(#~EJ
zPVeJr;P<}(H4RHiz=FZn#k(Ih^GGE*-to{q5`imUJscF&g36=Evg2x3`2Bv!A_+cF
z`(s-ywjJAk5O{04*^7haew$W55sQ(6;n%kaNrVRJT9Kh<SkO6z8xYUhZA3}9%M29u
zelCx&_S?o;E{(UB_UlQiY_o$1^IAAs{_X>Vr1zEuze=j6G!1v#mVSc8V=8k7p+_4D
z0!qt*t3u?3=n`YoN^)EQa(DS|<n&tVCs6Ts+!Z`vqAUv4MDze7ur(|cS%jTTk1y(q
zOcM_$%)Et$fbl(9P1E at dlkYxZTxA%UN5L@~r1gsb4LENqT%OkZA<+yoU-&%>O%P|+
zwedJ&am$3a0>cH`zRY-|JDL5J4P;-&Dv*Z!rzz`+X^9`p3=u+r8Wc1h?GBTh-JvNO
z<0l%)cSVPV2WqhXiFZ%F=uaY2O!Q6r5ImP{f)NQ`;v^GGs?g83)oYHKdV5J+3YUwQ
z!RzilgPz853Q|f#kHc58q~w~NSpbmd!rORJB>jNxDQlUn!&N>d^h}vr+gTfi+4v9V
zV-3p6hoBSc8Q+6VHefy`Btm&>DbB<q+7({*+u1Y(M0m7pcZ=(M&q6!ISfUQ%Ky<it
z<#-jt5)rz?^D?U<Yim0hZs%^RUW~65i}0m6XXRVcGtw4qu)8K&pvik-CdMwIIn#ru
z)POMLA|`P7!Km+E7Pei8qO1pqw<c0SAmSR#d{yXbrAv+1J_#;CZCDF;U4-;tEely=
z?J*vt{9vtN`=JDcT-}YiSIwM+>941hlf|JA&X*h%tpMD=pGGznMebVSpb)B0ybmMP
z`{Tc@)h at bM9WTZvxvK72K_YM<+UaJUTU|reIA?7ciQV$#$Z|F$IA8`8B!|9PAPix5
zd7y>;Nkq65N at -OLB9RzO{XJvJJUuc}$yJxjZVCCbfrT5;cPx5i=*o)tI!_u;T=s_k
z at W%|D7qS!BSVT?Xp;0olsoQ}}z|2~ZiU|e*S9(Pk)6e#HHAjqwDNHwun--UuP-nk~
zmg$H~9hi3g!UQWbAvDc5j@<G2dKycOzA{2|LqZloz*;tMJqkB^;?bW)DGrxcB6vNp
zj0wlWmY^>VuITdS{sd__E9HWLhcUJ>a&&Sq*0+ZLi);-n;8_S62><T6x#^{hZA_ia
z2mx$NO!Ok=R!+ta^deUJPR2sUhPFn=|D9$eWMpPzW#Zxa?<z22VT~!?c1o(~f2KX(
z*>rz9C0GfpiSF>wL0hpA1>MB`(O`Rj3X)D0fXcmQbUEs}uo3%7l_yk8Q1Bb8DJ#Wk
z49%L$LbEX|T3O=?qAa6ajP|i0JwT~se<!kFk2l)6+?NV%ugw&dCv)hQ;Q8q5s_i-3
z^SVv<xZ4i|2v#2sWTwO0^gMkkzw5Z}z!j4>4gX1``P$PRO7Bq~$e6|3EOGNW#veFw
z8Km^-&&bxU=(IEfC>S}=SoG!Q^9|qgC^amQ1p#E~JVb-eQPp-yHi%ja=DQ|uDEQ$x
z$RiLs-2FIYh$w+a<HvyV1O;$-qxbv#R`Y#_2at-sFR=BBr&*zyqdkP)rD*9i{!MVh
zbb1>*-?~HN?q}R5r0A^8N0WKI&q43S$nsFM4KP&6OgB5_TB8G6fE+T-d~uNu5qx8>
zp9VTy`Jix7Ikpn(>RYpJc?x4FXh%5&=(6a}U_NL$4bM}K$9Ih`R5$_}-pm$svIV`l
zDg!RW3|-zKM&s=ty&Xo at LLUhZ|GC_BRoT+qfV&id at igA45B;OLN9N^SPD<6p`8E9}
zoqfm8pX&@CauIyk5LAU!M*2WJ%$iHG=GaU#@Pe4y6Ky*a$zRuN1(*o>E4(;`Pu};5
zV?4nOg}Xj*>e$Bbokau~Z}tvBZk{&`t!mjrZrJ^>n9+ZPC%Pa~c3^MlAp8>0bb~VI
z5M=wPF{gLj)Em)LB7fM>i>zYIm~q*4bIl}w2~^#=N4iofQ}P7n&E#;?s#Vyv0_lcW
zj!=Ia_m}0oEM;FUB-2rwlXLE8T)zrg%@0LC!JjWzS!*`DT`eioPR<sOjG;%tkH0bn
z(rPR8sP&vrwF at _~?w*y8&ETQKj$<SyC8J at 0H~E$GHI#-mvoz3cU;i-tw2^F3Ggu3k
z%kPf4N%hmwq^xG91d-pQ{J^y$)N7$#2p)4ue7kUN->sZKKKuBMHO9X}0G*?P#_hO^
zSYhZwU9J?OZnGQNlab+>TDk+hzL!i5l01G{nJ^Cm-<ikNpLT89DTTjv(d2GcaIr`g
zd6B7r4sIdH6XC9oAHGUc^T$SB-^T-SO%2Du)HzUkFLtxDd2^SgFd7}aq4}y&mdY*2
zQh{}oeSNho>JjmXrF8dFpe#*^ucC6xk2bTdEp@}lhse%qF)%D;J|L<S`8X{nZPgej
z7zppY<AZK_d(YPXSMOY-K=^o7o{?n3U<Z5j(g}1`T#16xj=@&ec!BYzROI7oTU~1m
z^ZECN99|2#Q@?{<Cn+G_EIO$%ex<5Z&|03RrG$N~E04yavE>}VHZ&&OC!ftAhiXG;
zb$ei>2CYe#UZA8>hp+_W<tTZNmKhY;)PK~sY0fO!x-Xukg5%j|KK8kTUyXcky<vi<
zI#GA%r;aT at HJ5n*;12TGk&!>!P9>L^M$dEGj`F9!T!1NzWUU<EPU2?;Cy=!~H#Y(8
zBzWNXOy-powsUUmNanTSkgTj8n`$*HDL$%mF)ij)2X{a7{ww=~V+3@|GMf{5v?1}(
z+*@p at XZ8L`azcgeV?sM=wVIPnijzD+5=4lRtw=Tc#GB}s7DakKCw^--l`|aQ$<d3D
z2Z;sN9AV5l`@TKo`MPF0hheL3t8=zrm`gfTie at BhU#LO6<sXd-nic~xa#)S at amOfl
zpLhz4BbDk{s_OofgR!R_26Oy;Pv+|T3$W;rWim4fYC}eCx|_rCw?jt+6(IpOuJ!U7
zhgiUZfrwO$R`MiRi`B%3GCtgJc}W53UOT4al|Ue&-x!UO7BcfgY_I)5p8#ctl`v!o
z1QdEBoO_=<WAU`bX1<7rX)aioI+!5te5vyYb~%o0>c*L at RC2P=om-?=X7cE-4J{`)
zw(68YD|v%r;j&Xs@$nX~4g%Y7?j4!k#4$lqGESQcRpEk?`l`6aF!DO=B31<p^Q(<W
z{3qm7P4pnOn+E2A^8w3&m{8R))zEL^uY?!NJ<oxN0bOKzq-oS`G8t4~@egpm<B-&D
z!L}S!Uy%>0TZ(~}u;Wl$WM3(-1h?cDl07V=WSumc*ZcKX>|5nM=snlaZSrn`k0oyF
zl2G@(=g=BdS=1g$Zs&+E;5|?`@rD-($yc8}q@)Wntezdhf(2yDFwR>U at v~zW9!am@
z7nWP<0eaLJ<ec=9%ZLG6lpcO=8881A?puz%ezV<6YhFoQGJH}VF<hc63IxRpZ&Jkg
zOfsx@%1l+w%0=M})UHRVJ8?c}U7${66 at _6y*{DRpHP)Vw#+#YD`35(Qv-kEm=)IT8
z>;4y5_&%=ecWeqWa)m0V3R^adQ+6U?l!2T?V!-L}SD5u>KNq!Lh_&5o^vhfwf6n#u
zZUlrII3DC;Dp?IJ#q%~+Q$h0z+F(x~4(l`Jwii(DP$C|KyrfN=N!+RJW}E0!;1Pb@
zjZiM4uC0R}4XNatRq+7kK%wgRv{&tlJ=}wTTXZ}GeiJZWW)5{yJ#n%)GQpqHY-*jk
zgHVn8A6>?PN27%L*N4(O6W{wY+Kw_)uZh;yTEowi<>CZfUht<KuAu=`@hgVEUCp24
zy%G<CNE>vhK*t<>8fOkbSx;4F-?Id5m**n8-{4DybXz9J9>})*eyV|Xf!ATD>vf>@
zAnJ?hpYYpW>whUJiWc at ujP&o4?O8AWZNh9}q1X!9%%@72RKB5wrP`68-1WD9PxINO
zhK1##M=K;J5iofrwS+Smt&_`~DN{yR=qi2sL?d;wSO~rq`vAnO%Le_?8|uDuG)X`d
z1WsR+5Pf>0;n=bfOftxPiC?cmh)Y`<h1P5zYJNr74{D2c9_Hs2j*r^g;Wm!l9jSXe
zz?=Ci&HS*4&(U=RECG<a+nb5-UUImMvQCw_vi1UL4;@-TP`6Zsj86Z0x%9MFra3$h
z at 4K+qehIF84z<{wFZD;vxhy7PffvO7JErcCn^1;I1w-|pcC at 4K;9IgpM1wc{;+|g0
z_3t11>zFwMFUpa_j9)w3UaFo}ipyBv(KU`)1MXV5>x^t{E>{MKo)E`)w*IZX(1gv2
z%cL15zx#4Gl4n#J6N**HPz>85%9HybPGOK^e$@F7b;*D*Htry08+I0inP23TpL4Q`
zX%>Z9%b9MsJr_D}TFCo+P5qkQe${#Qf!aWgCbl`>U-N2rN$N&?*mWct;|Gfot7ZJ@
ziK&5XZS{>HL}5T1P)%*<h{z~D?7~d7H~Agt_WRO36#IAnHaJhe<38o^$|lZ=x=j|U
z6XUDvQng5ALkH=@(}G~46W(H4(4=S3CUp=LQ6(p2YJaOve}j$)f- at zT^QX+YPRF+W
z0KT^_C*)VK;XM$KFAkw+<Ydv?3lG;m9nQNH!TgOH0m(G+5y at 9?tJT1hG1#Hs8;P}5
z&hzTMq}6xRn?}{IoJZKuo*j<&(yUqyyg6eRzfp{|&b}ekiR{L%gw;k0M2vd|-r?+5
zF-EGRyx>)Q%aIF)O^oUFJjp0c#QmlI)<*`LhYz6rrDu4|>0Zo)b%%?r7_@{u?j7&q
zFJZFm8U{S?KQCIQ(}-9#!cw{sn7ri;JT5WHlrao8(0;=d>KiVMUCwo^?7kNC>$db6
z+X)n9-%fRpRqQo6RXrnDY1CY4Tg&y`T4CK_mZ>y`?V~A9muv^sXPpftY1t=kewW{4
z05R<T>mx~%8X0m$E1&RJfs=2sAMY$*-Q?!@qi2MeH8-Y_zqpzi5#zZ;XfaT*pC6>s
z*Zl@(7zCXWyx^30S(+Ij#G(*rr_5+p`QFv0zT!<xytX1=sOQJ620*fKqLg1SXMeka
zA`4~mZ3u1fbr#$gdQsVq#oaBoW*NKYZyeWku-0nvA%V}|g)!X_rmd<8linU;^kJAG
zCGTda at abw{D*aO1YA(ZF#$%=ps}8dXchEFE(sP1t{zCh7ZQLokQewfk#t^(!Y at +UN
zVaGp&4K%kic=W$M!vFBt6gZE3l3!Wy!S2xlpr$-|K}fw6J>zBmuvH|1e}NA&w!!{e
z)gZgLu{sQo?GkXw1fTBm{`~Ix3i;Uq3JK~75(4508UhN62g&v1gnPEKZr^Ii$N3Xp
ze7Hz)PQ*O$A at U*fq4Oc%NrjWbxRCL*lZ8$@mQEt2aC{E^wA at L$oo+MMMxv!~eGc!m
znuW+V5il}6GCevyE_z7x+Uh02L!Fbre3$B_gP%e_f^jH$XZ#xOrQA)fn_ at cxIMlqe
zevS50%~tDHrqY=I1(UH(W}nsK>+Spf#$u18+Eaf*p`ewx7tP|cp&?+d1onA3>(jDR
zb9R$&AUhS_rZttuY~B>ywBF}z-Ke8TEuOC8e2L;i7Tq^bW#eoZDzUGq^ri@|9o0J7
zlFL?|EU+G`{@VaKxf*Rf$70^s9E(#4oF{Zxn?zaJK8HT~F%LqN=`cF&N3&$b8d{_E
z9yDZU6vQ{{9^ODl=rfWi5(N^BfN-WrCVwa5_#TI^IKzyTK`8b&EbiJuDPL)LE$>>)
zb;<ZcMz)@JpyLj*3HyGwsM;!)r9UQ!;^GFvzTiZR_}~wTQZNzdfp9S*D#&Rxq{<ux
zIUBgLfy$XL6yn%Zp9Vd+UL*`wdRuEUl1$`@!D9K9bfzDHGsme;8`DtF%&c*wWi2d=
zw-6bN3RG{PK7x8FjoqH_PghYHKj_&K at yNzBAsfaaAnIlDSKP1hk?P&b*X&4BP at kP;
zGKZki=weXKGAHC^_KoNurb_L9vK`siV at nwn{7s%kDs50*gBR@{qw2V*3KYV^BrG2q
z1^T5(fbBc>m^XP2m(8v6=w6u_sSJ7x<<XN+udpepseEFke90(8Dt#`muOKGK at nPc7
zntI&J(~pvsqF&xuDOUQm^BD>2E4sU~FQov?e`~w|(_zP-b%;e~5Z?NE5<u6QH<gYm
zF&Gd>MvZF0K-^g9 at wyX#xDw6Uq0oh#dnslm*kD=6J~Uk0(#DY{g{@3IFk8<}hf^1?
zSBY6-WfooGb&H7lGY0WR+}c{U6nKrL*z1Q6bbb9mx}siQgc)<1R=JxBsX|>QzCgl=
z|8AO$CkKTQlXt?3JV-`}38yJ%5Uh9fFv4J_{0FLpv&gMU;%W<f5NcvL^r8WgAw#jo
z&zE|ZB at 9S>B#$mx<yk7drsX{a!rhLYHz?Ve()SO23CfES0D|K~vcAW-Qq8<9K_!@_
z<RuK1ud|w&Io929`R(0kdg$4U%?ZoIAXJWLJplItK3F?1)<82AbMHmZ4ee-1qL1Io
z{$Ad0GVdTIfut*ro*Wb-`Xqq_3EB}B$uA)RT=^^bZK!=s!qZq<uuHdcPugF^t9jjI
zVO6NEI4>A*TbM`A7h<L2scmhD%JE!*$ivZov+6wVVx`&TDS6XG!uxgE76;hUx;dB@
z9}G*H`owboEJobV{#!PHfbL-O_Uci2cd==a%aixpX|sE&jh8U1MiaKh`b_P;p~U=f
zHe_q9 at 6qsU^sIYK_T2*AZK7TFF1<zrUFTl;7FM^(qI=A_x%hUFg}V$Sghv&vUlj7~
zRBYm+AsGW2)z?nP9wq5GNOM6d&;mK6Sfw%m1hss+ByA4TD{Zes##7XwnW^{8QRi)+
z5{^f#pHM9bQbx?ZisvDdDD;)(c^nYkAM`!{b`*h2C%sJv+5GZK>9zo1&Kxg at ac7T-
z(Dlm=mQ=a#R0D^9lv`Fax3#h;7Yb5M|KLamG+T9i-F01v4)EL_RUpvYUeQ&kX?|?c
z%WQuixbkv-^_^>H6`q`3izA!U(>e!_qh8{L&0*+tRb64n&<}Uc1f}yR%7HcPtQ+a-
z=%j*X_ic3Q>AMq&m8*5vnza{m_?N)A^fM-5l%Vz({$$b1|5LaNw3}@qqFiEN6DN|c
zxv;k(jkj4f*jD<_X6;ZY1(e6__nL=RU2g0sP`=0}5kOTtpDFLp(a%XC6wEGD3x#FD
zM~^E94$GP=EGKc}ERkbYs$$Y;Hc1%`2QUJK<Cenqp?45_^k~YxSAn4|$Cxfc4iN&Q
zeY5g-SY_~k>0<AVDI<!4{xnpk8HF{KfGXICU;b2<4Gp`}sd*n~aQ0-i^xRz8J1W>I
z{sqilQyLRS=+Ooa^&=@A$4s81fN*9(DFHQVqmfIm6T6dS)i-|b{`lQwwPBy^o22W^
zFoS*i<^AoVf&NK*isv2IcaMt;b2xRupG91}R$U#$@O))WdbF at SEDv2n-!h_6dQn2Y
zgCAo>6f#LolFMAFQYg!%CW{ZH_>yg+`1c%o0L(ehm;Tz=*jFkWbeR9 at eYXpbT^|d}
zxa;%QJx|T&mSfNHvuE~r&->_&&v(6T*W=N`1mYIEFJpKH`FQK?;0zNtD&F1O*DpDS
zy>SG)%^nVqmydVew=c9%y1(h{xj&@EhI>i{g*y?@1>n)c+ThTB<Oejm4b4&>#N)Ol
zlm84bvI>hV*caL_GX^<58I7kT&bAnyj4zGwK)$VZqRQ<Ma_b=16Dg6xnmxQyAgpN+
zq9)cZ3_Bi+SRBBiB%bXT(*grnTxB}uN0gfBQNmGa1f1-s%}_z5gN;hL3B`dhFiE!>
z2*$!c65qaJg1;z at 9r28~M at GEniU3d+fROlsgv6JHpjrCB@a<ktcwg^+uFI5~5b{8r
zaH at o!yyo%@!%XS@?z$$_BRaVVHUy>?a$+m#&zyQZiQ_umAnA3 at o+3kZOy(XTu|MBw
z1!Y&sS<YUpm!pweySeX-FXA>hzEmHV^ez8Du3%<XYufy%tEhD}I5FLp?DqJ|*;rV&
z<*ge;tx_0S+$(=@g at CD8_SIKR8SHpIS(4>}RpWq9N}YVNM-s<sSy<GwDWos$<wLs6
zEyRS<`P)6p9vbdxbp3{(&QA)|E{GN~Hjdm5>XNix`U_MT-V|@x4+dw_2bNQkT at S2x
zv^*5T!ZFWrAT7Wjw3I)0L9b!{J676i#aM$Cpc5JU!yXr)ZOD5|6t*vHej(sXV&6D&
zk-KWGo48r~C{jni1z_I*Yyj8?Y&`e70{grAy8;CThqC*g<gJ>B57yZ?PO14BEg#*M
z-G*b=4Q=a-Ak#NB-p>}*TaM-%|FTzB<`tcuSGYd)cWQ@>nY<2#dhQK&_ab?ET at Iiw
zA1cQ2=Yf>eBQckYl++FKHt7-d0tHr-4#*!9-l#MFmwNgtZb3PS4L51H$Ora~kLglc
zAf$y!LmR^eHZSS}w73ziNg^161GH369l;e^0^vz1<Fyh$q!L!?P~?qPiQCPZL(L$o
zycwb#yxHq|8=mv#o_CF}K?TZg%3b*>PcOfb#(*oEEH#!c!Nat&FXXVk?OEapHr}YG
z_8&{guiw`kqjbOXT~3OYNJQDY?d}&&QYqL^EV=DgJl-KLVhvP*9zlT?iBvlQ2Wxz%
zKwXBJ at T-;L!F6Sxey3m$Jif2rj2yv2Wf0Zn0t|NQOG+ZYseOmIsAE*{Y1Nj?1?zFt
zdi-uK&u>1iL8qV9WEOvYa~8tFc9iH<d5-CCYHy>?B%<F}(x>v-Hzt)@164E5>T9;S
zI99_UibeQCwyTpa&51qmk+z$YwiBWt@}<sUv9-m^H2cwJX>gvWoZ#7oxjH#TZ{K?O
zB}n2whoh<e?UJg&oJMF+C2oz7pyuS&TwH@~ExeGHXUuO|j2eEKfbVJ{Y)L--&c&~;
zAkT>!QLJY|gBdD(uTmRR at QXAx&_aJp=nu2VPKZH&&N;z{*EeN``-U1zOVRi5;Vg6Q
zxB&5&n3>TvJYV`~qOPYRU~Lo%Z8VCkhzZ(-Obw%MSbDn60n9X=f(qA>5!=%c>J4}S
zZWu<h(m%QISlYKy!E9zmdpLqaZ)cYtE*)YBhW0O0<*C at I)V>RDG&AraoYW!t9ZgY_
zP0eA}RaZ9bLgjXNSL{U!x@?}lqE)u~fvdrTUB$(YlAF!fyp5i&oNIOZ%bcUD$sTs|
z)yFQ^Ym3ie6Xl0l+aF`91RoFH74en`va*VtKBVWe7z=VSOFlb6nyRqLF~7P<ihR$O
zu3>*Xc1)8eAKGO3SJvkq=XrQQ)sF>%s8v;D2e_7H3%IJtmgExhBE%8U-d2|~OKU9}
zSV%$N8;l6wmmL=pI+j=7(eb&cTDCQ92vHmG=b|%e`@}Z0lKBT77C5NM3D!a_D;MR*
zr!2mH38|>L8OOq^p9CB7ljWCM$pmpEo%@dmge$`<9fMHjI-KwDH8_GS*e!0O>60Gp
z5Yx~|#7;--T~^vuHL)a9&?)isb;B~RsJ at z+7<qUY8Ch5qs`x8a%gQQO+tL$oxg1T@
z85)N+mE1X=&m)~fX at pUnv(qRJ>nlX{t4t1R7~mcS?{HGNlFdNbi%=kivJ_MzK@~ZJ
zB4ucHYfVs4!o~_EHq%XkU80~=I)}BNvTImtCo9OHKt=B#5yQx9Vtyw7rhH`0dlvJX
zOCc4OYl at nkAwuDAjN)g)lOp6rwd*&*?ldPI4{pzmnvJiFieq_u)<6kYpygd3T#?hp
zNGKGxjK8|28<TQ%i5eh at n2<iXCcXN;-jrsk6P29U8=*3Xewu3f&73E`X~%AQOW0PR
z;Y<fZLIsHtw#Q#)%rLGA0rRVkTOGA_(G7uc)oQ6C`|I=z^lN5I_X~!L`uk_seP2D{
zRi#$77V*o)`E{1h<4F;0?qiLpAic_$e0HT*tE{9-w-)8jLr2Z$%|(r_`#vZbGNnLt
zbpuZfh?y-qYcn{OhN6$A@@towsQlg9LHX5sC);)Bl_vgJl9B3uu+}OmJbW&vOAB84
zjj2sKd8an-2qFKEHm|Bk(L80j&U$sW$^5{UUO)D%VsQ|iHaK&R8L5g?7uO#kzyk+U
zeF=i*1=Za>{q0v{T%s{OA9pU22TMs++o|0TBg~LJ)^%3>Ut^2bNs~3)K{@%di6FGR
zW7eXR$swz#d2QTk(fKlN`Rg^x_dsP$++usBAv~x1!bwNHsS~zP1Aya+O!0>Sb{RDL
zOyXv&$yhMq%3wN_Ur9Xv118A&tlq at MW)g4^g;XOOpvvmqd;traF;fZTc8HEdbW)qj
zz4WCEn<l1L9}X(8+Jr7=HsG!}t&aMZsRdU7XL8_YTPG%0OwP}LOg0v at 114DlI-p46
z+Nl|3rTl@(iLIHGHC7xzph0afX<#haCy^mzN$MQzV1jlJRCfzQNrQ)GQ^JY!=_X^t
zg(7-K3;u`){9qwy2&L=`>gOR_A|&Ql_71?7H(-Rtut8EnCUVc|cS^nEaY at G!7-m1Q
zT0vW9`>^w!;LEu*1yOh66c+7lSeYUGLskg?2LqR6RyaCNER|EFpw3;CWWmymb#c*S
zw_3)zsr)S1qP#*`86c#*9P0ruYF$=<adno=SuR<$rXcEEri6askC6F^_^v#&CWI8!
z^Y at -2MMsnG6jLuVC@QRdlrx}JQ;-&tHULMbPd6$NzerA;Y_7u?OvwJ_CU-KZyE7QE
zlHal<c*zm4<BLRJ|8{9qu2e`{^sw+;$Gn~4&fM|7HPNY!d^v0AJ&bqklu{>dJmc6m
zYb7ofELoGR8X%Y3b!SfWK}fFarQ?b1(l^zxxEO9XI4D5|=k5)s>mZvA)5Bn_;PD;E
ziFoxZD30{+Pku+sbbHfP at _XL-*`*YoipQ)vuJ$u4!#pmq8=w%fw_?$Ewy}|EP<?&P
zWQz^n>+tb|MdKVxsx^6X15wa(nw?iycJ102sIF at Gz4~v1dfC*1d%(qvfk239q5eM2
z%CmkUCM}oCEm1SspQ;9$TU<e+zC+PK>BFf`A}oN>8Z(=??dW_{^0LWWjt;Gx{qlk~
z{tSZjVLO2VR=}vNg-u+HNe1)1Pk{>J#n+ko`lwOxT$(-?{dzWBfK!=P*3b8=++RoT
z%}kc7-B#khGVOu;K?;m11S4#wpTQ4+dCU)TJPyR1tBTtZc)j^7<@VL!eu5FW;8+t5
zsbA9=7W1R=D!`hOW0#6F-mM=$8X1RlVjj0wH6#)$$<9Z};tttiaJ7Y^9T7 at RQ<V*g
zGoX`M&$829na8u9$~GT=133LM9y)-R56)r)%sUk=X~K`NQm^(NPd!8EyhLRMf}W+f
z_utPSr(1~jV3)8L2i{;_Y5xGZzAJA2|A=AM|3wTdx!W1ji`d#Y2^l*YI+)uz+5TmQ
z{|^6>#Kug7{}9Lu|BeL&Y~3_znb-+wnOGSJ+5Q4{23GBVr)2c4jp>DC<YYy~s0D4E
z9nAkjE}J?MGPBYvIvHE55VA7<3&j2bg8Fu1#^$DGPK2y%O!R91#Vky0^!$#7#x_ob
z?0--D$C`gIEfWWTUffCF%G{9O#?;D~kl{bp>06syxl{gEo17xmzf#4&<hPTd8NH0H
zgSEcZzarKDXkuYt;Nkh7tT@~Ma$fMSqkpwH60-m6=fAO#(>FDym(zFnM?2F$aZ;dH
zuyy)NmH$;OZER$&|4$$o{>s at HnFs+KOxpB<4z_mxk5Iwb at oy*$jU5RA|E%%9wf{5x
z4<|2ZYh~-8Xs2&z{Fk5?bY%M*uKyhRujPMbN=i(G%*_AD{z5iF7RLWVMg~Ggj{k|7
z2$}ws^YGBiIoKL18arvy%L$3lD;c{vY10e4If*L%WBuRdL=_ncng46e|F-8J*Z;2^
z0Y(4|2kZYQN#OrX3+Pl`kKb=b_Fh)qiL2DGcc(}Nn4(a)4APZ|D7&w0LLz{RYuSqh
z6_U#G^y_BMZ~6se$2_ToS(b}{&79ac5jYWaey)2LHt+9ER}8K%{#1$6u>K*B`MVD0
zmO at uN6|77p3?ZK7_rU(%<z{Myhf$hblyi%pUTUdw7u4ce=MBnX4G&$iJ^q1ldIiY1
zaNgn>eFT}f2zUg9eew^|@2QOFFD7`HngoJ<(=81i<<0fcQN&x{6XS(u6S;XAh{eL<
zyz=J)PJoS!vV~Q%i*1AX+1RPP%HU8;*Q6Qwqn#7=Wa$TD1%Z8}@$ZI|!@*+)^qgI9
zDjrvF=Jch=f*f$H31>^P9Mp$&i_gM0R4-iDPoTj~qhQ6w=-v&!_&VUFa$(;@<FXUg
zFD3~mtbr}pGj?oU!N;Kx<}_EUPqh29*%*`$Y~Qq6?Q~qB!d?E$;RDScc|o#q5t9jq
z+8QX~fs7Zp3%jD`sF;P}tx>13>8?Pm3!xsBeAfpOn7Y!9Fob)FlNY13D6Qm`k={#o
z%Dk#u)DN43;mSo<THn!cnCu}wA&Km)a{j{dYPN%gjzo;D(nw$49Uv&E4}utyJ2|j2
zSJECPwedRHuZXEI*0AnwMVr>hz>!xIHMy>57Zbfjk%11JQ^%XoJGF5l$Oc9`lDlAj
zpJj_PW!Ct6r}S_NuKILO_W4nXgl|PqKt7+MiK5ydUWLZ}7UF`1>4M&xg at p`+!K05m
z$w^S4<<W;ekdRmXf+C_xex}G)+D!npDdn8g-~6)c^Ix)Z<&NUc2fuC?@^*o}_O at M`
z3nywiM9{cH6RkSu>J4Xm?TE4Z8Cuv#?84RhIdF)7R0Dk^?xBH0?>$gtdy&B^s1s|d
z6XT87QEQQ~p%`#+_kUN{34%bmzm41&80E!Beip<@2UiK1Bb_M}%xPQjUsR%nsi^~o
zMaR<Pq<{hQ^T1>!);R-<Me}z|vIqcUeMx;)d18gOv^GFGHZY%;TfHVIt`D@)it(V^
zBAiw3X;={&+he}5byMdvRjDaRoymt~Tk{N|V^-t<<}y+{SUM_SxhQpW7b%5)g9hJ(
zr?8Io!1At2yg)`FL&+SUDL&Q}^a3r_xApj9V^wgKy(^JFfT(R882^x*g&{T8T|n+v
zszqn|s)UaH{e1+Iq0Ky^Rt2kHT%5LAr~LD#aQNJYN=+FNJ*pLLn=&oQF`CU0KPNOu
zs)q?n_2Es`H|2Xebd}#wly+Am<I|-cq%6tq at MC$LKxJ3Ex&8CyM(&cJqk~3s%g=;|
zLqaOYbv1IKYMb{p`p#c_p*N&Jf&xS$IqkpeQps=zB_x#jkqd8{Xyjjk#vS8NprF8~
z!t(k0j!bx?Dm7b6%cxUBdV^^4Vza2<yOIg|2leF;ZCXkBq)hjpD4`N{=s}~^IAhcT
z_R7y-x)y#qrDqprEOKFSHmtX;%Clln11x0CD+J2%6kHU#ixkG6QJhonJ9%_|c&Z*=
zF6<KaP~ua()}vf1SzpqfwiQB+mf{l_wcnOKOL9xxq>Um;Fi-zvVX(1A60l{t#A&U;
zTRm%zE&_gQ4RfsmH<Cv6gtZvo#x}7w++4|+<?P(skPT^zR2m=Kr2Fagv8WrG8}zAn
zb~^vOrxNVoiSA&{2pL+J(<VAF>`gt6US=Vr5N^%~ro_d2*GACxB0upt(zWYwYS{MR
z|JJKs(9oTyNx829bCvBreyk9mcs1t5Go+W3ywMyj*I!oc`n%;SWY^8EphCdNSt)Vh
zls$L#xG>oI7gfqVv*68nJY&!Iob)L}b6KqLdNhn?bz2Hq^D^Vr%!<&H*>UOsy1*XF
zH8CddbA+nf+<josHnkhb;T_mpS4*>W;HEmK^Y9bM7|vNwq{dYkp^P18 at tqsu6#XU+
zGjz~CBwE5q!j>1i1j&!(Yyv&N$c6(xjR&p~mA27Ypg at Mlu9&0367xJKYywI^3p$kz
ztICK1OE$L9;%Iy;D<v{218Zn?0p)64?V*3Yt_noiSbR%VJ*gvwNL0F~z_BTlX^_Y9
zDg`jAX`Hb{Po<AKGlLIKSd#+8%tB)e<Tw9 at c@bjkICIv!4pPHm5Xm<I)Vj#vnO+NQ
zxCr>-1oje0|Mm?&83ay5aW=sA<Z|SW8~V=BSci|%RII~%=m367N2fKn1I2ABm(&c=
z7UXCYx`8n<BTSnRA9&uGIt2X2sC)O at C8W1uD&;Ju1+S#;AzJfmb##gXzqFeKBE?e@
zj5Z|``nQC;q_DzVFo~?Qtr?$i0>C#o&J<Z$mviA6{uvQON+>EcFMcGEbHp}6ai5P6
zSRtNLQgs?XEz!h6B;V*a<@mXLy#t?%0shq!Djo2?G&7-FGfH=M at CYO7`OR7`gFMDB
z<;4C--DpgCu`#SUaU)mOuo1KAK2p@;EOB#cYu52l0e&+y<igOnOyqc~TTfPixLyF#
zNGD<c-X1F}fKL9Rw1t|~imqiPuO|4rLB%iKt+|I(7N}q{SW%i%lusC!##)Kwmx*M{
zDWNqV8jj4JMwW1mm4ITtojzbwpN`B#Z!^PmX3*=`>EC*a@=vasI&JEcfh>{M?xpP%
z#ciG at n($4cEFzhq*755}*z0FTFTnvp`kfWQk8OKly)?FboG$!Q%n?Y$HVcP{Av!$4
zVeI7Iajb)xBKHwQ#Wyn`tp8L1wKG7)cB*FqK30j#%Cr1qD{rKF{6ve*2|$sAym6~K
z(>9bbF(QW(c&#-u$R_q;tlP~yZrNSylBnuVQT?;!C#0F^m$sbfr?ln2V5#7P<OVFP
zI!1>2n2H3<B8QG=HLNzrQQ2u#Lc}Ca1}PsBj2oEg`VHnk_uf(a{9Q{<=Kx0sb=t-B
z^bX7~Gf3H;e`A6Svo8rMjmk00n|}2|edfcu%X<G3ES;HQPVv(zHfb#=4XrS`O3pd=
z(-U-A4vA|%fAa>AFEOoB9$U;-s>Cg6_|84&g>%xzuH4sm`Otft&{O>*F2ASl>IYW4
zesw(5$@0(WI at AYJ{*W+vSd5Ip9-{S9VJyfx74hhJNDz5K#4>>^1CLk!E8TL<&Mq8B
zXV;kfneETb>CoAjm$%YVw9<xffyV?73`q8SjE{Gua at LHfRjcIkVc|>?lQx`|i1$HW
zUG`G at -TAgaUq9Dc^#kT>qmOV`DjNZASl!el<FfB>{Ce%ri#)uZBmIE#2gu<Vt26bF
z3$@O7friQ}hiKUoCk~1I+`>BP-NzeZ at 1{35M`jq{y!)VPN&!^yoD_P)76j^sO`8)H
zX78d8CYN(;IJai25EoX2SrXHZzq9xyrK1hb0GD%fp5?1xo5jIi+MdwyUwp>b1ycTL
zWPY4Jiqb*b2H^?VMmvQZC2Rr~Br?`{%m+`#AG%;_Gw*$|bl-sByN85eIo5sKy1U9B
zD)FjPM$D+YfK;(=l^&TMZI0BQuip+%aUbW24Sf-JeTy&jn_4%lQl+AkPYssF9`j8V
z at b{m~A)a~q_-z%my2V7&yajnPJ at c`472TKp_sR52H^c}vmM`@C#>se^)gZH2hn%9c
z*dZnI9N*ib6bpPy6*ZnMB){ifpkhDqqUmWx^NN7i^>-m$;<H8e5bA^mVFEyjGmiwT
zSTaCxg=+yZNj^8)S*D1N{m-eQEM&bJ#ED~3J|zfsU at 2z{^te+BGM8k15 at 0#Y{bK$~
zihT3X4++ldpyV>nUiBm&NG#6yxE(vlwbE3}P&vg=v<JN}5`oU8gF2_#0K{+x`}}%j
zz)RkJ(!G^Wr*_-ieh7yzLYlp*=0p*dVyLUO`r{oHvVa0u`O_RkUEOI?!5OBnaJV(E
zHb-VOs`GEbl=&#^J`+GT)8V}cwaaK0-6Q$+hjDQsaY}wW6J>P$INvFYa%hWHZ#F{G
zZO>TX)pk63LnFc-b{-!7X=2)|U=Je2`|s1vL7#73?1>0l&hY!GplNy8tyT$)*N*Nx
zcv-uh$KQ3QVK!sM)Sg{z#@!&<mmg)O?KsGeB*cHa#cY$~*m4&7WpEE4ec{zEQRn=~
zYo{h-S(b^(j{;=b4i;0N=S-icc$G?52bnejbedyBh;<{vvW~fVupBtmJusNyuZjYV
za7elfR*k#1ji&BqbRH41|8TjA_yt|gUfGw05u`yXQqFFr->t~tl#h9~9;XZ`R05z@
zKEg!IJexW&1r!VafLpov`6aLgC34tBS7k42!nTQj_8~iE`i2&PO+=J1eDGbMm)(Ji
zOUkBhsU=qXZAgrGv~Kc6kbLJH-$tpF*azcbSo|Ew5<GA92`8S^72JUX=&%B+Jv!!h
zQf0pWM#8vy8TzOg;BpH)#$p8e!`FwjJ^Pg(<FxDIZG5$>cAOKxlog!Yh at b3;)OI-S
zZ-FKm7T1)YtwJ(Y{XdJCYhp(VGOaj|7aCs8!>k&MZOj~Y?ax+x>WS?iG6mc27P{Jc
z>czho2&@@K68HShgWv73d&jqUIy!EF9w{%u(ul8Cc+RVyWp7_BdiXAAjjA9z9XMs$
zFGv6}D)8)|9hEduM2Okhz=E%N^VW8m&a3o@(~83l_)OYB!|EN-ca&T`Xs^mLwI}s4
zViiUEmk(tW-Jrm-l|<fv_z=H_Xtca^=56?4Wlg1A*O((Zl+T|L-vSA+zqv0nKVV-^
z?MLQnB at Q>|>t4I2T#LMSg#KR2U(z-Nvf3CfLK at 7VKF&(Gx_LeF#~eK}n36F9HXU%>
zqbZee?XM(`PqtehE%^D3xL!L;p&>SxBbgsW(x!PMFJMK=%zEl$aU5qJxH&<#_E0fw
z$VS*}fEo*(5+TS9EL7P?G91}9u02r{{Ui5lw|42|AiT6a+#5MuVDr;HeO6^gb4Pv~
z&$2P~F2>WlXf2HqjmdMCH6qLFUPy!>)Owre2<Z}c{931=L62 at n574E19eT2ze0mV0
z8?$XZHymfJ!YV?QP`QXGoR`_*KXoqLu<$w}wv}^yE(0!nF&Sjoqc|XS&3+l((6Q#B
zT-l2{;x9o*KJKA{^D2QgpfYu*L&eJ|cHcw at G1}`0bXSc%R~xEj(26vT)mu<&hDhfs
zXV-6IuBPrZgpMP&&EZK$EyHUDNvK!qmr*6If6kscDK;89rDOBJxle3yPPLhQ8(z7t
zTSx93)GIk}4iMBn|45#@H7-(JbD~Y3q@=*wbWjOYvAMB8a-&gQ;}>DtG#HnOp*`O!
zJg|L{^>p at e%G|~C7j|yHSda+g)9w^T!MTpZ5%q+32|+2)-uGeGs*Gp44XbalhO#u<
zHd+Ls_!t&z!9JnYu2Ms;h2xZW&B>fG4ig)%+RsA9&EwRRN&=Ff!Zutla1V_EY+WPv
z?x=Vl#z&O4KE{urF<0_UIWk*L2=~Rz-wTk4&whtkLAZJmWMaS#7%A8YZ_sav?Sn4g
zzHd7C2P1Qa7#Z0_dKWU`*_A5sjnv=Ty03IsZvz_KaiG_78l+dSZ9$cb^`t)YY5e#q
z?9b*=U9%f9)xhEn1EX1ze*|-+M>Y%EaHegC1=mTKj#nF!@8AB`6O4Hh#VyIa%8Q}d
z^<AuE#rFv0^L-?0_|V3r{6x0=m3xFy_sV#mt!v)KUaN3xcE$fI<X~*kKAd~~G>AN<
z{*z$@DX$Iju?Y5>LwsJS5-E>+*M{Q~n}6vk1dqE#uyFl=W8Bg at 4}Ut*a at u9%yeUWv
z9a6~bN|4Tp5%-7p8Lc`ewL{6Za at nr!#zU|yn{QFZew*1h_sh7}c-iv)y;3(P##)Iv
z+G-%jtBUmMLv$Xj+*U$pQ5MsmGU`AjNH__<Gl<d8Noj&a$(H>Ro)g5A)ek|svB|UV
zf1WwPmt5qX`BdPQ=yUfTJ3X8r at CBhI>L|XBDZ`e1r7dW81Hj1TINPlmoX~%(fXp?t
z!Zulj5JqLD?z=W9Q!hCF%IJ|G{MAC>Ko1-fqgSBcDFZn3;6mWw{>^o4Tb>|w5k_sg
zvg#D;WUb}OCFUfB??eRHbTgpN_~3CW!rkY!<$9zNv=9AO+D$n=<otB-06C(H-5R*3
z)2mA1Axg(l_(MPGC)lzd(bM}n5$BlMN`%_n)xKlWy at 1Rt8~^a*El9M|r-nd{!}n!&
z%faE3NEJ%`BC4c#I4+SYF1n%7r^YoqK6>%n)%$Q<>p1}Rou51!%`Wd&7!KEMxB=TH
zK6--74T2bhef0r^2yw8E@|OdaN!_4gF?6Ue%;`Wv$;X&-Yi+!ED*4`2rg4QyrN`dp
zwEg^478AWEa7wY3#K=(yR1sE5-#xWnl?rJL!1XP5;1QxiiL3%Z&pZtn&iG!)Af0!#
z9BazrESYY2<Fd6*ZJ at Zc`kE57duGlcAob_#xeAo7Av3y<fm;0Kb_QPgM3nXb7nez}
zw#FTxV9w~{+SmX at G*03CM(zFt$9v%7)iEHrbRj<1HCpTYM04w0YD3YoXCHgYJAzPL
zcVp6G9&Z%)Jw-obi>cXfQ-gA#E^VJ+-T<p+Vs4XzlGmP5__FuF;DlA-(ky92&vtKy
zK|>*?MX$nqGvxB;jW4jk91?4h(=DZgZ at B)>2kB1(E*&DR$NTqW$jUS4;o(P_Yk-g9
z;ieXVo(1xJhnze+5JSpzZvlLRXo0yVh%2S>otZvZl0FA5uN}4~2)Lr_?gm!vGife_
zM79}caMC-C2;8a!XZY`ve!awfV}r!tongqM^FI4{Wy)3jcJc^v>j5{z_n6)>NU_E)
zyXR3}p!)6}F1zLLYxmT{$L<C+`TlX&9jEps&hqyWK9x~J&KvdepL?J~{Ji!AEs$vo
z*-ku&iLah>YTcv%!P-5=h!O<|8gARRZQHhO+kM-%ZFk?cZQHhO+ueI-GV?IWCYwz5
zvEEK4mBKl7zTXjzM5c-9tFc_|MuE9DEp|HBLxY>mFG1R+ at f}+JOjrFzHsdzI{8=5Z
zAF#65S1b6!t}BjClk<`+cIx%yWEwxeom<{!)a=%Y+K#Ok*g5d%_e6$Su5EBe_`%NC
zl$y_7?)E1^PFM5k<*vu_<Dg*8?R9d~v`@G`6=M3^@5r%CB!rC3Fmc}J at eeuVWyr}4
zPcQC-_NiY}+GH$lJ8bCdH;VZ>>=3ysm2PXhO at jnNTL4Y8rtC;mAFlut;K{Ra#Fs}o
zR}VpiVc)j?FN$b6g;ZAswPS77NjJYq{cLudI@;$zxyOl+B8X;a+VjfONz}CW_CEaG
zv;w%g+!nLCC4JwW+PfBiL9CsewR%8S^Tyjv at 52wKhiR8nZ{wC%-xTXZ9#0^;XnFT+
zMawMh*Q&g{U*|WDco!|rve(<?!~8oAE&ir=cDB!2{-~Vq>&MgP>Z(uYb?a)JJj<zI
zu~(trllA&Ub#3fXI(?tFmyg#*>f2--|Dt^@|IDwikLQk_-tYZWEDbcOsufk4TG!9M
zWv9It*5XCRO{1j|n)YVexYBDa?C$K~uu+7iluC1)lpEQvyEwly9{c<JW|iEi&DMtw
z at M!1mqc2XY+aJy5P)N7jx^PfX0W=bu=JjZ7 at Woi!(8biz`%e7c(AUX3CAT-dza6+L
z*)&u;xQuQI-i&LPHs5QTPg*Z(shdx%t(&cFwmEXogS>BdGVhNKi!L%P{9QlimV8hg
zU{zOd`mX2D$oTqM9KWl`vyF*wRW_}Hm at F}8=2+ag&=o8yAaGpkDj=tZ1^Ewr-E`un
zc&YHSe(!HWu$zygX*qbnv%Os;9Xxq{nfwory=hNbo12qe1`^rO`I4qHK3St)xdK15
z%{=l?_2_(Ao<#P{Yy3TY*RAjNc<P?V^Mq at k|G4Z-dGSDFUYv3*?;;~6Gy1d^uH8s4
zZQfJ1TS-bX at 4MaKpIK+s=a<WC;mnkM?4M7=&Tk*j(o2($S+xiZ#c2+I&Fmgy0873;
z8vhVRe;(>Dx2(LbMO9NkhEa&K3tn#!?B$GKYXla-7eR(m0t&^G?kC*Bvr9 at lMTRhn
zvVj@;yY8NmKY2P312_Ywp|!AFq|LK$@{U!JOR_4xpi^Y{uhmI7Rs6iy+`K4B;}dgn
zOvG%sFgbW1S@`gwJ7s at 4H5Hbct{W%Ht6*~Uh{rwDLvt^?!eqgPNA(2wucy6a6 at MmM
zF0fen_?T}@H^ozG-4uu}PqSo|oV>K;JZ`f~Fkf3eX=luj<=9nFFS^G7yWd8<n|!^h
ziW?5K(vV%giVMstg;y*1DN*_EjKztbtR#*NoX?^gwR85g($PaaeFQMpI73%p6@$bM
zY$y>KQxUNZE7n%7aF~_UQyP^&eX0r`3svfQwqU}tvVZxON~-ir&aTieaw8%!;P|kS
z2#~G#L_|bRho-wF+(cP^n%lZfCxSK=k)zshwm0RR_|n1^Ccnqfi*oSdmep!CmfPA9
zGRpRkHpoVC37LpuL}a$s5z&6kNV$iqqC0ZAUgs-=+{{1{Y*&$|fzYBWxJu-vkJ*y!
zV44l=lu8&!xoNR2|ITH$QI#AaP at 69C++KbxIDpzN-1IyMC&&Tf-#fmJ^A)kxZ}t!G
zD=dci=yir3xb3!R=D8&GX;e4JfQ}@RrJdD?Ts8=4nxj*6GAmk*sNsIL>wd4Fhf1Xy
z_a#{+=$PoM_DPs8vpxh*stcKqK&s;2fG+z$9gAwFr7GBSZ9U^W=N9(fEU3E3_-!S%
z>S>g#JN#WLjbIHPsM at 4*8kSLioToiZ*{q^5_hoL85FcYtI2SuSZ{X4LbZNVn1i9d{
z{7ihLbx*XVJA;OzO7$)X3?#cmqf?X#+&89gVhPRcu7|~CmE*2yn89lD<bN7A)p at t9
zXb at BN=?1<qH>)!B-JYFj31`!u*7P&6Gvk8unw#7%q$zY%tg1*s(dCbK7U%2NF*q{d
z7UE;r1iyV9oE<7_A6`4 at X45y`hqo8tcJk@@3~Wt3Frmfq*vP61fF^Jd+rCDuJQ12K
zMU=iur`4r;QmKrkepZEJz4A%wz!2~bXeLEWoiuWCmNr?!RZuaX_GxRp$LmCAn8DWQ
zX58Fr+QEC?D*U~QEz7qZoB>4+1FxdqHtsJrqj?vmwONET?g*~XtT5)0&B2>;VS}R9
zUq|y>c79%DB+iq8bH{~ke{@l-H166-b`0=e)=k}^Yye9V=6)XQ;v2|@u_bPxFY$t`
zjxoL;t{O(`IMERtU%D56#s99q`TlH0EW`y+b8$$?#K at 2;0U_?NV{Ye-p}%<t)+F!E
zjPo}2xf2*wTkz5Sen%LYhF&?xa^oEae3u)Kvf66jx+li7fLv9CH6`y*k+X3=b(KgP
zsEQ+B1{Hf7nR-zbQ9ljRCfb5BpoJlBk}=pEt)IYYaZIQNA-WabC|RT)Y_0oX`y at G&
z&Mxqr<ogW=cNO(@D;b>aEOdm5wgax3B&y-vP|x!+0J*rk?mr{JMfH;vdmIj*Ew8|@
zL?)#RT2|3N^FJn<tRZ>Q;d-{}nBe72j?)<HySNd+g}Hjx!e`KeGwiyhJJF2P1sEF0
zUWG5cWIN%E(1vdG2_Egp>l!`(5N)1yP at 1Gco@u>PP1~PxrCRb8fUm~I(9fBV4Vi_Y
z#lCE~eVo7i{nGQx^U$7O9A8^IfZRN-Q6fY{Yt%DE at 80fqi|5d`=$UOP7Ly<5ua=pu
zgXc}J!{>iP9qX=f(0j-J(^IY_9)aRFfc2$n-{T9N=fdBks_!dC6LqK}8p7JexXOq>
z*3CVGd1 at mu8*-j8>TQKFbLj!g&qkLT4i~*&5@(<fk|{4{cw6<Z!{@61561K$`YYEJ
zCMGpkvN7*gez_CmajULLZ+RvWNgT2hsmPyD)<J{8Ue>I)j^?_uS~(<37Cnb)>tyB=
zi&OcxBgf#TQj#^-bYipc2LI9$;qN)?)Xw(ir%_IO at j~@jJt(t}f2E&3g!Ub%kMcvA
z_MlB(wL{9nteOxaGtJIcOG7 at b(kpM+et|Vo%ibj~P6$>7hT&P at zlf#NOI2R+NCl}4
zc5w%c{j0+QMqveMLS0DhnnGfi4fS~=M><V-md;w&Yo{L18!j^KQ>+guk0*_)!4$XA
zFOkg&@bXEZedNovc$KKfVJK)Nf|{3+zMuc_6uGs-P!Av=*W-S#`i^jbVbj`+*JB2l
zn*9{pyQX}7Fhtfb3iTXtx6Yg^)CJ_SdlAR$ojFp~AiPR^pF6qGNDp2m==?aGr9xM@
zo;U(Aa~fJx at Bq{7=UW(i<7zq6D>?bfR;b;g=`}^I7W{a;@iyX0hw%$fwv<?RzU`an
ziz$Q#j)nQ3mg-m9p<x?owC>!s_0v7t?C=m4R&-QV7mK)rr+5{r$tl at Pba;$SBV*%+
z*ymQ(!jN^Laib>OGYb)-OA9j}>=~z>Lf^L`gC%b at Rkdy5ETZ%TQfOOT<7H+SUWFa0
zcvd7&F|gACuGWkGoydXk=Qu at vjRNz+JUE&YGIsl#C_+2RboO5ip2?@gr*g2KF(%AL
zzPfPrpn5w|V=A4_y*qmZ2B*DBPp$8uYTAOlcn749m?MQv)Ps}N7KB=e at XJ@?Cscu)
zlY**u#}pq>U at r3{>m;%ecM%T_E#(n1zmh(y>8j(~06ia?$cvhf0hz;=cvw(XX(eS$
zry4%&^hrWiq-bOEswnCLc)-*$NfQ at UB3|JEG|QM2DUH&K`dR??Y^XE%80{*Zfz%XQ
zHKs;zvt1V`?FBDiX4l2nv+ho&cZFt!&o34^HMHHSRDi#JhF?^sS=qOE)VL-%^?1;x
zm^-d-ne2S_p%>(ckk)cB%I}>Utr&wwm0=Z+-c2mMfPom(WttKiR>@JloZ(?5Ecw2g
zCZpWwd at RlbC+^~eQ+2PpPzr}eBV^tD-yw;yc<7zKa|RqHMXkXcGR&ISU;a_xx)dVV
zJb{<{RO#Wz&Y_Lm8h^In{apNA00%I<hf>8f;u6l^>wQY1#WlXTBZ&%qu*xLQ_=D4=
z6Nx$DL|$DYxdLSR_g*n<+s}}nm_}_*exm**V{!e9s at BgTA^i2p<2?!M<XanD7FL~4
zy3judR*N!jV((~K#X~!=8zwl<aWd**d7t5LpjNAy4^Da6<qY)ibFs at SGs<qOqCWch
zKFQ(snA4cSn9H0Iec;7Y`=`6fUhyW8GXDW<eDipudv!6q&z;?=D3z&hjns)(2TS|`
zuO+$Pg+9Csr<aIHek at iLlUzj?C!NB7m6C)ja?x;K5PYX`{Dv#GuTS`M%PpBy{%CX_
z&gBB2Xo{KObzY<>A=i-!pkdvrt%?SO*;)>b at I@a|Atw0 at 7aq9&D*>h>Rq_<(7*of$
zL9CZ(rc}iqnhXXf_-g~?jYvjN1FjRzRS4#la{_(f7^Hg1CXz*~sbbdESTltZ<0&|~
zy(&80MZI#H99M59RBuO?h+0d+N2D-EeEB$cN|z2B(4LS{Enn?p1^EbTt#q<~;w`0o
ziO52&Y!~akb8(f2_tfN7<$5X1BYD*%pxQe(Lrr^1-RZrUH(X*xFWK8>vxa4{cC|Gv
z`e=<72{WuaiZ+sGq0}QK;KFOfZE0APAvu_g^h96Y8_Budi1k8TG6rS(c7Huy;{Z)1
z9?l+&y6_P?F>tS%oFabo>u+|npU!%@PwQ8MYg2%5D?yVww-^$Do6fi*Y{gct!(7$R
zV5<2%P7!AmRDr-{iwfr%2i$bmg_>0 at 9{!}8jl&{k2-BkQPgR-R6Pvr)o at H<oHuUhV
zbE<xjb2|U at _dsvI4FvMyxBXkvdtPbufimm6vz$y&=Yj2eA;*4XVUxmWs>dtEzv4?w
z#jc$uHeo&p0)nX*gIT`Cn^4AHHHC$pwt4Tvt2SQ;{*3I|bn9%IAe9(ksv#zN+9NK<
zo4Xy$YM=db$;+E!ZHW70hD-A3EX?NFbRjl#sTX!O!uV0?lZwJyz7yq=*7S_44Z!=$
z<b)M<y(m=`Ft+1qV7um-SyL}Sz>Jr1-<SDzQqd2^z;SxD#p^+#8~qenJ-9*W<6U$E
zStfSBy=C^(F&rlHf_-r5v3oRlF@$<M5a~0>cObbsgxC~*J3~y;+pnHHg1#~)uPmTu
zXaJA*{&});TiA8xcVF`^CBUn*r>l%}RJp&-q+ffd-}~eB>$Y8e+Vj<$x9YJmysGK@
zdz+{4_wRdTl6?2bc%S$4<9T(IKs)U<XHxJkA<)6E7UR5juONimnYy*2{O+;h8(0~u
z|Ho$<7E at ys_2lO*sdWl=082o=D*CHMGwx)!<qgM1Z`~_I3+ZgPhNIN0P{_<U4s^wk
z`DJjW5rUig$NUr4c()?{Q_KywHQ34n)<<hEFfFpmxSRTl1<80<cTKnpGdHevmOPWA
zYET9jU!mU23V{9PCj$7*g}r99c at CZaa!v~b?qN7}cx5fkP1ICRMGq{~(lN%TgWApM
zz@`g1?UWgkHA8lg+^hChhBR$^gsKY0?d_eVkH+q7ab=zc>%j3CiZaG=#pI=otUY&u
z<U at sM)gF9=E)Gd~cPz!FGW$~;ZV&oBh#9?t<eS(!k!9C`Zvibk4(rrhZG^YL2J&BE
zY`2SfLeE3(a<xg6_1Qka`f2xAuGp{A(Y)^wNyc1X at 11@`pZBKd-WNF5V&qVK=lN at K
zp;h~5#uN45{uXa01C?6+alRMdS3?z&hPY~dc;&a9wWNUN)Z3R7*ZK4f-P}Vp%A2Y1
z$dhmlz+PNKEYi+cq(!X4!T8Qn+;f;&?FDaOa+28j{>-OnUhMjJd;J}DWf(56Hq1xS
zxspm<kDEa590g;vqlUayju?B^!zc9N86*5zYxi*2xp-5Nn(5`^yfMRGZFAWFm_ZQ}
ztJ!qaurHP|f?3os4|6@?YCuX5e#48S!eBjB|1r+1Ev(}d=5Ydwold7e8CF}FK0hm;
z*5Z9I6{YmtUyiu8YE9`oHS7d`S}Gk!O#@s5Iy11j8E4IIldT+m2D1L?M2Yc$aT)IG
zV97enL8MxXrk;~=-M~JyHirMX**#d(vT7RRTXC&aaM15*Y#?0r+4^Dx{G1yaTbA{l
z>qyYQ{`r=uMp`e0;vW52_rcF?4BYeyUHy~nZLed=U;vi4gbKp`(%x6&%+m;Gi^sSb
z!?-a=MSn^6Mi!{5HkX^7VwN#RQRkSW1H69Rdk|Z*Rsrx$QL~nv{4n(w<fPcK5%jY^
z2fB-~g~$YViv2Ue6s!u>m3skKu<+j|1&CAGSTR{IXC>5o{AjIUrxYSS$^5S0TFxH!
z(9d$KWyNOIb$KnwR>Ui3kXDcz9cvjEPxgbonSEVmPsW46Ol`=zTgq#HG*zUlfN}g7
z+7e5~u!=}_{GeNCnX%*!(|siHyJD;M0)W-X%yict!(!nQBnLeDk(I$|aIX@%@rBXK
zU at CyB3!NYKtp<&cJxiuk$0ATn8{&9OnP%!m7Ab3{ATe8kd3~UdveRlR!llSf^A$9|
z- at E;ZVZOE7L8#&1&|Egy=!2ij<2?I%cp2Bj#|uTC&Vs~At8=!MwjKji;ysM6m#^>a
zeG`A{dOpav3p?fAVWo0=fmwJlSr2FVLZGlXuPUM4E6`~nfIK0_1jRd{`SS4{UxpoA
zA$S5qlFc-7QV7eKS76K5e^KqUsW>m2`MzZPza<7$+WW7UGIo|^lt14f=ULj~-bW+8
zLp2B4JuhAbM_^QjL9rlrb@<VI9q5Od^a$dOe+BZs_Zxftyhd`l06ag!;;}s96inx~
z8t8>!QND0`>do;A2syxABxe*`Ou(u%Hp7A6O!2PpXy#aO7YNMfNCDuO{D(JB6mR=h
zIs>1NB`(3>Ebkb_nj7 at CDF8J!YgU%+ef9uqkmyG^oinw|KFE!I_-5RWKwwyZV#+-T
zz+Vdt0shpxGJiya2UW$z<*Q(8y~I3+6eN2eUU4oAcdwt^g7$0%BFi&nh+?IO*%`0G
zL31G7jiV;uUb^XZ9G2o<2}gZhe?4 at catC1n+7O$1+%M7eqbyH1n*C}%N|efX3UwJ&
z(mB6qX9^=1n$6-x0oR!a>sk~04Oz8k0el90*t{yXin~(?NUbE|4QSPjF5cZRXz&O6
ztkg~RW|fWif+qmG*W{u)Idc((PW#<HdW+4*n?B=M49ly>xtXnIQdTlbpVX%L^?XMG
zQf~2B#l|neZNbw)U3Hjua at _4>o-OpRgzUHh=!BTv2(#6)&>i})TF1RaRCuvF?`T3`
zz*I?MexA at u<$sAsXf*|bw3wgKfV?G69YU!c3w>_qVYMn4Vp7pp;k=BPE{{n}u>xKG
z=~{=kDyimd2i at D5FWDM3K2ji-+l*%)jaTaa^|i7F`C8C;^>fbYe+L_8-3joLoWZjh
z9;Pp!W?XXFom5zm=*zr^e at Tteh+E9etUPjxu2)KHf-Ug`{8Kyz8;^xmCthP?!7w4E
z$nCY4&KZHzCSNx1N^}2{^xR#KKmT&JzV)~|zJWjcM<B*Wx+4M^B&h8B%f`;%`up}r
z21C{nfmzNVxF<OOesRn`tUS4<*3sTQ&NA&cTKnEga3*6IipDV!mtmnF$V=nWRL+k6
z%En*|e;n-@>OR^h4DA{AKJA&dzxNrZHUxby$V-ic;S!r6#n}E7a~XB&jD`8*B%!;|
z1W=O;FoW}eU2(E5uU9xsq>pE0<8+#K$NUcOIWwTSKT93wH0IwMD^_(L540x^Tv0xt
z(@nh?m^7bdDO&t{Nn*-vmo|1Wx3WF(l38{+?S_UhEx;3KXglDEwBDr)kD8U+n3QgT
zCg2m$Vwq%+CW7q^?-}qKxbTmK4Vgn{3v^pn$p`(h>H>DVW613XstXN}5&SDC=P(-#
z2|7GaPSQ2-FrACDNCIvmttLOsFa54|<aBO=5eU$dckPaveXSL6Z71HIO;VqJPd`uX
z-SR&&bvu2M*s`#fswH>A9$yhpkG}Q}>;9nSmr at 6K#~*Sw8uq=SCq696ehu$s1<Jj-
zW{qc}rmNzkgsU7w+TO%N_(WfZN_o%v?W-M#eS2ZBvisQ9tt+>7V!{eL=HeuWguv6j
zfw$Vi6rG`$EEYWYd2$>6vs3=l2Pb3g>K`zfL1aqwHBVkeZA=$WQQU0v>y3WDzXj)J
z*c9~_$L4z5o^2`!VV}|l<*WXeylf6h>1_8`B<%b(te8K_es)fBBC|aynIfO0##zbe
zkYddb^@i$LI7?4a%HCOQJ_o5CIWd38blm1c#t3;3+C6uK70U=|*qatbA~_MOLrrVd
z>=)|tMc4n|df%*|)-BRyAO`Z<4)ns5C+ at RW8C_6rHn>P|L0a`gVHer^QwVF+ at GsU7
zW^m})UW<JogP8yILN~++<-k>^8iPFEU~~)tI;sPHumrL-U1ML~5RbbMF5)8TI{X7@
zs{+#y>W;<^tPLTiIcz(l48jk_G_GEGydM at KLuKh-qO{5{82M`^udR%sFFSOw5rS6p
zbQ?eTz-*Zy!xl_`-aB%414!m#1Eb6^MfX~yT;MHEVoZhf4cx|`aN{?b at D1`2A4fnO
zyHa$9m=k<p>)GoN8U)8sUL+#z3mmt|ebC4!`Q2^o+i<L6F%-)g6z_G8G>8*avI>$h
zN{D^Y%c-|Fj?Uq!ZAFHlFBTIQQ?EtJfv+uG`Ug%<WE3_<Z}zO?YZ?LcJxJ5d681s@
zydZ?Zk<4S4C21R(r?mVjsiA?k8kP|H!Gf&Kn=Q8>=(^xFV5nFTL6^-zH|9FVtq%Tn
zcIWgC at l^P{#x09(9 at 6UY8rG92OT8@iM~;};5!UPR=*e+ek~VCa!)q16-S>tCtFlFn
zq52>y?ul7vg<FYu0omQ&3_ghY)-}$MQrBq{ysUQ;1$L?p96C=sG*pW&n&RSzfnoB}
zvykD;V(UB*L@*7nt0kHl92K4<9zHIHX1gBt4S*g<q3ns}g^Cp+zw1Dh^7gx5WJ)pC
zsYB-HB&1Xle^0)vSn>j<>kwyeA*fymabj}3xF)fYMO8;hK1@|6qPqndT%I<j2*5)I
z?02l>CJ(VUMb|)=AgW!RD>91=d&7tl^~s&-D~I at 9yK^3}pYuQ^pcKMYx`%zppfx!0
zcebb?Y9cSRx&Bir9>twOx{d|=kiDO^h=0>i{%*d>X!jeL5+jP=BMnN-3AcJWi;Zb!
z&dAv2EGz>Xa)(!1M`(~7cfavl{CdQW1diFCgA_8(3J+rfHukzdF=TboaS%++ at GZxm
zK)ay?_pg(aN;jjl;K*SpbMUZ8MV9udNknK?bcaC#%t%rEWKQOW+kygxr}h#~4N4g~
z=UL}xz2#KKwfkzetvV`+Nybe>F(?}6*GXn$T>KOSyH*WN8$)%87-lGg!ThdEy2cmf
zPE!)a1%+6V%EAc}!~3A4G>x?=%#fa-{$OC at IM=6pW)0ay<;)TbDHP_d%)Kyn!chsx
z(*BNAa6YB(Z%V#j=<P4FjaetL$`)dk<?a#*uT)m{KwDW!3UkX=?Z8jGazfAcBYGf}
z(Lhc1-hb>p&G11#saTR6gM1Coh~kB8z<v}rs!iKob_FxEixCdx^swU2lDF>V1d1Jp
zu;wWiQ^TW^L!<Yv5UQ}`O33;$nmGJGp1U^UiJXy4M#l;PBAvYvJJJV|a(fBiSVbWe
z&RMgMuZZ}0C$n-CcN{B2g?e74ruJq9Vz9TEXtK=@0)t(9N!T^E6^HLzGjc?l)$D^5
zIVJI=(0HW|*s2LwTfxTlE)Y<~VYT2BZ>mZ51uNkqKJUmbV<qN>R#kYZ8?$3*jYswV
z#B44w=6SGO?T2pp^3 at rvvQ0T;Se`6lpW~-E9W?exH0iSH at R}e at tLtr}5I2KkBe@$+
zkBKlTeJMMLDN$g-T@|(4;{^FJeM?f{ADQp}{=GKf+MxzZD4R}Gar{JmCw;eMeWWS+
zj!9mvK0~G^)r2_t9L}@&LH8(Dj+lsQ6>)kV05AguO#@O{pVW*O^9|01j%k_JtcxL<
zx$!5W at z^E7^t>uj;1i4WkeEQ?ejWG}!UV+6rVmU9g`0ig4vDnTBq`CK(G>gTr*Xb^
zbkw_KtzQW{%9+jUode6ef27<$?;BQryr2O;>OVfJcNQNcOVg^hy*(M`=@e@}p+*<A
zl3t at 6pEu}DnPtm=zLo at l>~JbHH<tL94H!%*or|Qc^zM9go;XEtQ<ETyh~=|>rE)J0
z4l1 at p>9Ks;PHK3fsGH~+_tcc&rfN&+AC7V&1cd5su8~p=NaS%Qb&VYIzTTdUQT>SR
zyP=ZZqaO&%(TQ6AM56WV7%T0_xCy<=-hLtquhGS=^}WG<1_1@=K+-|&`ITzqKc(Y;
z-;o0BjcK*I)SQm)At;SOf6BK3ih_=i?&xRw5hWbi%J``jT58y5mREPQE^(Jk`Gfs~
zX`D$KtyMhoP3fJ4^7rwWCUKYK1}J(k!TZ>{elz)AP$q{oZub^`Nkv~?`;+ft7VCjT
zmOX~@**aKXvdoBF6Kx2e@>P<=jTrh_a5dzlVAqBbJi%$U1gdj>Tkx~R4)2;GOQyGG
zO at J>P0nKmz*0ggbJZujK at 1n4-T(?R2H_o(bNrrw>sK#fHVhCq<DpB)`p%6TZa2wN1
zS^_%28nc5&utXc_<dneF!=NP!35yUDGa`69JAs}F&ol&;YTm*ZhUpVizR)kC&ymTy
zQv)cGsvd(a0tFJn98Nh_Xg!?q%!Yq%)9UF<#u>>L|2bfQ|Iu5i>Y8|e<tS@@sy<Kr
zTIWkkxV{#fCC!tlcqU`vBbJm_n$T8f#EoHb?4z2Ou4J_cpupnI<##geRzD!?O|0IJ
z^tgO$4?Vgw(5;jqKvH&h_YkjXUQv66l;lK8>h9NFOU9WGTAn$Astbbps&>ADRI}NF
znUPt#fNTqUBh^Cj;^><dx9(P0udC-yp+Qx}r;CPMvc=I)rqWQ#E61)LmNjsSexM_2
zf>|apIF3_DJr<~}N1Ovq*9zNI2hsgLCX#z1GW4t*porymzV(vD5n_<%ciR4(__1e#
zM5{XEh99R%E739MyiguS2s4u-v6dl4AN1Vnw_rB=gu~V{?+-csu&D_}x0&f35fBvu
z{J(Bh@<QEF0g)X|b;DrHLw!TAisAGh9bNT>4!7p}pdYQOTE6aOk?;IHbt*kQ@;|sW
zJ|;d*hgW0oTFkZ1c|hk#4d6<x`+`Xur4CjQhZsytzO*h~D!<UaPQuc*5YMx)?2S`H
z4Jd-$us~W3#x{9VG!QZ_bbMOW#g?XBsy(X;gIQX80t=#y*JNEr44~q4kwZ at XhR*{P
zG0$+GNM1N~H8fwGA4HRne^ZH|TZyr^CA?VWL9A#QN70Q-uV;WBU(zPw0PG*~$63pd
zD?<icLElp4fv(EBBAdCE<81}j5gGtRK5O0bUOor<I)0J#NGD^A#qVxv`w$(PER2Zz
z4ujJi#NQaeMc?*b0D<sHqt^hy{m0^acTzM}3D$+hE(s27pHnL)Cbo7d8Jh7rs-OyE
z{Iu_R=4<sqH>mICd$IIhnL&pr at M=*?8{hiqzVX8)3Uj4jrfOdQjbRwr^!)ZIIn{&K
z@{I-_9=#SS#aq=Tw?-qf;*EroQ3Tf<!Z19p%KpGvY*uQJ6pk#tPUu&D>LEWV<xWM+
zJyLXYAKRr_$&g5pec%_gKWa<bKtjG1XJ2w$9B^SU(1HJ|Vc~6(tXcY(i-@?m1qYlo
zb0xR4U^R9GWqVQmGJUX8HgW9@?e{~qSV~rJ0F%$L9Qdy}y$iwgHG-+CN8-QT*!4f?
z;0GBAZta~K^Oq7_L5vM)JU<r0)M@~)68JS|5XwPEP+4gZ$DF=Z_!*!=J at p&xZr7b^
zM)_jlnYhr64%$#Cj!@bFgEE-S+Z at ExvrTOI>r)RTLB4TA#W^YNGmWA#ndu?mZ$e at -
z{c3u}1|ujm#@zU3Io$eB%2kriUbnZ?Ad!S(paW(hskeX^L<IX<lDu=~a}U0bM!6-!
zs54dPq91V2^|&L9P0XN+DBUoR+C?$!eIVz$;yT8*#H%(R+rz9)KV?mwJ%70c4r;A#
zAGkmPBTXN5wW;ILO^4dn6}`k49{aTh%Mxl#QY+zCQE|*s3yAAY#VAR#L9al9Tj23`
z6m50YTmeb{AV@!`C2bd`dR;vMI}#2lfW>St1JS0&Cxe=6&o{pWNck`_E2G-^vb61n
zF{XE?);}9j%d}<-pW!DTx!nAdxv#zPFgTUYcLe7eG=NJl0byc#@tjj{*+gTeP|c0U
z;|Z{?evm?@ZyqiG?PWU9 at wgtXm9)#J>}<~Dj2(#xp~#6b+g!9kkxx5aPoFV{xwfeo
zdC_9m`rV(1x;O?VE8^}m^&0ed6Sglx=P*_gx_IEBhIiOJAatW`{89>jpj~+PfOIM<
z8ZBEw at 5&qIR<5mA;PbR!+)xsmVu%Ev5yWs^9b_M0R14fX(6-p7)y(zVtB?xt!`jD|
zS9FUQ;ushJUYyZKCS<Rl-G86W$+l&8Ep=c|G*py0LinbCW1{rJMi*MhJQ+E7cgWKy
zv2>}PA}Qzjf|k^e2%m|26`iNDs_O2<k$l2n$-t|eAF9{PgM81Vw}Q)45{`!~)3sK&
zbK=qSHR6|t^L210>UDkXJt}uP{k^}ZhcXa!4 at uiWp{Ak&CQ)Qf5R?(&gUC+ehA5&u
zFD3BQ<wa at J6KHU85lv};Ek}aOT^A(nom#{sW at VN)zpw;Vj#-^LL<L2#De!r`A2b0*
z3{>YYwb=c0xUWo{kzxDEA%bmYd{Jn@`jUi}bwPzA1?%eqz8^r?y3kR;EVQ(-rRRq*
zs@<t;iR3unZJNuO{T55-5fNte)*5FSn at QO}AAqhL-w9hv5TF~-C6(SKY1?76TK7K2
zY*|mY)YBWuGdiyKN&vtkx^2UipYN_vdcO|}Y*V3gK!9qCd8>aNF-%oU)}EloPSa;<
z(Zeb;GSW~?(u)C(7}|h at Vz-Nh(8S=HWB}9VMcoX2r$i4kJ+&+D>v+9!NDo2K>g?uZ
zu<M0chtPyp5zQBFAr1%o*9R`ltS7?{jm#+ZW+kEU$M<Yj<m8+cw7e<CK62`t#7Q?b
z&DyJ6|A>IBcP}%^m#qSCbY_%5Qq~U><iVLf|E#0RkJK)0=(>7<Em|ZgUjae?%b6n#
zna)ifbBo6 at SDOHrP|hg=Wa~ooTJRZ+3x!E^K{+3|BgiRi`S5|(KOTUf=O82)N<jR$
zV#kQxR&23Z%Zzf+wa2Y6YRhwQr3|e8Sws$3VF;=kJbQHToYO7+<({|vAZ$;KLvnLz
z=2uV{fb3%D;vrTp9`(@Sb%`OLAsAUB=hR>j|6{QX72#{BLJe`VLXJkJe+>^LXYOwe
z|ArK)4?X+J$uZNc9~pfmyQiJ#w2rh}g?ZZ9b at M8iX)mf<(H{w_M2MI>9e654I81Fy
zx6(Z<zNf?IX({y at aMAaMg7-80tvx87jPI=&6p_viMiymvglIS`>{5LyTSPD<wO*XH
zg<n}9z7_G(qNdyW!DrK=w4rtKVoC`~m*8P3;5MUwIvZB`Jsy5ZBWD at jY6L`cb>y64
zOwSsL5`P4P=j;jAXNPoI4)gKmfWN`oVIZb#ib;|M<AtJzN1e<fX_QOd;ct>F>#C1%
zobXKo;z0jjW<`xKkmET at jlLMyj!a8cs|S;Mxd>0)U4Gmenb#|5*yO_0jR2ItzGLu>
zp8FK#g0CQ8P;*?Q*4?U<U}OsB9=pFP8$Y)b16MQH5AOytVtIa_i~6~F><RvtKYGKh
z3L7 at chpT@2qQ7TwN}<uDV;5JR&#){uIv;I&0JoG<**_lrUdVY$u1GLl+Vk;Vxi$n^
zRHMi$34MLJ;%EMwe5YRQZnYn<P=TI_HS|h$l&p^ukrcjl18mYwd?_SCuQ~pngZ`UE
z?8nSlFaB5#dDZ^VMUyz+CBY at g2-pR%3 at exLU3%l`&n+DCp`RCf2H%Am6^J?(e?%;p
z=gy!~e}`peV^pd0WlbJl=WEpEJTx?L`&zX-a|s>F(>1ApO4%WFsDP~Ps&E6OiTbmA
z)6MVK8;*K6K^&O>g(*0DHF!2$nU5m>Y(^r)=IakKRW#x6dZ}ukwO at UP_nb&G9YgQ0
zvYg|3Ri>a9WrLnaA>n`<#kNL1te|&a*5WuR-P+UdjP}9S5Stk|wk!)muJ)g_h+sFi
z3CXR-LDwpd^2|<1yP8^3nt#4#^+XY1zcGla7n7G85zf%}D^Xni?eDOyz;S3<HY_h<
z!jj##wpS~L3#jL<;Lm-vY;I1%1*NA8HkLzqIo`g<d^W)sGykI{Iz4wbb`y&3NJtfu
zSVkO-2fU1#TAvxdk&kaNr`4HXqMew9C`@nVP>LK|$R?jQNWuOgff8VVmF3;=t?_W2
zs-kLmb>MN&VMq{zp2nQHL+=m1L<dP}Bjb8Y*`ev?t0e($Wagxh9nTFNrZq5I7C}%5
zRv$xGHK;e)4%g6~HE>}_=W$o;+v^AG`Zv-d0GL*Q1jq^okz#U>@ab=Fm^`|eXl{x6
zfJMP)Iryt+W4;W+30LVa_ctoVKu|N#J)en_!OKe}G?==~)~k4AJp;vHL>dEmJJ0Sq
zhiE)|@+uLAl)s_H0$<EJ%sok_rGiueuZA|Ule3fU6Yl%3u=t-SR2)*mJDQW|j at 5eG
z0tBZ;fNogYRy&D$J~F0U8BlbzR(p^sA0-Un#h9rl<2IUW#7I%r7p<*<w+j3`8F1oa
zQ=2};5ySI^H!4)Vr$sT0co!K8#xH8t6~#vLONQZ**>4QrRe5Ht#Kfw|7`1C3vE5+G
z*<?9aED_M0X9Cqr(&X!0PgtI5MSctsi1SkP%QH8q+h#QaM-gJH1*Sc#0MdW?V4<U{
z&;%%u<Cq;#xrN;*&C|)W#Vz4Ym7kh!<xV#%Px4NnKk4yColKCbS<WPP0_yL}1 at Y1?
zGYQpwo3=*GZ(>0e3tO1Y`UDJJQ%;qyw;)YtaII#8^HauKoomhJ$?lf at t}$%b*PMxq
zefTC~|9XO at I-erar6}qgV%+Ds)I^}+o=(vpsawF@>;I4u^`qtuu7zzZEsXLU;97{C
z$PtnK=GN52zO!KuVn4I#qC*aiUqXe at ROs_Zjgc-kAjkCymMoLe_JZI7x5cg7fOM*y
zuaxGfa)`G&Rd0%By?J-)6h`BMgHd-Whrfblr{uk{xTk{O=e+m&VLc*HN!lt0IW^3W
z%1<kEJCq_V39;GbeE+&VHrr3H at ZZ;)^4;(9^VZ5=Al*1kSfDXwwm35cYW96rnW^iC
z1_V!1)GO5NYiF=?OdJ%Ne;e_s{(AdH4NlB7M3zal^b}z7BXpE7_;~E`li!M_V8VRm
z-C1d>Aw&HA^d6>hG$y17HLXb0bseXxL>#47r_%tox_>=)?qJl=qs9*PV0hyB;ISL^
zETGhwAmuP}yLVy*J<>+636QOp0z%iS7LU+{a4O1m at X9H{*AS_`Ml*vdBSniuaCg13
z$Z5fK at s}&pO=CwB=j)vGUnEAD8tGk1>KKu2(Y?e1^*&Ov_rO*-V3Qc*W!HCA5Qw}+
zb(un-CmC`H#QCMP$=+(hhqHwJGl?{@9$AgP1P;TE^Jg&~nRIY;OTtwxf#UBJ5sAf*
zR#{f21iR(EL0$Bx;$5JvMP2$r#tvcocmKMCbs6M<4?)4z)g#~$ai5Ot^Nqt<`BOB0
zqsax4XYV>Uf)LZdV9gW)j89(Pu<p!_i^^OZHvfzCD{uZE1&vUd*N_~%P;E3r>dKh|
z?6w+<%HUTg+4$s$0)Fknm?8-U=2XG5OA%rAVOkz(J_D~W8fQFr{V~!!rBRCuxe;VT
zRf4Oz+3-K%JzUeA4L;6<Vu$k<Iz+3rG%K at vCSa7bQs0m4qy|SS74QB~Pt#KHS5>qL
zV>{PPD`T!`TNjD_a`%v at ur>9Vu)$Eg5l(g}Fy~OJ35)!k6PnFtJPj}3i(`&}yR4As
ze$YOKK`<fIjr6kUE;HHAJ2Bu<nHy*?#!j*b5jh?yq1}yfZYNbRvsC<?rJwjVLlQg=
zJ^1{5ZEg+w^)*}E9g{_=P061JwCTL_d;ECp(&TYo%!LqcZElJ)aD at HlFE)kCAq6(e
ziQ#<llkncMJiO+Uh}!}BcprC9fLO67VEq$7Maz&%0Kd!R<4#!+?zIa=&u at WvExJH}
z>hz3Mtay at -Dd+zBX;OR1a3rUk#N}?Ri+zpcP#^uXaEI+^3-)4!?|`TK*AM2CvOmo#
z%CF=BEd><`6hjSf&<s;a5VMRrm!KNKEyZc&9D{MY{}3z&0EiZU*Z-SS^B>B|e`6{C
zMYS;gmxA&iZq5I9tL2}p@;|Va|2+Qh0sL>YmjA8F@}Fn<KUfx4b~Xmi{|C!bq%NI=
z-HO=rsJ_SXXKH>AwumZi%~7f*5`JZe1h?%-p+q);DXjAO#QX^T^l}s?tW$}qe5r!v
z4J$^h7u);!m3_q<1I at 2nyQ`zqc_g!Qd%)H9R3?mH^L?hZ07G93pUc5pr|ZK`!i<?g
z-aK^L5pZNCt8 at A<vs+vgS^DvIZB&Z1Z3SUQ^mTSr`)4kut-(wSbu-fErr9QZ)}ycH
z=EBzNrm2PS<F(Ura|>gZY5!@H&N>^b_zqTT%k#A9d4G={mkmB>I`ZwUt1x|Vzs-vG
zkHw9bUb^4QsVPZBVC%_`B_y}7 at ycd(89}ReJoWSjjv0HzmY$ty4{U9oyKU<2t#2^A
zLxZ!))@u~1<`!vlo3k3tPWH^Huk(6d?1>Xon!e{_!czV}p?rTKPd#nXild6ZQRZ?3
z0^UtMp%9t7QtoP+iM6z`fccxLU$pXgqPaaFJI*@MqUGE0dnq!N8db96!+m*~3)My#
ztsg8me5bj&{nNA^s(Mg0Yfv&3+nt%M7m`8P at 23jetd)OnCZMT9eM59MYm(`ZS(13z
zv^$91SQwC{vvFaKnWIRoDo$v}S9B!`Vb1~b!VR^Y$WzVtxy_epGXaQv2+S^UX`$&r
z&^Zhs$8~o3pzn?ClWvB{n3gksJ5|IjuB?V)RKxFnGFP=O+3bnY%XLYzJdFG(CsdL?
zeK_Lqw&cx=(RS^SNorlb=#M4bck9VRIF1%Erq!eOB7yYGCH!|@?@_%qr8DX2N@{v;
z<=)yHWxr$?c};bz32pWl9)#*Udc##*D@^hW)G-paxOX$7pU^sCyx$g%Ssjvrz5Ki!
zbRp6^2YNZrkp%>atyZ^nV4c=amv)7?`HJ`O&W)+6yNlaZ6IBtKcTnH{?#34ZBIy1?
z)+&yh^bXlVkT;x#PTBR&*A{%9oY0~$u#EKBj at vYRtzT$K*y5oSyz@<w*&ay3QNi-z
z&tI(Q@`sQfWf5!b5i&=;MammP8qt1V$@Y7}gmoSrxrVK4m~3 at PS-vK|EJ=Wz-}CcV
za8yPem?Glyn~?k`=qR?&sCj4W^Q*yTzQi3roD at w(2^Z*p!BnAeXKDKb<7FlFCY%-}
z$!-<AS6;}B;`nm>&+XWbyVG2#8>jQ;FTA5dRj+e!=3Ei=yDM_KjqUc|Z^JI~NKg<q
zr at L_+`NNpIN%cUJ#<H7(H3xi6U%MA<-SjBBId}O6F1?u!E*z3%?Ev!J*njt1MNK-R
ztsnxB at v5+K*A(yk{&<$^M_c0V`=*(OGds_UjodXviY>$Mgs^k+0=!h*(kWMyg9&k!
zDZN=+yrZc0HdQY`Ie2`N;$X{Cd|{5<F#Z-QBjFXl;dN7+EWO9zxJ3#5^?KYw-W!+t
zQ#R+?r5&A_%W3;VwBl1mOj*BF|KyV`-(@#d;p<M%oa!Ajk|>Uz0R~JUjeCQGhfS)G
z<Udy}?<nO8o3X;dQ5ApXb4 at O{vJx&Ez2&C76_zS(o^5PbM$Loir<bxyh00iWqgUTi
z)Oq>GW;BBB8WziQ=)vw#3@&Q&Ox<WZPrpC(Ai`=-EY(Zd)#upJ-HIY#=j>iD7U_Xj
zR5G|Ltiy5XDwE=8r85(?0lQ at i8a;ea0w1V5*uEsr<w&|CuzYc_d*bChE%Nn#GrhXk
z-gl?=Z-yay<n}ncs(7)32iemnVPW at ZfH81QMTdWCFx6lf2^sNw`+o=8|B6p4zCI1P
zM9VAb-B+RmV=`xtyt<E0_a|7A3Uf=%m|rOI6lAU3U;kEqvu~F{Tm8a&*<!%o`Q%@P
ztq5j~=1(6!aZF*2TVClX*>6bNXC?=q=`#h+KaEnG;o936JDrVZTaHlQ^Z)rxv;45k
z<<5*f+sTaj8<5C)ekKm<D?*WgKU6>e)swN{lDVL7r8wLD-P#$#`Xt_ahom>Lj(y8$
z9OT%tKz_&z20Zg^M9vS@>s81Pwd?(gEb<+`Tnh#}fc_Ysmvn-Ea{W1DL+ECo8}ZfF
zN8LT`N`ylubhbC7AUID<4?!RM#m_``)gPa)A;dkrhXMJhErzDv!B)ySn-$_7n2nAY
zKjxM52<CClC2c2JpmDEPRV}|Kuu6&f;2-rUQ&u2=gV^9uln7%W3j~~LF9f7IIwyQ&
z1{gT_nGFHw?XM@#4FbcUhv<BoeY&aID$M}72qe-oA$~9dlX$H`Wb1{Gd2)49r*@1M
z7<Ogz=3sON-Kd?0DOOpr^w&4qMx#E~&=tm*7TM&*;@X at 4&jej)8x6xT#Z~S$)&`s}
z7Xu*};SrH<1=O&ucaA`N&B(j2*?T}W!FEpe2&s*yI+Jl(UrWRJS=jQ7;mwjVae>J*
zw=Ul1j2N08u<)fy+CYcc)<~MUJ*e7jE|g24k!P+wH#@(zsuKV;>8gAEvC8F;IVl5;
zWV8$$l-t?RM6k+{3hel#hYi3;MdZIfH71t($+~4B>m4A at OeGmi>jzgMrD{Ssc5C&v
z68F4ZGt)7HPAbxoh|IO?h_E507+js0C$cg~I}~tA0g$bM2FpWh7-BMbk^V|mV%bt=
zQDn(w1exXjqi|G_=cVX4B$uERl~@HUN>Hc=SZ+yKkIasmn+I~04TSa*Y2>V?LYTs~
z(=9})IZWAT+<<&cmjheSvX*FL*J(WyNZQRsgedqJ<rTPRQv~K?<lqJoQGDoqrn#u+
z<J3Mv!a|n7E1zgD`de)sgTy%5PW{7EHSN)Otfz?wmomg-*`v-3F}p%W6$5VAzd0=U
zN0M+ at jOy2wgz+F3EaH#dHSc+yfu1E1piXcFAAk)+WgrsO3G1MbmcvONqj4Vq$2{BP
z62a#b697jNxX0Kq!IR6V7cOgz#~K0AwCw&e5?&UjM>7FLk28t10{!(BE|UTzM`6=V
zb;y3Dj)4;h`;*=upHmkX99%9|YS{*emM*_Q$qP_Um%*!461L^402F at -5T6NaIgxr)
z|0+<aRQt&gLzOBwIuLqFv>@)<<(T2GC!zP3Q>=op5Hr$>uU;PnWD+?AXzz%k9dzc<
zXA1 at 6BN>zHU)V<(Z8#8hm?Ml~gZm}X*)Gt?_5RW4Ci71w0Uae#*CAeb$dzl6n;0U=
zd~|frKzz1C9EdzD7DI$7nopVmIpUd(+J3)dA2cS^tde-KO&tyc))I&@O(vWo)R^>F
z((;i|qNA1NwaPT!#e;}M#J5`6q0))#6<I8RSS~VoNtNXdX?6}uNTCW5>Ve9itW#C@
z`~eyW<(vt)?a_HJ>o2*ua*YzJgjJPzqe;f-TFPqrb1qg%-ggGx5(#Q{R~E)~Bb_+{
zS~+S>FqH}9WI)x^P1YznmX`8)uN2)<KvKH=J*8dwzm^N5DFc~5({9b#=E;?HYD$5&
zUsc9Z>DPHbt>}_(30vNcC at -8Xg8pkh1=Jj(z0}I}Rb=A|s1umY?zGo<BP(2~uJV+T
zEAR()uA`kge=8&~kJose+(`u$+6vUsUQkJZW6t)lP$@)&wrHLq1e97-t?X_sjK2c<
zXDAC&#eHBn9M1Y!#ji9fVB47@<11;5LmbZ*;gUHfk8AAZT`P3ibWDe{I|tP`OW8d=
z*?7l1eGR4V-7u3;-?-9kb8y?6 at ruIB9#}SW^_A;h!wdrj$2#R!q2bzn8G!#i0JjZA
z+oK0g#u<m at Q4;jqA6hg}kl at TZM??258_Xxq8_Z|<y)Fa+HT~E$-9bOnNsIRLZ7pmx
zV4MJr{jjiLea;xW!WxmD@#7G)8$qrWx*UWGU2QeN(TaWSwVJzqXfQL2sM^3diV~qK
zK&Z^3Pz+dw15%uHT at YHt#EOfT71fKlFwR3sQ_Lu`%Pdm$Ev_Kr3gnm)JJt+qT}zn4
zgsEUd#K2I at WWeitHh6-R`!K*iI&+PEjM(mv#M}mVmr=G*O9tmt7=a<wv;#Y|2tUUe
zzq{H>S&&S&Lu1wt5$ppT0yZ&QL?iMS2~rWRsYZpR4_i~xF3)lS56|7!ahG34-^Vn+
zc%bO0p=ZqS;8C2#ZBx)wQ3cmRtI}gi(&;2kSzN>$MH9|MB>_ZZSm<dX3O4wj>#G|e
z3vQz@;u&HEaL!WxUQkd2jUco^X)JA%Qg_M}^Jb-Mv%I;3WcAESp}=WM1h=a&J&bqx
z`IR1=QUd3GuLn6y3Si_iIkaRmIlSmJZ*LFJ<lu)8h!5V}<GX_$2hVc6HQq~{%?2kk
z#6~Ha)J=0vnc_{KF<6e=83mMFrUf1p(~x9}C0L}};lx^Q$O8M`<d!Pf#Pe|Aih}f#
zSL8DONk{8siQ0v=`7>CB(iZyur)-<R6C!JnM~LtK-0<z|Ww_E~;3+S2wA<z}fhX3Q
zGVaJxIQLotFD%|(`|%Aiv#I6GanK>wejYeoUL1)l at svjC9-!J2C2o+|u*}^7ksAv9
z)$TZC8?Sp&G0iRwtXpZvb9A1NH)1a|^JAXdztN@}`RpR6Q>GhOqjt~H!k9O(wDQ!Z
zKWLD3P3dmWOlez*r>~GN5<EceF at fXP-ht}*$0FBwEwhyV-*xWMcdQe80;nVYfN(7S
zF!b<zjB!YTXYy{RuEOJP-;OkDNcA{?+2sdgCVuCyw-UPtNG?*Vz+(rot9<Xj|FSTt
z*G=4Y497?NJ%$^`J1c(cKVga69hCL- at ysl**Us!1_O;Z`z~gdb!|8H!#pz^DhIgl)
zQAi}P4rCf%6plcF8am53g;W(lefNjGNKPp*dPj^W<KcV*xd$FHuK{KbXAfuVrp`^s
z!fjwn_;${}cauzG<-PIzw<9)#EKhVvDSKq#EVKsE5XFbbIt>VrrIvyDEbKIF|8W!F
zeo-?->J5w-MxJ3N6*;=`lns~NwAR{>EvLfdfp)Ta{FOC*6~{FNq`r;8(y2}~6Q~BY
zk%sM)pmaL)=duoNAQkk}Qt++_+d=gYP4M!5)fE(GEW}t%U;k?jd$NiEyK<bB%Te()
zaz^Z7_A&y3WUsYvhwwJeAh>kWWlYhefUrW(1=E<#SP~Wc6w0RU`LV52H`;q~8!=om
zcDoOD#VV?IjBkX5gIfTF-pm@;AziSn7bUm)iR?Q~b?Wy_f{8?sZy7MU{L=YoRN$Wv
z69psO!txoo3<FlUta at I-?@q(M$uxXW_FqYKyV_^IuTNHuiG#icWx{qWa>2eHmk|?<
z>}(?t5DYu!+*XK4M9$tz+nPs42*dkZykHE+m%oJ!^=D?sA=aO&U~zT#ZXcP*NAK;k
zaaRqeR})gBSrPAaO78cb1bOXd?Uyk*k@)ajrd0AR^4Lx2awRND!j9GV&y)MVd=n7C
z4yQk*Ob at e4KE;SI&b$i{J65hmh+Ui;31WL^bPdU3cikgG(5aaf1X-&QI^l(#GA_Y!
zUYr*Y<|(`+GXw=>Fc<h+&0S?x+hT)ro_w_1grZqTh+nGz2Y5h-zxJF2dF1{~UZl1>
za+upzM*>$IokeORpxlo(!jI~Bv=NS)M;f6XFFVo*x0>xrBP`eQNF&rUYT&Je{xX==
zM;aZ&mPRPv)VsSkx}lDBq!GBJd6(KK;MGQZeS6ZsuJwo`)Vw?%al{9&qlO&z)n8~f
zA93VSMJ3wmh?T~UIszwJd(;ssRXgG+%Ge>;E^}KR?J^ho2syFp(MKqv+xyf<I1M#7
z^bzjjP32d%JOZgE?=E%Sp1cjWC*Li*C*M8)>_miVc(wVyhQ?5V7Rd;aXn=lyz*$1A
zG*1mz_WrP`hZRCgdUg`5M+?nLLr)SfPH4hG3O9GhhDuW}z|Z>Xu`U4%Sr6D+3?{`>
zjK>XS=F)a++^)O(+?qliw=lr1<a5#6R0R+tz+Pkf;O!cxH_#``Gr9B&<w(&h40t2H
z?zk-Kcm0aw0qirj%aO($$7LH)(M(ksIM40<bCk)Noa%{eoIMjx%h!*B!Nzh72#XDU
z?Jq^pOlOr6rC=5snzgToBB}m>dMGM|-IssAhHO<N&|5~~Ad%|Q-*eQd%rF>dILhg`
zj>adwMKDVDeAn0L at wFz~9Dm<~ZQqBJ14w>dt-tUDO$OH?VvxRW9{T6!XrGbgIvfo)
zt9T2RrIBeK#NF2CyPQ|RB)<&hN-9-;U0Hk_su}WXW0U^r^mk^mgbwq4ab?bZ{-_z-
z;P+0I^<ej(;-%TU8OI?*7YW1X8c>Bzh5~)y22<-z=&cTmxHeJp9p=*Zd?N$KA`}qR
z+2UL at jzblLgu^+bz{r5V$2f%HmghIQW$8LSWeEeVj!}d9n1|f3DsNXY4;3dsG}t6q
zt}s}~@cHc!&O^re(hAf=hmRzqXvp-D=R7)@8<Nw>VL@~Ce at s8TBaw(YrN94=lGqIm
z3E<Qnf)yNfKVIa!3v>M}yUK`vvqwTr>bf(TwkQ70uR{JW0aV6|=I)3~p at 5R%my at u2
zdBvZhTo9-f7ol`VWO<L-6bEP}+}i4ad}IqA#QI!4J}z(x<UfC&88+zHrNev2Iz~7^
z%MbLM_pa^7;4C7P|4}Oli(n0>RGY`}p#ja`4Ink0N>or&ARtP(utf-VJmE%XvIWc$
zuyP=uUah?AJHB#6UqY*{U_<VgJ5IJNvSlaTFYf6nT?v!Dx_*nD{TMzpbQ#P`1z?h^
zo7Wj0SLA4m^Lv-8r;{0%A9ljdxUQE4{&-B5d%bXm<kx7?MZ+QfzHE~wPIvO)_Cvq3
zF8Dl#Rjslc<H6m}@%W%2I`?~ZKX91nPzUio7QTa#xsC>O8<^$<bvToVR|`qsPl57P
zA9hWfQO^hCk7AG-Gj(|0bX5mWVTZlgDHaY at icT?^UW{%raacyIqfk!2fmSucxuH!J
zBaQ;oeW#3CjobIdBeN$x&dS%YyrRyzYrb3#rbs_A-f@&kbQg!H#`KYU^?|0VY(r)b
z!q-X{h(I2ta#v>DL at tD0H(q=vKq6$sz1;0*)}#W~65o|cFw`@<P86iiTVz+Pm#PBS
zI$^4D7P}_Gy_CDz7;bXG*;6k%&m7(=@4LUR0m8eB0Q%et<83{1k;5+TmLhr4O`H!H
z3Yh3Wo&w01=X{r)4s*uGopdk}J!>*_zm0u3{nYo(=0WEr2QN`apL)}r&#%4m%=JII
zt0YU79E9!r6m^1n;n|$$Z>#RVZ9q(s-G7x2rNaZp62fRK%{5QDllCA~K9ha-2=$oz
z^|#T!7(~g5)!ptkSe=ioO`1Yav3S9cjOz>M4uEjZdF2w0m;|u*>f$sRkbI-y?iYvs
zu}n3IhOsNj*^Y7pvdjx|xO<PJAF0WDCr4;D{}#r-#_UJJVZ!n3h0&{nocqw>`@v2{
zK1z6oP3{13F7#`#ozREOek~OTqx%3^F4l0}8AeM7e7q4ss0Xzxx4&20L9CJ$)AFfr
zRFZZK;Bk`Di?v4Alr|Ihj^ZO+c~{EPD|jZTuyfLXWV6)v{{dW6oAAkO^9^*Ze<qz}
zlg?q<(ew!0M4tEwb4Yzei2SUU|7SL(C^+W}>Dby3%YH6KPky_HyBWgdjguT<vp%r^
z6E{miBYGFdR9YX9ti7!3x_Yswd2m!IGH{sI<m>-tJRv9GT+_zz<H?t%9?UT0|J^~a
zZ7W&V_aaZ|Fyl&*d+Q%Dw?ia7fC1V12hg<-`}v31g{<NpBK9c^$SDHwDH>Z7Y3XB*
z1bh-la+XAL8iGLNG!D3vaimV;NP3wj|Mv7Qzlt=!2<_$*aXTZ(vrxC<XWAnTlXbRi
zXqs9{Yh9J`1X&Y^97SSqB0P=6eYUSOHHo;Z5o8i<lh~SOhShzI?!s&n3RZXCK-1!E
zB!)7=+DJTlb{~IAKHS`VW=Pykdzk(ob2DmhSR0skO^F(^cI_#nNtO6HF2k~0o at 1Nn
ztH%*!8!d~;k$65X>s#aJo`~fO7VAr8#5OX9(~-EDGeturr9=-N?IQ_T-{<1ph})?#
z{7xcpJeXjAx#iI%CRh^PE;fN{YS<GLAFf7Ev3tZg_q1y95vR{DH$D=R at fMtq<g#6`
zqF5h^hxO2-)!5C?3(<O$7`G$#N8_(_KeFq-cOXANOmgKF=rK1^B^|KDt2!V(xL>&D
zc3YbckE4m82$2V~vHrhbzhz~0gm6JxFLRa(<Gc-2rTC!FDz#G)UKyN_jxkk(71CKu
z1;h(sRuj=azp(08njzgMA^GBlBt~jc5xDTQKiJ0g3w^9aOnAi+X)2*=a73Exo64>6
zV_oV-T+j7K-zc|xIQ-vCQET#~y}eKxEr at gy60bSF+!;x{hgk@<MuIri;s0yJ&HHJs
zS?lfYNRZyGN#M(sc&-+FBQZEF7>N-ojKqA#ov4iv?&Y{I{11;L$b7(RVv;0Y?${Q-
z at Ix3YFmeoGqL=U*ZAzc0v30kwN`jD4uXrVm?aIU~)p*3FfWR(k{D3gUFkwx^a7?->
zYb3=o=`kp{45zH7Ni)&S_dM9dV=hWsip;I|9D)pcl0M=&;7f5%dYwj}ecElKRfcuS
zdVzWRtSEdzCo}O|5Ev*F32e*gD2Zm?5f)0fY5q={K#4^lyd$p%L&vT~Gj18dMbq4c
zZ7UjEL;ESD(|3X7&2zupOl4ee)Df~W!whg#xrSS++yy)pc8ORSQqK_~j<JgCLLw}F
z5O*t7I4e!&0wT?QO}}`n&+Aa8XwynDdZ0i=7Nsw5jZ6Q`zaSUPDPIv at d*`YWWO04b
z|04O-;u^buL42+J>Jy1B?c)?buTxyGeIP@$3WqrTejcKHr0tH<ZfQ0>j+PZbX*C^)
zhINWztqp4cF^M5%mOyfv`+cR2%xN6RX at Jye9EoYx08_*?0Yv+3NSP*((mqeo^w;Yy
zh$;xjWF at Q$BC$8IP>?BoKz9Y<sP`KRGNlPPRY4e8mr_CIG2%3R;L|>oit<O#-)X!?
z%##AbAvi4rgpqS*3JAxP at -eAO$0S~kLOM8X=cIIS+{dWWAqb^%a5&mc<tVZ7OdzHl
zGJ)}@Du*n2dsPnIQ%mKL{jZhM5$6l(U}Vuu>EL+mox%`F4e6N2UXG7A+7^a%$UTl#
zwL@;+EP`rBRElZGM=fCu&|OxSBc>g)YD?*m+E}wn2P2y(r9-Oa6k#X_GwG0{KjSq*
zI;M~gEtgfLL(`ecq0i0r?Up#tl?YjVS<R?$#0nuCu?G{5>D{~Of}FxUhAD at hbCeNB
zA8jayWXjew<<L~#RF0B-RgRC#P~}+9_p^d>`R4m(qVZoenX8%Sj7uztQiQ~`k|H=B
zb{eAyMj*9H5ghF{gCbV5hCL{P5jra=VzClGg#eZ)cT$FY!7bsT*ND4zas+eVqbwF2
zvBaFU<OpHMdy*yAu$DcTf{|&*5~kpA?U78e<R&Un2yk{X1;@BTV~P at w*wZm{t4tv?
zSeK(rp-bM%6tX9)U1bW{^=!!$ddf-^oEW;v6C9pZ8pz0O at WhgyXO<D7tCTz;m)U#e
z3AujbH|2>a7vqVKg2ET4XC{6NVmzTWvXdyJLS8(<+kCD!hzljNXR<&!nDK;^%%#_e
zyHE0jluT=tCnTB6H-QU8zivbkalKGS$chXz7*oU=!4$CzV+!5nNgmL1yn4))ZK{uW
zT;UcnWo2qaAvb7LqC`=$uSD^28z72lvY0oN2uOfmTJMDfCad0fk-&`p8vn1^k_09%
z+Fm3ejoc_Cu)bU6fW@=T7YD5G{p{C>b7$dzW!-vmz~-X4g#$LX<FjzUjx#t1Y$n-}
z9Ka#9!U3BPbzUT}c~i5H0LP;j63BSOrhqsJ at SJxS5|B0biv)I9ACLeaV;85X$2>0<
zSk}}z4RGp8FA%v+7Fg`@iWQaXTD!1-+$OzPV4B1^3wXUa3%n}|U+k5s_$`RDzzkLZ
z3rv12_eBFsQ;;;kMMJ$j@;sT-03W at b*N8)_(7-Y$nl!Mr$4(m9TJcE(9 at i^%gsjdm
zgVTVoku>1Da2k+ZW`iK{pnUZBqP>VhGI0tIaIUOWod-CZqZJ;={DTMH_W>S|&L=yD
zUq!|pf&3I?Xh#X88)*%#h0D1F4fY|A|1g^)AI<j0?asDm)2@!#k9AywG at c8@dO3sm
zVj2yOfmL}9%fMXIR<XDHH0)v+)gIgv?;HbYVw-cL&1hOo++@%Dq8v{Lke~k~NbTol
z8j<cN+*$x(`V1*<Es!vMm$&s)(-%NYV at R1MkeG&~Rhng*0Ad<L$~1xGG)IZLOyfXU
zP($i8juZ>ZQ#Um?@$3tt(!y~O7Kxm5AuNn|!QubaD1=3JPE>`3q-I(Q3-QEHUaQ38
z_Ik}E?{dCI9P1rbSxA<eRauBv+dGwoWAKnE3&(y4Qx-;szt5!u=B88 at j+c1~Qx-vP
zPi5g)_hiD7;fO8mma-zB at 0qe93!YhJMRs&Y%8KlHo|F|m<Epa4dAQbusjQHkrRYjz
zHgtvLeN!31yB{-kMJ}_Ssw;B+hf-IfSWH(w$_ZZ-Tl$-Tsfw)m^i&n8kGZR=Fhb-O
za-m|1WRzo>s7S$>i1>(eT$Z*K%&wxM$@ER&`r242DiPQFbcC$L9y1^+DzQe0O6<Z!
zMR!>|rkvxXgy4&|i|lA3R*H(G%Bs{vMUzP>Dw%xMl=oezq!3Hl?7^5YpX0-X(zb3f
zFEJ62gttjf6Wm7GVVoxRw*mN at o}Dbg#B|q-B at Q=Hr?3R;3nkS{eD+HW7@<=Ju)Mfh
zXrZ#WyeNXzW%WW4tS;vkinx4$`=W@;2RKO)EH!Htinx4~_M!+z_AL~_v9^xR5gCuz
z6j^f#mf(1`!V<jZtM`;WZ7L+eyB?#Egr71=!kYmniOa2JF_ at 9qBneLavtJ`xFC4*F
zX+58F1mFE$g(JLHoFm at Vge9JqnfNV;v&1p4;4H!QVQspz#Ifh&EO9tzyO2O0%xQuv
z=C(3|OJ-4_2`-suyl8^cIeiyM-s-K;gva$p9U&_-%-}TPYa~thE}SN$u8YCcb0lrx
z9BZ}Lh+)zMn&4bnr8-S;Hfa}{$ozvR-uEFy(H}0l7CiCG0Cy24Na(c^@zzt2J!~N^
z1&JrlBAus%<3Tpic?!}xMrh|r^e at -djVxIX!}2}FoM1|hGUxf}pZljv0F>#Dy1=KF
zAt?azI~3j>&eQQMqOfqDC$gO<=14Ovitk>l#Klco)snu;d2*X8UY<?coK+43))RQ;
z*OQ!VZ0Grv9vIJ(lXF+U9_<FU)!UKl(Qag0y;8j3^|5+4aHfgKGtG&dX{z5k=<J#1
z1kN-Od8Rp$Gfnf<fU3_lC$Np at jy%(x$Tgl$%HAaJoqr?H6ykNQC$R084K)RkKH{Cm
z?=u+KMFmi{VT($sezit9q5^zvB at m7HEwV@E=+B7dGu5Lhz^vy{6-27dqbk5Ho|?<_
zk9`EG3L-r&t8*sc=toolXFQ at LDvX4SfT#ekbdBeW>)Dec5*5y1{fLT|v`1CAr(s7`
zxa+!(s_2yCQ58gTkS|mPnF|93$+)TAqJl`x`C~*X{iup+xgJ$v^+!Ica=S&U^4(AR
z$13Hk!xWLKs72~g6*dUpq+3-GDRWy#Q_J*gHs6y;RoFDMKO?+pdOND3rkRhbD4p%7
z3Y(^E?VAsNOS&T~+DtP at Rqh&FRqigND%$0>mOMwlDx-5W8y_Re97k1{tNw at zOBl2Y
zQF+r3QTZ+hMWv<VSFZbPpYGEe-z(o<>G4R;*R=jVkt02AYl3IAIekTbs5_4PD6j5#
zq=gxW?f}=CkM02HIUe1)k)u19qxGvh&nna%>>})Z=#K9?*}S at gQ-sT<?l>+F0sTC=
zIKuOl9V(AG{UEJ}q)*^*eMBCTK9R%q!x^H#Tz>**nut8poXDAGy8gbK+4xMNz%$1V
z+$q}Mkw50xfulMRd1u*?M|Gz8%~)?QVN~mHFP+HgC6DxKx1Suh`S=*e+hIgMc_f!{
z`)PWGd%ykUc*bRjemarUPZMXm?Zid;Z70Vc&4f)x3$vLV&w6{wq24E_mrgEDFMVYP
zz0`1<|M-RVF~};k0g4oX-#0J>aoZm_QEWihbZAY{bTm}{-|l-d^VWkzLur-*x$`?c
zyu*rWU)WZT;Sm_l-`tAeDq0Lxv;H at yso{LGd7w;qEY}>t29M(s+lVY<at}0at~1{b
zhd_g``V0RY$j7>NGEMP0bAz1A$2lB179zQNU^%lR0ICF)B>mg><ERv&WFT*F5p1;|
zYdS}RW|B_X{Xz4<W>dp=pnBWm-{1$Pl(Sc^<tg`5b2zyYB`Mqr5ADrElzJ4iF(;=R
zfPcTb;}<#O1J=-s)7!;Qs=X`Vu%~Di93``P)ebol(~5TIP7CW79i=EU8 at 4jE_Z{f|
z8sxu~r^K~JSK5|UO>Xd$?1zl+wFLA{VCyyyWc{Q3@%i);n}vRdb1w}}Db_{{`Yp&)
z8*3jBuRnI;4isZ=rML!zeMuEyMPes)Ol56=(0o$6IsMEzbd?>05&rT^U^&-jo=y1o
z!BBnOTFBiS=u-jugniK_t at r~Q+3N}PIr)7F&dz(=A!FtyBo_ at IyogKPwj_)WOXuZE
zNswj{`DlR`(^K(?w8dut%hV72xrbNa_XZ|8`1il_mnTr?9RVsJrA?YXtPl3VX>!}e
z)_2}(PafQbLn`rwb+gnC6w~*mR2KG?t{B|YK5Z?Lc^11Am1?YU4?n0;VyO|8Tik1}
zx3YM6Svm0M?F<$At5ts0Jqg0o at nC@3f)ucE4~)iJSx{_;Hb_7<3%0xqFcc_W-OWc1
z(|REM at IzOf*Nwib5>-x}(M}x&iR~vR9t2>V{0dJz$PA(*b}4osZ#-X+_dDD@{w#jL
z_6U27Ml~JhLT-v}K at xe%n)a0<@2hAsY#)gp8g7|on&2 at UDmrFn9b{B#$<k7 at vxRZ5
znIQ}58ATOn>Jh%j9QL);@hZn0rr~CRxwhp%oSVirpvWZI`#nRi0jt_^S(G{y8QqGd
zC8-oHY|QElOWCa+0dzCXfRZ#NvB_FwA at +h)GfkDvTe`lR at tk~t0Hz?>8fi;5bZ<*i
znv<9?H5DOuv2Ka(36h#gxauB`92Mlrx~cJc4~n+vy(tQ}X!>OJg?JTR2E?V1ahb~c
zNSZv-0t3R)^lI`r13w)mfYi$607vXvrzrF%IAhiDvLr;d2y0P_O4aD9jBeD(B?g)c
zDBF_C3H&0^p#%F5&Hzv~K$9KFG2Mu}oA+>;hoaG~>MA#_*5=DS9sJV8J*BC+AibPz
z2o}PZhIHkESJ3w~7pVm==|bmJqB-fhvO1ZX12|OA#gozLq7~)PXo3LqHgwUss2sh5
z{%k=c!3I{t&^24pOEe-@)t4Y=WHr|VIegUqexG$+^1%5;Og(MZOR+mSIvpFGnJ at Hg
zl+BUCc|{tPH79vZ9yfS<(HY!=`!9-TN^6&2i^ff{b4x)dCJz-AL`jh2W`91I=`9)6
z1-W4pfUN%A)ZyLJq6f*H9*}pQLToCZXcE~r-eMSk6!q&69nNbXyFaAbV;3D4Q+M-{
z`fE-zuSsM%E(aWtW0-{&HyN-qjr5WM)d6$9-y2!hVJXU!R<;^>th`yic9#~Jrqdzw
z)LO~5GBNA^*vpb8p*Ce)yK;g{s-$ymf1MUmgtP~bQ<8uVQc1>Ak2P44H7Or$=$68@
z&~W4f#W3aloYRay=VRS4dEM*<Ym2}lZB9y;@1^U7)>N+2KX?(%Kr-xYM)Bi)r(Q7I
z96dB$pARY5ZNfKPXSk^f)I5;1VC{D;FVWVr5%t&HW2s;>luckh?%(k;rcl?w{_5da
zd7bgd-7*$^XLHL~Q}s>$H`n-{##K#kmHmue!*;V!a`&Mg=!afqOR at rENWq5+(o0CD
zpw$tQoE2y--XhU!I!lTB*HSnYzVGh$7Uh=Sx29Hukjt#D2*|8ji*mA+Y-p)ox2;)G
zTuw~~!s0S3edV~@;hV=${i+O at Qe^Q&O3KkF+r4%DdrB%&6ky3#X10P+Yo!L<sd=}P
z<|-S<VH4YvGX*)k^G2QTGrp%K29C+)BwhBh_PTJeqWk4ron-H_da%$o*IliL+#dG~
zN*gbXtn2`ixURjf^y>82!4x$FJyq2=HEwmrT`%*`Cv*RZ%NvYVA2e(vbAto#llPxw
zK6x_+6HW%sgtHK4cJg>m$085kU%ST{e1II-o!!TKy6*g-WX(pHHG870n%~mk=t0sp
z_j=U2)>I~w#_JLbs{i5|^Lq+b#=rZ$Xg>SCXr0-@>hrP>_GT}fIlp8yX!Q7R9X#HU
zMR26J3mk<!R$j}KXX8ba%nI_Pat!f(8>Pty1|YAY!?t2L1KkPBRJOVUwdi3rbu<~2
zF$ZDk@&DE0tfgGt?d0nR&JP(LzR9KgP;QZ-!<Dd%7CIa)ROQ at yR|`RKzgkc{kYnr4
zZ2xY+NAxDTZGmNqE1v<FPj8T<<?D?3Qc;hRq<7f|Q+Z&g<UDliXHT;0X+&mB%An1a
zXt6>T^TevGZ3-qMe2>B#2P*XuLj?8 at 0Y0GyWW6KT1DAB*jPl?`!)xBj)ab01Z%v~U
zHwO(5r2Zf8*F1%?#5Km2{Z6bfVptd^e0oP1AzpOTJyk!u_R%}w#JceC at W6y5dmv5D
zo=f~ekFx at 9cQ|L<HOrcQjG;G_cGIORe5Z_bm84b$tn<2bO>QNQT at Q?g8~1zhS+o>n
zXcs^`|M{*6Ep9w?2Iv#CNaHe2;P(db0y6bvf|#EG@>?~c>Ond5xy54 at 0_9LtwXMwm
zbXQ4&Eh!GWN*w{rU^q{0v+lobOEM8e{@<%i`2peCatJsiv$q8Fs}%)Yhm{=xNCKV%
zYxBm9X|v}5wVE at Ye1tz+Dm$G5G+651^qS2Zu9}ni2)xq!1vx=oIoB2Hu%(~Rr7a>L
zCO7n=V)gXO+^0vI1Y!klg0|HckDIq$D`*OSeupZf$Gy`Bx7`0YpfUYJpdFX412^DE
zYE0yQHl7f;1g;6o2^Dbk4{Y<EU?G4@&wTdA=yN_V!s+wuc9jC;KX*4B?^@objR$He
zL44oeo5I#7k<EO|@^sa0<Pv7W)TbxIut9$V`plqi(_>2|wOq%yv+XU0rIYgTIG2zR
zz#_zH5BWhQ=<GCLyxA9-xNvK7Kk$TbdR1SXRRL)&H=Z#Pi%8Z)e7j?wBvdmf<Nlq?
z0HV9K!EF6%TpH2Px8l8W>PxX*Z#3Oc_m`mSFtatt+1?M4hY$7{>g?U{5`Ab>zn7|E
zFy!GqleD&-0jfF3tSG!=sO|L(nYD*QAY?U_TSWp;5Vn+jziZbTib1t;dWvjm<z222
zqk1Fn{cb5;Hsi9qUZ>bi%bKc(EM-(^BpSGafEf)<yNYbKmVPzecdZ$qC4`-htwS+#
z&up{&oXfF^n0aH31Y-8Z5|a1KR?pEOCPits3c0hUN<d4pTF+tCw&iINcQuwlYIL=p
zuy{7L9_!M&SB{y=>I at ex!n+Hp;CwzwPp(Z;M;Ei4Yq88s(xj>a8=l;^dPZ}R!dX%3
z+2CpG8T>=KEK?WdfX<QiSejsV11*_oG>kywyym8$#LUHE*7iUV at iy<@SKDv%2)~oG
zpeLTLS5iAt9NCSk@*8&d(<3FABORF?y5{3qz=tYlK9o&r>^<`~$!12uMruAvc9TLk
zwv1sDPp*AD at Q<K<$iWH<xHw)OprqiSUt9{G at euox)~5rLs=R_$GU)`XpmDU$usNA`
zC9|2dlC+&@7Ao&~dX$>BR^nc&#ziA+xpJ%*ChxBI?|lxz&B1J4-vVn-TCL;5$yio?
z%#(vOy}oubmdSh$+U*yk2~xi54M!PwH;Tn_iVvY(!;RvuTp<GP)4ZIGI2Ix|aa+Nv
zUbdTJV#AZYt_S~I7!CF34)Gs?4D;)~6;qVO+}bOE>oYW4zIPI?u)eRea4F7vJPQ`G
zVD1|Zk)&7PUKRG9<}2>fzO1IVRjr|{sn%y&zeNko9>_q>zTe-$(9%@nEVg|61YL at 7
zNjlRq^cwqygKOy}Xxh~D>9E>G9JhROYOOI0$Qe$<$<yi4ug5>T)aDfV?-ul$)yR~G
z9{-%RV2Pie2NTK>m|^elttZrsqUiyPXGS|x6N0LNNp<&ejgbUn4ak96n|^oH)gs>n
zoS<Q6pOWU+f+gFSqg=@NeO3&K^a2^$XPaAvvne^aj2rSLH^Eg{IA>8zsP^Bl-?e1o
zvfHzR;!``j5O>rN7$|aXu+R(?{~HwqD#n{ydRg&)k4Qa|TDdp<gd|uG9QRjA+mgJu
z{*=u1Te9L?#CXK?FGx#`dRkBo5)SQ<V4MOQ+Em~cw}6}JckXEr at bT!|F`EF#*6$#x
zzz(*VSQobK>qN{cmkt<rz=q}oOr8NrosyK^Xj0sn(khkOexQ_IYd=^Yh41eZ^We5q
z?MfeQKQ{*Y7bG=Ll=Ncp51#!Pb|fbjgXH4xk`vSS)-Ors#?K$w^1}=oq&;^ArR4Lq
zRoY)Ag``%Jje4n-Jt(+sB&}Yty at Yq>oT4$Kl60V(@c;`Ocr;>Csl>W5#^->8f&drc
zHKjr0g2EG(N}?tF=mlm~sNJPxUp>tj`exqPTw1A>e-2a(Wj at f;D&e&(FOUpFVoJ+B
z^`tXH0;Amp2BB73vSU^Bn8_2X>rGq!!60IJ1ClGU-zX}wo7+-!v6vdh$Zl<XA?Zs6
zptcAJmO5Z_y}bGDcmuixUQIzjf35nG!=CtC6Bi at 4-5-n_&av~Ja1oopZk at PgR_?5_
zm}7i1w5gL`-h3e}nf1l+$P?)!L7&zO%Fo5BbJ0;$haauD?jT!i{v$2z%C4|uu21?c
z?p*f?vY)CI0KTeIFBG-37$rNEXvPDhD3EafjWwu0k}~py!n0>jtdOGEWJU)uOUaKU
zn*zED&hCV55jyf2p0#?S<2qla;)l+r3^?)h2g?u0)BD8CXcco}Z^E_ClQSdZNwXGL
zKPuHT%dSjKv-Zjw*qOVe#VKL#5{<g^MN?JnO1!$NY?X1MmzBDgqBpX8AR}m}_fLkv
z6ZJabta0KZ*LqWLSmOj^WWYghiL2VnFyY%OPH>MofWcUTkXLLyuOw_5tZvWU3ss2B
zbIb};1z`#~_L0P at xsbG#RTy&u*t)+p7`T>tSE+~2exE4PwJ4gvHnU21UMLx6>q`G!
zRiv}D7bBOmfL=*6g?J3*$4h7B6C+avJcj<6OOsbd--$>9QnIlUwh7It_vhV3Q
z&HJaQ9mB&uCuWvCt*%@zse#SBa3nbk9h<5DO_DMF`6J3dRJEPj9}K$$a9>1D#$~UT
zt*6M0n;8Q(Ru=B&iQW2JmI!gb>lQiD=I6j^CUA<fDrbQv6Y@?fwY^_wK*jXc!F%_Q
zbFgtZPxv~+PG4vvyYl1yJg^xsxIO(H`=Ii4KUik(Q at 1>Uxtcihc4UpMv?4q?<Z7%T
zm*-E8T!Bzcf^62C6Fn06N;TQvDK_v-5ZryuL2sJ#SA{h#DA$QuPmMO>?upZ#Eh09q
zZ?uSvJ<}MtPW6SI+1bRh*o%3f<S?kM3!A0|S3L at fvaI0)#z?XegAQESi=3H~E7`G@
z%)d$E*7EgBGMmfPsa^q-iC0dkoqCNby^P5n*ma at 0ET1zCEA8>FZj>~2F_{^fB9g;{
zv6&2iezE0wX%P2vo*j95wEV8~z|2WyR6KARf at IPK^nSXq>J#eSVfywD)*f>Nm;VOG
z<X<E&;ro at MBz*4LkzT^*7;5t-e2|ngieW$wxkLEkjWyOy_(C#Ae+ggUb`d<_at#jL
zm~nDlCxTVyH%RybSM!_jg?%gYjD#<+hEWo}gI~gTNl5tk!h8uIzR~O+=hR}ogpXQY
z>jdn`*>Pq=jO#i-C%pET at CDv|T!b&2S$)2Q56<59621#3;kzYZM#S|ce86kpgfG;#
zF>b;KNm4-iYO?np#usp8N+;uk;nQBm7kCZ3<^lrxGC(=P`u?0~87?wD_orHu at gZqi
zdl?^GXX|Brzfv{rS}(u*k8iBfZ^nmPtG%p`ua1}X;j6Z9)`t)-ds!bW*%zl{(C4o-
zS-oE;rYiifKKRpa(g%NM&`J7WNM<5Q-!H>7U>0xR112R`&7;MP|06-umZYCE;9P<I
z;x}~#f$Htf>5v2o`JCt@*BlcbJ71jVhPTXmbc}`M)(Zyv76!6hSe4X}94d{evZ|&I
zf)Ia+$3^pir)t^xg{q|!9*k7KaqIQm at J~Nn;79Fm60*(qOEQ9Fytaxp5+_k`aV$ve
zY;Y^Rl}ki5;+C54>3M at 4mxNK?s>xBy(2dgPga2g=4 at c#R!0et(*-bnSX_`H6v}_uj
zK>>3!8pS at iW+O|<H9o!x$nmlM$98ojS(e<eeV?LEph(9w=a?gO%&uAs>HZNS0H*mh
zink(P8XAy9`i$){q&@Wx%GvbhBBfVQoz;vOA8wnAEEY6^W|Vu4>CxE;T+ at QWbZyA<
z86LCvl>T5zuCblSSW))drU)D>PS_#OhX at y@6+G5rc$6!n$L76bYwF&y;+;k-!F$Um
zLg$PeaYJ!r&v=P+8CiS0M0&W<>SjfQD>y|@-NEtsQJDzMTe}{)63+ at y55zXxsXOhM
zsYa`sT(L?Eq$nFJW~5pbGF)b~rbuU-_EcUK|G^0 at Q!Xp;UfZ^E-3Jlmbo(dVTIr*o
z!tv2QR*>p#c)MJKe^1RP*W^{BER8F2;2DkZl|}uwB7my{a*(nSSLF9$10%+!^A_2$
zJK&g|{Pm1L`uPk=L?#FFt70#z?jx}6V7HjV5h$zL(5gcFRvX8H*IOlfA8b^rz}kfN
z<a6_zn$~10%cI4yE~ZYor*-7t+%Tem_KFewEwSS_Zv}Cn&DgQB_*q)IV|7(^21v{g
z((j^20W|TlENyDVR1H^Fp!r4}V9jEONVsc8){3!TPfPNQ{&bAW_VMt5tM);heOJ70
zW-eP1M5Sk4Q-cmCiA at g!zb(w#xTs5p at 1)t1Sv_gP4A=XqhiydvKW6FC;LhX`8oaL<
zpgbQ#Ji^;f6A;$gmK8n at ofNem0irnYa6!c5H`q!fv1^5`NOQ}QZ3rl<9a%d%9~pOr
zZFvYdFFJ|XFMR*~{qJAhJ at SW{_j(^S>#Jnc!53anp3eXL?Q4Im`17j}nOF0DG=}(A
zyc}cGKNNS*Ry4QDQ}X9m$HQwL#Xa>dDd05yUGvwk^o0+u7^Z>w&+hHhljCjJ*WjIj
z#zkMuydM at W0e$~-75)rqu+zNyy>NdhC@*iukZkgR<Xr&bl9>4-vdV`K6zGmtN!cMH
zB~}SPh#Y|~uO at jqT&<Dy!8>pe!=rXkEa>4bPlqF9a}wpIqsk!yATDB*wKiGPzu{V)
zl#4TZmzoud%_;aJ8lSU|VmN5HS*NY4!wpebq}^Peixl2;Vd{$i#Zm6!pd%7#IuAd;
zYm?{{2V!xs at o;t-SAZq*^320ik|)ClC`FmpR*sn0EMMwU%B)u^@H#~j=!;+HY9xU<
z!;@y}x|}J4ufaH?$Xk!kT)1NOi1Iu?|2=NnE1}0xbuXSJJca2FqB3OISg&X^lCTxc
zQHkA4o_Z}%tc_x>OUp-;&{|A!AAtu5Rs at zl)`mk9dNNJTEx@%H)Jd-#dU<E83fW^4
ziv*Vgw=hO{8WGkc_DrF04xWvO)}bmG%y*joRNe2NWe65)zEiFYFO)w=c3=HI7ls01
zjszDmf`wIv8Lc}I#ML7*=n*h<14*bRUp)Ls!2z!n7ZsPt=iuOa^6la5_cy9*X!9Hp
zG43ex;p&PF?S>NeME1a1;&SWSErxHP at j4xkR2N56hG*t(wTn}vVE at 8L9r{02C*YAs
zR1CE|6a~Pa|DvOvRF8KprIZxWW_aHDf`qL_Il}y=V at i42hi7&=qfH*BRVzi}WrtBB
z!&sP<^FL|xf}@>;n0Jk)gqq3LhA;;hI;ZL<RJeGh>-V2mk+d6hT3we32kYe7>e&d7
z{ulK0L87Lf{&*r$fEf2T2;Pj%0>p|!Adg1e>b{)+8AVt&AL%5lDc at D#(N at n??e}Q!
zijgnfjEPM<XF!cTc96|HEqG{-o!@2TKqAE)z+0n7Oaoe at DTK+wvikKS;$Rf!2)Fqr
zVJlR at sJ?nD*u~Y}3PwHkQ^CtSeK{4p{I>E`aQ6p9lq at Q^>ovGb)J2=OL38<-<Nd$7
zof7W;6wKo(VONyCP6_vX<^f9BMOnT}*cBEEl(36^lvBbkjxwJTh9Uxs5_WOsdP?|+
zJFt%eC5+txRn#yq8&3_xnCP at s%QupZca=V$5_Uym_fiRWJU{a((G$mA!mcQ#+!FS*
z5S~%Pp0ke<_VCSeOL)T}YWNc_Tt4f^0Jw^z{eql|7!Zi=r6P7kUhNbyFtN?)k1xB3
zfr&NW|7-M9#l*yVs+ghMxAT9k^OW%;o~+g2UbRHv?G*AkJqr0;u0p11{ZuMVf9e>X
zJ#|jM=P6{wCEyo at yvU(WA at 8V0A%8~w_e1x41%14<T=K)p_ku&p_lmy`6Ys^zQ~zY*
z`7rT<jspmLjsF3K7{E&?zOTZW$S6_(`!2UmQej}Aoy=rYN|!)XMe4u-dn3;wY0MOp
z+X at b7IxA%phujLcT9;*zZQSnM>^Xl>0#$^xQq^rN^N!~nt(i!8my=+}ty2sNvYnUW
z`)4#BLgXRDqfJAN2D!pL{H6k=+7&Hx0H>;$7rZ>Yqj!k_TMXbq1*wLQ12f*Z+%b%c
zcBEiE<^J~eWLcXFM^Q;}_?|@-1k4q+b9t+>)n<D|9I81-MfFi^9CT<r08l0KD1fYs
zZT6US^u>vMB}JP<|3}t<u2<BgoAv!)(JHW;n at b5Pho?D7Va-HLbXPMCz9vg+a1GnG
zrLHVYv|vwVWmbY#{X=eib$l}qZG0*cbMFwGJyrxZHP<ak%&1teRI?*0IJ4dVJC*?a
zB8$W9Lo8+vP8ii|#^tNXztZy+%LevUm=ZgSK!i~O%1GEdECe#Yo!9xC$|l%I<n9A9
z>*a;y`JJ*vFfK%v!rXzUn|>7liM}sDmes#Li{<4b1W|qXh$~i^hmfFQ%zg+7%H768
zNRTGS%Vs1f at VX8qaWT*SP!gowA-pL`6!;!LrvYqAa{sUB7q$08NzguKeJBYOsUAuK
zV`e{;1l_2XhmxRK`P*+(68N_2p(JRWd$^I306|s{2LT?fzhc|QY(;AMP!hy`wnItS
zm9{Zc2+E$gpd{!kwH!)96Y4`ra7^uok_19d95;eCCD{lHN`l?1Y)XQ=^x6taLiF~-
zK`^DpBW_3caC{*oMPYm2lmxC?J(L8exA{;KLbe at B5-s)dMJ;zzlKY>mfV=IG2AqCa
z>b|@m3PRH^L3xuVh38lZ(r$-Deh$ZIpARJgq)IuI1b{1TQ<9J_CAq&zNr=aGC<zX?
z_D~XBUhHrXnwGzQME#{C!+U9YD2e2UlI#VClI#_ilDH<VAC#nhQj+04zrU0u6qk|=
zd2lF+%QIa{;^H`r!3A3{BN<DCI2Z}UbQ#G23SUOTvtLGn%gbdX6X89K#HG?+M&gQ=
zFC&>r6FC?O;?!P75{mDi!FU+SzzhgR(&P_k0l2cv1BF4q?h at x3YTz9d!Eeak%Um2w
z2Gv1vjj*r8a}|6Riat2@>b&;xbvs}C!B_5YPcr%d#6-H1r>^dMX71Qo;}C2;VkLHC
zUAxN&y3)H&N11S>E6q!EWkw=Lj^#UZZRtM>-Nn566%|G;4 at I3Cu}wG?H6nc&82j56
zcs>Su8Ei!Z9D}yfu}tx>gNz1>G@{21noKd36UGo at BHQ~v>9kZib0<b7?&4b(IkrYc
zPiF|b<-qADTDX|q@`cY<fJ_nrL!K at sJQv+<bB1YM|A7RMJPZ;TGapYR3J~M|_Gq>l
zn;F%-6g6AHifY}LGe?XfEE|t>64s({1s-j6pxZqfWoa|`SrfR5NX`tb)h_btvtx at Q
zukHO^Mh+-a%<(s=r+Cl;TbxKcq%bXi{RsI>i*6RSBC0rO(XQg4MYoEB7VRnyTC}S;
zXwj+SO^c$6H!X at X-6QZ+@uo%h|B9xCGTyZ4l=1)Qu5vV4a~qcX6m<mc%Q at x<^EvC6
z`2K+jfRt42anub;rC;I#f+35%j6)WA8HX%7WgN1|%Q$3_mvP9VQ^p~Syo^Kkco`oU
zzI_ZR<B&yO$03VO9fvIPI(Av)WgN2TlyS%+FXNC!UZ&y4Q^p~SqKsV at MIB2P-7<Dr
z6lHu<gmwI#F;^D7DHH}4vdbbb<d8+DkV6)EA%`sTN<D9hLJnDU*Qrdrl0&(iN)Bb?
zl^nXqOZkB(YBH#p&#zU?ar!Cd__`N!=pwIH*F~q6Ll at m;>LM at Z&_%bHT^B_$hg-dv
zzuo at oqTMV**CUMF at F=6R<w&Em=P#-lq{}wRVNLi;8)er<VsqOuYj*O67=eKb3x=*U
zdS#<Y&?5$sJs_?jSZc62Mn5xTl49#xPMAV9Cpz-xUhXoc%B<fuGA`1NpNjrlp)@uW
zB-y+AO;dg0c6t_W)I0bsDhuA}>>C9kF*s6lHs?biW~oEg)H;HLi`NUq=BvKz6}5n0
zZ+fK^hrT-};W4VZufEkM8!9y$t~QppU%wo5G-I$pGTVaH5!z4>|71v+GS9Bi=C=XE
zO53L8Q}-Q{IIDg2Bv=|FH5oWT`DeAe9`3YX7k1JPezzNMS3aJcL*iu at J&?`h!c_ch
z8LCa1JbM+TP+jcV1N at Dws2<tMj}`eIStH0_dyWY`33-;`55Q?Ig=Y!&@%&PTy3!fS
z$ClgYyHwt$%PcBS(}j~ESH0;PY$8xLE`wwCr|FtuYdf2+#Vq%Eny$f`+S7Cy^?aJH
zL8M6#ti#)M?>d_<ehqr|rVE(L({%AU3rjRzgC(`obdgW}>A80GWf>nm*Wk?g^jtiA
zKP?y8GxO=ONSE1PIN~Z{$m4Weu$V?%c3cutemX8VXhg#WrS#Kp5#TtP{TAWx?etqu
zj^n~ep6IucJl=1Sc{xYF#iwR|`YjW;od%10 at BaCfEVtjj*&_oetet*~ht6{PElwZf
z>9<VQdit%C>3K_#ZS>o_&W;zq_KwKY_tS6r^>!L8 at 76DTLMDSnSlOdrTdLRRbjy}H
zpMHyUYWehAWPU5rZynu!dzaa7nTz%GTO2;?({J%{2>Ea;uJ-xm^{3tTgr%q5hT*o`
zyCvG~-SbyeKTNwlbkK7mV>ulF&UWaa=gf9+=m3VY4jlltedqviw1*C0n7afW077MW
z`~x03c-BD&gyqly4{Hw{@N?tP0bp;34gl-Ebik|ELkE0XKXibHuX^bKc;rI|bW=Wb
z5N<ql07DNp0LbmIK~L5+=EDXs1R3D}x^u7rU}+B<zyPJ at Y(Q8}1qD285C&%hz}$lk
zfLeao0ESr(8{poh_Rp_eIc)HiJYlHiumO%kayuLF^z^U+3_Tq-;C0g1TL2FmJnN`O
z{klcH>hy;V^!39Ax?6wYi5LwQ(LN3v0PcC%0NIh}!v at Tzb=ZK3zpArApu+~wGT1<=
zc-VlMm>)La$Hl62E3fwX<@Hy5*KTjyV1qDh%KM*x;EOPz88&hO-(ZYajDxJ|{_xi5
z9d7u!hDB}pqx2dy^fL62P1r<R;5<V$`%p2n3?(hB_pTr3LfFtj4W^J9Vd#arep}&W
zecnlO>o#*U1Zit^$H-HlJ&Ki`-P&L)nGo6pAxeU6%t)_&3_rj9 at dHPrmEcFFPb@pk
zMbPG32FvLm7%Eh at 8Hy>wAt?`tlr{O-c;M<R0Bf at l{AQTOlcQ2x^MgoHii1G5yQuTW
zav`np$EL&#)}-6+U9>&%G`ooro at MYB>XK4KCN0ZU_B(FIMEvO at Txec}0c2$M(9yli
z)tDi0v93Tg`y%dL+kt#$_eAjZwH5bjbB#F#;b$5o7E9C2gs)@pH5boD2p2yv$%&7_
z&LLC)_L_o4u)13sy1pBJXu#ZH2KIfpl?aW`(AfHUQ3tcPMnD&1l&ZtzAu9*+>8+M`
zy?acAhQ}nUC7EZkhYU}#Y|F|v<nMRx=_RA`;TSgmC_Be7{LnCD=#BZclfpW|i1=EO
zpUcwc$M85Oeo&rN*csOSW<h20oOO6caZE~2<TOuTC6dO!-mu)Jg50_JeF{0v##DFZ
zvMz7LS*(Jrh6hH0|NI+UEMn7NSMmOXx3x$^6C*2=bO5fTtQ(mrKMp-DY`65Oo#mf4
zVaXzo()e1t8iTdP**3G4df39CxahFS;xT94C~x{Yh!O;;)vC0 at G%7YKkhW-I+MXM?
z$KAcQ?H at NR)b?VZ;M<@sw`gWD_x^@9Oiq;OF9vhz@{@Vufp8<YharQN?ANLnC_sKn
zgx9ECaks7Yg57p=hD|iYcZu<u?{3LCZ1Z86DF#X_*NcL5davzWFHIabJ7bWV7Z>{`
zLcUdvwJ_w&he4M8&K91X+kN++YcOU&0wV+T^Nwn<_zy3}wES+#t2c3dtnQUw?^6W%
za<64BTPW7 at AJeNr6O~E~ew*=e`f0??=0WErj+e%w_$mHAM_XX#)^2?okz|B2<GU%%
zdGB%{pE<sko%r{bPdx7y#{u}P=qk`|$D_De{`~SdtK2kzyJ<`WVPZlMeS at QZVBj3a
zgj*kjCAp^ns=F;CB$M<YCtDI4MAn4Q4X*TtWSW+)cNywyIkOMI>W!oHrro_L-px)m
zIQ-Yc9`(S@)IA1!w2tdVd{@nLIDNS%hIW at Kwj5SXyqR`Si{T6<BFKk&2KaK%BbXIV
z at m`LBhMUWDL*nkd^eIe?^sm}y%c}mNo3%0I|I|c;42KDYJpLD2LiDBW&9%ZY6ehm0
zO`E>MMI`D5Pnr5bUJTMM^+0*XQ;zH5N8?wU^$gA~Pb^*^7h3w<uzSqHF^O(B at pqs_
zY4G%G7z*k>1i~;(Z_q4xe7w8lnvA)!UF>x7E7*(a__dywAY`yPdJk>i?)cd~nMJ#!
zC>^WmoL_~4+L at Tr%A@*^!FX|@|GVL;{$p5FFF*g<$nJM?IEFaG4fl*=h%=19jDOd2
z$hE{NVY$a#%ROhSIXvftCFYp0yyt}FPBNBP=Da(({GlaBEGXu$C59PTQdcUX(oKz8
zmvvoR2&WG_nrC1^`Nlv49DE2#3tXCVS0l}9(M$tN+e&B*HTsIKg{g*@@S#a#4KJQW
z(U at z1)fF|^0M~M*$KQ`TVzl|~Aj~#7Sq&whWjNeeU$9o;U5L{SE#Ag3-T<c=E%?JD
zhWC4lb6BJ)HR1rb6dk=2r!_x!%E`$xeC=n)a3a>61Dt&-l^8-M?WC%OLIs}QTrulh
z9MjHg!Mvjr#wP|Iexs>rO6%$t3KK>i-BMw~Ov87Ka<E?BJY8bPEJ_ZHJ;1wrb7VV$
zGxL%}!Qca2T|&eCFHY{=$HIgIR(LS{5P$CMgIkv|j^#%d at 3)wK_|75UVg3Q`@BG7u
zN5q%Hv!0QMj at Spv58gIoy?L-4_9Cbmh#BJ(R^gk>Z&fEU+rP8aAGJmKnuEB<dzuCz
z9^!qVx8jF-2hHuHkAChb#A|NPfTYjLv&sm?`wl<4#`1V-2OOpNC(q+9J)n7ru3o2Y
z6DAj^+Wx-kZ&s*rgr5HYMxvYz?obvGRIdff9T+Py7Xgpz!vhB+Sc2%5|3V?wXq0ny
z-5s+LETu>!U^oJ9Js-jW7O5RdGRuA_$q0=?I0hu((zUJ$36_>bYZ#G$yAN!+=@;=1
z=LNG&w_r*F*1)SViFk-wH7LQdoqRDV5qr=uD!o_=_O+mE6T8U7xTJ3iYhL25ydaAI
zqVGdzdgePa(G|rBaqFQeW~fpg=Z5ZprTP#SJJJ+oPCncM>OweIk9a8MT9gybhdPW-
zzq3le*z<bg&tuW}q%6_Ug82zJ`{bwrir1`O`$;=`zJmNdECf5Vrmkm)WlfG5tb5k@
zH~!0ZwPeYW!oa;x;S=;rK>W at zNAR}${@V%tm1VE$DT)*zL_{F(C at rV2LW{@G2!Bdt
zTu)RHeNJTteNW{a{-DZL=!;5MsUDf%OuCQX`J|GKmN%Cc!ZiMB+^tddT_wewitxiK
zi>)uK&*KobXk at JzCMXaVMmFcI4KY^#LO^iJo&1$O327<<gSPHJk^Bn%YlZzpaxVYs
zZzOEm$1W<Ond`oAB%lc0#qQ^G7v(E)JiRm37Qg90*j5FK+jJmIEAfN%Wm*G?EQXXc
zfn*+>HG9dD$C1pVNX_F&<e5X+BJ%_iCfbmaCy-*IiIx3z#S0=*g7`isrG#VmvMMFS
zZlI@>AnyI7ln}dtU8MxbJU69;wh^ca1+l!QwEU48^gCW7Xs41ORBj6;A-=hkN`m;N
zj?k?dLb}k<5X5co)DXmdjH)4w+>{W6qn(tH5*y(ZL?nb07?FyE at RX-l36Vv$ln~y)
zoT(wP!O##O^HORE;vq)R6e6k`8baG{kdMGgiES_q;gcM*iU_Ic3=xTLF%kLbC!Iri
zFFVW;6A_-ZrG{{GOsX0JWZjz@!tJt)Ld3>GLwM`Yc#WXbQbVL)X4McWXG)0NITyNJ
z;@p=bCLx}{6eMN{1&KA7f{^=o6A04lT_i;IIm!qe*W>QTDrIw;gh(k*N=TI2B;?~T
zL_+5C^~~U27{|VFMf_JC=WO=;j6*Dl&;-YKCQTq7_I5Iw0OFc^?2FYXX#%m at _n-+N
zch97W2~~ay2~1Y;qz(Q8{&3&d2!5jmPk;g+6J_HGAY7T?2`0(2lPM6Zfe*F-Lbfwu
z3&eAcWDCUpNF4rwKN5s35bH4-Ta?&(t02M_oWU%Qu!T%{DqHZPtbe<GF{mY5$Tllk
zpfdL)S0LQ8xXCDOaK&T;L}f(7GwLQ+ at Nt^GG_K$lFpd+hh?X&~_-HEqV)v}XPeF_;
zq)Aqi1-HnHE3nMxxLYLcnMKJ2v9aI^?wNC6BhK2%72GqeRj%M-PG5xfh?;jJi-_xi
zb`y+dU8Wg~En<#fi&%xRg{*QX7sx(dhI6`S%@L2woK?!&)X0KQ(C9?SB5G}9 at o^eT
z7G%<xCzOa10b#4X5CNfV<3$7%X8YI|lUgAH!o2N81nlcZA%gkUDi2H~q!$mMNuTu^
zu at 4m<nBm@?2XOl56du4uGImlPSg{S~0bD*qlm`&sR(Js2LHk7n3#+0+1c*m3M3Axf
zO(IGJY@$~o0-kfdh+xHAsM{BPm8YldbHA8in!$4_U?C{?+>4t`F!AUsZ&N<6xe62T
zdEOTjkV&00fdF+tiof at aGr_y7^oupK6h8%VCZJ#jWdg#5IWH=hGJjG5y5{zB(S2u0
z1yg#oj at Wk=Dwy#!bt+hL*^&w{a_&L}9 at isP#HqkDI2HIDNd>+NrvhGO8U>EW&5N&J
zv=>=$C3fKg)XG}bxd7E1t#E-?+qvL<9?AvmfYLeq2r|z2UEXnl9BE`l`nFfD^a5t+
zN`=Im!(AXP$7s44xSnOV1_#UWddzrrjcF>;J$u?0j?Au;B8L at V(Xd%G$G%M$F|0sK
zXT|(VG5iOIw6vl_dI_oNyeP-3p~&}N^^w=;Lg%mv%9mRp at d7lYyxanb7vLGVw67On
zAd$zAk|&VJbJ;La%@auEF{I=PB=cZ2cXi3*NW4f5sd*eJFH+=g^3I8yUl36k#6 at 2?
za at tjW0pdM(Z2W9D^@Ue&_r>%D$T=<b<wBmHJY9~*>GiaM%tNmc+iau=4B~6 at VFGjM
z>sc!W2C=e{2 at GQ2hY1Xj;kT3p1~ILh0)u$k6T<|Ck<(LP5X+)WUotc<N<h?wKR2%=
z>cSIls_Mc^IwRGE7fn0WMK(FBx}Xr}tW=lFR<_7Xls4q$vKP8#1gz<xlovkEzN@@&
z3+$Wn5)EVW^3hWIMR}$EBw*sgb3Q%Ag&Txc6&E1)`iqjZWr}jdwnAIDV at ykY#NMt>
z(>i8VZINR7BD6!=n5iuh*Yi}wv?Vf_w!|EvEwKvI7FlI^a`HZQDv5s4R#6`*#7u4B
zQdyUpwn#B=YKzy}q~(1UqAV9ILjCvqO*%f_q<5KZ%tK5>iNf8IQw7}A-Y=>^r|-fP
zsI1vWWr{8QR+(bs_U^(IkeZvU&+%R_T>x at -8Gv28a7eFWZ7-U@WuCpz1RvcgG=a76
z^P&kp%w1>#zjGFvz({*rI8Ct1okA0wYZacz*c&SmWeUWz6{f(PFB{5>TDK4d>;EW3
z;oD52a9`j=0d1l3-i*p7QK0tEdW~qk at B|Li?4R=l&VS9q6W%Y*6Yq}FDV~;<_$i1p
z#kO~YGX)xC^cPcXyF$(s$YrlbB?8}AQU&*KD<jY|^A at T=&phKr6(}e9DztdBw?Y*j
z*ArF5slqcjRrnl96}}3m3U2K3-jsbLb?83kY_Ac+<VmRlwX#lisz5dGDpcXscCL7z
z2a`pA+E;JC(}niuK~wJHyAp91%E%fv5n6#`(!GIqI2w- at Lro|nt?K|Wq4(knnTzil
zpRVOH6Iu{8p`7D6=2WnFoker73FUY`<^Yz at x~@G!;X*k+!+Z)_iR0U<FNtEf3H_)J
zd(hq<>|qtJmWX>kN7Ab$;-0VL5ca5^FOkS&NXZjO<QZsM7Te4dNaQi3<OwA55URs$
zop}Or59&zC6G(c{j(LgdwXX<Ei^MlwD96*^IU+5LtUW_pI6kd~ws5Ql!nLIj^wRF3
zEgYxA<VbvZo;8rOR~doKbA+^T+)d3?To at Vigt%}_=3*P^7rqM>7e<y{Sr!+L_X=&{
z at YuUbwM7s!6s#>A-zPn2jr9gsgtbL77(cSM=#;aFi!5r-5EohYxFIgO&0WNWk+ye;
z3zzQBP>!svYYQWzwbzKJuMihK&0EApI$*aDm+Tl7m(QNkFK+K2U8abNi}uI}agh!g
zyNC-T{kV(N5wy~G=FVJPq-)M9BhWQw`dM7GYfcdtEhmMzNY`|iz+K{cq>8AxWCj(N
zoWsQ>SE1sft4x<E`&cP9x(}NBHKLy#;v%(LMO)+ujb2$>ve>FEpT{BELQmM1G@|r2
zqMhKGy?a!FfV7tW^}8UUK;+thiV{4t)raK=;o7~*4-mQ|<cH%iTF4K_%{H&{QxTIN
z&Os~Xr_3Vq!;4t6$`2O(7+r|`@FvXciu at q%g at FE)QiT3|6h{PT;#tB~O`;AY2;Y!G
z)PV%y?}qp4i|_-9Jcg7!fkd7IA$~2#fykmjN#a1}ps(HDNgN6BF{Gw(q=?U%IOAK}
ziAT|@?G#9~6Od(3KWQn%bkflvbgyA81*G+zEp_lVTq|1&@v-Y+EmaU}DdezgEFC42
ztn4VnuYTe++T&9C3Go@(P6%&nwVk3A(M})5;dWXR9ro|aEAn^czx|`Xfc)maU$_v$
z=nGc~ochDSV)_NJ8W#Y-<_oaw2 at C17zhz%v?A;Ktz5nxzZ&TO7hyO;%jJEJgH3CuD
zXanS-<R1_57oK~-OL1w1K&Ak~nt(OBVp>6XO~b8&iK&5OahD)Yo(YEu0iXs3J{DT;
zZc`sNWuP9?<l@~U>U4Fq0$dDD&gZqdItwHp8jda7Y~b~yHQw?+TNMtuTVZe4^az7t
z<DXgnrKR<3O+xlj9Cpzy0ABBbuf$g9$Am{7yNWUGEP3H<Dw+=8 at Mr8PrtbP_HR`kJ
zXa~4$#3osdaC~fOWG$#6ZBM=f_^tp6IdEmz(<Fdp-x~m*sRq_)ntq5<2~m>(oi)*T
zdhr$Ks9!s7 at n`_7cle;VzqC1Kdxf2v4W4=lE|+J>!AaSsG%>$%xTe!mGat$!OzD+l
z2iUY(>V=Xc;w4FUU at bpfg?=MOG;MA<nubfP+4i*NIl7iBoyEq7&x;qBVHYP`fT>-6
z>`pB0%iWHiUtc<WPVze=%ktyv(wB51lzps5_5bld-PMvT*^0yFy^31F3%<szry_ck
zz5i)jk~xR0TW^#hCkKo_vdFVGiaXwpThLi*tjw;%7mI6m9gZP?!Js=8&oPuxOZ-|v
z+h7jto-~tX-)C}?+QLeIL;4+Yh at 8ku0gGG6xwtZEuJZ07z{O*Y8(M7W?D#4;=IQt)
zL#Mv_XB5HO+Y2WYx5y{4O|I at 3wE+hgS7tO={_I^wrJ-zQ)u&dMN@#69kXhra?ERl~
z9cU0W(_XdY>Z-4+v4GMq5E<e`hK+GBl6xR_=_p#eWDCaFn2fRy9c-Ri at T!|ElzcGP
z)>*r-A{N*#VpMi6Urjj}#w_uHgA8;!qw+HLAeYaXn7gq{9z>~_Pz6!GR`Zn^&=-#?
zJCE|DD$<Toa6h#djzv5!*sR{Q88;dFe<@0waV2>PQAl4Z8+_`5!P%z&=jJ^r=^51Y
zJfnu}e_+6ak111J%E+8W?r?Zh%)4Ckl5ms%SIx~ko6fS7R!RP!S$iIVcnBUT>No7o
z?*R~xBGZ6yMSbxs%c%Iy7N+vC#d_wGwh`9BapAnjiqT~mRo-Ng0&vf+<!qJ+;@pB+
z4XtZ+S5Oa93MLs at kX5f3%D~BEcuSlQs%Qm}Voj|SaA>SXkLL2B{%%@5R(<6_yrKH7
z23F2JE?0T&O=-dhEufKGZYomOCAXmRIUveNZc<Oz(y}}`A*yP*iVFiT<#fZlnqnkU
z+_{>sFqFWVCoQYj78#*uJz26zQo^Jl2HVV?71cPcx{7mF9%i*akjq!M`+x8IWsnGa
ztI3PWd?lS59RIra)ZA)M7>d<Hlrprw704{totrDJv*&no1<If;!n2XZyBHVCEci&z
zXX#naRU&OaNkNdu{Po37chaEIqo6D9Z1K1#81y~f5dPrLg{P%HIv at Nn=!>Vco-L20
zdiHz`+T;4<he4nG_ch_w*6(d)_}m8bEmop%ehg^uH2A9|7l`)@BJVGgEX;r=CK2>S
zXyAr;wU)zE3;nt3;f{rZ2nfXmDnSV^)NV2fCJnHQF<rZjF3pU}2DEWzg*G2vMaT&?
zhRhFon_KvE<I<J_V|m(R7<)YiN|p88?M#DJ=(DE95|*=NpTKCgKpG%yenye&^Bi0!
z%-(M}4#OsHrp=(DRw3KtV0II9d at 8oa*BE9BU72jJt(GqNv=ID#7ZD7fKdXv`mcn|C
zP+}-FsCLZ8Ff=C at E)1xEW$D=kepn$Q_i5oxyvRKR9UO>U9uf2UKnaHR^?&t*o3k!g
zd&?5*ETYh_h at D3irW^|w(5h(rbo}>!E`#&w5(bnm3})QEw)}vZXyD{Pp=h)ItG4fR
zYc?t_B;%s_16Wgt^)5plT}3HWOc(DDRFsgg^M8n9{Y`=XrT~ur{xc^xAFLSF&I!~W
zpmB!55{6RyBX*Z at qFivaeu>{Ov@$g<1&d%~oPy(9%TY_`V<rspF6K|5u{oVquI at Xh
z7kv*gq`z6%>(#UVLMhH#@Bb-0`L%@E&{^H*d9rAExXX&k!>p2ZR at d%pkMrKuc3XN#
z6la{r7aXPC6lf%7K+GA0v9e;!1T~Lv96QDSIn}$BgOw6hDXCPEIa(5bE({kxcZPo$
zGRtr9RvS_wy}t%TJ~OlQN?E3~?U6!(e?v;LlS|7YFuP&Mq#WgGFuhCm7<qf{?Yep9
zoFW6RHqt=1y!)n<<4ZB2-l=21{}%~Heoa<r8B{+hmml6==?}N&y5V>jH4Sal=aq^}
z+di*UgruKGs%OO1#9W;5{EqkUyA3 at Gvb_Q#@tf7nht`7~1FwOzDB%M3N=7qOdBEYO
z8sRg|$tK`0*Xp%d>=3VjeyW at 8>v$xxJ~5sg(o`=6 at KVBeV88!!Sny-bsjba-Z8`Ly
z;BZ<~Ct?J{vrO5Lqw|QZQPS7J&`m^=+i7I`aVDRoL%vjFHI**da27N~f1ngD8twg`
zb&juHbq at Lhl<OEt8Hp63C{Fl-_%*#Waj~9-PIV5~o1d2iZpLq$>)XK+;QZ6y&7OA#
z(kr4>b$|-|mL{h>5LGb#a_Z;*y at t(F_}m7nT1Wup1*`&?1$3rRM!ju&h7`(c*N1}O
z{LJT{58Y-aTjos#t+WSn^;qlvzgt4-Lr4g2e&%P*<)g5^kPVeO`?O?`Tgg5xL;sh}
zwl0V(u3f&M_1s+s1 at 1>A`F`AYU<H+l&kMI+RK7=^O}cWIE0t0h29u#l)p*m*7y)q~
z?s;f+_4Wh?T$ms8#T&)C1g)Q9UTQ28llF|`D*LT{LA at R8^*`D*8A9swP=u9KSj#i1
zjmtS+S-<MTyvSK?d9$kOGU|e at LteP%Sx&=WV}7eikDfA#N#ROO$Dsx};7$CIvMAu(
z>ml5PI*_q^{_i89svSKTDA^N|ZOhO!-doGFSb@#8yp62y+Vy{hIDlH5ge3;ZQIqNU
zD=&35GCSock5_vvT}96&R~S;!rPB2t5{s@=2LqxwQYR-=jg0GmY3$gI;YkUO&C3+a
zP@;#yKwv50;TS6CD<v3WLAlX;D7{eH6Po|E7`62$ERg8-FP7;vydV8A`1&%0CW`4n
zY=kish$s(Mv>8GSOT`QeOW#`2px*+IU`OEI^#8Q;MIK;72e43pBPqa(DpXh>?6$8J
zGeRsU at JIk|AWdLCqF|{eOY5E2if>GAHF*VfDd@!R`@uSNjs3O44sM=q>xgRSi`nH?
zExDp7v^$vo#@^4xjwQ1ZUiSHS!%6A;&7T&P#^*<|d{{ux<Lj${j^#ScOaHr}+DgkN
z<yr7*TnWz)Z86-1ch=ibvg6Y{#Ddj;+0wwH5%)bFtS{#LTCgxLvE>$alLv!>kSi(;
z#Ri?Utf)A<>&)RJuQ7CHegUj4+WJ~h*Uk-kUQ1ZbPD_<nrK%)13od?H9r!iktl=rD
z3WH7K0%tktRdnO#^?X6go7V3XDy=bOL2)(8wO6KfGcd5&YWyxNbT(RX<2swo3tV|N
z2K^*1P2Pf7t4=ap$C*9gwJUMk<H7i11>enUMYQ{p_qz__&XSBX=gu!G_Y68HlR at 5F
zUaT9=e1Y5+o9&l2P<0me8;($xnzcMa^E?Wo&R=q}U9}b0F#jq(r+B$nP>*p3XQ(J)
zc!sXA8B}`FDp$;+L?Zk*ZW-LyQuoLeE<Af;)lS)-_ZXZhvV3Gw1{X;e(N=UpvcQW4
z9kG4VlPPag3FO;~C2$hD9;`?Vv#%AF>1x%8R~POaX)KzV7TycfRy)j|Wp<Tn-fMd{
z!Or4E6pw|)OSFpTj*5c|`cR-y7 at 59o)VexX%p?z#1h2WqSBW4H^;)qcEU`n@@#><6
zGEUu2CLC0_6#F{lBm?t(DNgSdYJfu#^WSmN#}eTk?Cym(k@^-Y8`yZ+=pYQBz_SXw
zdM+&8ljbqh0=Kc=9t>Q|+qL*2wB@>2l=L;uXu{31OEuoZQZmh>s{wZUy>`3sOo?_|
zW>wm^DQOE4#*Q<y_MB1YiTRQjXU3$Hhw;U}VX>KA>60rwJKr*N8um|Lhh>!ZwZgLV
z6*xadK*4oeGzwQp(6#<%P)a{PlKi2c{r_xNJCbBM4)gmIb%G9kD2cMiA7_qG5o=Z7
z-(7-$UMj0A(n;{?OM)bHQr|M_GKAUyJr^HhS*m%_lVX-HsEpTerR~lUyPx<d!Y?RF
z^!YY`wFC>8NW}#o$EYq0_sbJ;Lix$n*C2*(&qR15`EmhQr-MSo4pJ<oJib at Uug)30
z%G+y+ru)IX_C9I5NQl*41R;gSR`)%Gut86w1YM3Fx#L?Ln30wlaLJ*^LRWpI<>_Gt
zn#7lVb-~ru<X`y}|K at lbG3&|Dme`iCn`ZPru047v*7o#b;3|sLIy8eCD^62(jwtE{
zy%}-i3dT+gOW_&=Tg01WlQk1{`ohlf$MA3+?~(bF0jm1l5{tCSn at -k=Ie98-Fc3|L
z8nx?~o&$zNNT0m;REKK2J<jEfri(hMTzzMBN`2waj9(q=ZA9~REdb?lwiuc*y3Vk2
zz*NUVo+j3%P<J?CRW6ww3Rp$llX(Y6{?v0QS{`2pE6e!F at Cd(eWKH3>mYzwE at LOnZ
zJgtNuEah}$oc}aC3BOpwRbC|g!ZL>l3gNew<oO|Vyb7-df*Da!mtP}TwIAUZxJrsh
z_{IHPd`saMm`#bo?}3l-yDTaEa)J5r2tRDmk0@(8R6pe%!Y>bbJt}YwHIUOFqHEXr
zHeyxfK&2y)pO^3pXV#@tWbgDO>LdIvEa7)qQurlSaUBd#bJi&dzc_ar<Kq&3u&nm+
zC_mg(98cvJc!<;X#AmjTNBITz(XI}Gh`y8{UD0g6jp&M3D!;X!%$4#3Yq~6XG00Ey
zDwW?iuIgg`(s_S!4KMa_DL>3|<_>cUtI`=xI2*ks^@kW9?UwCz$-mNe8TI*%CvJ9)
zm`ZS|KX{W1O8mjyOX3CM5BJ!_DgM6Qi>(H_`1O$cb6K90$BS(IY(U4O#bmMUfm^Lc
zg!Pg_@}hKDdRcEHZ0NO9iO1Ha<IGsyw1?#gm-w})a=^RLfxT7D*hf%9f_hc+nfbU&
z%#caz!7!Q2o?BGCJ;Q<ajz>Qe)>}1n$fW+GWu!!Yx2)@?v8h#J$%Rd!1xq`xxFuid
zAyMe7KIw@%re-vye~X>c|5B+<dlZ`w{>%s-m6{_0vvST9+rZ-}r(v4WRWNqrh_2Vs
z6bBCODr3j;>K*Uw%iiJ9YxTB6EeJ-s>D`60LZ3$cUJ|2`Z2_4nQ3Sdro?Wd&4v7vg
zC4;9XaP_Noc*~Y}wT`e11 at eq&dg1JJY5l#W8IIHIqlc}KUeoj%88=1?Ju{AFoEclM
zHOLOEcwGE2)Lmi9GZjhCJ_pa(8CIktZ(;q2qnRVvLQmS^-u{-D at XVdM;kl-_1!M<&
zTb1hd_|{8To#KeyOjH at f>X1vfh!8hdVNyKJl>^WMz*60K-~qQS#<zZsxnHj$;PR at T
zxYsUk)f6{h_BMi=4+Qtby=%)u0JM1KrR9JsJ`aC`jpFICSdy6w5D#D4L8LuLvh@=6
z49<A<whT`FxVm57^6HQ9i{4nR(e)JU(d)V&;;RK4V=?qbn~e+H$(Aj_(I{bS8CND?
zH)czVlKYhwGv-t3d^fF&u#CjB><545jHUcJBbLM)OB7C^6>Vw8u9S0VTZt#dTt~t2
z&>e-}yK*)xomp&safCC6(L{i&Ne=d*8C2nr?;NnsBg%<g^~qd^s^fls;HKl2%nnJF
zyeOp}!+FAFmPbLP%Li{^dKq+TH(1u;4ac9DE$56`IAiPH^GODsVg7w6J{FLxOO{dS
z|54U;j_~F=<=~9!u5&fL4@)0%PSOPFp3ctCK^u06CtxiO-WX!5#5PHiZ4c&*nJxJ>
zVjMh4nwy%SE1F-VQ at rOw1Fv8!bOLI{L?T10K`20)#R!jc17eMdJ{2%pc;)<&x;6|I
zX$k|r;`Ie!QNsh~c?5Itf)iffed&AL3_W7;YrZQ{uboTJsFWxWom;O6{RKTAaF=1{
z=Yj_!?iC^%Yv(oizR06MFQg#^CQg<|q4>ec8^1O`*F^oB5RC?}Y@^pKm<}gZZU)8v
z2`5hNKCpU767-D;qV0s0v)9yc8{E!wHOtSTaYL|Nu=c6ZGPKA>KwAlWA~Z)JTaBX!
z`fM1+%I_2D5pd2t+3JZ_*Jv+|23PdCl}8atNyy<ohumUZe3$fMN at l>8PB#tTO-`SE
z-QP*ISZ*gBHdKW+V??$3ip)U2o`f at Efd*ki7ZYEsHC?@bWWV6|-V<g9!;Psxz>vY=
zB}n^~hE7B4gLEXsK1o(y#<HnCl!^UIUS at Wlei-jQ)&p~RGrPZ+-8FkAl;7<Hc~`o!
zN6SGVCRcB6H>@JigEOc|iM-j$xm#f<&9ydj0r{3GO*U>2C_+k5o(NA-(;4ALXwD2L
zv6n9nN7YECD+?!yJsVSaSEWT|3{vC55Cu0Q$l>q;;AR%}Lx{1Iq#4m!l;WVzfp`fR
zM<2sV_KeB$fCM;dB5TjmQJmj<j04#+B?ptNu{Tasah0+SEdfJGN36!#W!0Bo2<gUQ
zukVPEma|N=xp~sIPtvN)TF-*fH-}GV{9VMeu19Q75r}X_U5{Qc>tp!34QrvxT~q!t
zXv#5!zaw?7MQQlD4YQB;DSsI_<$u3+q=vDjpd$Ub4YlLN;{JON|8AiBkVJq>u|gl&
zPgAn&r7)<mOb}&9jpzEIr~~!NkNb?aLC)5i-=|pCOTv99+K>@Dysuh4KLU_rk$5fP
zDLJijtZyZKG`@|*W16lgmlyP`V2{BgZEAi`(BiyUI;)@mPIhDGP8<v1v+485m0R8+
zQ4TJdmWks^uCF!?aAnpxE|&fn+_y%V07_<iIys?8GvsPjVQ)zZ_?`#BOCca9XP*pD
zSHgL=nvLi3Tsy0S=bI6C(2W3j<F at bbyJ{|AWSEwk6*&}!-swzink3`uH7 at -?v at +-L
zV_4D40f|PmXsl)XD*hXFka2Hu%YEd*%0mP!rYbt^@PKvF8ThHR8F6ngs%<e&v|($C
z$~Mz&zR3QaY+n*t-X@*6s_1P-{vO$s!(X}f`91kEAJIsklAq~_?tTnk7_gQC6QwZ>
zp>ngAJqF9i>op~G@~-b|?4f9Mr{UT)&yg>^w1jZ~^I){Z at M)lvJ5eJ8j&V{YBNAfR
zwpg6Qmy7Y1a8I!#E9 at TXYr&qVumStB7q`N9_Qt(%_IQ1cxHKk^M;+SGklcEqO$F|q
zG+$9c0 at _K&oukLezI=6Vs6rm4#U=q)=TXYe*=yf50-cx4gO&t5C1>^*q!T$@h7mUc
zl<JM}<W^h6(rDhB_s3D6Hk7bS>$kIb71e5lDx5^2F_N~A6A9Ca&ByhfGjT~gZ at Vh^
zW;BP9@@r+ZMGdHrccSEA->*ymxeYyto57bdm^fCgJoL)?k7Es`#K33kdX(6{F9Ap<
z%-82&Lf4yRq(iGj#2QOfXlLt;{dq+67kDBiT~5w)F|4#}UC-x94lbwqw{*3SSM|r4
z`&IpMrR!sxRV6u{A+o`mzTAsUFFjpHPp<59-#iWFQ9nxj{`=?OU$qoTU}!~%kXoHD
zR9U`$W!dQ;@k$CFdJJh6NfMcNEMH-XmJzJGETd%h{%k?hiL*z=oj>4Hp&9}fMfPV4
z9(%BOiou^P#Zji(!qd)ww1kiTPj|U1S+3hqG at q}SFPIWZycj!oazmG+>vGio|I`}*
zbwse$Q8(6+NQ!uEkOt||OWsMp;tO;jMC6qqEAyoki?6^7OcIxXZ-hg7IFV at 0@5Ggl
z6P!Z&59DgOJ38l>uLRkOB9+A3$~$SfxvMpP1Ni={FXBS(opib1ve{orkH!dMA=O^N
zFkQe^)f<7pr}zS&c`p5dT-p~A;#YEojB=7Q#CJl|CFxuw)Yc0 at yoG>v;^W(QfZ}cW
z&cM82uLS8mWD)aZeId=ug#&`P;TM3v#<GZ_A-)i5(IdZp{q^gfR~RQ|PkO&&RDB1n
z`}uqxa`DEBE6!=xzX+yXk`rnVQ!iW7ls6&(X)$f}nQ!}u7sJ<M#^Vb0z5O07(ci{%
zKFMkRDr?BREEm_D8{5dutyptPKAixcl2f9l0l)gn at fj$tv<@<Tc5nc>=;w-qSSVFL
zch6tPoa-}PRRWO3l<4v2CgU3Yl3H#iYi5+Sx%lQ7A<e?n7#X?cyA~P+fH*gsGX_$M
z-!Uq+v70?6%nb~Qy~#xgObQEu9~RqK<EytL06&tS%$p2MFZJ&I5th+9xLrNL<aB;+
zHRF!1o=;Debt%!1p^;ns-rZYnj_ at so1W7X<m!bRb5om5+VNUPnxmS9V^5B-fj}gQV
z?q_ky%ImW%AE(~ehsW=v`8y$DJae!7>@K<@i}Ue*d9pYd2?j|<VwzrrBTn<vIYz!u
zykP$p;T7%HP0mke{h0B1C)$6%=2&lQnCsU0cJXZKMaqaV$<@d5%5BDsvVl$!KxTQi
zmM)*mZQdbr^?dq5MT(40A736o at M#*2Zx_poO-mPNicOxG-Wk|7H6OQ;X=Is$l7X!w
z6V?Zj$cmm$7<~cMr*L6D&hCO9{XSA|1qAEL&~UJ;pvYg3p2xh1^XUYr6*G?F^(HC_
zevj~2RqI;eGc126jd<yilA1ZTngh%}@9NPgL5q9Po^QK0{9H?#d5cWX at 9v<r9EU$;
z=27Xob9NQk{CUiLS~K7G3huhk6d%1x0Imo{9W7tl2Yl_oq6=-Wd;{y18{oe2soSCn
zzILD_^_6cRU-|E6==Ga7%MtemJzAT2Y}pC&_J-ewt<=PIc?+4Jj>Hn42UtDDhc}o_
zbNz{IG4JLXq1XMmXLtnC at SkNx8O7T34$u)=KSWD}zYjCliZSFJvLEJMa-!bND at 0yo
zxeo at OcA0mE%h=2{bS!{+*nG}{8OG?jv}C0vZuzi`1gx<(t>1;>V4d}2#^cxE55RZX
z`kghdNLQt%cpQM&=J-ChJi7g0y{EY%N7j9>Qap$2)o?%_#%EKQ`7(1D)Y8<<YQLO3
z*^KM=3gII|k1L9rzs&sVKKI{eTkpDwEL%(Y=lt!VHAdd3ck_s}>`yM8ZZq21%*StF
z?k|{abr?4W&8-1UuY~FQfsdTGKJZY8wXgNE$Kr~GtdujIFDrEfpY37QQxf>?q`!Dc
z8I4;rP4(d;sQYV)tT{9<QQHz}o*^E at w`>ZBJqh>snSqg%-c3JMKXTH9Kj6zBP+g8u
z&&fH^7ib5B6PH&8VyM+MCNFP<cCWj}IWRC?{ge9tdjRf%IJfCC-vLXkidWn*ezF+m
zpuC6B3wU0U)!kpf+U0rW0C$l(j{dbD-4}hn#lue|kFI?jN8euY8 at YamphLgDe*g84
zfByPc_Y6=_Q!iBizJCAbSG%qyKfFtO18B`cP~YV1moQS at Cmw{- at Vbj((0kSmkhT&E
z{sA5Zy4w;>aRUfDo*QT}-f1B|p;{r{GYddXxC&|6FL#3Axyr%~jxXnL4cDh!i1+IP
z;6JI$xNI)Vbf1XIGac$qM<Lxi^9~03g%m<4p7kY;WZsD&Y2+Yt)Z?J*X}O(lF$wGh
zJ+9M1T<>gTptz5D5Yw{GfQ~61#ykbh)(;}}k%3NX2bol^XW%ky9K>oE4E|GZ5I|;K
zSRi5LAY6SupgGz&h&RnjT=nb(3Z>gYRO((nE6_oN%3gHE#9E1udzDNs_fF_8Qx0OI
z>_w2I+7D6#gS;rugRs at DP^UW|goF;_b!aF2=Qs_cA(Z<fREMSqk(wo>G51u$HQqsL
z_7R%mWd|{;*9F%YAH<96Mi}x%+Cj=Hj1DpEAf)Fr-oST)Hdd1cL}%S;pjgd=NUcOn
zf7&GnVWNY~TCJE-c-ld%R%M23>Yak<DEH-~iPAx&)xCV=H~`f=@_thrfD0tygIKMM
z-V&)Fq}CL4 at 77MjHH|>#EL#cYyfF at 9HEU#GA~=9j-3qQTIf&GXiw1ku!ssox_BD`M
zYp_5^)I0*QR`>ETkq#oYDhnFh at f9f|b^@VQdngFiL=so^WiNGY+7D85LgAY^4<e&%
zr9G5zC0q-&x<ag5!8h{&R_nC70MvtMjZ;PEt#ulNYn at i~;(h?gr-N86(~9O?^GIED
z*R9a5TRVu<I<0Vx*+ICrLy%ePw4tTfGlLkFy$o|o6&)a23$>xiR~Z7rv+jj1Ukf!z
zRUrWLs#~G$AJ4!ywNM+{e$6BhsoDk7G(U=##j8x;_FOt9ZS?!b#kN(T(}qJ<_T0Aa
zKSNB36<bza4%=rl;$oAHV$Z%E*GI=yk8fM&wm!z7KBU;PDskHA>TML8xfOfbzCQYS
z*J7LO>!X)nRBWZ~Iw13#wPH)#*GCg)eX&vc;kG0cb$BN;1jIuixI)hCB({A3ntG(k
zPQ>=DIwqgi`5+v1_#r;{^@9){z at iL41YJGK@J^KZ2f}>>fv at H`h>TVU6qp`>FFVL2
zDFjY4I*9eU723SCgUFmekjpp-8vn3^uu38LuhBt>`$25BLeTm9Jcx?E5_B~3Ikh)v
zfk?NVNVw}E at My@Oo$v`}WZgsH(WyTW9Dfmudj1e7x2O&~(bfxrJm3Rxl=JTd!YlWN
zT!<{)0EJH4zzmu60niFj+&7^1YL5{C+OU5+iA1S?2z)X5AUwt^LCj5BWQL4-2+BcJ
z|D9Oqs1NDzZRn_-XjK0<LL4#DAupmI>_o0s2;{|>2chT+Z{%va$ZS#w1P9YWboN4^
zIK~@*kI`xqNNBHzK!HTo?}Vd`ypcu=%t-nVfFubmwt=|Mj}3=MZ}|rB6<1$GLz+DV
z+A*ZrP9$2+Lm*Hh!QMz0S|rN#4raHV42a3-Mc)ZB4|*qwg?xJmG)&01ok%o`H!>`|
zGN5XrZ|#Ib(d}eTDFmt}I at NcAib8V{i<<Bd2%XS)JJGm}H^O+<34$Wp*-j)>-$Niv
zg?=EH_C+`v+)gI7LZDQ29>kiy5)|B#-hot#4!08tW%v;2rBH@%1nOZC)*TQ;RP?-^
z5S`CBA#{CZTg1>eb^>vQ+^a-UE#Ap!;R=G}I&47?+zI3^df*#DpR=|IH$KSZQ3wJ9
zUGbd|jYVt}07)!%5WVCFa$Rxj1l@)<SqZO|Zvw5>+bcoS7g>bRIV-Vgg+RRx`GK@<
zi>Oi|5Ol2{M7X^Y2$lcSUG7?zq&O7y^DF8LW&x5wvd15%&0b^GthTED-<_jJL<Uq`
zD^nXT5JHcGhT{dHqIM7&>mWAe0~r#I7l^*b2T>jyL15?`Kzr&`V0GIFG at q=4h{*>+
zE1Ho&1ri;E*EmQ-${zrKD7-EhVm-*xwGn*GkPifrP-ITp2yUeHg9yKaHp{l9Pk?Cc
za|bH&2B4BhIvg=#bdW{;1Hq%y$wbkZc97Y0BdBvcUkUD%r3fn@#KzbNI>YQA2(?vY
znQa6ekJc+e6izKNqr`j@^zqII5g8vymIi>>`?jHg#w$RM*eIfNZ3N=4$w5}smp3Ak
zD<f)j9Atg~y{Gq3i$o8Citt7h)giSmh(W182}(7(@;gBXDx&D353(Rh<p2{({f!`4
z!-gJ3lYZLbrRK$^d?+rc(?$nj?ym${G`NVOWxo^7_tS!2?Rt<U^8;!5 at L32eH1VBG
zFGnu~rJ<!CAb3~*K>F%Mq}f5tyZ8%1smk}oLa^!lKt{d`Ky>gcLI0E}CktxVgKHzp
z%h)U7l<y1rm+Y0GpQknlk+nV$;ruQrV%Gz&q&F(UUNm0_G)PKf5Vi4vjFh_y*-UQ$
zT1RyUl=>53xvF>}$mrCT7#nSUAiU at xE%hGAWO*YvxTKhSke;#(XK$`fUI>)KIKW8t
z1R`FJUkF4>mbVAtCL4h)PPqbtbG-r?x`T}M%Z5UiJRDNbRnaRM+4pY(?Vm0DL5S8y
zC{)%?cot9>S8t$=u at QW#@<BSwMi3-z9R#&;Bkf;G2cRtb$P?u78$o$B;e$w at 2WeTJ
zZ-OU9R at w&{t#4$7hTjQ|eJ|7lod;pogS58qg^IQMK|0Y!I37Ja8M5yMKXp9_U0@*|
zUciWF+D at d=LE4m!^mruhr0c#iQd<r{eLM+NlrB05)AlRqGqZIN5k1JUFgm=RWbnQf
z?yAW_Xzp8~PYgQ<pP<RU6|SoDLHh0ssPRSXAj9^R(Q0Qu2#<X)XhY?L2+xD`w(kWw
zG3_9Ow!QEQT1E#U+4jQw%KAJA3gbZ<ZF}J at H(NhQpZi|Wbwds^bl(e#k at X;ZEaP2k
z>m)*SkY at W<5W%tzGWxz1<n;-Nx4U87&7MdgyeAS!8~av}scHx5+m8rWv&TCK)Y3+v
z1ouP&p*@iZs!r at 6GWtQ<o=F7l#e9&_wyPnis6CTFC^G4<FP8=W<`sW2>5tzI;;G_6
z=nKPESH!Rz?!is(dx$@SI6 at n5_<A?B+mD}L0NvkW9<j&{7YF{(Wf$S(rnGM}rZ3m4
z-{ZP+d?A)$#tYI{N04H=3xHf*QQ%;9%H46~%kob_ipA^;45*j*@k-NTFY_;fNm6Gq
z%Jd^tcR$+74 at XYK(MOI-uMhR7q1#x-6%yN>&F-e2d->D!r at L|1=F1oNz|t at kALM4w
zUz_D@)S#*sG0ZUxZw`K%B&((ufP9K^7t~&TEEji$3)8z+O!u1YU_n>-(TKxTybwG9
zs+gr)aKtvE-nBL}04z;7!UJGrHFW2uUswb55ngm*7AA+&xu&}r)2F|BzsED$VD<<f
zbJ<^;<(0Blsf{ZivrNR5dfH2UnB{b@(fR2y&MU_S#nWYA%MT}nS%#P7bc#~yfD*8*
zjK?%(&WzS{m226I=30N{w@;T!ZqEF6S+^O}2lI2h8P%^f%vNU?&5Ljm9=mH?o6cO_
zbLwGouZX(<NkoC)Zp4s99^1&xhScIYd1O*&ICYdHztJ?HGU|e7#ir#2=P_GmNy`gq
zO>?%FzKHCRIXE+mlrv?mS5}O?0U8A;5}YgPya~8znM87hgRDx?xZc^)B$T;_*=6SJ
z3$zwzky*71;}INHxq1Z;X^y*u_E?kbyxvu>w}_kVDj!f9(V0j1#S5UDRc*pCWu_a2
zzsLQ0s;|$;vgu#yd-p46tI*CFw at H@MH8cKM#eaVN^QGt&R}sj<j%GXi4Sqd=h1hxg
z<PT6kxd0rFuz4(+;MWsqwC%@F{s8%tf4&Sc%d=#TVc7>rm7@?hq9;iAvHMFiGu>a_
zN9=A(b47UjD<d0=dZgfuxw7{AJebroNFB#?XvvHu_TgD!Cb70%A<914Fex+1n=`Bo
zGNc_o(?WQs5F9yKt#Tep%vwA+!{u<sZ6Ysn@@3-P6{D#;DHonuXFZBz@}vY)V`Z-K
zC2_1F>o#M0Be{WETu=Tw9JH!VOH2ol+8q7bt&u%p5T#jhv&!3?YqyZU;r={KxHEqW
zQR0s7X{ppj&1e94$wEH5vwK*b!aZ5;)68!k_8hlfKgPY8m#jR7k|z-WQTlV{I5c0F
zgl~SBO)aM<aA9CE-mAHEw}|ag{LKqtB};6r)mq>In5}->Rjx3oI*Cr<-*IJ43zJ65
z0oNRPSyD>I;;vRo57#$-S5^v*HGSynDP+-<4BtyM#q{q~`+4SGq@%adyW&SS>XaM&
z@(F9zwWy0~!#~l<ZU7xG+fOL8$hL+_-9_l$eb+dLq?U9iS{wu~h at 9d%4{MbVrOYPP
z>mx7y>6eMAkoD%3sj8dj>186-Cg=1EK%V>foEbcGhN<%zsOC>iy93pv8dpW4AS$*<
z#p5+9PK`sQbFWA>H?PoB_6#!D^_3+^OPH%nubB_CM<2XFk6rxNCiUeUW-FZ%0uUgY
zNq8=nqxJCX#3ioV?J?bxJ^;Sae_hwmEzW+uY#Y^Ku9mOp^=_8Vp1T<W at L_i}NMEg*
zu2Ps%Wqw9e_Op%**Z44huw at SopNm{pxO^zzqllfdIATTCb6H%zXI%6LALgqc^v&cv
z^Ea3pvB9fa9GbF6PP!w+x$iYfc#Rmfl678-ju_9lqO7!iMKAmYu3FqIrRJ)|Ke9yO
z%Ckv2$S<3G_*T4&(nWfkF<VQb*0`0J^SbK(y_!|U&1hD8$5oCiqpW|MBa}@}FF+%w
zw`u|zbzWwp#psnG*>XI8{bb{6Sez%P+l=YF=5w5`A8UGrYwasrt&$r$;o(#AUb_1{
z?o6#Nyl3Gln_7laDS(c*O+;3T)OLMItCDJ23p!P7QH1Fz*|}C-m{tL9 at gdT+q)UAE
z?3b1IrMK-aoEg)L`|aQ;^p#sK{o`a=HZC<i+I*TEZ-58z;^n`tC#|B*`08QUstVPv
zC+djE(RzvXyuDd3v3`X$k8OOEvh-J?yQt;Prs;pX`XNPWFZWM*T7EgMb=!~jqyPBp
z-~ajg8|!*;sz3j2KmPaCTf5(39n$RgSMQ37C^z}_H6`L-T9%#PUrR&2UjPyYMHGql
z4ZJI^r#<6+ at Bp|Kbck=z+lZKc5`+qF`$-l;cLV;w*htT(!;_$ywr%0gs5WCGo?o(&
z<%RJH6uod?2Ak`)%<!k$HfNw%EZT^x#tjf|e{O`4O8bu_lJ7}8#O6Va_Twz{uFX3`
zBeb5xBFJ{)qWM9{+mRha*R~F!3~o0J^@V622bnpA!$g0dy4l9^u;B)!&DOg9w7ppT
zpYD3el4Qk!06KrG=oQrABMGsNUcwvq{-<pws0>6BqbhEAx<>$xOlipD?K4KH?v}<n
z&fBBcsy&Ud=hN7I9%IKz?P)yhygy25ku)|r?k_lKyr!|%nZ{GcF<w>g95bnZ#^^6b
zOXG3gp0RVLvHE#?iE~d2x<f4Y5;3`i_4pM1)8&FOJPF?QIxaEIkHXx~eu?&W$;`KL
zyzG97izUG#xPB4#3rpC)9l~N|^%6^C1~|WjH8#tvk_07G`z36UPU$#|<`*gW=1a^*
z62akYm#D}k(_0c2ntF+AeioK*j;9Q4=&@yVO)_vm$t6}>65LTQka)vRnQeR#)^E!E
z(@ih&I4^(1P{pNP5@*c^P$=qKjNxN}#v6ISoO%NLp`O9xE&PH0r at zO&w+%x)3D(x_
z5{pCnBdCqmFVRw;f;FD9WyV4N5w<seiC0U48H$qn5>ee^aoB&vBfMu(n at J{O`}iWI
zjx8c~iMyOKkUd+wWY`PvmY|RlR)?0w`bdJdB1*<78b#wJRW?j*Ct%5E9(xp$ACTw;
zv|uZgwq?Y-@)0C}s3xaq)RUJqTX$v?c!8*2;!$Qkg4hsmHxrp{8IfW>;?Y`Vv8XpM
z>An^LtLuJ=MiD<{Miu%kc>5&g+!09Pcb(!=lc1%H$aKnx!t at fwuc5F%vr8g0AA;P{
z+ZQRLZIN-oEHeq>&2`ORVJ4R?BZ}8Y5PIS$IK?8by at dS)1zG4CYm~E7<`_wkhpIkA
zBE0(+HoK=_W8fgjGVV(@^tzS-8I=7|5<_`AWFkF at KE<Q$TOw>WlSDUq2vQXa-djZa
zmN7jEvKG6fB!+_bmQrFJCL&nvm#EfDrq8D!5n0STnmR!Z%;IKA5X_2PGNgSGbOIsR
z7hW&1I+Gx)>39mR^|?i+UNXBRL4F?Xi!`lU;_G|>=}qP(=Hn at -PnAp2xMiDOqSlk(
zwJYt5^r2fsF7V+=a7<Nu3gTC{EkiX4cE;?NSg&6MyCVdVeO!Q?cF7d`B4ynd-_#|8
zw|)Yq*6IuJm0I0mb0)#|=zfU~`yx0HL$K|uTq4>o at mZgOiIp|!I2PKxWY{>RA{54<
za*D+QzoeHDf?eV561`#Sd<yo9#WGZUHpeC2H3>>^oQyAtLxwPFI{_g=^H|+qfTM7>
z5QL&QPEHZiOJ?a`M0MR6*-`2x!xJDV)^Ad)ZZWka*c4MQ(cWLeN- at TkiRgU_q9bZ<
z7Dw2AOY1z9TEjL${LV5^b5B8dM1_3`w&osNsmB;mvd=UYe1q4xN8?JFJVy>ekW{&3
z%=Q$VzfD4rF8c*$GYP^aPnM at hbb%CQxlJ;8kQ{<^+1eK=(a=$1c0Yg}!D^di^zjrN
zJ7vu`+KZu=7_03eh?{&<pMto*h4m%DY<@~dcfn%&5ERttwJ2UR3G%1(ON`a+5Ts2O
z+o!bXwJ557$()u1X_LkFAqWa;TbxJ9A&8r`UNT}fULw&*wT_*^YMaEGB|+O^@)U%d
z=$k0YYMW&8JUIlN$e3S5rEc*$k|2Vz+$OOpU!;~em^wmeZ<pveL6A6EZXbf|7tIqz
zWL)B|Nsu_>m3&Ey=7}=mJoyOzca5$4Q}DMQr;4C4VZlwJEEq{9GyGF}G)hgdRakJ7
zc)kRC&-^K<A<-vM1}pF(c&AuzlW5%eB{J5u<6vN{Pa-V1NqqVjDSb^lj?HeDj3G&I
z#Ixf57WS^wwisjmM-*2x#3xZ!-KW&(j3|?J_z;wF#`+{K?J4-~afm2(gy-?#q4I(j
zO;A&8X>Rr;$|8IS;vh at zLol-|Z9s&PJ_&cu0mz at MxKELyTLurALl8k3>64iCr{Ey5
zHN^;}tinlHLK94emZxZ2GmK2iQk=wj#vFoOG5Z%;)dw(oJ-`rWSc!iFj#O=1j3>+?
z2&4 at 3NrZ*?DQz`3kjX-P2ofnneUdTfQ&0w0cVkGTti(x-XUtQ2kK^BvQW^7;C at XQ2
z{iq9iytV|y)OL$A<Ud7XH4Q11mHC9hBj*LBt?9$IVdhU_8IVy~mLGzG?XpFf`IAg$
z{- at N{f*4vE7V0F<Lj4e&*{kCiNUeH-G50?OHwZy!W#vwyrcWt<R`Z2#QP>EaAgtRj
z=&N~vILr1S$g=DMehYrvRM|4v861Lr#IpSqt+vH at C_SaD)Q=rr*efJa_6iR{R9G1w
zGFjW7(n=hOc$GWzZ4zhq at Q@KTo`MX_{q>Z-A~|I6EPDv<=j)du6|>8DiimGfb{bD9
z>l{LHDNG3u!K|zh3=tlsPpQ3cadsmQkqBTA6xLXr2ecJJ{{mj}>Kg`=#Z&qMY+yDp
zzoA9S%%UsKL5w}lQ;KPl8mX6p&zMi1Qdgw<5qwYGpgfOX&}%ydWy4p3IYj#3zhW!!
z8*r at 5kwMin?8jh+7>j-b&OE=Rt&;sC7JW~aZX*d^k~X(E^VU=3hN3MAHb|9A>d+)T
z`ot`4<~!povb6!Q!`MSUMb at eQBQ=`cEV>HFA$1+KKY}B79ixY!5j+Lqam;>6*YqsJ
z(pl{i1hGp>PtQW&wNfvsmY;><*YuL+f1fpw173nED}IU81gPHQ7VZ6#GBpXJgN{pD
z&5uGTxuHu-g4HVh5;47`wEQRpa-Wyf^hf9kM{Sp&z$8I7?0QL`=~3ufr_M{X)k})!
zXCdH<T+&KfnvUpf<B}e4+kgLIR^}<rUJ~{Ae*|Y<=M*ZRPGDvMR2--nPM)h6a<Wh<
z3VT`3Z~xD~|F9J(Yc9Pve$02D!-1D2>Wcpi<8r2e&&N~N$BuK86=vTWGmMMb;Ow=;
z8;oINC&Vv?O??cxJ>v4e(DCZVU(Xn_f#S{g??7Q$mzT0;Lh&!Yi_7|4HGU_XG!6}m
zvVN$pX<-M;d0+#&wqnd#4A_VL%Q+nDk0)=9w{VZp2w|kGf7sUi8F8^Pxy3$j{9N|u
zz3VwdfLNFR1^hhzF&jCIMu|UGcCQ%nRF11{y+gCa)%@}?#INJmE^WPqYfiWsB%sz2
zYd_ZK)nak^vc_=wo|E-)>oJ2ov|^~SxDqqChHLHX`SIFj7`DFF-hUpoR<o_kW39Wa
zL8-A(gh7!%qpoXZ>d&vnP-T5=wuTHhoI$M8+E;tRCEkBRS+l0x^7t;YhBn69rR#Er
z(fA?0 at 9*n(kFu74lgFLYKEt at Y*KvMcZGTyu)|}L_?{0Ig0qaaWeXjSf?;lg`95M*3
z%QuL{2AH~kY*~N3XQuD3Ui#V(9qmJ`=-Nx`-L7MgP(#=H?s;zQv%c849)GLrvs&l6
zc$W3#(%0+P*XuQ7#n8>|H=?gK=u`G96H_k#r at P#dYw2wuoNE<Yfr~GIMC{y&Lzko9
z<w$q`sk;Hp8FD;|Bz(g&4-!B(vfO5zfM^tyUfH0dXLb7Ru3Bzmi!jOM;)rYY7tubV
zi=Z`ma$F57b4TK`nkP$d(S)c3-b6IQnulpC!0 at x27C#>NtZPdm%KFgzGS4Q%>D4tg
znxVF=K3Q?MIinG%X8ZeoH15B?{`-aYUR$OqTRDwsA0O~@1C}%6{J|sqEYBEwjAYsF
z7vN_H<_dHF;u*&e at Qy3{Q}qV)X9pq23lm0v#4RL5&_o%II1t5wF}(I@@vs4EY6v$M
zVH=S-wfXYCqHDJ`W4W|U9mpNR8EK`%BeMvX`0D8g=ShwW2z5qj!z?pg?XoOAmYZg!
z%l8U9=1mK`v4t)uTg*z}Z)bX>5%<oHr!((8Yk-M#K$yLDslQn7pU4;2GVjakZaaSK
zdo;5%M|A=4AAdI1agQu+-jU=U#@%aKx#cx`wa;e+p$k%d7h8VGTpo at US4}0K=A9oS
zAq+lWZZ!`Z4w|1bK2dx(N8J0et^D!uJ*E`-&13oUzUL2*>#SRb>@Qo`V(}FgBn^MN
zF0rg0x&mE&f6?4zC+5|5HCp`Ti(F>aG;?9buh|Fr8CU64KkZSXQdm)OVlA$Z5}7!n
zAGYCpw62yWc?2^ZM~NbtEqk2#d8;?_D7wF%Rp^CD4ZWMa7pC?30Y6-^MjnOHa~dX>
zsyo2A)7JJA<FwOHP_E3&d+tx=ix~eMp9b*f%b)?ib5}?B034z>E^A?^ke`ryMAs=Y
z7ZCQIrp9>6*3w3HZ{1eihQ%p+xW-kptep?Q-Q!ucMEY_d^t;EqP@(wjCYf&eu$i at C
z)!%K2_?8u<7DDT~W%uXf1N=O9!#4<rU$aplwf_)w$xbpk9uVY1G46gNY_UCp!b4Qo
z$ZBWq02H~p0X{+8q(-k0hBHmw@^S@`d0ZbO?!J(62zyFQZU72%#oZO3im;@k9B2kM
zsAciyY_N<=+^wKzSoA9|xfP*gzb)7DEGlXPpOuz_<`-epmA at YGF5|Ld>dVDup8WJL
zo&6KW#;kT<S8soQJM&$Yd>L_ at 0Wi~7Vh4CiE1Xst^A3@!D|h_ at 6ds1z4_fzd_82E9
ze!g7Or8GaoVNnt~eRa<PR=nTyjC+XW5I-a9si|lt{LnH#q3=s?ZNFafy5J14$y3MY
z*}m(Nvra|#K$xb0M-TxGVjfV026f`^`gQhknk7}L=JAxCy*!Sud!0rt at U3Mp74?n%
zTt<?6o{0|(+ at Tp&YnG>@niA-TM=kQKEV27rNkwAi{_FB4bOUMrnWk}ioq5VPkE^Y%
z%=$RJ<sVWl2ifV{+iUo^Wmj7E9DA!5plB at TJ0euGPw2~+jr%(GLF4e)&9DtAB&BBJ
z^mNK~M&{HAJGN^JRV<cYz^d&zPdW0t=V&@J(Tau!j;Q{14X4pcbk$E7UpkNL&1C;|
z9k$<{iMT!eJ_ExpKhB5_xcp5pHLmaSUGRW~-~RLO|M>bBDzu<33g#_^HWzL;Zg9o_
zF5mz8s-<{?@~2m}Rd^aChkVG-m$LQsZz)j1&L#BwD4Ywp^HE3=K?)+wi4;hwW at B#B
zcVI|rb0CfKg2YFa#c#?IC)fo%UJBgim`)}wBS9+wg+O}0%Yk{};wwW|YlRY{;s_{y
zB+Z)`_Q$h_1Wngp{wC-X$Pp5 at 4H7)U959=Hd{3(KHmGGE5-{wcn%hoVL4%An)$iM?
zn^q~&mq|Fc9e^pIPB<8nuon3vZiqTTeoQ+ErAwXA)(5fP4gv!?xi_f35$^}9Hs(Rh
z6O2UMQILqXoph#+IVrBPBx}xtpd|f#Z-TNj-lw>+3!?o1PzYU4s%a9s*PUQ9R)3Ji
zChQsHNzlb*{s2ldyb<a`tm)N~xSDTd;;j3FP at +&!kgS?yvDt0}UYOSJAd{^D1Qq1O
zkgwxGl+EcT0|s%$vsn5;+^ZhMXtg&|F*EU5)3rE=>*+?CyQ7oPJiGzgSb`oM=^(;+
zV<WICw0tiF&0txQK?yyGu}^PAe9$?-oXbfQ-_n8q>U<CtIS7uA(-~WF?MZ|N_#pH>
z8^|e%(ljN>qOIEqPLGD=Ad~a1H)7vFL3(O^5Itg42DOgHz*dqm^+7lj?WBbj04bVa
zTHOI1hn>Ln9tK*Q?~HI>)dvx(+d+Kzd?j`HW{Jccco6rc8^L+ld?n2X(IAs1Z6in_
zn&X2g=c8BBi#Iqy8OQ?+ug;xVo3Et$BuRpLr|me18?>8f@$vJWL;(ZPbKfzPs6K$m
zd;=(JYe|g0U?XtJbg&0mUaU8P?+zf+6U?y=f)rT#Alil#$e$Bvo%RPoM}H857GRel
zY`&igp=GfiWYOkr1Y!zNCTZQ0DE-n#ps)OjgK)BZBdAf2E&Tcx`$)FMixb~V+}(xL
zSz-V<$kYSi9L-({x=9;Jlm>nyu%VQfg9zp2m5gvaASUe~I9q!FTDW&o!X`}^9Gwp`
ziyVZWv%eCw)?!J7nzRx4j`jdU)-MG6l4Mb&HUewH4sZ~kL0<^eAe<abXRGx=lrpsu
zq{LdkkQQzagrm$!j52kT1=HhS`u_y}Lo~@6^&k~eYY#H1T(4w=wOp`Dq7NdRbT$I7
z)Y~h8XA1j?|2P*Oz=|CNcIvSPnL%{}k>MN9PiAL0h-y2Ca?E>)Yv~ya)$SlR=RuIc
zRbC04Vt8SYMg7|dlE6F1PUdq-f);KW9i*~1A4DmMH$jgpLtY8T&6Nai`yfV1+z1>Q
zCGnN?aNQu2{&XXFD*fp}l#=*D at XT=L6j^T|I}tk8gBUgOm5k~8PQO7-JjkMl-3Z)I
z8NZMch8|>66E}jqSoJ})+8be6hQ)`YEEL9r3<~2xjJENW)NuXq$(_#kAbgz-KwO5s
z5{|eDl+O18jk5 at C?hb^?`39N~1&xCdyT?Ij!49yfq%Q<j;Fh6rx}&xpWYP$41X)2N
z{7OcVBucg22>j!G04eL0pp<=XATbK=MvxSg*n=#Z<&D4*(kz3B4-O<sMcoJ-VP6j-
zQ-2{YJ*~hH)^UPCJ>9{w`UcSAB+d?YkcRpolX7~K3iHSI at k*MH79{Eid=umV9rZ!v
z6`(2ms6k+C9veXl&`%#^QB7Y- at 6!p!1UAKk%-RkDlPGTh2E)e at 0-=^}1g4R#;~+!j
z7cw+~(O=&HCX!0JgSlSg(qg}1DeIFcRdgdr2a{I<eO)7o&|e?K=&v^dTS*yxC3Q%W
zMazAYhK!))KFFl7z7Pn4k0-6cTT*HdqBP+ffw!b7d?igS$)MaG#JqLf1m04{D}l4=
z2`1I{2C$a>jFp!B4Rjw*T7$3j*0K|!vL3|Ncq8!aCcfzn#*$wBAl%^%AS)=WuSBXQ
zne^%#!RSG+eh{UwZUin;+ApNcnS{b|0BdU}9p9(=O7KXZa!3}v`bKa-bmj+93fU`x
zZa6`jQCANlwB`pf(^rCR@^Pni6d`r>AdAgpBj_2l=C7ptxI;31e{O=zpt2rBo4yj9
z_41AH=-o8y2N8<vL5vOMm5kwSspA<m>j(KC-DR#MNsa?iy}qKppp6%J$g8h1;@V0*
zHd6KftK;rKWHK_D)<QL2Fe6Cfa!g9|jo at C`U5_Mr3jyh?-3Y`)Eic5z2_O?R?vw^S
z#O8V<t-Y?9)apl)tMacwFET at Kgg)5`)aJo%I)~ex&37Xxp=`A`Vr&jaQZ(w2EUNd7
zAS&2yZ)CnM5kQP(-FQQHq<X)R!DhP==u9 at -BdHg#cyR!zOltQXH1^teP!u~(rpn2a
zKx0z5-^id&KN3rR;}Dr_zBkgT+ at Hh{5p#VYsap_Z`@NCHX1fsx&D4%0E4Q3XD))^b
zCguwmDkmqBHR=?D&{SCO#Mp5U1ijC91LesUyn{yBz5 at l#yIr%2jX`y?``*Y<X?YT;
z&SEEGOXbW-r*7W}bSS&;jj%<YK&uvDQLStQI+VTlMyA=3Kz}T?20Sa2?Kd(g+mFOF
zzZ9G=vjN{oXKC38lqhxkk@)2RH1^*eEVkb#fDrBZ1fay`MFw^KMxaC4eUGI0xB}8y
zY&HTRN at 0H^#_oF{UG39}KK=v{qU95*eFT{l`y0Xk*pF{yu(lisR7+8TM+eIk`!~`*
zfaW&TDckc78m0aTB%eD#7M1=+Fu$-l9|%fyZb2rS^OHb$DHV3mDfN#edmchKX7=VA
zv2I6FCA&yahp7H<q_H<2Nm(n%WVb#L{Ozi<5c-uv!Ho<K1xM0+D8Rgv*}re3vmzZx
zmMSH|^>R!&67#7A#5gA0$YM`FlI&t0vuUNQJ&+`A?PPLrI1*?<5u}FVYbQg+?2VxN
zjHNUh$YzcbM^ar>gBXX41DP5!NT6`329BhS8Kkjv9!YXt3o<!@9LUgM1Ts<5>lx%;
zcYri47GO|R97%D>I3c?^xEzRS+Pf3)^#Gl-%{yqCEAPZyfK8+XwaJliV%-U<qm3ZO
zihLx+Wzd7562F4lN)QVEcYw%F^$uFMJ4nSpjLf4hIuIz7xn4-N9Y6~Y?;smN%H0UM
zW4(SgDY%ZLu5lyi#I|0DmHa}|EH?rb(Ao<rsox0ng_r&b<;v;tNUAFRUtf$rTyB+w
z8Q*^h*cl0?)=d7#1Uf1MO<XZe!<P{<Df{mmTKVrUfQ%HbQZt#)%kp0{=V4lUnp!>(
z5Zm)P{5e}2ObQm6i>n!G{&MCeOpY_!3kvRLc5G40FQCN<!b`@~msXr(wb;o*hzlT`
zxiReo2dp}$mFnhNaZZ%iD*b4 at merO_2qxW99x|(5&K4CmU(VQ at GErh_W&bUYq19+7
z0v&piW;~y{?j{SgFBhPW*0(vH(af?eWm$+3UrlL6P^UiLYm?a!*>r!EWi2*7Q*p4&
zz3-pGq)X=)Urk-)m(u**BkOsY{_Rn9<t>(v8du)y*q?i(!A$O<*{^T8H{<2z&gb-{
zjiXCWRtbw^O<&F)=?B;IHvD;pE at QPx?!MJWgq9Ttl3)bYe at _9bP;@h!d7%1PN^KFy
z@&1|>KE3+ptNGDwJWMUeK)F9ptH!T~%((S(xp-y2$vot=+5PP)yy9kfOh(p}M{{}{
zPj~l<IG#0PZTaefYN&Mf*c!NN`qK>*#&B<sxSZ}@zp`%2bK8U3*6`&lgaTRl*Zs>V
zh)UL#BO{7Cpq2or)N-FMFTDODfvI$hV6YL{Dd+a&xxE-UPjfW$)fcd(tyqtuh35*5
zC|M~&r%r#Khhn9eR|e&m_q&P-XRWJpU{eBF_b?cE9K>dMTuYdKi`Jgc>7aO>$(Mck
zR&%=YH7ab;<GOq=&2xP(- at m{9`I=}OSNeNJNAK?W0e*Ji9dP@}8;GA=fbr4xmpb^_
zL6d;oe)0zVC;xmcf2Ny9(*Ah>UWTTp36*B^6*PASuWLqHa=650u17m6UNiurp)>}{
zf=B&>RAew~_NSwpFUNf1fmHdpN0eo~dtCuqxp+ytDf|&WD at Ba4k93+>xj{SB;<+N>
z%jDw~8Em+W&Fpje@(WCyk0y+Ydh4%N<*V>cyt`Ki5WIZe;(2Y_SkWom)bgafbbxcs
zcus8tb13ofb}a|6_&Cl-E6VCD#m$JE9`_w(xJv}d0bZsT3k(F5f68RE>?ZW2|GF37
zS=aTeLSQf|x}p-NqUonDYk$6S{1*FP^qHHiBflA1RTUZE_U9brv at bYnGy9mn`~pUQ
z<zHb{8MUpg_%BvM&zFQluBSFwRqswM>gZ@!EO at 2t?))C9!`x<A6|ccLm++T`j2XJM
zIXnYKk^4p{EH+bJ;>e-Um>~?}OEbw~&xHH^nt at 76cG5@HS5D4u5AeeUV}xS{1?pm`
zv>h}h=k}AT!h3tivhYS*4z{mqAMIE3?xM;N7CR!Y6xNk5sFE<vu58f_H)w_xLEQO#
z%!qiCxw at OC8oMkS8wZweSaq~#U>V+Z3p@;2_I%tmS?IT@<1q8^FEbxt*6JY+7s6_h
zYAtE4U76RGukLXx30)cH2yKSTqqX)D(J||J(BTiQtKf|KG<kczHq450N;AZ4@)y??
z0d;Mox$JI8Q*QGrs!e7Mg`a&ftC07v%G;8_V0XiIugeQlmsfaukLO;>(~^^-wUyyM
zwB~VcQ$CJxpi<g{LrdqnPWxv!Gdir?9dclBM>+fUO*Nl8vPc1>A<R}|M6u at q!gTNV
z=VN%k#)Q)4;2g at qBrADY<@&bVa?K3wX at rMDh3hT;BZPC^URqVRwKiS#zG{opwtCMU
z*S+5JMEm2?bJSp&xq7gtk^L at L1k!A}Jiklq4CBP at rYSe_cf!BF{`23j|NTFG|NWIy
z3gw$6Wd3~RJdvdKAU|K-Al~YCOP-}zGCMvx9^~gn3fzV1HBIS;6S?Y6$wLWrCbd#^
zp!g%s%8?Y!wj|hct{+H~Br3INkUA5pXcG9M^do^RoH|9tn^*flvP4g5uHsJwft7TM
zMi2H6q;#F4HME%gLS}sjbs;~?6zIyoGpW*xrA=?H2SUB3Q?+PBM`Bs0vgDQdNGRk;
zIMAKIP~Z_%4XbC8m(E~K?eIiU4*f at lt})JlqY4QsC5M^=G0A$8 at r#1>1UP*VOypMo
zKq$IMAe>e`5-M+<Kq7d3Aby=JpX_w+0R3u}Ad3p`NH_?Q6xGTTLB^<o7geY6fz+Wh
zbZmp8(}7Ss%^*hkb0ik_S|>Ag-#<(CI1*||<w}PKS)`66Pk-_7Li|psx!MTQKY*H)
z86APka(y5ZJu!f$-4kfF1fjM(k%8m;36QaDbw@(M1Tr*M9zo6{K%etS5bHxH7*yN+
z11T*BVZ%KUcq8cy19cL@=uB#FL8QzN1dcB~2-U at zGzr}@Q2+D%KvG*l2DQO~AUnJZ
z4MZ#_yCb0%bJ9609tet;cbH-7z$cQ`;GI}nA4n?!7G>c(KqdA5G7#C+Fh`P;1(~It
z$UxQhRxt*eM)^RB_X>~!jd>#TSA at z$Em|jlxb-RpVyF*i(!34e3yA~ZfuLV`jR$G0
zy+ at LJfVs*EP`T>cxD&eW9iVl3{RS8uRu812pXrovP)XwhNnUwDdb1<J7+{M2$)U3z
z2?x#*#8|-(1Tm0u5QN<kG_RV7-paZ4ouIDR>_MjF-#dYOz1|_4{-wLjktDfcDC+kq
z<^&3E*lhW7WGZAyA#3LTu08-}70G5<Rd~rvZdg8ANX+j<Ft$%3wSOQzve}<se(v4|
zKmjPb^7m8r+z${#V5?^{*aD5_rf&%*BbDJSOU>7*y0!<X-rA7M at aoT-<=eOcH>teM
z7%rYK-p`}S^0(o3Fir9)6<5}AE%|jq!hu<P&W`I}EG?3wA7Dr;@v@|8F_*<o;21R5
zV(x<uuytQrtffb1>q~F>wAHy$bRc14hn@(Ah1q>AIHoFLjvm83;ZNWnFR=kPU9q}Z
z^KG^qJt!yB5|77xY;C+vq*RhQe1a-5`S5fFvsr%JvjvT~^%(9V+rfbA`O>_9fk_%W
zkscQ3v6Ltz4y158K+Yu;7hGBncVU<3GV<e|>)Zh}C)W{&KIU*YV>owNpQpFMgkSlU
zcv78{<(1B_v&NM-o+kVXW*;;+W;uMcHn$$nhvai;RX3^R#*655FUsL1GM9s%ppv9N
zrs+~YyfruD)227i?~mMj^l@%-?w#ai#&Gls>+ at oLzO7--bMK^i5$?t at yT-LC=hs`I
z)vR at T+ye@Q7kIZ5K4cNcHr?6%Hou<HU>VivZ%Sd-G#~`#4fAwP%NuTCie!YRV at uH-
ztv%=EmO;olFO7^@t1~QGUV*m(88{K)V=lA%1+)}#9(0dCkjxm3`{j~mL7sb=NC-qZ
z`T<@){bioX3xXuPW2UY;y{BtFN9qh`i=f1sMSnac!p-vJ57*rQrNy~O_+EX|O&&~|
zd5m5*pXVi-n|PZe%-C|Nt<Er{nQ6UC at Vsp~oHFFsH2%-me_s%r<;en>*}bQ!FYt2(
zY+gi`SH6My$^&rVh|}ex34X4CNcLC0fqdn^FSHe7WL5Eu^lJqrDzbXZt0&0Qu28^k
z%DgN5s=ewGM1~)qMXwv@<(^`=!~DL~7~X3>RpA^yD7_XL0v1m<gEPXi$3=$^w$v`}
z@^BY#e at dR2ZxiN|az at L+YBT7e(9FXwqm*$Niz$)87GQ7>H(?M8TYfsSABAL$!Q2y|
ztMTV!J|7edYq5(oV|a<sOn`mqeBL#VNFwy;i8bhvIXOzUTQjwK)eLibj?D9(saQs&
zQi~)Tulwkh6N&#HH7%lfRorq}GGc6G2=p_kQof0~CpUA?&DQ5u=gV%^@Ui%K4zIW?
z&bjk>^gS>O_WUxvnpqBi0Q+shth2tj(T6SR-#ioM%~y-(sSw-|*v&F#9#wigVW!F!
zE9WP0O5AjBnS)#F^R{TJ8H>x3bbsw}NHTSn49!E7I&~UTdhjEh;;P@#_Rkr95s_X>
zUr9d#Qkh=hrw<UB_YxA`JO_^zc;VSz!OpDi-!Ph&L6%^dHP(UFtF%|#|5|~+8`Ctl
z8$iv7ZpdHJbZo5wIQrfTc)h~2w0{GyS)NxeAT!jl^abQcm%VtGD!FuN{aE_)il0dN
z5qb{Ay8QU__dmY=Y90UriqwVp{k#15_p6pNwMP#z{Ob!b8ye{$Kff3;fh~;!-yXgk
z?;a7-4?xT^M1Ea>dx5MrM at d`&+zvPS2DQLJn`{KTMFLrgxTanKazfci&1$(49DLb6
zTygQ+`fJ7UA^WZbQ*G<Ru~gnagUNOKO!J;-8`B`|=GnDcgSd#;0LlZLcqK>_Wq*l6
zu&;#SEPN2_Z6j#kq;5n}U1TFDaBUpK)y_hCi4wFEmF+tnDu%W-cb9DKAiNJj)nq-0
zjqP|k4nzAvv~An!Uf2%6wttvW3i%);`&1Hga3_Q6K~(p%P$XbIi10oa>R1^E;jzs%
zO7yHd at osM?M%%d$6c^)zc!66<YMq^gW4IJ=0X!+&K`3JQO^~M>9c1u0i07e|&~JSl
zKtwl!>`>((O#4(4P0voKw}aUJ8AW=~gXrLm;A)f(;>mR(R1o|1Alp|U&P&&W3^~WR
z?q3PY80AbZGLneZjd)$RgD}fRAS4POgvL0CY1;_oT&o9B+ULU6m>fhpZA6fq#zDC5
zb1Bl*0fyQ^ED8V$fpHM1ZKi{0WX2ca5_)+jkyGBFh7jEeksQQqn~I at vdOwIp)Vv86
ze*%|PPDb=Fm2ox72jR%LZ$i=Q?u1G`h(!*56X*!hgJ|97LKUO&fs~LmPT1N;Q0nHs
z-h|jT0-0XqAY<@BRJXar#cI0}T#436B5vNGhD_cGciBjbwrwXA$$TY7`&76;y94CE
z2{e-Xo49I}gGix`Q2g5oMRNa6u-y^?ASmhW0E6@(8rl9$P~#&US0W)fZlrPeDHL3*
z^&lJt;7xGDst2LQ2eBvsC+QG)esCZP_2ANUBkCQ_LeLRG-|R$C6YivI_>Mwhcw4X$
zvau0JjNYyU`G)EwLk?iYHsZaT97Iqz?!-OM33N#t2Vrg-fiSt+?$2oxD$7nxWFwF%
zm99iW-X~Ginw7u_tQ|zoArn-~OMqNtJqQ<Gi8r8wu7o|b6AbM;uvYJ&w>Vz(6yE&p
zL`*jVrQ_Yqg*@G+6PB?Nh at aX&kTGZyqIwWh+X&=PlMkdsFXcp;Hv)lF#+9ImvrZzT
zZ3J$j^#e&yGVjW41<+6=H_)R5BpF5ru|7T!Z|a;t0!uqctmPX(sjPW4&|Sk0!YUs~
zkG{7d(V!VR2ywp>)by at Fpw1!%tz?WlplPb;)h^tEV7|%+5uz(8QSzO at v8*42d;LI=
zFM>dM%K-?&78wnGA(I%Y at 168wLEu-Y$2(C--5bGu<!7J(p?x>ND92YYdhQ#M?>Xy2
z;5#-ZA>oZ6d)4uQ5b-1fHT+I$l}Xg<MzFnRSAw;RO(Ll43#lk-p4qOXjXDXt-&_a|
zvc%pi{6X76=mY3HCNNKS7lLbTvGWQKG&zVu4BW}kaP*3h^qjsCsqF(PBX^FJK{o(j
zt#txY-3StzC%-F!vYKQt9mGn%5=2wavp{-x0dXbGcoGlT1wi at MJ3(-hPQtDz76KRa
zgm@*RO+dZh0BV at lNlfZSpq5=?TnUXni4I*!g99pmAhpmW;-X_A5S=bMz7wc54&CO2
z(Q&K<k{V+8PFiR(C&Q)4M$FUVg+NP$>okaLv>;c|sZAm-O%?*#-ERQNqV@@xZ6lE3
zo;No_E<~<m2u)(1B^Lr6-r5IJx=iBNumCuhi<1k1qBEUDx at -ikm(T}Nc>;EA@&*v<
zo;_CJmGzqq5z*QN)^sHuZJ5iFD`_E$oM at qqK(>#55cWEMArL+xidu(kcU`g(BK<%{
zh$1Iuyb;KD*C88G&ute1vBu$S9ZJ5|gGijuo4|2gcU%ee3AphJ(7k!%vjTF_aRbHL
zBsS_s(9Ve+M5}BBy4_X9g+SWUn1sC%S_qW8w>=wi@%IJ;GTl|hm9&s#|I=OW*plNo
z5cS_G-V*o)0lWQn+MF|1%_*yD|L#NpkdjoZtV~={6iNJoBx9 at xQJn{YQuo~4$&&U;
z at Efvi;Mv0#N`{SK!;}mMnQTL^q<2eVy!dPcAwYp}5Iu(=enlT-6?r8n;l)e%K*q86
zA7uK>AA)+oi_Jj>JNrS at E5#cbp)Ch0VV4J4Ya9ecn0@>pGV4wzT&(sWlU at 3il+dUk
z#=gA~biXnlL`ORaT&xeUsvQJvf363aHs6RKfYmG9gf3ynJ;>md<REnV2B59jUJtUE
z<r{&nYx6-w&QC#Amih-$LyUv4!yUku9t1*`eexhW=Rx35>kqQ}@hL%<XCFL>l;c|h
zNjl^~hK^T)u!_3 at h_UN!1QL{&gM%zy1~vl0$tHIYVIFS;YO;<8nX^5}V`6B>_9QCj
zr=SbjvktPx_ycLnvrXVXZ4V+RfC^-TI>@l~AlBzWaP43_I*8V~6U2F&4<LGc3N@%U
zd5|gRTM%odKgbyML3G(cAjPV_5>%kxlPrqljlg~EHU|+;$Pa<`rvyI8q~JY>vAJvn
z{!@7nZRfY3tK2y|S>^l|R8+SInd|%(bQD|1LB<>pqIP@>qWat at z>?#@a4~3Q{ekqj
z>Vn9a2Y~~%KgewDK}-$+Z<#)b_HzuB^O_H`>i(&JzPK$GZ*)dOi2Nq-#75UElaT?S
z3{>_`?3X6j0&}5PU_YhC`>2*bzF=3 at DcBP-ga=^p^1vox&hjyR_+r$3p6iO|6M+xY
zizg??M=+dc6|)N#3DdO>mxeglOoNp2;Q at FbZQuU(a<ig^adzJcOei{EjM2vw1z085
z=8J(jeW$a<(~ia-TV>${7|;j2;f*-B5yw>HTXXo*ncV?|lcUu#*=eF((X8<@cjCjx
zjISBCPSc-5efGJV6+VgK2~a9E#T%?q{N>3sp-91}eYC4?04Y9PD6naoPtMu}YsInf
zKF}O+OCA96DHZo{X)#=F<*|&IoK=w#pz(2=e&{2I)AZre*Zw at B4QBIzFxG|qShzH2
zS31<y8<&cf3bT)RS89y)+gt9N=8DH%+?*wLT_KDjxMmEmjjfTO2S8(pXbcm#y@=J=
z$v~NmSkC!p_nk<OrOy=`pX)Y#cpGDXUaaTa=4KxH5DPC at jdNt?(+d)__bR*DT87&N
zs(1*vc&AzXZA&i{$;o4I!8bgoBN)y1S>u1d-5XhBk>45_hG_Qmj-b|RF<5(~(CU$Z
z5<xBQu4*+iqK5;RS&=jD-H{15ButA0>Q^YtmFelTNy+3!-?&ZG0+ASf0j<82DOtDZ
zTgJv!wsI*Nk69 at hXr7HIscPR>$);Ja%0XQNP~DZQ#mlwynNM>xmlIeine)#dpm
z77Ohy^KWK}<r-PBt!EAVe$?+DUq8PTH;>Hz9wi-{l6r$*J5Vq2pEsU%zHkAYHDY?3
zGQh7L at H~%K{srQdpI?TY7|}~uhkoy%3h$2!X*Kl;;_M(1zG<%X6Z?nV-Ds$L9UvQu
zFi4<!SIn<$2#{$to|>*0K4!#gMe|eMqB7)$n|8(L<Zug3z;H({Sjh-Oj$zF(uWI8V
zWvrW-?~_k0^K;=eHq&Yh4tsf3py>uKe?GBSz2zd)2TKnyP?@cGzR;ZF?>65=m_EFC
zQU<`TG at lRiBU+wy>q;|Rck<(GEf(5 at 3q$pCGXke*o_CgDogNo-KrE(JKf-vK`HjP_
zVTsImSz0Vir+dfC18z{m53J>`XvVIa?a!^|%QPvS!;~0a;j1{&frWdd#q1TkzD=Be
z48F{To=&r7yR5YOu&wYLi(saUKMq+Se#13h!LopwTyc;^virK_N*vS<hE$8%+xz9M
z85NdyO?qoZ{M+y|TB)yerUTdtU7jt82)+c9`}&h^Kc4yLDajssmwV4hm3f0-9I#fp
z#WkdL)V#5SffXOGbVN`+=dhW+NPGLJFJq>R6c^CHIcX*jRR1 at uWqP+_nk74L@x-KH
zjQ&P(VJuGJpv7$ZnC7kc2AN>P12UFpG*$LEHA-W<<J4xhtqepsavXumHY%Uz*g{60
zGPdLnL~#!S$jBr?z#Bs_OqR{t#M(VQH?R<d78)*g?jK`s{YXE6XeK;4pRTX%T&acD
zee6Db)HrDRgKwuBid%l2U?h)R%hm1$v(ij~Jx-Gc5X18-VbxD!WzUs3n3<J+^cdZn
zWYgu-(pL7+>W4n(ZTg`%9iQi{@Z)|N at iNyoHLn|%U4l=?)fL)iQfFkeT)@jM+j2CC
zRXn(2kLtplpGrMj5aV1&%DRfse}s92R*%|Z-Vi at FRSV4&YJ&E_O{ONC(PL(w5rc}4
zZ^5f0&PZ?5M{6q7d~TYOv-|m%yI-ZdTCx1NyR&5$@p`bC=GTTBpu+YTUa}EH;1!J=
z>Q=8TiN3=e=GxlTwIZozzfB)LGO6-;zh1UDmywf89;;=7Lnhp(x-KtlhweU8ONZB}
zJ!#kK{tgv at Uv_;)R>(1Rq0ZT|sIDjnwSQs at bEfQ^n%nLgqi1y=%o=)UXoR)Y+YFcK
z2k;an!g|j3Ib&A)^g++^uTeZMMP2IJO_r9(_DgY3FFiePw1Pb0>=EXgb*fx9)b1<S
zyo;40Q3dC1hQr$Nyrmk<w`+;XR)hm}d$z^l6+B%qWVf0}=FhM4UB2u0KY#uG^&diP
zK7amOzW?`C3nWp^ui<!qf7Pma?m>Qi2^@j+9wn;J at 2{nZrS5=91}VdvSK9!3Bb)0e
z9WAjDM3Qd+ at +j=y*&1u%EkqB(R%Qob7;@U?<&gxkRohM~)D9{w32&7om|688=)=B&
z=GQqAoG$wAP9Wn}eGnU*r+S2vK(k5TNyCi<C6A;cFi3d29S1>A at 0=NGwfMeT_ae6w
ztWd{;m^f(S8*Qvn>mb>6aJJgi3DTGnX(Nj6pia745~D=e2>y+}la6n28eTDK0<V}G
z!5L7hfXv#Hc-`6vJiw}a5Vm>}-bP3eAuQAz;SGl~LkIZvh(y>cHZns?9)uEwq?#r%
zhWbWW1h{n^oFrJR>>ogF(N4h9?JYO<t;kP%kjVu0iqw;s&VyLglY_XHVl^1P?*YO4
zuoo{gucQ=@?13m->p`T>gS045uI)6*pbUK_;V1y>yN#q|QQLtQ`mLkFHYcbCl)ncN
zb_9?%<1JJ-wv2;JN=PVQtuu*H(r$!(Se#Jg266HKLJ);L_Jp2Cfp?I_Ch$t)OYklW
z>Sha at FYhiAd%`u2_tAsQ!Y}h0Hj?m4@=mJy%oE<$2T&BA&U=gB1?sGEUp<IxT9EG7
z6l6R6(O-$;KoU&U;0<(Y);D|fmS9l0A4DmK^}5>BIS~Kz4`lj{mt;vl2%3?9>y4n|
zCSm&j8$fNX*dn^i(g&Hm?7b3d>>$SM-w0I^*Ee0b1yRb-SJEZ+sVKnqAd70_g-{~J
zKGC at -h7K~R#FYxhqfv0fWtDg(Wmu9yIdBj)dnL$3 at 6S~#deHg<sjDYh6p|Zhp&nmI
zLnRtXge_+y2q$|7EqXS9-!fiE#YIw+7%T5aa4KwEuVl0Y$`bT{y2~b8vK58G{T#&`
zK`nn!a2z#+T&r67=HIQo!H77Jaa;HDX&e;<wjaR9&-ex)8={4SXa;zZu-AdC=-``=
zJ^g|7=IOhkANb-+MA#4iCVKqQ66CN(JqYC3`VL04bO7qkC*gnlE5Y1z!QSfuw1v+6
z2Id-<EH>FhSegF at Jcc_);pkq8t^EU$X!**EAcZKa*jLh~*9GOZ?1gMzwJ(GLbJr--
zL<<or;6%<4C*6af%<PeYv26Vfc%8yGs55-;B(mA9AB072IT at h~l$B8m;q&Sn;dKw^
zuu>PmN7$3FL+B4gLMrI-m)e8y^4ct9kMWf-W&JK0n_~gu7v)LLtaDx at q=B)yuMCWW
z_XDQCd%l7e8iW<^Bmf07tdO<$SHj}vHOwfdq!4kRKZ!p{9|R(iJ^d>gA#OmF<>o;s
zSa%_NN+OiY<11kq*Gm at VcOr-K`$4GrwGi{t at g|2a4*7x1c`ng565#`=fTbr9zO}Rv
zVUPYoP-$Af#MreTgfGTXh$q?yK%BB6C$go^c at YU|M-gqSkTqr at hYk5bAXkfR(-Ofm
zgRS-}X(1Uww68=UPS-4Cot_BfDH~}bvaOKQo@))fDW^ywYV#Y}lr7r{^eM%BBAa6V
zK at eg#%0w2^Jds1`{vZ&bY>SC75D$QW+WCQWm&+tNG!eW%_9|pINd)RrMj at 2L37nn@
zq~y{<^yH-=9Oo=#YhDWf^>2WJ@?5ZdH&1fdzdoenUQvoCVwB<!f)s1L5M{%95WFSS
z3fVocg{sMhm57{K$eDR9JQnsT#Mq4<1PxDiA=~5!GPFBEM`kxlWbIzaIVTb5jH!i~
zu0$Yvx)h=-uZ3`(0`^E`Af>hzvf3)-$V>#`vQbC@=mpCP*=HvL%`{gbOY>4tNWB*#
zs{pgS6cp0fUkP7O^Acs-co1kLiq1qfMdyP+%&~7IvON8s<gC0Ff_0=2W0!c4<9p47
z7wbW0^GwWSc`f>o?nN>a7WPc?cZYYlOxRwZWO{SYWUu6}DK1(v`D at DA$z5}~cFV*h
zFST86W}+jxYwCacE`SpHNgzw+E<|({!l%KLK$fgth+6WOG=J at 6!bj7SK!h|YWXULm
zFTW>&=IEyoTghQE{q33wAG}Wjsi~uootJ{bkW+}57IMb=f$)-(Kv&+>`Vea)5oio4
zV5=npiP5!?wN at eL%tRnDW-r9jXBHGjvqDsMA_$hPLiR{YE$E5e3z2gQIWw;XfvCF>
z>&Y1l@?p(F_Q`8OK8#by+WDlA4{aABB88Zx*ev`vdLg><S_sb3LiWragZ@(~Wc5+V
zNk7^Mj#(i#(o!2 at Z?8hMC5LUu1sR2`{9&ktUWu$-h?NaOEzDVnYVy~HTBu-4UJ9YH
zUC7Eg0djU;3euqULTskcc-ON+wCA-T4VDzLPVyJ!cFrV!BAh}_OR?c=Nme0hX)Qh!
z$0%f{4Ju-jGYMo(E#zok3fiF0LTu%w5Svqgj^r<eY|DuRvRhsXN<d~IV!e>!QV4;3
z2SN1YlS2qhDP(I}${+-KFJ#Fn<m5y$2!XK+v0SMQQXp4q5S6?Zbeo<+b}rQhDUdS>
zM9y=q|Nf%Ks438?A$0zwZR}Cr%J~!}V3k3)KUk6eq<EO|h=uPOYc#b0cKG`jfGfC3
z-8MNscl>%`MVL$NZTfT`<1>6DhBxjRoZjxYQS~wT#DkY>ca9GBa&8O-?QP;9xJo|z
z1v-j_|M<8`5yfx&Ibh-f`}Eer5fh1ut(e3(G^}r&cVw!!wm2sh02L10PCu~RxZvsk
zxYOL)xcR^7M2%mw2*BE5u_LQS*2CmEeDY#9r&PkZ86h7>xkpw%Ox((t{sQ73qf~*h
zy8m{u{KMRR#bj19*y<}QTN5m6zAaY>H1$rl`|AUjGV~hb{L3S=EKfSlu1iMP;trfF
zZ#2;;V2V31%!qt{4#zEniOloJ+k2 at 9ru&!D&|^O(O!g(1((N9r$8<UCo_lkANsNnz
zNo7qJo^vnO^*F>QFZ~If2rzM9xg7~p%;K8HbT)fGi{0;U at x%D5fu^~+#>e#Ov+Hxb
z at 4H#In%<AU;(II>Uo8 at F-m~!NACoOHreA>S?Bna6wa2foW4hjZ+-BT}4%c~5BgV}S
z+Hb_0yh3~(&A#-GF~92Oij+AsCVOMzJr_?<e`+!bJY-|bO$mt at _yf&z=6FbN!qE*3
z*^Z17y|sY#CGyil`pV|H9#&JmlcTJh;gw*|C>$C)S at Nn__wKA1Ud_KXM^qt+BC_2e
zj5AsQs8f&piQkScZ-<0+Gh}~;?<>`xR-RwFqZrD(`M2!u#5`SP$8>96V;+_J_pkr`
zLV54E5U`Y^v&1>w{`0gCLjC74zuGbXn(#1QypQh%`11s+mVQy~_yM+Pw?46^^P3+V
z4W2)NCY!|`(Lg4i5H#gR1CFNo^3Y+V0s8GA3SYq-5}!Y~<rW_amsw0-<K7!|JD3`G
zN|syxu;Go|KAXxfa;4#~@wpUv4e3VHMvRJIBx9?*X^uCFF=t}Bp+3IN<DKL8uLoe?
z;;(%($Mp)oE>SPVW_(?H0Dc at -C5kO&3amNbme1`?Kf}y}SZO!+sov5YxdpIyRPyOQ
zUMIfY@$z$X-b#FZ*l$hXaXn-``obLU#c|KU&InU1Mil{$#ct)N(B|Iw6q4rRg(s1>
z6{n{7{tO?xKVI(Bek?v7(-DgnfUj1+ZE=*zgMZA{ZwLY)ME*6p+Pey_G0oe8qUsy9
z%`L<p-tj85xGmgDzjd|gy$u=);fU{CU%7)y;#VtDg>S<M*@*Gc-EI7p-`wS1vR6gh
zC%mW at 7u_OFbjU3g9>VQ)+_Ua?$wJRdcF<3y_ryF?-{22tm_#T#%Hp42r(tLN2|BiH
zkDp-1zG~wQ+P!$odVKV`EO&BdXzyDA{{3}EJnh~k;SE9)IM{K2J1!oqcT`arVa|E0
zT;uHq2o=d%t^Z)x>|GIMH%(qi%Rs!ralP-W^%ggHAj|ywu^Y5qE<V=1aCbHB_e!Gl
z!?YFScKpX@?#KOpo&JJ9)gA63%HMN$#6k}+*DWQuuzwufMcHIwj_}V!p2O-UH{F at Q
z0PX%9mE<h=Y28|}Y3fyV+w&D*X&M)ak8d<Bm~+bW{0pna#|3VL*)+xnbjFeYoY%&E
zE3jXR6nlhe_X4p5(OkFScD>UzV#m79wOYiE;uq%Fc7MHMFY*#%>eJWJ8+r4S>U^8w
z7aN~(IQ5Fa-Q20`Zf1sKLQZ(bLkciT7VX)VJxw|H>o368L-W>yCT{ACOV4MPPv2Lh
zOpl?Q80LG|;~k^ZD>!33qfL(OK|1qxRiqnF+VpL at W9Aso@pi{2f(S3`mb>-}x33Ya
zM+~nhU<krsE4%RwQ=x46v-ygBM6=y4HEwv?yv00r!PQ@>z)i1brcep!XP6o)t`gfO
z9i;!KyKI`{xN$Ii*RSYfcU4B!`~XN^_alcr>|vu at lEXwA+1ipNuV%7#{{6lnd#rx3
zPPtraKGRK*00F!lbJbpc&40SJz}!7n>ef!f2-Ys8#>q-y3s<i~?;u$KFw_w}OQyc`
z at vHUlaqLI*T0e-PZ5P=(HWr4CE9>=KrtR8`^J(W&$Ln at 6#86xNHtet0mfwGeBXk50
z8#F(xV^rgIaMC7k8_t_KCJa#%FzarZH#c3!!;9m{o1&L7_jWO at d5eLLbhRSY7d8?m
z^+KXGTt{P<W&dkzvi~&Xwx8P0;Lh~B`wJZG!eiohHMNc+vQDdI7{2}Kum8OHIRPVa
zR@`J2?Rhm%!!OtIuj}-16V|n7Rkt3kqC(Ih$>ke)zELZCY07Kpt?N_2zgo_;QEa;G
zQ4?h5g;9mkIdI~vg3DHw?ID3%zyM69VAJTe)r3=6W53QOYyM#nxWOuU#iVQO!~_9A
z?pIL*IKn20YQkmB?4V7d0>O9C=T>@*#Bmj?D-J>EB%^u8PDb}e>qO>Y%XTx1<13iL
z^7^HwkL{w0S?t~th9y&@v6Ec-EOG5!)rcjtk<djaiX at I}pEsgON(VvvIH4}X{blqT
zBN+lMbrQQTL&O0wvArYO6F(Kykt`c&*6Km>I&33btwHPv&YP8ulDcv-(uUF^B9>7g
zJ#Zn}v1try8MCaduQ&z7G7*7JXwea?;RSyX9s~HvIuwy+_nRGoE9&?v2H8lpBd{aV
z8$1CuAzVW5Nk*(As3X0R?+9XM$xkFp=mfQKBh~8cjv$|7b;K at Tdou1cOf;uGu|jV3
zqTr0KVIy6p5@{x~cO-OC!=!i89f4o0 at _^)?ct%PfkVgF>BDE07s0HGPUG<o>HU0@%
zV~=Q-+iiXCpp;5PyTW0jB1G&Gn;el89cetxb|F$z;JOjJj_wJ9Wy*v|v8(Mn5tI#x
zMJ6<hgsuUY<l^6HCe^=7u=q~mkO4$W3q(+2tx^%`?S4lx3Y|J4IXluXpF1Ms(nq9=
z&>carT5+T#`7S|fto^tPYP>07B5IeR9YOaI2eM@!k!CGpM<DZ69O+8yoha^ZIXYsM
zXa~?lf+r%yk=DqRMpVk=NVQzskz at kkyC9aZfs!i`M6aqN8Z!~}N==T)8XRe<5i!kN
z9jSqBp%F at UL|Y_+=IC|-WuR=U4Pi~BU!REB)n`YLy<MFXDfAJlsHr(7n#dHo!&<}<
zlP*k}#P1T+{nUFO`dRnj9+;%s6NrnReS1>+QGO?;_>v<gt at Z>hK01)4I#R9v?}#aM
zairhucEoOw!4b{8t#K)3B2gV_)&SlK^i<|GgVap6pvz)uj+8(St56T3j!0u$kPj(3
zl8XavWmC`-?j6LYlEa`jW6X}IvMId{ZY-%f(h6IGoCxAb6$%Z+aBR^Lj$TDs)#`|@
z<gJF5m&K7>lOrwCCLqm|G)GD!TMcR=sUup*UC{g?Bu7NTpODn at Mp|t|ATVNaq^c5u
zoLI9XB}XC%Rt=75U|Y}=QXI)m9BGMdLH(8Jh%*H}k((o0_ogClU?GYlqKOC;#Tp&S
z*@0Rr5y*$2juc^2h*nFEXbMCi{)z*#R!3T9QwUAXj?~DepdK_hqOB2uR!G$mDQpW`
zA!J9gL`Q04TM!GeIZ~8%8Xy){bwt_mxN@#FIwBr4Ayc at OrdAxOiDDBV?MiZ_1fCh<
zKpc=t{=(&Ujm43y;z(QB6tqDOj_~;kkOsjM6?PXw)5H-;$&psr6r{o09I2UXMTDlR
zBPCL3BD8@<M|34y5!xU%M{+5SG|r?5B_P?6(%2THKypBpO+g88Zh=S^M;d2RgcOL;
z5$6lUrd3BuVpEW9MI6z<ONJ8Qvka1(11*xN1l2}`+DKi=RDux5$q}XM854v66>1|Q
z)sfcN7K8v5Y9o~sDM1N@;7H+pCB%j+HHb#G1(l}R5#dS=(m0b6o=S712DSw)z at -{Q
z8{2{yNYN4MV=+SuaH$5#*}qdOnaa=td~`tyXHte1aAV~{ouSdVP=g4UE088^38CRa
z4O00PXf&;k6t2 at 5ZJ+Bjh`4#s_G54)bD;*QoJbjMKdB?7ur2g`F4Q1Ok|zp6Lz2gc
zaHR%mT&V?ppCpfwN|GlELQ@?moJfMc&nE>$xKa!HK38gxT%sc!EJ5GrLJd+lj|6?6
z6%UBA;t}+HF4Q1gaRC~4BthHfIt@}ejRc`##RF1U at hBu27itjYJW{lMmOLPt3pGgN
zLapfgEO|f*OCCkv=SmHtoJoql&yoj3xKe|-iP86?CzTbCqVIF11}U6LcnYj|@Wj<Y
z(f7GfgJf1bAdMA|LXvTz2B}=A3;Mn|psaWb+CJB55aB#3X#1>qKr+{9kjizspzX8d
z0Vynb3PQtm8bmpf3N#r at 9uQ&41Jbxq7sQ6!JV<59Q_%Q16d;8&si5(*<N*<uJOz!P
zOEpO51O%xpc`7=8a-eXju4w!$c|epUPepLJRD%d-5(xMGir8?a2B}=BD<m099*{zk
z=ck*$+}%LsqlvHhH=bgZUVLyrfzi89H}kaI{rlzsMb}mo-ve4GvUIxp+x83Al5vs`
zC=wA#2yTrS8GCo%pAe9{FE>BWKRzv3<ow}wT6E603t~P`KQ2hQ`E9|hZ9e^HLDijq
zKP`yb{L%;a3zX6PM}MCFg&E3x>gfFWwZoSMmUTXlkEQ`1F*^o--zGd>y6Lfi?X&5Z
zxxbXh_hUV=|Mtt$f4Q|)VV33ByMNpqcH2xcw%K=Rn;VXRZfH<QJ4YP4C(`gl#v$4~
z at wyz)`scUl@^O1S+bZ&W0vX_VnT~I#>C+Ny4c8A&)BX0i{qVS8VfyX&F}fUo8{_8%
z<pGoSy?uGQ&OMzk$IH|C@^pWE-gdWJ`?kBW_A=HUvTkcXzStaPK0g0(emZZ<E#L5T
ze)=$;uJ2)E^Yiri?RAXrZG1YPpSIsWV2Nq|;oC%~>2>?%bog at oV|v&IeHx4(pPnx#
z>@%$Mbbovq%U}QR^tEl>ijehn|8|=G`?T2ZPhae)==@+VM|h9t={PM1a}k`1Fwd_K
z<ITtIgNNz#6;EyMN3P8;V}h4U|9QN0#GzC;eE9b|(d&5S<9O$#fBj*z3+H!w|NHZ9
zhKt4ZI_c}nE%GDJ`(0iSA9fkXCR4Zu=kDVPvF&I0h$SKj599CK!y>cg-RbLRJiX%o
zOAo}$c>8Jl1`WFJ0VdB&xq2jT8_8oMNS+Ti*P~7IhO<?!ufdJF&2>gx+W^-&9>y<^
z;}5+0*uU+wd?${F-4;W*#_{p#D`JK7V5j229w$Tvf9ycXY+2LKxxj2QGs^+xZpK;v
zVLJ9##z)8L^5yl=tzva8qe`t;zpm+ORff^?5qm(2%%7J-chPD3ALG at s<2Z5zf2B?a
z1+=t$i6Y_C0QSOrFfi~mya&&PO%)}2xTH){E%$HC7d!GrQQgvvFA}RN>zSDmk>#r&
z$`!>g_jre7p!R<6>DNuYWtOh~&UX+VijNA94R3)M8~Rs*0W7nL12 at P&(5 at S`t2&na
zL0JGiE2{}U26_&h(m2NCj?Ll7F4F1$Im>@nHGbU4$v(mrpDUMhp-POeLG^uPoc`{R
zdpKuI$6#M~gt3IGz}TkeFGf}<G(uJ~BxFs1tlapzaurU35Jcn^bcwzNity)AOn`A`
z>FA}S=GAJtEjg~^UH1I at 6ZFzIj&@*SzRQWwWE&jiuf)mlw4Pq7moCOrtUKYph1s!z
z01pMxUV0U>XqaE6pm;|VSTC_h4#*aylk^*W?T5k7EA)gu=)W%YAP*=?IM?kaaCKdG
ze#8~>U}R4HeYMURI${d=6 at 4G^T~M%_Iq%lj8UYyG#v1stO*(I*`PO&-2|-xlJ(A!|
zuagGD*sO?0xzW*`eaqpCcqk{oe=@h|`tSV`Dmuq}L)xYF<tr^$a2h9qk_iI{;c~B?
z?VIGSwAQ#>F1d+nl6=cH$W4SM9IGoLPjxYm$Shqe at h4rEwN5L^T?mFzcKr3Xe<;;_
zU2WjnEO_LZ$Pxy1uUzIgc6c6CgD=5z2We at z<bhXbXbU^G{lyRC!k{|K*VNbblY5bd
z^{qlWO|#s at FDKvUiYT7d%D;lWZ&xG~N(N~XSFxy_BzS}ka at S~<1BrnHZA%)XYJ2&t
z at PJwHO&V_W!>fGiFa_XQK3Vi2;qCN at pV7Snz(v1A<jYEJ=_!6$5tN^#LuZt{8*+HE
z^~=^ya``{S%{ErgT}6oaUKfR|>0fnZsI-d`UUi8xG1E*9;t2czYSRSUCV8<)Fq6gD
zGgI8Bjbwo|ILM!<o>VYoN4kem9kOkh)@K<ztys1?@jz;hU)!GV6_H^f2P`89d4 at oz
zcP}Jp;^0668S;if;v$WSe~$*fBriBk`BxBVM|AJ{`N{>#oCTR0g@=hUonuS^juOiO
zKDvs*b|ADzBry+WBuywT7T4l+KVV6wJetL~zz9$(D<(ljh+GgNqo_J}Q_}+0U||v<
zj!@e!kTAA&BXbl~&)D<n5VNb0A)Vw+`QJuZL&xACVlQo7p?7mt1iaaY>@v|BCwiAp
zmKntjY7~qb$dyK$0s at A+c&aGBxr~({QEX9XrNA8Nl4l9$87)mHIoBP4C*dq8b at Ecq
zSuC1Af!LW{#QVNS9!|6QQF-^3W^lwu=vP`Wa1)`J0^8RFbEp7#jJDg$NV}4>kMXU8
zW5a!iu9QKte({wo`v+gdv;C;bJW at f6GsvO?>FoF+#)pO!S)xb*&qqlPM+TptKkvVf
z0)Gh?I1GM?o|>{uNA#&Bs<p3^KMzYQccdK#<N>Om!r%=kdXfQZ4&YcY;eDJpZVk7o
zir0F;O+oss+mb+buj|p1$U^)5xJdSlD-ue?5oactX*lF+F_v5fKwbnw7!D|*M)~Z-
zK!gb?NK#Ndm5>9K>Nq_lRD;Mg)L`Xh6(QYRbfR9FuG%RLt}-qSI>S$rp8T*JKvY0#
zfjZliim_y2>+&=z3z?Z|M2&TM!jr%-yKL{c;zoalsYwHZBGQSfLxDu(Oui6nn9{W3
zw3ME+Hf1#o$Jr&eL1#*q#byhEQ`H-2^7BnUN%N&S%wqZ4MDSDgbp at 5<o(*hcl#FS-
zby>d2bHdfXSfRqViPLn}h8&}7PP7Z<FP;NQ2?Aq<k#Y#_0`3|c?%^!uVo};7khCDd
zO${mm0$Fr0IG{!?4P^A(?+m!JINvR>E>7*3yWD^q6(1Hy9CNA2AQA?oRHfz2f}efM
zu~exg{1v>&r&;;^707w>M?ju#W6i-{aJpVm!Nwk}2koPPn3;rf&74~ViteSzq}I5;
zAeq!4L$_u at H<PcvE!Elc^sBsls0}l#`HIvq!*l&WbtC<E)f*=-fCHXF at E^t5g58;5
zH=1kC96{}jWDNazE=T~)jM_<{gIy2U20lKy3)f+~Bz0D7cXTJVJj=kyy4^M&9U(bA
zEH#IRh0{!ZiQ%ELmQq^-mqer=8|I15SWy1ml_!yhC;2Xgo5pedVjUC~oiZf9(L4>0
zzkWaOT22BVe_aT`1Okxq<$<pxzAX(&LRfHZ<>*^xI(joB6Y1eMY83^gvssbaM`1UC
zN>IL0uZY@&13jW=L5hr;6D08)-GUQI18uQ0SjyWcF|d5TX at 1YEJmT17E>QGwz#Y&3
zz6YzC;c;PA2(fXzC53ub!y^QMBJ`el%-zrkDO|OELrCusSgbmoS@@YYmRv%QNG!+)
z^m|+ELF{ES4wyLg91EGmLSa<ijPpiX9A&&89&+k6m@?&pP8&DJ%cN8&0%bTgD%<bl
z%xf9PX%xIRI!dv2d-$b}Q2iT+#$9{JPcKSc{<_Ik$8xFNrBSiRDX1Y?D+(acS~ANk
zsEjy{ZnSn}du>f1ieeC%!eqU3kSEO(KDax!ZQC|KW81cE+qP}nw(XrA+t$v^&HMe`
zU0lTd(@~w>mHAY5byh`Jbw21+kY9&X5y1+h|B^K7=dhB|{q#Q7_kJD;1iwv2o}*~7
zIHsi)45&PrN%DaO<`45Z<j{9flHVwC;t5e!`THW|fjS<KFh(#E{c at h9zce1sWQ3qz
z>jgI|IX>JYfG;FWo@(N^Ng&>@H|I4Er|m*@IDA+W)&- at jVTEz~yXn7q#n>(DZ^1#+
z?ZB4MZ6h*AT<Dq;Kw>BTS#+PbGM)%cWx9M at Uj+DeI9Av%T4roc@@f=1wZW5Jf8FUf
zO}ap%Osj(mkD!*aOi=?g at sx|lTRD~<^1k3Oe&R2#Z{QpghW0~5weyNCY4E5|bgphv
zj}kP{nD)!5di{*YAHL-S{EikPvP*}Ph_4W6YSWEg><sM?GHxuh&wmr6Ay#7%6)f!7
zQX$$c*O7A1M)xI&63}FqwNVn=!#n)QCuF~OeivBW74TP0uEH%WT2(L+j{It`EV+0;
zqdIE6Q=FXPqJmB7lKHNGRY-9*2Gh%{ZdsNtrqfIjMrUqd403QSDOlQ!y$f(@1_M!`
z6xYG(psRIE{hokqErcRzZ1~B{$MNnOFdOilkh)VWC>TP+BFqo-$w2Lw+IJRqrczae
zN{4VN6niein|wB+Lr}667vY8+5)pV1RX52DdYUj#h&nI-wdZZG1Y9145k at NWzUkc#
zJw)1yeH&Wgz}kQSr>W39<z|KuhfC=gYs%~ujP7%TSVVCLYYhCi+XcU6Nao*pwoUuC
zPK$6lQmEhFOLRx))-aH@|N1 at 2_WK%Jio~Tl0|OaNY3A_Rw4!Nmhpezv4S5-UuzvYT
z{*zRgnEOIX&(f2#)qt{LbQo%>skpLvpfuJc`KQ`1It>4!-MP#Hg6e^_PyfesZsj)=
zD%$7G`F&yMk?qc59g;6-sw#WyoCuDLKT|VQ{K6^q<8IK760pRlIzgfzl0aLD&@7gg
z^j*K?R9!<E6!ancpZ<7Y{4<n37)ImOHrZ3(Ah{ouWm~f|d%k{nFazzmIUqh|&&d?q
z!hA<w{Jp|Hsia^;-AgVMmR at 0O>2<*8$<O9Sk0<E9FPpJ!RkU}fINcp2EW#3daxU&5
zB}%@bBu(9tg2sffkOG2z+|4~ZKzlEAw6o)>doA?O-{|5_qYwe}A&@2%g#xY at d8r^7
zc^Z6?JDDdkXmYTHp`w;Wd}=4V&1h)oW5(FGOh(s*cBuqmMCrOj3(oOj+)bygh)C**
zYZ&>ejhO5R)RhNyL-&AadP3qBT(}8Ym=A-nd0g#U$p|eR#M;W!AtU4fq{x<$AkBTC
z>9SdDq0ni(dT&;CRSfP|v6VU+v+7rSwGaZVj|T1yifT1h5O{uu<?9zR<kK3x{GNL+
zeI|k%=&4BB`_UVxll$cR-+^SgCZcJRH#zmcamaz>b9)o?)qlRkJX)^UVsRE;yhZpy
zQ5fzc)e_>8V#uwGT^R+U`}=44<c+8H!^v8@#h<-JdpT5GDlH^i8PxmhJF~V|(mJ!9
z>VIQG9fuxkpjG8?_|%+QWXgB!;h at fDD|jH(2>&B4q#8iPAQ+{v%M>hKzW){>lb<xu
zA>I~)NqshFtV#(07E=+B-7MU}RNpK>(|FhchkWXKQL-vT at jY&T?<v_I3g2#KHG(Xe
zIEaEt*ZLg@&eKJ2ME!<el5HC{i<VLjQs}C_!7x-j3U at jj7GrOrKt)re=}on%AdNd;
zV}i*%7S0OphUI-`(17j2d(48t+E?Z`?-S8Xr;0w}8><n(MRSY0vrC0L6|~LwYF=B2
z0=vnN_6-SIkY3hB_FBx}2lYeTDDaRom%)Tow!DOSrfZ`CW9Q60O)GVkdhE0keae$$
zpClRslm>rjgn624i?LE?lp%Car5;wd20UnsONESHizT8&5gS0%$Nu~lJ)#;|7Fn>u
z5D=@i91~-BSJl#Orl at ct)@D(V)mc%je`S0Go=LjgZXFj73Qz}-{%O2kKf}N!1^6SF
zMiOj9jAO9#JXM+H5$Z8~`bRL`ka)B&mE`P#73mf`NqN6@%GZ$a4)?=lmIO@(5K?5%
z($%$(iu|RTYCe2CzaAR!NW&*9S&CSz2 at MTZiuEs0KYTwtK1I59$kox`#Crq|n6Z0i
zYn5Xr5^)_-`B!k|d;sOea(dQnz_l7^S36Lkb$8KyvP?2f2FcpAl5%1;KeZ0JG*8Nv
z>2;vzRc!Fg5M>f*$n1xAuoyoKrrEQExstCK^dR`}ptSezwhjmWY at K%~Bi6V9P8ZO}
zi(Z5$WIYNkuhYt*@nU$g((fjy!VF&xktE0zpX2N2=hat-%*Dn|GD-DiBa3ayRLgy;
zv66+1^q73K)mxBL$oD^5a>Gqy4?)0_=%x*=&fP8<{^jSEJbeuCzF+H%XFz!`PL|#c
zu{o?ZH_FEslH2xPz8EQph(bA0n0B5jt?dA5WEntene)#=nda*qVoYT#7Yj`=p096v
zQpRO)3I2gr=6R!^=E=w~`BGJz3MceRj2s~AQG-lm==UjX&XOC8?UxQLyLU;~fHJRS
zoUCou(V_ju71H*~-j`(!oZGZox)~>$nKlkd>J{=#e5|7DMK-qdpOm7j^n;J=C0k1s
zprB0 at N$-KQ5Xh`K{D`<Ovg*KjxIAUC at QP4#09Z`F8Ic=uh5h>hmaTaupx5)Mwt!>Y
zeS_lUq;qz;1 at kom`(fpyTipxZO^C02@>{x2fEzh~>A_X_NVTC6k^I)dCvGW at Z1l1w
z8H<t~1{`-tpTv{wt6 at DG>8_UWV;tz|9|<*VQ#oZb=c}(R`3UB=w8xQSBbHgD3ej=7
zhVIH6*i0t!n*8=V?@&KJ`|)HHl~><g5k=N`4StPx<?IVLm%PZ|;uoB}H;?C3-6kt*
znaYCtSLb5=^4#jPux#eU*%3I7`2nun8Kgj8$=YtKo1h8wYI>njU8pP+OELsjr;wq}
zHD$UsHG|W`bW8qWs;1lc03pK;TE@<iI9mEaG at u4RYkE)R#7p%H+G0Hqw0ru>OuPth
z?;)4_X$RaSvtu=Tk-A6kF^pQvARuR<ZA(z#22eLq(wP4W1F<=Nm&3d4A(*z=EnedA
z+K-*YeS&bn-Sv}qZfo|7bF<XFfL;7lh*T`?#s_;Nil=@+z-ESye0O)d1+C%<njSPZ
ziAzXr*>R<Ho!-#?A4Dd>J=`lm+hc}b=$g0!s#=VC`M2vYp61n7ghe?$bA{8-D6WW?
z1jL|5#JnHw7scVL`bb>LfRm*QgWx>*I8tCQUUfOqj1AjI<N6?{mZ%Gc13b1L9$455
zZwlAs`An26v<#t3)m)Ezc_eTv_wdSR^_N!kNCAp&4N0gF6dh@{sM;%=1-7G$kEu;|
zkuimHm;4CF-9K{j9O=nwJ|UBd@>q&YcB0N;HT=_2p^=<&1nVh*SjLJbTsYZ^+%5&I
z^!NjtyuWf<sLe3rtQK5tl=?7g;8I>Ubz+_k$!1vxbQE7afG+};V5vbj_}>l#*!RiP
zI{)M|EAQyEz;=(`CHQCj`h4m%?y8BL>?DP*^)j6oQM at CUL2>^!0;<{J>J+*>3zJ-B
zy+)WynU8pS9Ki+R$3Glgo7~<+J(PEPYCa%%;)T;#1c33 at I!7Qps`kr3sg4U?Fq{3I
zAOOw(=ju%|>cdDVzGiz?LqwU9ecBn~4j{|Xpe+fa4XO4<k$9#U;#_6J*L_J9L8Gcn
z^3mTTnM#)g=$h3fwz0o@#1JRqi-n~allqoa1<^5hT&?Dsa!8rz)3%%$wgQzO3_AzR
z9wUR2F)DLnmvUykmyc^%6cK&E#nilwj*klm)gMb+_g#DmM8s6X_MjUuJI|r{2)TOh
zYMY0{+!pk5hKP<z(44Xg_mDXwzCZ&8bWc^wg5+*9Iv5I9JK|eBQ~$b(H3w at IcnoRe
z>K<A3POaIG(g{i4h_8zb++ukJTGFgXstQ4VbGusXrljO!Pf%oN8HgCo=chly9NW43
zj*e;9Q}Gm9jW|cZ36sjP*0&_VDWKyJ^UumQ_wDDaRK8_^64nAhdr{|a4$Bjde}D1>
za@`^~8dd}&fe6V%DnMYuF&PY7hx&tF6dnygnOvu4M_L*9^T^et*1xYHJHDC>biPda
zg>Nq6#i?(oBVm}ITX(v?_Wt}RP6J_t at 3t>Y4RZtyhn4Cz$gjTcpc{lQ57evNN~h8O
z+UL#gQ(FZ$iBBP~U=w^9Vw=;=`QjupYPG_U0VU8-c28XwzE9M?xKK1Q-Lz~isK1(d
zQI^(o3Uw1)#q;|{)U4zCxh?Pd23feS!6$ClDn^#6O!&*eh);#}DsXzibGI?u8nj+y
zBb#&~i$wuN=dEIJlRVb8anPIbP!TiBm;6fgD-W%Z47SLHtT9s11(Tp4M3x>%4#T3W
zM3TNG%MhN~#G5#@dgzG;?jNU#Hn&h(`%jkMEeuL at D_>O}OWEP#x{)Zj^AUnN5+~{)
zRSZC=@(65NTN(6VB8RCT2=v4NS7&5)N}(%ht|KbtgLA;<xp4`Fi=Fd6n)8_T3T_<6
zYU1d!4-t1H6(p1qn6gCrrJLBNV^F)|H35u7AC7dHnPu>(E-16lIE8qN?j!fGs8^wT
zYVz7|Xgn!KUc-Dm&syZ3m3*o at rHMkyd5~wZtNl$9C~X3~<<)$0bX~93v$QUJs11e{
z4*J=Vi+)G>u=7pVLD*~ZWYEhQ1k!s`3~g3$7BRDFbgo7r5NV1!2&rnDbXB*ja)FCo
z_dt8TC72WX at F7-qP+6iHrkK(0y+a}o^IZhnCSfYvvPd`c1#50f>5SzP*ipw=<$Y4L
zVdm6AwKj6%MJaFwF<7+Io?Gl#Tc<Y0^lGXl_17H}5x+X2nt$rlP}J?6n^j8 at kyZ>A
z?&=BZ;n9?`r3e)d*9$F2g;urgijdmnje!n~>{=4CuD at RcqDHbH`;mfM5GY?tx<)CP
zdNj|A$$BUZB=!>uQ}hyB6!9Vo at 8qU#phB at D7o{@L*@<rC<(MZ;!4(k&CedH&S!HQ+
z6(_$D^DHyQ0IBghn+!W_JsEb!=Nn@^nIq}B4LDcMNN at kHF>Bj7A+(o6FC-pEgqVWi
z+<Zh-ItSJTZlu!Zn@?4HR at C8k;Wf(OlHQ<_>=>aT5V?cHQ?|B&T|1H+FAK=2BR*V>
zf;!n8!|SOg`+*GAZd;9Xrf#7T`%SfYW3~H1?iHdKuTXVTn}~BnddM7_C!2K(aGvpD
zhHK?a<`vv0cF}6U_!TXP>^vq}c}oj2MfIaLxK%QI%UiYDs=vP at 2|pPa=wY)d7^K-k
z#EPsS(0{$A{Mm(qg)*@<c5-$!F|dLCDcKoX!ZI=uFcAC{czEcA?QES*Y at MA57+HQ~
ziuB4J_9pc524?>q9DgnlFtPqmu4w0M;A}#`KrdrrY+)c|=dQ)@BWL^3#Lmg2Loe)T
zXaD~bDw;Uixi}h`{3vJsPYEC2{|n&1^Zy5cwVk7qy at 8R*e`uW82$=t8kdKdE^at9H
zOj((Ufcby*e~N4bER6rdqpZxxK)}fP|CE^snEogKsV47eXQX7}tVJ&m5T#c(ad+0C
z7jbtMQ~D48k2*0WMgnH`{{!d$1L(ij{~tioCbnkI<^+rkEX<s2^r9Bl&L)oZqSimP
z0VYOv#y=}K|F<)tez=R%r0j at 05XWxbQFvPY72tQ`tjGlYLD>Wtz#P}NgGN(rt|k|w
zHFeE;eZIWex2zB21 at -%h{HhI6qeh564tDsN?^$fV9NXU9-Nqn^-QuuC#U>#~^sFJN
zL at grfi{ep`=(FPT$Yid|2!tn`{r(EXLpi$j6&zmMFjF-4eE2jScrMFY8ZuZ%le+nB
zUv`uHaEukJa?^iL-Co$gK6h?UD8P^LAKZL7bBwLV{Gn?QY27`1cHNNqNUOoVx at CpS
zhBMB4gj>6^bK|$c$Ucr7;8!o%$VFQC>suwp%wywb$nmcecLMj_+8xfv?(qJV1K0NO
z&eLOqiA_ at d?-^ms%T^B72*Wm<8j|%@V*{>mf3{bfd~HQ+XdLoJUg?G-UL5(flY9B0
zl(p{;SeH7TcheB|>Acp9^W*jHo(|9M-DxON%BPa0LCajl*$glKQoj=mhALo4F&Ih;
zlDffxG*tpk?cSZ&`l>wCuI}!|``d?lFCg#18*+yD>%m~K8?5zNuTOO1(`6HjfGH<X
z0L9C*H)LmgQCk7Q0bM^$D%qV+n_u5!z#PFVe9*!fdaXlGOGmtkoQxD0m8X`3Bo}iQ
zzQSR1CpK+(e-6czY-z84I|Jct*C#p~GxuQb>hXsSOM?0Hk5D1$Z}X--$>*fFg{?hj
zEbaw3S;e&MWgZh6q<D!cus%1S4^Q!m{jIcDr>+vPeGx<)M;~4>glA5+1hi#4D5R+q
zk6?+*dX?~lYn$&J9%H0pIRZw731 at CTKGQb~qBJpnc=(I?UfI_~1gt;z3cHt43Ql^g
z<~8t}X<;Tgd#W*#NLPQx1BJqam33`i<EbKXa8tZ{D|Exh%o#23-}v~0o#P2{5vsk4
zrPfhG{VdzxT_X1OPKimw9{8Z}hrXvWhC}DHLF^)X%4+7=r4hQ~93s~{cJdBj%5J~8
z0SQoiHYSo<XG>^ztV8XvSr5$lQFt;6{@F=~d>kJm at GEb81WC#_?yaOLSr`FYoCjr}
zPZim2pjmYlwS2{qUQnQ9Bw&fB>5)oEng1MwTES;n%#dbENS_Ob)230*E$DU+zgX4o
z36dkXNl~)fXq>xV<2z0x>2=DGIghD-HEeanZ_=Tp%HP|*fxB3gw~c%R<(=~%km0@!
z_1);zE!{ch1`6*1^`FasmH`jwbF|cbaivQn_;hHN8_?;}625C21Iv;EA3Fad{)-Hr
z42pj~52u!4mr$e!^#Ho%w)6 at qCCG;p4>dM{o at Gyd$W5lMbIo&7l3P;qBKY7y$Oo=m
z9wJ5)4j<v?BjW(ACRLu^+tr8oNA^St&s*A%{I#!d8%~0Ip=S;{B*sI0H1;He7x3_v
zY}y%@=tsf{BNKL!*Wf0BdTZx~E&4CdPZ}a at E1AMlEUc0sK_Xa!S5cxiY{tmj$NN27
z7e9~x6L{+bS+fsWkA4Ds67ntM-#s=deEOp}Tc#0MNpqbrJ$7suOiPEKWDy*w?yRU0
ze~jgTnC*r6c^+FHd*AIs;qvE;-$0J}RQb<O!6Uw{M|E7hO{gCp$_=A)%ka6%&{c{X
zg#x-MC3D;&j%<&Og`t|qHY`aDJn{0~Q~GCR1|eBaeAyok=|)onQ}$<LdvjTK6H?m1
z;|R%mF at BnyX5N<nqOu7CG!Ltn?}+ZhLYPR6Pq#m$ApD97r_bWKU at C@$&LKGN(UL&J
z+{2>WnmN|!Z^GRU^Io;Mj9$OTFpX{JGQPK!neYCf*b3&b`5NF1H=r7dhRFMeE9A30
zL9k_x9*z<ZZ14%DJo at nDM_)WScx>onh6uR|F7`hN_1#E<$Ywy`nCT>{$kW=i=}GT*
zSZa~eWXI1;LLm|lfy)y_zT2Q#qeRa^iqR at N%8Z_)Dw?|9%D+e~I8yMJN=?ST(Q|YP
z4I01-n0FyC{+T6b6PLQ^EVkTh)Q8r`7N+PeOc^hhjgoe;?!A`0=kw<n`6xD at NZ?u^
z7m*63Jx|OAB}lE{qtt9i5NYxvm}K=*5uboYD9e2K*_g9vp3X<vYLA<Vs at M#Zi+6=a
zKRy+Gc*S(P+|tj~mQI>zIT6d~&%A5ib!Lm=Ezx}BL>o26i>jm8ED)XQlgQKBK5UJT
zf@@r34QE8knNJW~8(;nOCboQk)J%{4Lr1o4EJ5lagL6Z;1lrN6<vlCEMXn)1;OZmq
zT<k|acatLdk3BmIu*qlOdKf8Yr~<Ey%TsMw;w~3?l^gBUEmcXGjo%{F?3X8Im~L)7
zJr2lI*5aU(b)C?jqFNyz;ev~v*Ad!FhhaW|pzIrRHwy^lz=^Z>+(8Z%buSIXKz9uc
zL*^h~7eBxsg_|2E!}ulJ$VVxe9 at okgau^b5KGRhUGQB4?5lL5}DX!f?k~%r+NhktV
zdv5APH311)=>DKxO_Bro%jzTlbk{1LEsh?cM#hLc=^S7C#7D6uzI>G~JfT(NEwFo9
zTmrZ_`ywzA?Qeu>5Hp1<4wkndz-t+*;%mQ1FoJL5&IrZG8n&d~EhN4}vL?T9OmxOM
z?-1xinCh*|<8B7<cF)Tzykda^-4^O7%NQxRrz#&ySLA5jTI}Dwgx7wHbY0N>8BJ8N
zMbs}JRenrePIdlReO+L=5S?9HP#Iosv7$*+UW at P~cg~jy3ih^@JAqm0Fs2o=g_?{E
zF%~YdMlKTbW~>24s+br+sG}ynAf!4X_Uxq?a~B`tqx{Gr*>{>$8Tt~dDNK6jk75;h
z0W(ultwE51H`{b(;!Ex(uQI%pRa0;d(zu$A%A&?%2&+OK?^L_}B6RLbJ1- at m*pnn#
zE*)Z=rX!y+UGA0N&5wD{%U8v1UTSI(SeI}3mr$3wxN?ugd_jyE9fycQP2N}LrTAa&
zh8qRnbd?A}ncBQGBJE$m#n|%eQ8)c>i>kC$;>@>kF{&9TDo${cGc2l!NI}zWsLDu=
zct_Y7RNRXs$6yXPn+L@^0QV|UwrI<|QjxJYsEU^kr4ayshUWYT*(Y=|<acuJAufxW
zb5x+Jf(vTca`OtbcoggZ1fw>d^K&Q;H0ULj`JR-H**Hh4@*45^#AejxwTR_Dh|Gwy
z!%&r;;x0XbreGW9?jKl);~rbKZ1UcJU*ug1sZE}sGBr?MyA!C(ivfC)E%-I4Gc^0n
z<E+X(X;X5+n3a)8cbbCC<o5H`7Cm$3rQu4XYw{Bu1X5Q}lk5Y5`G=6WldsyP at rr*t
zg(hp`E%C=}5xb=;qemt;e$2YQvF<0BL=6w}nyt#`{Ehj_JuT`ebg?SuAhdWFt?<&6
zCoYk}4v%a^=08@{nvKFuO&4j7YmB_V4uDwuYk`VN7<<FEMecH)8;?X?tU2W1RvUM2
zL*vl8f<*<N*sJ{7?ja{{Uy>L>lN+p9N?!(+aU^Z|<G>jd2V<KDWAKK?nVHDQc-cHf
zT*Z^ohDJ+JRNkPGQ@#qg^o4oQ#<!)))6cNy#)(#K_5%Ap(F8<y4Wz_*ai+k>zY at d`
zcE(#{K!igwtCgNPl}`LkPtXv-r_Ryapyr%t!!!*dz+D@%xC`b at F^MbYC~2qbPyg8T
z?Fy;la~1s$VW at yg#l!=#%HwbdP><ELO2tDU5^DAoNismqlYd!AR)&XhU!5}-nYUQ7
z3QXNP%b)RShKeOeLrBypQNK%nA^NBm8xcysL&Vqi;?}$YOdx*C{#Mk<lS#GBH2c{f
zPi)>&+3{+QiDxi?xadNt7!FiC=+Nx+5YD(oqf<$6HxMcoETi*ard#auXD)_oV_!V}
zx|r1al20=jQm2Zx8TIU#N!A3!+!QnZ3NjmjEKV&($(L|4*7EkO4yS2awBgDrz9s{n
zhC{FcWmuXhr>ttGc1XZG6S}?p>x{Ks)rR_8bIkSf0tj#x^88mKVK&_SgKy2Q*&U{^
zrnh=@m78)A6H${_hzdt;>4~T_*q6&s5~8mcFL^2UebtOIal|DUtVf>L=Kl9)R%LG7
z^kI=mxOoA at Sv2Tb0hstTdU-QM&0txOV>}e3*X8fbUiiTnw<7SfQ*5EabJUtcQ&MsG
z!4}WV0#w*W%JQ}%yPh~AR3h`<3?Z{@^A6@=g&963Az8#lOhy3Z at 5K_L=2kdJ^W_;S
zGR;-ico3WSU!I$2;1jprhFg9^$SrXn8VNKI;g<f+P6FANRU;5Hv>v~CVdHJ-X28Y;
zsirx)e$g@$vDn}l2N(2UAaK=`{eD6tL at i|6x=+YA5dsjV{E~Qns2Rn~HxV8Flj?zb
zc24ct4N-wGRe(i+%9gDbOhV!B$t;tK<gwCG#C^%_`A)uI at Ui3kNJeFJIoir05Cq%|
zff%$x&a~~`A{coH12$U?5T9LYE3DA~+P9R-N{G$2rcx{(IylJxRe=zoXyxxL3Xrt>
ztw>l{1`tPAFr6drr7=fDVnZqLz$R#UfIn<P>QKp75cM}M_EgV9%@@065)7_~Rt~z&
zpMfXSge%7NwpGQIGl7iHMZ`a9KB^+pKm|I?LUH~+;BeYj*jst~p6+Ud{pgrjdK(7{
znO-?W?y}t$Ls~|!<P>T`hpbND5Sxm&XcdcOg0odGAjm-^M2&|!K-+xmTDXgs4U3}`
znBu~v9#*3L>2c1 at cBUS-W`s*)bS#|`G;h^jWlcYRp{W8j=Xwt*2Nw at K7Si_gKfcJ6
z>t-evqPmoeI?Ojggs5Ti{&9<z4f#%}=|W7<1P>Tz4)`^<01-$ftpFGC{P;*=Gdm0P
zBIeS<9K=KpX?wSg3LT(hb8oNv4^Xj?JC#P7Jc|#);hEX60%!#{a7-8E!Y{>QReR4b
zOdqu7>2QJky&@ADz$+%94!SEELJA>MC9GBcyN&W+bHwK|1qb<1dR0&Ptw6m59G-t&
zNpi}g=IfPlsie_mIS7wpy6J1}dDuQ)NX#Eg9phMgsU6Pv6$s^UaPfce=G&V>$lOIN
zQ*zlxgt@%Au7Q1#dw9mk at FzgIP=%UB#M5#RL$RsktePO8b44#)0#gqFk1hK}BI05b
zC_mmT7|l}2!HtB|GvjU~nq9?YBD#<jp1Oq~KL4#5a$t=A(;uU)>k7;fnN;x!b%L!#
zCP=5e!~3q2Fvsmn%kVvrB}N3Ewqj{Sjidwvokc(cmZRd7A(kbBkAv4BQrH%|dW;39
zyl=l3fk;lE0S6`{D|os2Bb7W?5n;~mAR>GC3n!vR-PNP1u26x6aQL)Rq>p7;D!goE
zsD%$J)DvLlD-KP$rQ)?^xZ^~lF(?yV at XDM*iVW_mxUjO2 at w2CzPtw1CB7nWNPw@&0
zLPEnkpXrrkXG$H~jb|a&m&HlM{TcuF`_r7?zYjk?H^^_&?}Dz--pR8`R7~;#JJ)-i
zVK2=2 at 0qFcwWW96(Hm*QP;K&ols%2l3nF4r<9w42x^~p$lWp?C!h`O=7Q&}c1tKB&
zos~^obZ=10#f9cB0WeYSmUQVSU<|cwZiEmxvZBil(Ie!A=M<yBjpbZPa>mfnv>*WG
zXc~iT`3b`zS^}o~e%UQitc<F2a at WnZj^tgmh~8qBzv6S&%Jw37*cB7uivut1_HMmM
zI&&1TOQPrXL>#+Mnw|jVBUi_?Y`v3n`M7<cg?SxF&A|%I1$5`bO5dbN+E)O;equqJ
z0C{iPQlPYaZ{XCFh>+!`5w`{$gumVq!TtG3dGxReAp^SCmp2k_fpjQt1JnuH+ba|t
zz$9M`2DrxOn;s=*_$zTtCwK}IUXH|<AO`@&FFj8(L;iK}II#SWUuFY7!wdiR0h-^D
z`Ku7X7R0c3jjWJT#O`FqAnSpFIvDuV=JrngT*$yea`f=x!cb=Pn6}pSc{Qda7;!l=
z$OADd6B$oQG;FLxWK7HuiAp|1)I~=-zJ!gv!x5x*XI2UK{s8U|<4S>3xXCwNs{V<b
zHVM&yRBtAcH*?x90T$4V?pKf>J~F9rS8TosawjT|kt<aC{H)-1m$YyQzW&SKEB%z?
zcqgKKef23-WO}t~5#{m;C;sEwpZd_Xfve^ideS|NVE0yRg#mIS<Vq>B+U{k)WOTTe
zKBvT#Ub8UISc$6Vy43=p7fbrLM?>dvNn3>k9~|ib+sg!zhYFntX$`%$(27-_D3W}c
zkmp37(|j46)Hyb*{%}Y-St9vZPsDk~ZoupXXau}q?Xp|49{NIX at es(FR9L?gU4E}H
z3pw!<lqM_Y7djs{B&p8)F7wW&>;l|?j-xrEpM6-{r(qE;pe7d?gO~NVD;0BXPO_6m
zRT~~5BB_C#mw=0hz@|`shx4)T+ir6WRE*O!<Im(y0;2^jBub?kyjAq`B#&fZU_SSD
zAs=b5LgY-Sm};Bjoc*Z=nNWZG*~bV!vGCi6nh|`$*KtsCjrZI3cnj>`pW+72RTzFU
zC39NwLhNwnA1n4-wBo20^*BQ;9g-xJ#_g^<5?dxu`N_aP;m(GAE~W5|rtD8{<An^G
zMwUxjkTajVYu(Q%(cJ|wM~z(oD$Y9ADB={4!;Rf0{;A=<_v?%(zG-agT#)OZfB`QM
z37(>?awVBr5X%Qh%GSk<xIjtCcI7(E??orq2(oY>w-faD)ac5fHukJmDNe^y+!eco
z@{3?QrQpLysDfvkA5#W*h>Icw8C$DoKVhz!D5s$i;us%MQQX3dv6BF|7uRUZg+DT8
zXOM!HxR{IYb&Hkrr&Tmt$h6}BQg^Ldz{G(N%Qx&lw*lR85LSW2s#@Ami6nrx^3piP
z#_`F8Mk!^`I3su(tW1%TphM{Z&#B*u9|fMp-5QXZHQ`eJ!!7~oL at Y%~x0T1|$SDKA
zD?r7a?W32-Gx5Un23G|uhuGo5>GbnkkAFqj;<UuXSeuye#{JCLN~;IWK1`G0sGfEG
z-`C}RQEN~)?3Qs`EQ5LnIYVW5P;=$4QUfgCG5?#Gu`2fy3vi)%7rX&vOYvL%kn*u!
zp|+Mm9%-?7i-mlDQuj`Hdn<26P->V=BQBEm2aJ~r<+rxj!#>n|Ncpl)ToL=Kl1TkN
zM<Lu$4bp1;UesliNUY{S`1 at bBSM^(Jj)`pjRL&^3?-6%VFb<-UMl;n1Hk%y1J%auc
z1joWAP||&ENN!xXLWG{q_29-X)l6d_9)Zn;4kU#zmC7&-LCDW>01;FIFEMwxAc2Y{
z=R)`<YK;LumtMrPf(;JDgo29}lyYGhMet2rZ$h{bN1vxTdMpRdFf1YFu<+xmJJ&3$
zINiaCCItOpVqp+PBq82WK=>fWc?xbb+^@PR)yEh~QOEOHP>Qxrzf*LDpxo32f$Sav
zFb9MXd-Dr^qU7Hdr^#^&_U8DWo|oSb%7+x|3etnHgHdk1K1nj9<`_va<w0ZDzIR#H
zjRAD6o8g^z&0b&?6ryb~Azz*Nj?ka2zw(%SU0H~KpD9=muJ2!RyDg8&bE!9G`4`p^
z-dZ>OibF2e?kok8SUL+$j(jPy!3Op6-4vQtVfZVIGMHP#5l(HWb|wHjaB%`6- at yYY
z$j3$lSD<LpV?kgCAx8HZ#~s)*PAX7+HXuINzp at N;899Ku^KH;DpA_(vVJm<sO}fl4
zi4upmNl%zRIZqzGm2&0<rqb|>@jrY$MOIK3ip1NGWXOBOU$_t at j&oWL;^3WTe at u%p
z$6v{m;oyYXlv9lC&5~Ho_w@*$8fc*XNvtP`eL*lvK=HXj#v%-_fes(PYTa1FHb0?;
ztpKj)7WS`i@`3Z<LNssIZR<!#j^wP<UfVE<(yQoiSCBl5W`}~erUA4<x{xb`3Fq*K
zg9YJvymKN>vulub<px<Ys&;M|+jhKRqRZP^<!e^uv)yBJJI+ddhAHuE>@s2&g~po9
zbSOq(Ht$U+1weJY9??i3*Z9Z%c(K1mFZEyn8~tOVaghu~bimk}gBZf+BZ4TU_ATcG
zPztwRP6#3OMtAcx)pn1M&0w1$!tC{wgGHZakDUtC!gDGL%%Qpsd^XqY8zy5jVy_LN
z;0oY%6ArvcinE#=h`n_p_d%JmQ|m)6{Df_S$Tms?){fd2?w#_`GJEt>7i-aV*e(kd
zV$DJl=+D;$F2n*V{5tH-&|**XP(&m4C?yiB8syzFH9J-Bzmty(RFVQb?-4>UzomY<
zzr=iF(5;PBo^ZgyO?4|m$kSPN8|uXpnO%|o33nAOz(9DXt~X917VK3f^V*etxaxe5
z!Zu1ibg+UpWpwG<wlQEa2M6#S&A?Z;7`Q}%Ril8Z+wG+fU32!p`e6vHfU(f)We)nA
z<i%)32xTy#v*0g6kUx1?n5#h@*N$T at FWb+V6TcW`aPb@{_Tp1YxYJ4*^IwUc!qt`e
ztpH7Ni5*P&E4|aD9;&?{cZ63xsvpdxt!m&35Jl%@r)9aVZrkOk3T<vR-rqsffR*<P
ze!s8Jy6>-=NUsgl7v86BRu%8e%-u`5d&qrmnA7h)hWcdGQ9CpRrR%7CuwJ{8z2_UQ
zZ`k?4<$r{va9Q#shCv+}szaX>+X1m4q8-#cX#+oV7I=$Osl-fxJ=O-9QZQ9E9Dz>Q
zVgg`=Oldhm*%LzYOs60AKK$@%+cvNQQQHikEu#>_vf0-zl8&r at 42WLTKX{g4(>Ovu
zW-q?Ou{XygpkzYOv2{Wd8x7l28j at Evi$+Cc7*2!A-I671I^LkkGN>eA24Kx3Vy|({
z=iVb&@oosJg=GnBudzXX=2p|(pLw{Hmu at TrjsLe~n?PxW{*m?v*a~rHs$o(|WO)6i
zapjk7+@*4Z)8JYj+yk472?1?{Ol}hh#x|G~aMk%ENd{3eUrG*$6!674a)6vkpQ?YV
zN`;pn=~%ADE5T^COnqs{kZ8k1Cxd(Kf9S;K_TxJ^qN!wp5}siDLshKr!$(o+&ajfJ
zXR!gJ^%E!s|6D%3P*M<q)pinPN>9DSMK@;HGR(ji>wXYX5w<2R7hnG>b_=6Xfqk=N
z%gFMN$xPR`&>Zi<2Sjr5!|M9oT31NqyaHJvrH_MI3D?W>3PinUqG=fIrYBf8Dg);j
zwoV}1b?;JBvSn1divZN+9({2YUZy5pWM4Zc<TIKPM-y&;p at 7d6c@(kn01V(5UBPQG
z^bdHfg){_vqOk at NNzV~($EYoq2TPK!Vq)>Eg;6BM^DeO{8qWk<R-Bf_$3zBwdN{)e
z1KJM(q_|}*^E_GZ1u3{}8cg|~J_QBdSO)Mbn>N8z-o%EE^H6{ATv*JoHK7Ngxje)v
zrcODcf-rz+i17;Rvt_ohd3muy+)sUx%(i*t(Ky8<o)OBzK1JOSLcl$@%a`jE;0?85
zy>NbdvML>$f%5AXq-a#xx!^_b`{lPOv!F5qRzUXx#GR}Zuu41n$+uvM3An>M;ybxB
zFs(tSX`OjKDM)1rm$@yXF-utWkR)Q3>dDL2xzFtD=0YS0<`(OK?BmdF%;ZslOWSj~
z?{ZP_KV{t0tmXZ9ol-qF5}02dlxzi186L_Mg)P3YN9Ce`?CzOVS1~cU4^d*za3SNQ
zW#ksQ5{(Fn<~S^|I|(1QS`;ER#~=9Pfm}Gv!EaDC>&6rJS4!YDPQNB+guCPw9TAND
zt-5|jL;S4X#%y89F<-PzF_rCm+wg_7rn7B?kXtQua0T^+%oqFxE3s_oR_h_bKSsij
zdgeG<$?<U?13SaQ%TM+u at Cxg~aqm(&29liD;BYu2f>P?Wb++l|kO7(Q=D9<5Q`elt
zqUfhhw&39yP~Cnk!+<|%-FmzLgG}DW01N;WtCu<YGb7G0fg?wzaeR>k<x$lRSfY}e
zuryvK=_c0lwS>(u=-&M`As%qH-e$z%xG_=fI-{=a@{|Jt at SOi10o2%%J$;}qi3Mi3
zw{8KoJW*`m*069kZ)oCD1c2Xym at CKgaRPusHhfJ8rC<@4DLf3DQfHePJ{Wj9A_xQI
zi;Y)^aLuJ at Mg*pC7ygRb<Jvsu!nO{GhCa6W84~%f76g}{HmI<}2k|T?;r8M7j(wxC
z+ at WyXczdz53rVfZGv5_Hw)VYdUpo^}d}5*RSQm(94{U%?g4_(=@)r+ro&>#7e(}=Q
z2CNq4m(17x0G?66xARTXfWoG_k^i)!ZYnB)svtPSctpsRg5>oIZvzuv+b4k18t>rx
znWcEThYJm`Wd^PKg5Y)vyJm#Xx)dJWfo<V;$N~(;xP}C^5holDx#kz>=^Iy*rd~Tn
zr%)v$;WVXwGWY&;*I9Zz!(WWc)ZFIixu2|vLH;vwo`L`a(Z4_OIQ>rlcAg~=<x=eD
zl^by78AgbQugNPy5t>PeL<guruWs1+A<rrgQQ#Ruh!+vhq8*yoMyz^{r|%(5#xt>U
zh9g37x&8P2CpQGC!HlQN9KDez!Tu74p^mCi%QXjo4#KH`zFvVJXWz9Hb%4ez;tsCI
z8xz54c5+}}*$HV3-kHb!YOzJ0jF%Z9vuePZM4e+?<QZOx*AvSjEJS!yX$(?@A&tJk
z2!7<EqL7!u(<Gh3TB$!dC#Vf~=E`Iwf&%Rc%0Bgm|HXR~&@v*z0g>VOcTFf8w_`CZ
zhyv$9P(Pm(r~Y)|9GQ0STfHvzFNY)X|1vbN*Du-jP6*R=O#n at hLSO#k#OU%TiZQl%
zWH1$dKHfQ at M%(Z<-(*7=@@L&yt0w<hD5uzA4JR<A%%lZ2%05RAnK$i~H$jD(-+--z
zD5P4!uEf89Zs{!9I|Wl3miM3aOYp3$*atdSZt+5tFWc4tHOZPqxU6nzpGKiXNln3C
zraED$wqp>Po~z`33ZaVia^Fxe&^7*`11|9v{_NXsS$jpPVCnt&`WP6n(Fscx*3vLJ
zPD?c=ko at 8sL={->*&~8V4#+)S${hzK?)al0nVy}4_ at lL~GojZoGJVjcK?HTq0WR87
z?QA2Gwh|3S)=r+(hb-<9y__h#)6Mlq2O2z*TgKp!R0RTMc5J<%N?>i?19c_Gz+7MG
zy at D0->4_{SoHV+OKo<IsUh-7TtRBId!2Ia#!c|;g)Z)$jNP_d#3)o7I0HzkxA9YYc
zE!+8#`IA9UT9udUM=FBvu$Afn&JTJexrD|mbJ`HWCd(Jp<gFj5IU#4R3$tXiLn<5;
zN^L)u>Eg!G%MB6xrqNVD6p|*b<wY0{-=2V;a*mJLvGRykW`<E)I*AlMCT--a2+H;U
zfvJFq;3cnl`$x`HUP_o&vO1#i798|zNla`^(L$+&<ZP&yEUJ44RMMPlNcdz_f+#Hb
z233N{;Mvj3ld6FgeWR%)S|iA}8F0Y#@?PghyJ9ON4%fM+(<-CJhgD%Km;9a6(bCB%
z#1R|NF^yDC4?*HJk!~~ijc(s%0L6N~4Az1LVxqE at 2OaVTN*-FlLZO#$TuTe;HjO>W
zxdc_KtPyg5?C=hx@=s_cjFt+XK+*--bp(Ym1mY7+MR7?%E<xzyOpUbibkTjY`5;-y
z!_5&)EowdAlGCoOoK=W>%Ji}}oha1tMN8RXzyL`Nd%1FZM%MMCThSzszIu at H&NN7~
z<skj^I9loC+rjV2whtXT^Zo03a<?F8AvBZMchTDl-E&1hepC?WSx!yrpL?grXiNjd
zzHR>%zg`c1n#Bo-%B(rrG?Afpzq%*PrUDI!bpLdnm_cf@{=3b0oVi}3Ph((4j`VJs
za1&y^HJC7a6X^#xQxr9j=}z2-#R)44pY+r#%a#3D_#MMl{5J!fpWi4wYt^0WrUm22
z(^THs4SYP7>_UinEX_@##&F**8AkaKbPv`jg}~k7RXOLWpHN2FMpu{8zmoCj(Ifs`
z^694o_#p9riLZO8AXFYaCi~m=S>@m$0q%SvWnZmhHd5adTZ+WV1M{tuf<x at Q5U=(;
z+S@))&#_*jV!uG0-{98DMLcIUEm;F~%@*^QgQ%8E96h>pke*^<8cz);Tc41SDPTzB
zed##F7=z}JWt0!3BnRxnn%%AEh7R81+l^+&+NLS at sTni<r7O9HhpQ27LrnsQXK;oF
z*)Y<K6bMy at EMBLA=bt#ncgP8)V*HA3w3t`Vy)qh9Pd*O1uX+UOCf=faH@(r86i`qM
zmk0Zb+n77qMxR~G9v1M!EWXE9Vp|m!oZJc~eq+8-+cm4h!yZSZm*(MLH%u^a$#F+q
zGf1QH5;I2rdCzeDe!b0Jh5Z;h;{m|CnHz<QIv&bp2&j6>Ap{>0aLh2>GP(O7qxm#2
zk?8qmhUMwz^5e!LtL%Lm2zeho%ov>BH!P!Z(q-)5yHkqj0LRjgig9EUB_CV>YEInq
z5onc$&Vaw&- at LJA&t42T_<0o6iu35F732uXZ{ZpZ3mPXB4x>Gj;1>3H{(abgd2>iF
zE98QgBSD;C at uEk{jvv{7dUI#v)g1)NCBHC~61Z&$q|xwIznX*-Gq=?xg>D`IyHf5R
zBDI(0nvCw at bihZ$4Q&qvZ&>i;&feL*dkZKf-@>B<bF~tXC)Tbt at 3Ls`$Q(M;+T-|c
zd?WGp$*ju$dwU=ElUrxwy<YE$3upamFRAt$UQOhCL`5NcmyY%ySTZhRSd>v2R~yLk
zQOyP;^w~5k4-3W$3wu1+`)B{j at rS3Ep_}l^^`D%k3r{xO2_`En`H1@*4Qgo7puNS)
znQ#TTh?bFIBxb`PKV}X&Rm0xv{p2fOrDR~--GN^i{6yN?L2V at U)phNLt~8;Fy7<<$
z=SzT}u2U}FO+B?T;A9wI at sup=Yw+|<>D{+mj^FTlX*0_mhVPhOfz>kNdG{!F<86 at c
z at uaH*!$Gd-W1nNmUuX!k)gqN|CmK#I0yo*d=>=Gq-hBY3Ipy(FZ|GsuYDY@*7WVtI
z6{f99RW2u83<T&;kXn&>*D3qX7DY3gE2dj%j!4wUb!oxM*(A%sEQ>49!I@=drr7Py
z2oFouq-SuALk#C#mmM=DY$JwjI<47=p4OJK1ZCcss_kbm;m$gh>gZ8Z$)u;ILHa>3
z8CtWZ9$y`Y-&)~2hTzveiO&6z(db#1HcC$2(?{fk+q6YtqMpN7?!ovDv+RzajU;*j
zUDJ at LA;v8CI()-vm=sQIqq=ti0ja at gdI8U04Ih)hqA;)?i~<dB9j6j1DmLZdQxc7b
zG?J-JipV}J&Lom^g$&wd5YnDzB4-fv)&o!zX;8T+B4v;SKA}Y3<vWsJ2`UuznFV^t
zz~$qWj*)XT`D7S*NQi@#jj^FT0=P5kJwj3lavdN><oC)t{$!ql$QFTzV0gKFf{sJG
zv4uIMO_1fltmP0BurTdEGU2Rd4p;dSMp#mDU5jo;Wa*W;c`^1=PE$-QJ}S1K>lw*o
z2-^Y~qr4DOEWvHD67etwfL2J&P^my;H88eda5_9t$#)X!Qxa-SAJF7O?g3Ik;rT{v
z|6IZ<#k*zkBxTCT=zQsU#OMw1<-2gzd8TH_P%JE0Ve&edt(o~oot*6E4Fusd*gM0W
z%6l9Iw!<dVP(mv>$W_8{6N42>p}|VYUj|!y{p-wVmr@}zDRP6o1BuL7Q_&4ou43|9
z7xk5FN%bby%y6))9&N&-p;p8rW*oSDV?mCOw64)gJgJ>4nwG(+MmY^v_WIfTPR?X&
zlcd&MmzZU*Jlok;d&sNN!lShVgzsTa#LTL_*~EjbLjjCqV(@r_Oe5g`-RR`K+{Rsv
z*%hr5MEX&t*xg?_l-e-K!J^*aM|A|e?!3-?><uyVrNZ%^wi(^eTE}ILnH4P at M7y=d
zsh4_Hv);g)^l-GU8<In>BL^|}Oof9{<_+4(jfJyng{^Mr_n(E{;{2_WL8drZwu5<n
zks%_3S#+!*t9k>Ew0lz8y1wu{qURa6wP-mYaADVVhGXyjF!KUBb||vaj!u1F8%XuB
zkkd-gL6BaRMy^hFpBUI1+vm{_di~R~SUoTJYN{IxiYC;r79(?-hlacwq<&Sj7$mof
zv--L<tDLnuj^zthJrdScm0_eh)|QC9yat)jVh0&fh!3zb-;}j2HsE046vl{vQML3@
z+5N;g3RD(0&-&Zsbg*I7O3cYMh4?3q)gp;F8R5&yVl+e2_dui}*dO+ag$&)e>&Nye
zPvsKwo<UEMS_NY#mzqf5KYBe|HzR at MvtQRxVVd&FuVH!PpZdJG=$3}A>-={-JlVFn
zb*>B@`Oh!xY6r5?AN&m1Ro at G`bX|@(>==J9C`~tzUtv10RW7vsjgv;q&D>G(vB4Ih
z5HKC^_^Yw)R1)EF at F5b9ts2Fj^{ic8d>;5_4GGut`r9^VO#`QHOJkqz4USXJ+}Ayi
z4)FPKY#pATpse9|PH0^|H5OrGyDRYLcS9|8<fG|X_uAs`NI5XLzx;=WKS#<SZW4zE
z<&^xIrR+Iq<88DF>F0dGpScHbZuq`xEKIso=i5q~Gk4Qy_e{O{9dd0WpE#^G<dv78
zpgut-KVFwUK}*LSwlB)LbZ_iY!pE-aKF`Z;_JAln{<Z0SAHxZK96WcF*mV$GOo>Bo
z8Gkr*;<gf+ZKI`25?9djp6uMvGmtOc0smoSQTxOPksLc&%Ns)-H=1(jx<=r_&pVlk
z^$PK-WrzCSSUwI48A!gg at RVCNW?gWo>N44ksC#SEE9p76`GmVQ0v-R|P|8q?{+5<^
zo&V?V;J`Ib%==G)N8A?vo+ihaOW4}kUB%}IemRDu_b0nNr?2<tW84}y&S>n`*xDR_
z8^q4?FEc0Kiditt+QAJq-4{<X4$oG(HrFcp`Cam92>V|PP_6V`HT-`6^lhKyu6)xi
z8C;_6=-sq-0uYxcTCn%_K7QxUeN~w5Izf-t1jKc+dG6M}v7fz`29Nap>1}bj?b*w_
zncaG!;gKGyiWy*Whz;LM+JxNSmh``kr7|yu8n?M_1k!);+rhf$z>Oxl=fDZ4+D!_p
zU>H5R(G;2KuRNuws<-!+yJ<yd<?5a`0|izAwfE<`^12qC(J!Y2ucXrE2J1U<RbTt-
zsxB&~0>;x%*jI%SPpE(xqYO*9jC|hp1Tep`t<Eo^`V9wl!(O~85#nMpraO?htF~Is
zNnX0kf20Xmk}b!mB-Yr9_!N>x{cuCh&Y6$qxhn9<Pw=^KMpu%%HqPU at Tx~@M-b^U-
zLE)YO4ttvDJPED|>MOw`7G4Rs*(T0r2n+3p^UOjC4Y{hO-ZZw6L1%o|2=TIdTyU|A
z!>i=$qxIzZ7^}TJYr^9$f8Kozev=HuyR(5D{LUb#0 at 2gTTq{x9yxe4zA_TQ{5e`sp
zyJ$S<N0y=kJhv;toJvJ9il{n_68}r(*!cED=mT?j+PYJVy0eRUKs9h>T+fY=njxO(
z5w{k6c3HK!oHG(`+jn=U9&I1UXgzD(b%uM2Z~i%2ixbRJ30?3=v@=ceU2^ysElD7e
zMJTq!F0r$wpbRj2n>j%nz}ZAY&jbPKBXS0NYE-T{ydT%57AKJKN3IMHR*yOaj^DP7
zTL+0k*+BD{i!mLiFu*jWxFYp4`0HZu(t=1r&xFW%?7hpMi=jdPaiC*+B3xog%zVD0
zv}lFa*Kl~(q?<2J5Z)RTQm?cDqRe*CUXoe}KM2;&F)gAUC5eHZ8De{ssrD!ZbEe+c
z+(B6sFPMyM+9d!$3f0d82I_Gq(no}9<x!wS3J{8_5>JH07Q7nVvWgK|B1|P4mlTu4
z2;W9S(ycTQ<sPyNUTKymg(he=si&S7N4pmxG=mdG_$ovq)c{_oglc<=nGcue64?P%
zU9fTL;musN<LGIyMhrtDfb0R7n~sPkV(Jsv1IrHvj}gr2Ri|3f$d>e^XcY&(_+#fd
z<|VPQL*~^$E^d=L9(>6OwS)3Yg(GV0QA;20Gm2vKFGSbqm+LL?zk{dvnTeTVon_tj
zh_ZJ=X{tmf&@x*g8f+GEGFIIiv41Yvq_vP&R)@)2Osy(Ib4ng_D1FZjtw!&YkoYO@
zl#sFQ%yv<zr6{=<_p6(y8>5_V3?u$%rAMK-?n>)q+CovPp5a!fgHlr$QXD52!scH4
z8w3 at h#HtO^)D(8aBp|_!u2*f`U{JF*YLPR44{uGVb5e3)b2w!?)K}qLoMB^bJyg_c
z$A|#KA7f61cJwvB%#m5Y4mgL^PRv{h)~r2u8{o-p`fLS+Fn(F4aqq9hdbAMh&N?D*
z#+sA)rK|axSf{UKvqfKDy$3dF7;s2fiFC52%V3*cn}}4Bte99o>6^l1kJ?vp+NF08
zuhPw^=r+0*k7!h^XRwj7qp~LXS#Z%szi1{Wia_ZU$BUf)Q&y`T6&X2L+-j1jLZ||V
z!yY{kcnF=?VR^buIF%MQTHo_dzTB-OgUcE4_EA9=8`N*yDlIkb{;(K(w^nj9X#Kwc
zi$HY0K5m%DD;uVGPU-1nX9I^Fqn at iht9V)1eeYJy7;m+U?wySH>y^G<cC!t#n<ry@
z&R1in{CRf+#tn{{GmtMXJ7&qHJ<2gM7_+VBuwx9r+Vh54cy4~GD^;ANHY=&kN`3Sw
zrp-%o^b98dtTrdB&C7E145oN6$`<6S6JJ~u&^nlC!d4Mgg7Kw^yYi?Gh82t35l0DN
zSP%yXU|ta0Tw7sEEl7D`MlHyl(}Lt|`Tg)uMrVae9of1n%oeYSJXDdP_;xt=5Jhyv
zwXVvhkvZoT(b-p>6wa+3MqKJ2@;$~cTob8i;a)MV$Px={P(V?oMQ%Vxj2WcFLGDM8
z5(l{fQ7Rs#;)_+3807{;sd$w8k)*~+_HB}qCMju>q9xh4Sphj_DO*E&uyWH$AqA2;
z75RJ at DNK<{6sc2 at 4>ZFIaXLd!ngvnRfin at HUl0=krUl8_D4?81=roX7nNCl6;X-7q
z%Z at ReeONwMC*vKj_OM}!KbXbO8>aZ~VKz+u?%_Ag{nL)IK3~64+4|1h?skmv{39xR
zRPl<k7qLDlJv-C-p!5n#-_Z3^O#V^n)tUTb*Gn<&S8RG|rg~G?Ys(ig#!pQ at swXLZ
zlhSiAi(ht({#ognRexrsUsn2Nq2E(oxsZ8;%J?1POI4V at TL$M;3t at I!h3V{-zubYM
zcAzsJ6T;aP){J)q>^6+`Z|rf$Gyle3Hcau2+=0Oq&&UZenEU4qQ$8y at d@{yc?V{rc
zb9>q`izn?)>Fwkb0qi!+{YA%5X8vWvwBE~!A^1Y3_+IJj<r4s$H%$3qLfH*tJeW`$
zrg*D8?-=95LdH*Z<s!xi at F=JVmFIC1Fe!5q%VA{g82w>nZJ2HlM%Iq8fjaZ}Wy7>S
zH-Q(%c&k0^nEB@&qkmCycII|3)(>X(wBzX?&!#61tijywHjMQE#|?A;vSIQ!vSA04
zzma+ZhcEJljQs$dH%#lT)N?TTTiL#ov0f|tbuh(qvVVbje?HZfix?GfvV#Znh}vFu
zjPblu*sC4zO5x7bA-*Z7nf!xNG{_DH_R20gcw<i+rg%{)990KLrDs$f9F?9?b#PSr
zMWt_29h#J$N$H*B1Q<+uuJ)|ri-{3)*IO~gTkSrtO!ekR`XXP*R8M)F!hbM6G3DVl
z%-6>a^LW}`HjMY1&U$0kn7WJ@{ec7Hr at C?xPlbT9%8-TKJj{m4-!z3aQ$<E9Iv5pL
z?6xVqFy^;%M}Xaq=Z<Xbal<@c*)YX(O>xcS at 5ByF=KgudSdVY|E{y)ZcQoUq{H2~j
z=^11P1G^1#|F~i9U%to}GOdqFufe##t37O(?sv849kck-o_c>Prua$qbCMGVIPI7{
zf&9vaOc}E3=&W`u_Z`6uS?y3(JJgw;$gE~aXNp(U4ivQmMJZg=1X1>UN<{}_eqg_0
z?w>YH{zguO$>?vSK7;xC<X0wtTU&Q!%m))itnbY2amOs4wwDd__|oobqpXbaIbU1<
zuwlx#+Vh67U$d`?SpLE24-;#{e0|z5#S3CTp6belOc6rck%i}s+Succ|HpW_>^QC)
zVR+w9p``?j6;Y&k*(O1NlT8w|fh>Y7e7fx(2QB-=c28$Ed8_k&DPF`X7CA{Uz(^mf
z_>=ruWXmMqHF*1(SA}&joHH&+M}ryDeA{5mH_Y(JIDNwmHf#e7I{=X}Zdt)wb|C7w
zY%uEEU?*p)r&)j3VC?ULpPVt at u>y9i!0WheaQ4qlrN4=@9<cf$ubHzxa8cliseTz3
zrT>XBUzFv%@m4>;y1`V>vHnFpySc3A#HswB%Z3&6v)&x*n={d~EzbF}toMzxpOX12
z%lhBqq`zq}?w=~_d*hs6$7O at 5o^CMeEgZ5ETvRn;>ZmKkRl}`RMjeg9om3$v!;M78
zg$t-c><iZq?He&CS5Z|Vm10p-BT>${*2%>bZ4oJ5w57*YR98rJ|3}nSh{<kTYE093
zE*sWXx~*`luz8xi=?u5z4bxB_RfV*#sSu?EgKf1uLQPFbQmJhlrrW at yE2EA|&9rb6
zV&B=D#xYCHw2l*|TWy}CH>o5{m9%U<8ahnG9949Jh1H6dUP98Zv?U--c0a&1V;*%W
zT?&YMx_Da{qVhs3ZNnVZWi9El()6e*#1&OhQzKC((dV+kueh+hC|ToeWr(__wAIiP
z-H5shVIp=8XvbOCjOlh<8cg$TgK_3=R1L<-y3rK+k+Ytd#;L(MA6QbojY(Y7 at qrzS
zyQ05{>*t*F>-fUhvi!o2Dn5^*#aWN?nmJbuuxc>XGsXCkQC~(s8DqW-esZSyy2W{X
z$^4GI={>U+ze2?vSIlw69FL4MRLtSTIe%deFKh=E=HOCRa3gM*UQyA|v2;|k3f8P*
zHFH=qhc&BcT{yZi<{RdpVg4HCpD9l~ux&BXE8{l!iF$$k4>lMU89tb&jaAL$#9MYF
zBo?EI?BWf<Ip;f8!H)S&c}33abIgCoD%`OOccuUHRQjJd&j+~hJ8?eIz_!7tC(3%>
zIDeIuEo-yKGM8*;I-|%qW4MEgc;eh2?jROdtUr&N2IGoTJnO?9)#BVA?kE<Qg&y2d
z4W|8Fi?bfwNJan1n6ILrjB)*S+%_2Xm*6jF%wL$Fk!ikZFs?^4KO<wlX8vjBua5pP
zruoQgV8`OX;U`+S0cPX<#2X<po^W9AUq0c$w!!GPWqw&!uXS8CnEcHuTGnO{!$v4~
zfX5x%0mmxZMaLPVo at 0(Xwj+*Jv at 3T&PGv=Jy!|?8#FqwVePG+-Z9dRZG&t`Vuxc>P
zFZ at i5<ISaiiBYeP7ZnYx8%+DV24lV~Z-m5{FUuPtG3{?#O!Sb~%&4cBpNjddn4hY=
zF&c40e?cY2{AHXPjQy8Uwm9oWru}t`b3K;vXV$U?c5FmfC~7}+&3ObpE)7OKxNI6s
z^~$&zMT2pNjDI1HL`FTs95-x7Y;>G4>RYxWmhFgTJ7R;MoN;{1{Itw(7yRUm`7Zj)
znC9DJd{_FZrqa)iw|rpLVv=tfoaa-;Md{zhS)cM6ShBce{dI%$egV4%WBy$FyKx?G
zj(##my*f_)eP%5ya_%9S<Fc%1VAWupv1AU*vZ9q at 4y*Ex0hSF$Jylll#w6|<O!dn6
z!W>_i;|p_iDJvY<HW>AE at DJmOw;M?5cq1@|LUlAux|;^m;Y=ZCqP?7Rf5YrH%x=T%
zHRYA9tbeH(^Wg$gR6jAvM_vPK7S~MAH5m1sf2kPr;X-OKmUD{d?S>1g#asVn at -H>x
z&$Oat4Xmi*#zcX~O at s4{7ZDuijCvwC%sGEn1M8acvwfMpgPckJ9310}^Qq&u#oNz-
zDlUV6oKar}zc`~`GWwU-FPWbz`o$RaRPc*4&DRa4`L39MWQ;GtPtG{s#94nSD>O0f
zkGwYXZHvixWn32(y)mvw2fsPfeA8f at uVH>0wjYN1ZHoGRW-V)AT~w4|6>iv$M8**;
zbKJ5Ws^i6-k~rT%z?Q{r5qH7Sinlwk5myaH{W at +Moae8n((#QqJz&}3thdEUf8F5h
zAFyjM%`f9{V^xURa9`D!=L#QKWz>~YMHH^K3UQfmkx}J{B$<8|MWo5}qf5gbMxz07
zpem|r%;Q#3Qz2C*(dVas#{S;0#eX+UP`J}1{8KvTy4&&Tuid5W=cliz$X}9- at iLSD
zx|(3PX_~vm*G1M)Tp|CB{INnc5&D<({}rjHQ7E(*lKl8D)4;WadL%Nx-9P<izZX*{
zy8Y=%k5X9KP4Ew1XV+;9`z!PUAA;cZetnuk at cwdnf|DnN?Dj8D{Z357{vXh?7$-Y1
zE-<xj|NEyM<V=%}IX0yDs8+L&_B)-MQip!0a+8rJHqB96O8>V#r%pk2%IN~W^?dqV
z38-omUFC$y!E_f5n}KS6jWW(ZE>VqEz(=R|0AA<0Gw{&|doaPhJSB%<&C2*bf9v%$
z>;xP>nCEU;CiuYXg`PajW*${PN02id-(IHNlmJ(s>s6r;+ZLbj_x&Hk#?5i#%m{_#
z!o1*BO4tSeF$YojhLCAm0xAin6*dKS;1edeKd*x?vHm#B at Oj#IKV60$tfhZV4gZRd
zFT)Q0QuN(D3>*gW<L%O5P%g8tMd6(P^n&5Y)^8^I@%}XIpx~GObh-}${@TO1W)%Hn
znEVhzj=JwY{7Zk|mY7y3KxPgLjBm4nj~9sfA6+_DrXR}>(|2Jv1<csOKEhP&Rx~gv
zFl94A#z}R5I1hg0w{d`@)OTi3;K(>YN7;A38{j*;zrv(O+h0QHTZH2c$n|f>%ZpzI
zxVJyvA3^F%H|So3<S8caLav+E0k%{3al}iEbz!YL_<EYn(m~TC7nh5itZ53t*}1$J
zw>d15!yQOMLBUwCD)6R%`v at c7j(0yNy at NIC-gNh`q51mRJqF<y`qj at x^oK|2)xBI}
z{1)<Gqt+!wBV8|De1W|TtI+*tY=~J&F?Eg5{d-Ws7}rme at A2!sI}C7$`jhutrLItY
z&~zJw at D-7Yju;clOKj!roQR9RT|eH29h^)*wtu`2 at CUvB4yNe-@p%o;^?ZrJkN9|m
zoa6mVh#oGl;dy?<$G!VJKa$$k5lS$<{OX??3{ZY;htuPD>tGTw`ZoBJ#b)w2ZX#q=
zX-DIsS>mdsa;ZLH!XJ<0eeCvwKQbUF!^zR04)^>WT2=o`Y`tFYDew{=DftlK2B!~3
zKD at v*&-cgq<=$OS-Q(-gZ^WN}f<3=HZrAsBSj3MkH*s=8D;FpC>Q`g@`S6bw4vR at X
z8K|XW7rit65o!fKr~4E-KhB4DUu1zU-%`@;ExioOmiZE9_j780Ief&odpd(qzFszu
z0=d`J^SJvweD at 8n$M?hc*!Dx$Q!NQ=(!x&XcfXs#K=900r|9q9`!zOvdSUSJbh^Zb
z<Ne-!E0W(own+CH-#!L^(cjLOx5&XA|8fq8^Ouw#yWh at mspvk9_ZqrF4Wu5&?tZ*|
zyEZn89t~k+Dx%lxaqw68y}Mi=;bQ7ep`8463-lh^;{V@=P at qr_;pJP*INn13+p#-c
zLpxN-0_XVfbHpT+#1EJ5YiPd4=8xFrKde{FZXL-D-RmoQc at 2y&y53dm%mhIeL4uiG
zW}}g=9*Jxvl05?Q^*Q8WRo9hTb{2sF>yk~@gFHNB$<-3LH4Df5_!V~q`S`=<+l792
zw7yqCc%}4nQuX=tUIv}xmE(#Atyd<xoT1BgbU8s6$yWs8>tFqgl8}Zz9L)b3$3P1-
zD7zVC&Omm3<}PT{eqb2tFi{i0M6xZHKx-}0Cz-OeQ;n<izFAiTBP%K&uAWYp&k7%r
z5M=(iwzGl8Y|rk{Ewu!eM at lyWp>~*@?89f=G|pGbyi$`ZEzkG2-Aw;nB8)&ea=FW9
zb6Me6={VF5h!mrlpfZeofVJPHi6()FQ<$lQ)_UB-_^ZZ<IU#dSczXoHMJm~bo*$3P
zU!P7__s{p}V6VoEx0PhM2q>kk!=h2+aMEGfUvuxTRr+f<G)zi`TrClORFfA8Wkm%7
z>JZTG50A^8tU+T at Ki+b+OGJr{6;qdxG``tlCl&6&<p(u7Nj8gE-8`S9nZHYNHu*%?
zlL1zhMglwZ?3a_YCTu4p9)-k2je+K)R108?)~Mf~A1;4aM{9TjBayCz8`I&6j{hV<
zsfTpY)5F7e$O^-c>y>-XJjz_g9k-OOkJ_Jq(Ly8BoK$mX+(ACkcvg$&Mk$g)*@Z at z
z#w~yV#*yDllFqNc&X`H}NV at 1s%@0auCam`5TOEFobFEh1j~oeZlxS{96x!h0;q+yB
zzLbm at M`4mc$JZHsx%{#?8z%lFfBoS<Hf8;K;3AilA0&qna0zTE;$rK6S!}<@uTT8(
z<upc=JG1Sdo`3yt#I$^C1J2W6J>68`Z;qYeb|#3_J5vYY&6Dnuba;?kA;Q`lPsAQp
zCYu3BFL(TLe`P<6!vubWC4awMK1=&Dw-W}Q>u&$}#!-jD%V$9O!^ifGy!lScc5ubL
z6ze1Ze!Vq?^0NN;=?lsn)96V<)efddQW31XuatiKVK`ii{*S`5y5I+GMsz(fUt-W~
zbwv5bzJgV&9Q{(mrU6rv7pcEHT}NY<zA;bBdr0fhmDq2{uqs6(rrI|(uo6<>J|1+k
z2 at +RQxhyU3xEu@KYQ4f^UpZOqv<b+h<ONppXp;BT!XH{{8rO&&zUu$Cclu6nd`K{~
zO+%~Nr_U$)+FPn0;mw?vN9Xwa;Tfa-mCjRHo_k(Fyx(isW`6qRLSCSVX7wR0&sU!>
zLWP_6ONs%1+w$omu6TW68tIycGfj$KH7MZqJx%x8G%Y33NX*)M8uJ<&yrVKT<yh;U
zV)HQtimmCg9$Z<|u4Xt54XQO#Q-#BKHJg`d5*~OVizZ;59QQPvSJxCYP#;u-H)GjR
zOg>aJyhxHRi%%IT-O+4LrpYS^w-nW2nYgE!y}D+EIK|_d;;v?RbxmzYq*)aB5yOsR
z at -YqmdWJY-m9VSXyoP3^Gh-b^?ORRvN)dzK*@<s1{(a$u2wQei28)b_t*lz=>vHw!
z!Y%Ue=Rf=fb at FGZ{vovj%70l<C}JF!j}ns-idG7mG4>p)hgpS_C00g0=2qTOZH|*w
z%9>sWqA2bi)o`4xTGhBatQGS+s?BkzfOj?W9&vt0Gu$>+Jw{CZqAHT`UCriZ8lc7(
z4MIRaKmqQmHou`N{+Jv~1&^B&_f?alsWMWMf?7p?48Yi)YH}P_h%AUwRanc%UDfP3
zR3q#lg at P0AtA^uHwH2iuRaK4|oVKf)9J?w5oGa at 9G?c@>YIEFFJ<}OWg}SflZYi{5
zNN2eH`QmTitt#y~?^IPn_iNMHK3#;2jGsp~;kYi<*($*in0kE_Zl#Sa1wE^jzsKnF
z=}aTQ7n$_XYNS-NQl1ZOxl;R7NP at A^zN|szj5CW|3*{QTdVr{6QcXimitMvfnp&fZ
z$q$kClKAO+t at a(SosjU?^GFgAaR7xB?ii~Y_)6j~fpVxkacbTiVqy1;0JX^IRJ>&g
zD(96aUUT1~H<g;WX4be!G|pv-t9hu#3vyIc*7w*t*p@|QjAr9da!+JB(<4O6B}bKA
zZYpPVM+T}jYGP%iN)42Oq%E?P>ETM5ERg0ktDH1cNTzLBgJnBvw!v?O2EmS<^70x^
zbg1iVfKh|$iqQbV!1%;duFi9d+yUc~o}%Wa2Ep at C8oBLOA{$_QIJ2mY9<>-?s%<a;
zllu`2F(KIz(<IN??3(J_vaNulwl?j7vtm%=p?C-!xAIWMbA-#4w{W?Ov=1)L-(BjU
zwI~HEJD+-_D+|r9ZJ8rqU&*}0_dEUn#7QXwm+6d?Of_ at Tu-Z6jSP3TuFluy&n8ZmI
z8!{q|Di={m!s<ligoBD)ouUMqEGfaVtH1<u(UCKQUg**6JLU-WC`Ts|NwOAVs;w!g
zobj3wSyI(ydXy5+EE*<zU7^Sd^|)597 at MXM8Xss8^&S-!;#3X-9GM0pPbC1N2U-a4
zVIEoNPi`uNEOO?}rSs6EcvWB!P=jKjkx6QkC9-C2au6hnyhfEJ_uNn-kK%9RL|zps
z at Rm_1UaCFN!3U?ZYsEZ-Mxa=wo)D%QIsLa<r@&HXV11~Q1(KC{2B%>{G7Gm5f?>K-
z;2t>{Kny=FgykGhv={c6 at zrG^T*9O1det{Q<Ep6+3M)IKV;Jy+AY7`QN#oXUMcLp|
z<C#TE!B&U{mu7qPz<JMmSIebu#ZKAI<N>e7TuV>e{1d5f?10HC_gnA`09 at jkN+m#j
z;;p2Eb(tPP2zs*cE*oT-&9x=&(VE4&X}*)Ly~R2i;^tfk5n+@>L>Su at 9T+r&XGGG!
zGKD;y#3-#e+LXc}lOiBy?>CBoT-G{MglJmcC<1cxX`_e)I<)Ub5zzQtH;PDXt%Cp{
zm$=FFXdzD)V8AtQ6am at CI#UERp at oeik__yAqX@`(?eWg$QC7E6gvcds6al#!3XCE|
zQzwp)I<wd)M-16;HOZPW at y}`F2;H$w93h8nVdDt7M2;Ir=q3Hk5fTXRHj;q6%8=D5
zV1#y%garTWyS!KjMMyG3m?=U4(aJ^<6MmE;&hW(bmDW1EhNuLg;HVox2sE^x41$0r
zlwRIJEs}v+%YWQx5QKmwrpW?ead9IEiBWPhK}Z~pGO8sqrn(WtB=<y8Hi8)bMi66%
zK at j5%Mi6?&xRt-$!updDMB=nKTZB?Jf{;k1G82U4P9bds(L-(gFegIkq0f~vpCV&6
zMLH7U3YveQ#$r{P&9Y2J_JsqJac^?{UEvJF(#|+nwka7Hwbt`=$x9 at iS=46f@|jZ~
z#Z<>&S=ppY<XgojZqd>#OZK`)*Tl?sfvc3|!ND!80jzyG?xota_+_vp8AxPAj at BYX
zw$U=>GYd8itWdcE5g;$BKD at 3<IMJRvIKQJ at 3g><y9zA7={h<&@7{wRMPpKrWggiZ5
zj5p7&#$mdBV3LA6(@0*4U`xD=#)ckm?}I|f_q=8ohioD9d9vt1M(`W+7!8kWb^Z0$
zr- at 99;(0yGN*8-gbd8+W-g`D9<;2%pZ+OwQo_R-ry5RNLZDP9)#*Ga-e7l9 at gy>rf
z=Y6(6%=`T*mex*Be5)BY^YoIl68pbB4>AT`^w#-#V5<3fFs!!c!LZ`<pkY0nHe(R1
zy4=uU99d<MY2f3ga?;_d0S-y!Vq*!03~8)bd6m~HWV{Jg_K6<FzDq-<N0oM;Wddf8
zp?zK{NvAiF4F~BsGbj{sQx$9GYO(f?v967pwOLR5FY8s3Bu5UzzEUrk5VcWyk0a*P
z{r~F_1e8mAeDkRSEGk8D1s5&a>YUg}0^8MV8~PsJpc7ay at ygZMIx#tbXFYd-Xh719
z-yHIbwf(^Ktopem&Bb|YLady;5`SZaoC7VN5#?Li&QUBA1<3=Y5S#j9c%o5Kl2(bj
z&B%Elw`U1?px7<RP<7gXq0CUyg4R#y8p>&+ at b>4KKvq9L^N9yQHQyO_B(<FU==sTI
z%%7Y%xT02lV=tP~Nib~4s`Q+mMW}?C_EPYs<EDghUC^(D88U&v%q=BNoG*L_lM=9a
z``llMvY3x6JSUP!vQn<oq`@Swdchb6Qrkwq{)YqhNm#~d<<~q%3ti+HYTVDb(8-?3
z_9F_M{x!Kx20Q%shvi^gNd~3wY>>k*!PhN_Qn52!`lN3>xjGmCZJEM3S<q6fsfX^>
zR7{P7ud?a1#x9yP7)?oS`j)s|^NaPk>id#CMMKn9OpQD2W*e!C^KCK&y)VuCcN;pc
zpycZ}=Y0LG=I%So@!8xZdN%h<>u&Cs7MuIb))af^KMdVN;4AmVjxvc;X2b&|C@=^&
zH%^dnNG2CdM_>akkUuP>`g?PQilx<JYK#^6TShLi4V at O)d*ZQf%oDj}Jv25ZIB-KZ
zh>pDYMeH*;5dn)yp0|SK25y7TSWetKt#U at XW6tc9;CA4NCKkBoj0-&ZaNFk&jF at oL
z-u$GeXI74~CvN8k3OreTV(^N<^W2c04}Xlyq?&>PS`DMYB76q-T^~Nu5<Ax}IAW$?
zX0Rg9F_C;`i at rQ7OwU$S#HA6 at n#42x6rXlpvCS3kff@;;s!^lM>bd*pM?&$yn){t$
z6d<Zg8A88cKY~r!HUrxp$4ZkUKpK%TOID at l?sowzV^)7 at Y*c!Fp(|sy?zl3h%KHju
z-7YKl13$uf1Zau(2cj}sdt9+|q|+!Gfni3Yu!3b$niAjFg?Xa+d}&mhN?W;VGmXdC
z@^-F_u%fdZkN5WvoX_}|+zi!(m<N^kLyxalu0iQLr<?C~NxmMExO=%IbFpaZBA*$Z
zFEruvzH-gXiWv!erBQ~e%t+*RdSJyn&d}WQNsT1nt)`yUMCF>xOJY^<R(er)dRH7K
zi5!|lwv)=ay}@wqhIG6k$=7fG`AVOO*GCGWwS1>!nIyh6l9I?|(GRW?%u_ljw!f3N
zx>~yjGW*P|y6;Rs+sa)Fm{K-g8f!~qP3^V3vJVY0TP*RW9jy))%dj_g8FeI)^YyXS
z1qOZK6{mQvJ+QD%3>oFM6FWz1S9JmNL>u#^!FmhaCx^N53*gK=QHoJwTWcCI#(es|
z^NUraF!r;?>;s2je0(X=b_V(w{(lloz&*rntl3L~xY8ru?dW7^Ba_7~n)5v;uv72Z
zx6kT)Xxv_2gMs9Hh(Xd19hqvz82-Rs`92%=z~E&~Cyu0}4dM&U)b2a)P_tm)MCeUz
zVAJe<&yz<sYT`Wdml_BPHF8;i!wV;nqS)dSr*&H at G-YS)!jCHZ^|uJtwj3%?f+f+D
zU|m`_!Me0au(a*0U?P!Z-?wCg(37}Vq(<Nw`(&<92tnZ84{`69e;duQY2~uSy_mKm
z{y(}7ER4SpObjoq3W+>vD|aD5?Lwoz6kM=uEh_q2tJf~HdEwyhW2i+KluKn83Q8Ba
zoh=o8FR)3!ZQ~Q!pgV?EK=*S&-zz1-oY}JFle*@O<yD?Il3{G}Xam5uOjqLnmBExz
zc*Fv;+kC`I*spDkxHXfTR-?o=XAzdq#I;_2736_pw<P1#X+!Npaij&JV-b23(*pVC
zntPI`PXGK&CZ5Qp?Ui0f0?Nze=zREO^5n$99rN6}7tM$&VzvyKm9D$q1*m+P_EPYs
z^Xp!0af92HFLJ)ZnG`Q=Y%hF=GbKm>Tc7I-$e7~y9g$IGZyDuGxw>i7V3M~*eazQ`
zsc)m-8t}qa&K3r1>4Vmq{N#xDCgZp~oi_UCXDogl-emNpMN at c@t0*~<t7x+F=Zver
zUlJ3ki8fb$N|Jys4y)8qpp!>uW2}nOJmbwMnVn=~D8*L}`j$O7!^Axb-Plz{MvXlg
zsG23vb1VSIgVf*g21u4sV@(FGOb(PB%BW;^H;l2yTJMb-wa~4(7d`Ol4!0z!H@?uD
zl7Uy)QoB6dVQodfify#^$c`9O4=lD(>q{f2b$+3fm{oPOeO$$kSg<&OTQr)OyINxA
z^YkJ8hNT71s-}Q;V at 4Kn!E+_&oHtt3*I_GLR!<V3TKc1l$D`zx)91qtYd$o>Z-bAR
zgM^|9_j#z`kfb^(#uP5SK5#r at p4c3T!aXaYZXA=d4(o|-P&yQG7;8!wG89~#HFYSB
zb>^23z7m^h_WkjBre9s-L^6{)c8p04W7T94BaoU4tDVpn7KslLl))mI%S2eNTK)A~
z^?v;&0y0ZfW3;>n0Xd=v0lBp<0&;7SfXw*x`pkllSWbu|sSyOrov??cz!UwugPM8b
zt<DE>Ha_?=h;%@YE&$jT4{ZHCs%vqz;whPMIu_X=1i;>i!I2K;2`m#&jU92$FKjg1
zl`CRm<Jbair^d+DcIChW&OQ<tWCXafs6zsZn9tCOX^juf at d_ruha>bn;WY&yND1Yc
z>g|E)TOE)D#5Zy2iES=j3E!98MUGK5C3EnhVDZ3x at B}tX;ATrFqEsLNlZK~O at 5E(4
zd`nE<*0vrR*d2*$dTQpN&MVy&@X>gO=R at x#s>xa3NyQUu?jLD(B(0q+aXy^^E>5$3
z%wwj}6JvHJ&5~K^yZd3lN}APQ8hbQ-VJK;~?l``~)<`25Q4G;EvcKRRP)Z^N3O~sY
z)M)JeaYc*Y^9&jhdFM3B7xT0^p9Pw9Fi$j}FO6zbnahZxM`hktIeTZwBXJAELswU2
z-8&e`M6g8ks<inN5vjR1r2pdL at 0TP_eUbas_EW%I@<HCED7Ty#ffeTNhZVD#lw2o7
z+%iUI+2HqJAN6;>K1xU|oe`=|*4`U&UuSn%mHNJ+QPnx(w-eV&=T)}1kYuvgNNOr{
zg?J#u53IKEknDT{KEQyLWY9HtQ})ZD6J~5*AZajik4MM@`vYsCQTsX?SCSYVQM;O%
zCLOjgCSDnIO&LnnCDuLYh&iuN6u#VpDSat8w37oKm7`?d4CfxSs$bmRWcln`yz0Cg
zIbJqzEMJuU&<O7>eIU^w>cgb&PW6ff1}Q0A>U!YN>nA)%LIxRlFus$}H|O0If6ze)
zFnWQf5~6h85Y>_ywYFaw!?y07Pq0P}^iAeqSC*cv964kWgsOd1d0w~zH4TP!?WeV?
zF#1&HvmMUKmB=(WP^`axE8njy>6V<1)$^TrnQ+^pookpz(j>t-*OFir_0-u8qcUe#
zMJpgz-gg%U at ho>&<_Wj)(m?afFLY9T9q at 8^r!CH(HOWltpnr?Mack$qkzh#h3&g&3
z!)feIHtoXdC{sIu+5#K{+|~Gj#U#gkX`E4Dg;dh4D{bnbl7&IvE-q<yB~rS+PqnJE
z74*jDwfa(ce6g2IuPGrZ|9?pxBPC%=vzG*6Wl>;}l+Iw<cM{{~YJ2~&Ud1?;<u)+y
zQ_KZ+AWEVr#m*cUF!l%yI52twN4~yER_RHvvEFDg!F2IUdJ<JEmiWXOJdTo)p0YZw
z5j)2roVZ3<7ni@|jkiyj7-3tRMP9hseh=&j>cm%|{V~HgcN^2bFJ8dUs$e6ym{#ZF
zBF)gnUW|x4!3ezDte_0re%5K(mIC&_ZDtnYr at lV_D(^2i)b0Hly1oB1>UjTW6ukfP
z^?bA9tnzyM^@_x)1J_`MIG588Ew5!wtOZukeM7i1k64tVV~yVCIL;W@@^}3y9G at Z-
zJ85X>lBkuLD3p~DEhwz at J-`5~(G6fRSzAX-G>?b~RWXXtaVfwxnxJnFb}WU}uzxWM
zFZLhj$bJ=GO5&ZkKhf9Bh*T`8nG;m&jU%fRcSedqn!}6?iIyb%rY7!l&uDp!Fe8iN
zp0G4bC_zIBp%CV2HbhL(m`AONN9r9D3y29tP9*Os{6s97MmFvdJ1Dwuu0<tDybH&V
z!xe8U(P+Q12{T6j&M!o2DjnF-6KAgoc0{v at Z`8##*2F%f;mSq1ugo0}21v#Xc at 40}
z`ID3}e4!o6DEEN-%a=#)v0eDSPefe5X)>`wr-RtvaEYWN<R*F^4|_z7bh2Lc!5A;v
z&xpvv+O%b!xZqfgd9sQ+j7QqZbcg5sb{C)Y^Hml|<5uEj`h6 at -e<&CdKIZlaw!lU^
z9-$-BSEWZFRiqu05Ogl>ky#`?-Yzf+?YcF#*Xv%rK+Ai?G>i~ICn&6u`bfW!2`$a3
zrlZe`w)(<0cR$*ZH8Dc+BHcu9BhpaF)y88JNRC3A(3ob!$u(C5#*Cg6KO<VlH at Xhm
z2Cn$@)oNH{7GrB}r83f)y+K^LW_vK!(DoWI@(RP;A(tI+b=&7!pm6EvtHnSsc;JKE
z+74?+RF(I^BA76BtDu$G+IxbG!t9_}lJeM6pSB_GmDv5eum<<^GK9;sGq1kC-(aRV
z0q!I?%M-}<Rs}WH=Jr-y_Yyfn4Le>1PS^%}%NYBcib$<Ev!t-^?uFvK`&&<wc8{3s
zcXbSA5gkz;j>=>|4bZ4O+d%E^HTyP9-I(_LUD^J&v);I|AD&2UD1kn)V&TO%GG`6r
zj3&GPVNbHaH6t#7W9(1V4)%uogD(VoRqjo-d_~bGrVa7BW36xcmMRyn54TRFvyyc}
z)I;)e(@K4Lyr@))A^8e^$WOjWLN^JiV3`=%<X9MO2DZWWxIfd)M=j`7l76}+%~G8v
z at ujnWl~>drie#zVPiW>l>+264<q5&1&@09h)|sWv)8xihLbS4FwCRe-Rxx}L9})_e
z?fwKpHuq!ld%#;wM1)1@{Vibau+OVth4vWk$VYx;?f0i89!g#Cv<9BLZ!~UV4Gd6e
zI0)Z0Hh>$Oum7Sr>KJ at e_~rsHfwE~FVt7TYO}1$nsS^{7Bl>v3Q8%A=#Fml2HSy|X
z>xc!-m1S3P?-^jl)L9eqp;Zo~vYMRVqjlnUF=(aQ!1B72)ZOjn1}fX}<OVlv3ANh3
zhK3!hwf%q`7_+EkVUF^n11+ac?5VZgJ3F_nf)S=IF_mvsBBr3}^{W*eK at 0FAjxO<;
z+!ID5wb(Bw@$iPxU)b5t89Rc`>?;^>Ys4}Whtb81qDWg&+{+r3Ou at y>s)=nJT_U$2
zTz!m$z0h!wdf|3?l at t74g0z~f#712j$&Bbx`vt25BL>qNZ?(YMU^u$2r%XS;*X>u9
z at +3uizObT}muoGi&9cQKx?L`ul{9(Ua32XrQUp!#Gbi+hB?`t%-mo(#X=(YI**GQ`
zYxRt%0(n3)a;{;$FpoHi@$8yrYNV;iO;5S77Z7XrL at fKAuaKqVS2`v?fl6p)Z>kC|
zU9-Gnli*S6i>3MB7{|u0_lOw=n+wduCj5kS&RDHu6ER?=3Clg^^WosIeV-q%2!Ds9
z)hxZ0mN~KT2;rF-_(GuKDpzHq3$VpI&2dvCJ|0(CBJ?*&!x6(Km3r_p`-5?)qI!+k
z*YDVnhE_ni&SLRIk?Y8`ld_Q6m~eBH=V~Y?^%3Z1I^~IgwK@>7sazCuR$8K&Jrp(l
z+#hwI#v?Tjvid`bJgU`i6d5GZ9-##|_1LpXsTAtF=eu$f`82Ud2P}#HJ{Vqa%Qss8
zydw|sdo><*L_(ej#4fRsi#=i0!OoNgGGgJ*W}!JEzLUMhDG_*d;0EvIkttAWb!***
zd#ol~xgFKDQH&0U)~;WN;#Upoj&O8P at jg4pFKnkrZH)GlDqk0%C>y0>H|`nx#QvQ;
zfm_9G&5*;u_Ug_fUS25hE?BcOC(>?=q;du$uG$G{G0Of#g#w$6XH$?|4Fen7mij1J
z9+FBav+>wl+44c=SanOIUTFo!H3Xs02)`&Zil^ZsS+IC2#NBrk=*V*(@<bo`#wAS9
ztofT=J5tLg2TN422b(zOfE2|p5r~{8SE%T%U8e#jT~>RI7=sHm@@4V^4`15e0tr);
zThtdmw~2^XugQ<ILYSd-eZyM=?9;OeXB~IZ*YRI*iaCB9e-@{ldVY at _Y|<i2vp`_h
z+;=$?iV-Y0dG;;7 at IJ)9@))Z?fy4O5?Rd at xj#^4I`i!uHTJ*;e4^*a4TH-<fjeYpI
zie$FA^7lyz#%=h at T3`1SJ*d4#bTYputKwls6JeZS4coS4gnKC3mneyYePNE at I4msY
zqZUWFlK8;d_q#ZwD<a`xbBomPNJ}1~_Xp$HtF&tb3i*-iNYnOx=m4W<Z;#0An9;Y*
z7?{x(8ur+vTN6u|Ja<zkWz;ef{i6S$h at g|lR<`YaB7IW6hB0DnM=h%|x;~BWL<(id
zgPAgGuMx(Qh=)kvrQ0<j0r52?d1jj!OtNOvzHx1fGZ3NEjTJT*aVunMN6(Bs at FgfY
zJ_s-`z1v(oiWro-6)~I!-=_n((xKEoIHM at l3BtY_$UFw|Rbo+g|M?K-NR2mY at qfP+
z-?bIj;Xt3iK1%-a```ZIfBoJ6`ge3}zl8t(`0&2%HzhxQ^G at vPwJ6yWa$5oevqS!B
zNniQv4oN<#zCM2c_~(bWHQw5vI0SRkjuVuYBH at 8v>J$08BHl5jF;eMZEo8#6C_Qgt
zOW{gaAt?hMnRHd1BgUp!^cu%nm4up{u_NX!;VUDCkIYcb{0LQYa1&#a`FVRDX*3fx
zSQ`i76SRlbbF4a#71_B6`1L(C`7D~nQ+|@;sa&h$|LAi_>GVYFt-CV}(Ji|X;G->N
zcSZwfM|P+82e!>5u46Gdvg;TghN3#7-`Fj?9<efCBla^gW!D)~$YjW_2gKHqUB{J5
zp0fL3%I<|zc4I#ED`hu6&~MfC9jqcpbz_Hgo~fyMhFf&wkbS-e)G9}GBPkSqif*Kl
zqMD)`cyA&^H`3nLCAuEyb>5=u5u>}M#QH&0|2RN_jg8co2*-z8y(Krs^l>CN4rfPV
z1CL_2<VL>u%*2SDU`npz#u}vLdV~*~R9uH9^C<3xskl#siW>`T8uo;pM5g4%rSplF
zk{hgAmkE7%_(*Otw_YP8FYA%q$oDO{aU*LS#f at 4bEmLvdwUs_+dICVycm@8-0A$Z2
z1(G=)P17W&=a6Q)^@N2kMi#d1Ih<JHf23DCu3f>6%(aTU0R#Q-X6(#?0mDmhW=Mgf
z+5WIt)t at Uso?xIrkGy@(Ve?b00!MUn#Ja;RX4o~tpp6GSpgx}y9&hI>69n7>J>|rM
z;PZ)+HGzG^iJTO5V8XV&TvTF at XT}8K4N|IO=^N^pw4)jln!WKG)(M$o at Lo0NWAox;
zBe_mU>VnQ7Q at QICnNtdqtSRMzwlq;af7z9w{Cf1e*j$V#Y_apKD|AUK%@z<#z*Kgo
zY0u2OO<`dl`xSO5XO27NgPudxT*qb>n%UQhAq|1c!k!w8QFgzG<ucLWLz*OJ<!Mpf
zN~7t&|56k8<6+NUU7|{ev^KWjQYBK~rJDkb0uAYQuj{k}i#@*D!xNF)a{`Id5^iBG
zVN%xogIqoc+%C0NlRvlG!?{$xKT?q8Yk20*BO{Mz1m3_NdADH;n{iD|hTDsa9La!f
zv0Hrc2Naq&C+%u1nE5XzSkG~y&92=KYu0V?w3WGkwd6Ccm4KdgTkzjjRM#C<M519R
z-0ouO#ble9N-uh17zJc<vIBb#)@osWyS-Ff$y#{C$Xn(+`FpO{I+FI-zQW?s&N3#w
ze2QH_)hL|B2Jb#~5wM6c(2Eyjd*@a4#746e6*BPN2lsbI?`ANsMgBUGi^#Z5*02aM
zTUC{Zq~gFOpS}r7^W8 at LL@M8nbv-4iF*xTdGln90I|M%-<!IVR7AN|~@mimIUFPLC
zua}D<Gg&*cRW(b|CvLYW29~ZhQ;7Y`zQt^!O5hOZ1wIblo-d5hWPE at jc)v9UZEEk*
zbcb#62EEw(bKShUD at o#IAo<F(X&?ua#4LM&U-rI&54~lvyTg**vl1^VnhZ~4Qw2>n
z&}6nq&}0KmR-48>K{h^zB^y}sQ;2e?g-T^0$@A;PQribfHjre|h5$^fN0J6fHjrQj
z6eRiBBFT>(BpJ^WAcG^v`*&$g5iO%9NHQW6W(Nnx9oFT<e8M61>qN<RF&a5Z1Yogb
zWYNgfgC)b>v?AaPELl+wJ<tkYA(m`l$>sBqV!ujn>kl5WVuVuamj^ug^U!1*(FO{d
zjN4sS40EJ at Y+m2;dqNo&Nj8w=O_6p9hq^3SvQ=eREcvm;l8;9$8E+7M086%dn4MTM
zs-D<J!IGn>4+lPtc(Bm6zS}2I622i=vfD#^L_8Tav5iYJPSnX(4xaqmcwyxJ^CNqU
z6mwFP{NG;^5ITA9!1~v(A1VI$=U at NsfBnt>`Zw0J7vX<CKU#8d_Ko%z- at c9&)5rC3
zEoVWXEXOr+k8SYlb$P&Ek)J>Q`Qsn%t}$`{jTU$^B at I&T at hXt^d^|rNk8er;96h;n
zUZ#6sC$+~28$Hi{=ouIQ&zMbnlqdSeE}sI}+~w$~r6;y$dMEwlJJ`m{0h1r$0UHWJ
z>>&pSj=+O=JHmR`!mf>W^Mb%`dGn2bom9a3`CDRsMyl)xIclJmlXTbCK;ijp1%xMT
zi1W5E=VUuk0m0Lkvd!}~x}5q3c6w|);M=+P=frZQrXIE$cHVGYnEL4hC3y9s!Cr}<
zgaW$uRzR- at 3W#s0pRIuS4WU8WQB}1Fh&iL=a)iE#dn7wHFV48{iJ|ohJcGTYkSKwW
z*)#>gegKJHf=dVg*QNye^5}O-9>-tW?Q<j$VlMqSe$pYr23pEgi@@a^b~Ek8v0t%S
zj1H_TfLP3NY$hSw{5nxL?U_tM6TDCXMZXLcv+;>7%LX5KJ5;xt1Y&BSI8*BJ5Kv}n
zpRIx7rQ$-?*oD1o9rj(lwiiIDaiTmO5m$XA593(sA)Y|0XvZ)+tJKP&2x3d!4I39R
z?N2rOdoe&SYT8x8KjK~X%{?yB`}KGzgJ9QOK3fKbZDM{LkPLo6riX;Ub};^IAq0Es
zcM2hRtnbB*ZUx0 at 0vlLdZUUl=-PfOAr}5Vwo$I4t`7SD<mVNjaSAL7x at CBAg9H4~4
zHZjv)^h721#9YE`gY|+Cg56bL(&zBZ^4UTtY$XL!=-Qinz8-IlFgxcj=@V^m*-{Qg
zOp-g;4H0#32g-O$_B!QIWFiYaqf(wXyYlDebyEO%(z547k>aBqib{%gP!2`)msWmU
z^!86KpWoYw56tpgf5`zc#OByagdtcVj8F|lkfVOK8iHLOihZxODRx}^(MPaX5)#q0
zCPshCndX*cEMIQxv*plh-*RT at 5y+wY2I`^vjje}n5Y$5ql6GOS+SNZiU&?xa2btW0
zDDor<PC*oz6zzl{I*Fzp`mGQI2yec|Vp0yl(?gtWITRix3vvjy5I4Aun^S6uK{yTB
zD;^{xgA3VH3Lg;rE|!l_>R%`N+H0ePJ at tX9^J{W%%)&2N;};- at u1z`g+K at who;LIl
z-#<TB4 at CqTRp_CJQ1FeU#2waEKYSvI;qgQd)_%|$S<y<N9*Q&?NyTb>Vr-Hz_(ZSV
z)??^_TnHG{L-4ejKUe)Yd-GPAfACQKL?|Wy<{5>pOF<N8w2f+JiqcTCtEe@#V>1Cm
zl5w9{8V5)s#F1=sCMICU=G&48cFVjUB++YA5?v1^QM}<OB3PU0Q4vMq6FVozd6f0R
zy4{Hv4bi*r3Dkt02tfq9il0yrMNw=+(~J{Iy$M0|`+)s_1ra{u?eq$w>pFsHyB<Mw
zU0;HzIP0+S-w8rZ!QLK>aL*Ew2qWBMX<Uqez4`@-n*IIjf`r?p)(aAD*L6VwcJCK7
z_{5xF2lmVdM%3c%)gO>>N7<+akf<ei^8txt3nU&pKq8)R7bM)yX``npA{+IBL_}y8
z0^$zk1qt_PBkTiZ)r%2G(q?$X2=^p$gun=-b&QJ<k at oyx1ZuVH#R$}@_7 at Q<$XWh_
zM~Hw>MA)_mZQ5RpK=^50jKJ+`y$Im;5RI%iV-JX&6bmBUZktw*UBsbA3y5%gZjK9x
zcx*w$@dzUDhTVvbpU?mcBM^kvRbT{S><?u+op?YZdbd3%P!zi$;r7&m6iC!0y&#Z4
zl{WntvY!cy&5YR+xr1`~6`n3mvl8drdsV!sM~^x at RlcXTFmu(_jG_An8eaiYgYQQ{
zaFZD};f*#Q;PT!l>d{j`bJ)!!;R@%}Nz2qA_7NwtG~fzk*tVC9;^Fbkm?OMls0~)w
zJ#lQLoe)LgvJKG}QSyl~c&|F^v3ZY~?KuJD%6h{JyA*vRb5?-^41&LF4rkA=S<BGr
z?clGQf5eo+=At8?_4tR#fre`{L9b8QX{J3hlePqhedJvjt+o~?paC|3G9}mI`98jW
zX62Eo74}jGwo!Jgh=nu9o-ys-!Gyf+sY^A^D)i?sweUP1HY_PDKvZSOd<ImO9~DCO
zQ at BM>f#$*IPb)Nq|F(wbVSb-TiA)U~8i^DpWzBz$uDc-Y*XRmS9sXWx&!*-1e{4;c
zs<nO|*>gM|N<v26R;WF|eF`_6j%qTd!@g807{C8-QwkyAaJi76U5!;+-jN&IbDZdv
z>~DlMZCgBT%g(Rnx<cGB+SWXUd{<j6vH47JE3}vj^OM{y#b^&Ljb8M?>{K`@SsYk}
z^;)sTEkGg0vg}V-<!HCeSDKOnEW7s6VUMRy9>^TNv_B!HG3S0 at HqJsXnNjC?FpRfk
zr{`7m#KyD~l`^l#LlaM4?~ZUT*6Y`atZ_*;S;?h&Y*n?^Cq~@!wolNT?{4B3()#YN
z>nTay(K*vAGlsG_^Fuh~QI?^-WbBrW=CwZex-85)#vU<;o*CMwRV~x>iQ6*53DvXb
ztRVZBed|e&Pw>p2`vxD2ZqGNI-1n>}7=-s-W7wuPkD13U$~Jk4Zu$K^qz<~4X{!7@
z7%Y$HPRyA9OM12ASaKslzfbWlFbn=awmcaEggL at Mj?iOh9bJDjG9p=gy^tle(J!*d
zDl$Kjk%L~MpY4EeIIdvT-$HiSp6t<S5IgEXuD{*F4=t+^z}&fIgOUxW)lTu-L0Wcs
z;3vg`Y%UcWn1g^v>j!f~PyKC0UA(Gih~YA0HXPcKs~E>Dz=>DIFxOggpYsyVw+*8J
zEzLBBV{sg63$SR$qe8_is#I}&VekbR8U|;|5``shQ3olUXYNG^Iv%%{F0GV+aMK#s
z3iGolrGOI+w<?{Uc_xciqpV);7ZZ_c;P}>!^IF<5HGUu at j>$2dp&M$>eZ&`fQYB5U
zuakvRiq4qCIb~``s at xHNTJ6%MxVPu&;T(8w+$*3<`3B5hz_Y5lXHzV?rncevk^WOf
zovYq(><D%ZLC at zsi_I6ek<HG;jFfNdza!n_jIz1k51*Xwy&oPNC>l|q*dR&AxI
zgHxLdJJE?l1+=;0u0bX1?usL;^06f7A{<zZxHhjOx|^p4IgvGQ8eTAJ7Cp1Xf>B!}
z at 08OA)3sfc>OLs-rdR|zt+y3h14>Iw#+*p&5ZLrXk$Rj-$;H%6;~={-mE*zjHWo(@
zpC?gS9Dm0Ews^gjSSbjVAOFJK(b9TbQQF%Jaw_*1kfrS$;E6}e{q;3s;MTuBECi`+
zyG^pcZ-~%LrXGQQ{QOA%<NGha*{@&i*Pjq(_O8EQA8jO4hyC@<^p at 4aL<T%2YXelN
zVHz3wyxyxuw(B{4fB*R1%{9a#6Y5wb@(h!lT;c(Vnk4be(p57q at eI!-iiZ0J_?1`^
zZ`k!k^DglQGA>8ejO?Q3)Rf_w>-Cg+OA>Dw$rRNmC7xL_JAx}bgGCC_6`tX$rX-1H
zmgW*%63=k&<ad{N16;@|lq7&f{nRBMu$<x&@8BZ|E(;_cV5_eq@%)pb-#N~qUPs~K
z)Qu(M3J;eYENdw|b4=(OS9oySy7b#|>tS27<T<Yu$;<61JOomoq`*V$MftAquG|4}
zd`Ts4Sm|qWg~j`~Cx_T7^OPK?j>5w!$xbft4A$~c%emDdAKd$*BTtyExv(l<LPDN=
z8F2(9jQGU$-G!fEMmu(Wcitd<cfN7--8qExogebiZ at 5Js0lB{Bem~DWa#mid5|C7Q
z at S{|VE4<@pDZDR{__r*yjNFX!ZWek>hlRp)vCw0BW1*Gg4~h{iv at 0*F>SS8^(=A1r
zFzt!st!%WH12+vp?h?aYy1g0!0FQkohT%2VojS|nHm}jZaOvt>NN~eVvq7KQpiVlG
zn+Bs3?s{XpzLp1Wjn8{0hV1qCpGjhCM^W>F<n$9RW){4{JLH3T>sj6w^sN`nLMKi2
zwluY&4yN~N+`&oF4l%f+ at J!T!2-T*hnmGcK51mi96w at I2A1vAab61m)dMs5^9m5bU
zJu8dB4!E2KsR-$w>tH!-Ukk#+*2@<CHzmZ=h!E~2d4O)Us(Dz#*@)@Jh2s2JEXv>u
zykSdEHHS+q+R}(DnNqRZP+TvFcTXZ7X7<#rZ!5}VBBnu{M9rl|R8~x}(EEsz&NKwC
z`tkwKO0kL<mmP=2i4<^4iVCJ4wv6`-MW(7HIBIHAdGp}rrOpM3%TtODgJVG6jf1fP
z{cOFhNJTTlW;m9n3iLg?zb_SGXMmz9EvJRRc9b0f3(NE(!W)K?VDTHQp_<}QL9e}y
z7Y```;O;lCu5vK%9gNQv=G&b0hUHjkSaZ_`PG5EgK3`o$)$<#!9BXfjp0kXL7b!61
z1-qu*X=`d#ahaisn at TvEc;@Uliw!$fHg27(*pDmP;m at I9?t{5w&1*qKAJZBynU#dr
zA|qWS`{`(0SCq~TcZTX(oov)?#ZP7NI2PtC=M}K1Vj%|GZNB(XdcL3FZ3e4ATP=(o
z`y>RzJ-;MKh~QXJGcnpVd?{MWDLj)}zG&F)F?2?y`F$_eCypa^Wz&)XXF!<0kz=z`
zv>t4eYk&Rs4_`CwPUW=qpO;1I9atlz>-V)p9Y?>LC3gyB*YAeQq8xVpZn$+~>36dX
zjkS>P23Mnc!3Vcr`p9>~(B03|Hu%)#yWx at 97bM@!GP?x5m&UfFk`&s+d+ at l@?>ku?
zYXE_MH=HZu<@z0%;gsStLussUm+!#xiOctckK(&5knezn<(J|Mk{XilI8$dAaQ%)`
zi`QJg`z at L2((h&oUUU6!xL#f1@$1timUrl9g#~Jt*U|3?q#Bb1Jc7?dxqiR0$K}m?
zGI4#~^)0;~i|coMbErf!&(JsTDgBO9)=R(}-ofa#UiW%rnB6?Bw-rtOvsI-`eO(IP
zof(Hv%9Pg;@DGe@$sqZDwoAUB4;=Y^P9XX2Cp>zcS2${(cizuaI3N<1E*i>5iT6dt
ze8l^4vc&tBFgQExP#v}>duTSaj`G}<wOcgDTCM at Mp{W6UY?!Z|!Gs%fQ*#Cv<RT`p
z#Aw^(mc|fZY{(w=d&Te+oJ<eHY_$-1LpxgIJCj*})oRz=4$IITrq*Y=25Qk^-Qwyj
zOd?$)K2Vw at aeRPe0Fmk^68)DFcNkM-?$1{hf`&IAB#F{W{Rbz>^Rc3fO$zAH at M-ON
zXvgl7*zOfmkm}wYz4*d;E#DCq>NUsewX-uaI~R59_{PdABangNAv!5Vr_}V6%hZnA
zxPy)=8Zt#Ei1q{Ns6O8pl*t!lFty>-;s`;sD~B{L&Xa6tY1IpjJ+)e0R!>(Ei!Y{D
zT1>6HtIkuCI>q35MrzS%aQu~@K7_E1a-J4(rS+jw=&s4tbwn+!uA)wK;8uC<%^i0E
zD)UlP7+I2!B~_lORC;J_-l^;eYxua17;2}lXzCL^b4MTC(OP8h5}ghf(7Nc<-B8O-
zuZVNL=+%o}^a?Qp*N{x1ueqU1I?klj(kzk21MSunj*lVU$NK2u_$)SV5tqf~j5ChM
zV%c36xG=9g^L$&;+rHkAU9euCK%&-jf+r{~_t)33fnERluw+YR+clE?eM=VVnM^za
z{rLHj{KxlSezRY{+OIz$%j{i$zdm}-KD~akI~(s$OM}ZqqW^qMBSWCqdzI-S(?THd
z?;pQ^^t4>(c$r!swS{Tg{p+!OUKXnr`%lYAQ`u=x5^6-vLAY$vT+i?-ELqgse8q67
zN}5}agBXjG)NR2az^^8aQVTl%`pO<U#oSjr at N-JLm9>4vR8R8J((sbn<NELh at aO+|
z=hK4cj$}S_u7zP}*5O<WOV>8cwZJWFmT3-45wrPV3k#H<2HT#Kcj6dZf!l?#!6cyv
zKr51Ij`R$+z#;cI*uqjrJ%tBcx=szJ9vp-1ZNXs6H;|Gr*zz0CAj2I?iGqQPr)2NK
z-0Jrj`C#5-DUrCs9I;<9*aFs=m3*>w*A{l{#0AirG1=ajlkJ;D&o#J$t{t=dT~Fc)
zGo4ZQlZL`ry2N%*Ka(vm=DugL#hsFbvJ*vyx(|*Vism|)J4V}YD;R%{w!k7~A8g%A
zDpC45%g;X8e#uZ>ih<-<%N6}t#2Eb<YaPF0sPzvj4%BiUIK~Y_t>56v1xETAVY#6w
zdtyaWgY#N8H+BiBG$NVE<W3a0L*tRBGW@#J53fv!Dc<3qMp|Pd3+FR@{k$w3X&p<)
zdSfxH36~Qs47*BWLU(1Wxk35v^<ubGVotQMH1>K-w1A_;3=6bYe>lh}J82rgQ_I7~
zr`;3Q(wKzBffj~dDNCAHgms}%(VKWm5p$r0Wi^tV2i5S-j1>l2$12IfPeAL|R}8d{
z=@HDc2gdkt8Q=z>BwZG-FKqCfX8l0_BfZ*jB}s0eKd0ym>>&RiTizN1ggwGQGK6`C
z<mmchFoKoYy%=lpjMrqbDw+HQgWBP+&AQ7$G78_|6=R$AC<x4w=g2bN0uK8X(_oj-
zbkD)DC^NreoAo440cNX8^Qvq+2av0Bz%V`viAUslHo>nq4fYj3lxH|^&1i4lliRF&
znQ4J-)+;4oyw>YpLAShG*1Rn2sLpS;s&Xl>v&}M?uUKYX-1`;V>@CN&Lc at Lb`2g41
zb3&}M#|5slbTPI#-Qiki{gQLv&wGx7Rg^Xnp9?L+DE2WJr5s%1O+E`Ps<7>|@_f05
zjN9BZ at cc>57PP~|Yz-pdc2<+8&ugz!_rn at oxDM>gpdVKztbf~h%ayT>ep at W(?Hm-v
z<-9~2h?&a{pwrXK!3bUwH?FsA5tn_)t?_vkF?AY(??-O8UP$7$PUGRBU>h;<JJj{a
zyh9T at goWm*qja9iy~)CTQ~{H*8ed4kDjd*|R?g=U=+UPpQGW7Lv`yJzKi%)K*Do}g
zgBz~Q`k at K_2r4Wa4Wa5CiG7WtGK$41WCd)@%R&(bYh?zV at Hs__6i!vy>dZMK+<D7O
zE;Wvz#**wEYF~&dY&=4c&^RdJLuXlLy^uWO0d-M`2MkfT`OPBf3&~lSO#<<e(jqCN
z1uzlRdtoV$LHTa4zq;jM(wIZ{mUC3kmJ_OL%Q>pIEvK5K38F2h!kzJGeqNeq%Q1~O
z4cDJUju}gi$$g5fa>+4G6HRKeC{aJ&M#hKpeqBq>rO}dOnkxF?OOBr+_#5(Kl-IT7
zz;RIoUvliSX;Stjr{-&^`Edn9OU_4de95WBSndgr-D~3v{6{U3OQC#J6v9+5%64O_
z&!KfN71$<nIz(YdJCF)96~FhFH^Bi>|M1Q?sDfpr@@CO=kb^34MU_fW1+L6`K-Gb1
z98i5^f$AkUVdWG)4H42&+l{KQ-2I>`T+I1kCN8nsjjA}i)yqN*Az&503fKj!xQ<|R
zMPu%DVAbPZFIYt;OSxec5gTtH3SeyQ7aD=6?>*~7)62MF6*ldls_NqbP3Ic<hE<@U
z7RRt)jD at SX_r77($$CGq>PM9WtCuWTeKdkqd?Bw>fZ(|MhE;s}fU1|(#W0^jd_eUn
z-LSB*XjnU_0!(+iLDjkE!Bl^-9!&jcFQ+lp#+(Quc007()<iN*A}p>4+Bl3bQ21T1
zQ~Lz%L1uoqeVf!InmgHtH(W!u2Zl22tvY&svnV~NGzV at HYXN;{>{n6`c4lX6`K*CV
z*|N1GTcMei6M4k~s+vt2yxK3_3r^<V$Ub{xp2r{>Zfz}N#Jx-4`QXL5W(>WF(H*tb
z^u`v^#HxkRt2#Ltj$8%JmqyP;*Stqfp6Z9JJE-f`StbV+D2lY8YGzXCDm3gPzR({^
zNsIa5C!?~1v(}+IC6QiQDE-(?7=#-5GD?(-SS$k#&Lc$U3wMR;R+F&T&EiW{ydMo~
z_N*PsMdLf$KaN8MkUg?jGqY9PVI#Myl$unx`J+IlN+=9^)i5T)HGxtV09dq#e3+WJ
z0Zyv(`(EtN{arI5+sCrS&fZKIfi^`sHJgp&(O=h^_-FuX7EzN+S}MdwdGo51?O~d_
z=#mpRle^Mg5GIqyiUKKmgFuN}+mlwlD+t4dTK4N<2qyQEucu at 2X-C<byu=^cYO>#R
zDTemHRjVn=WmC>u5$trQ8nCk-6Ov+b75740o7j|Gt*y|Ac(=??HpL{@TYmP|OM?vs
zVrTOu9Z?0#7&Y=~5|91%@VY-R#!Ct~SJkxx){^JR!gkGs6V_)&SQfUIcUWX=PZ4jx
zo##=|*{a?sOGCl$d8!A~?4y|Y!pP&Jyh2k_6vss}P#W4V2t)9)(~AcG?H)M*z1Hhq
z4++D~Yksp}Tv}DV3>|RlB|K2Qv?>q#n|zX)M3Cytuk(e$_3AmpSaQ!ggY})|Lsx~D
zrW>>nX`M?w&n*H$vejR!5nPZzg<1GA$9|n4n}ct1T;X4SiQPm&$d?<~hG;GMGBlO3
zbH3cbmlZrPq=q_Bk-8sX9So&2rv`e{u1xgc7?CRQ<@{#QRQ1l68~C!=!_*70e>z`o
zV4Pxg^_In#FWLFBzt28&zKjEEBzNnLwyE*q1Q##5j4Q12$h`D!w*~6xLTUu%b&+KR
zaWNTAmJ#G&7lZaqM4e>0fh at l+uH3+t#VW{*8<=tVg+|O6e&qR~=|!+buIwAG#*%j<
zibLdiF??Cvw?5(OIloy<^@5XSllOXK%WbgpgDtng0E;XiQCv9`qRMzZYOuK&{7zIE
z9&FZ-lVv1yT~s)V at Zcb-_QpXZgdcFO+`yHIdg98+$O=p%obO2=oh*O!7W(Z!-?EiQ
z*~W~L{r#4V7SCjC0 at 9DKZ^?iB at wdO*uix$0zcHvS1^;_}vpHvtzh1vFJrlipRK`Wf
zT0%847+bxqM$Q(4Vpe~A`^UH5w|I=X-<`K#nq=I-zSgIz^j*U+puD?}V~<IuB&4Vn
z^uslx)xHawJQc+O6zib!U9jsf821y<`-0ZNh&fvtMM||TnJ?{}1BGB$h$R*OO)QHe
zI+3QBrj at G7<O at xp3Fdf-8Vr|a;wJt9ma=Ep<eG at vtUS1&Wpw%Dln=_o-y`w at Bdz;+
zt9+g at +56Ip2v!vV%8Sg*BL}(2zYtyOWkv=D{q45OF at +4tPRz*4gpmy3K>`F2qT@(`
zeD0YRZ3Q*+8{I?=*PjnjL$sPWUZOk%NHtH{uLq(Su&ueac6*bpru9Z>8kSSYS%bJz
zUR{8;!>7493B9=B#a=2Cp5Zi7;2UK^I3AgGB=l{GAy|u#bDfdL(hfi&NL^fRP_8WN
zLCAz6#T4Zyi?*8Xx3EO+{<P<hKQ-)6%x~-Mn%PfSZP at Ko3))Hn31jQa=pmDNTQE8o
zogSd0w+9%2F+~AkZ_LBNwUTX`JT4h8WHxCS7_2oxCz{>lSp*BkC%$RAF5mlbi>k{m
zP>e at 55M$q7nu)pSUr=WwzBr at 4Em!cOGwHq*!pTN?3|?!b_Q<@(?w3Ka;iFz^%VpB_
z;cFfPHKv9>4JNB<!tRK*44H at Gep-+Wh#_aWzuKetNrkq9T_Vb=Fw9=_UbM=7Mpi2G
z-ek}o9>#!-`iOjC?)+x3gJe(MFKM7Pwuz4x<<VnY6l0Y6>$5s_tI4sC1@%i6JRc3~
zwWvGiQXWLTz5e}Bk*^*Fn}m7=;BJnus~DK-^;R4f3zbh;lu8+0?6D+hW}-TxJ><k>
z{RIL{)%bleC)dNaHKi~czs=TeW?~b)E*qx*{ioX?G-t2)x-?)&h^gf}MIATFyJ#S>
zH~M#t5X1vwNt!XBRwh?IuDi&-_uz at y4IHBg*(11=0DT~+*TcH_&@sg1Uiq}GCi5{l
zKbkki5cW=HbpY21_r2(ay);1K_LyP-W(XiOIV-su$<Pc&qycLoD<Zkx^0TX58e|!w
z1C#|$M<lhvoN;M~G88tZE4*&&fiT{Zg`QW{8$Ag$Jxw4Ohhd(uK8(}C&ZRdjY`Caf
z5|{?^m<5}SXISc&7^YwJ6b~ZwQA<3cY72?#IvVHSUL5S{mFYy!gpna2GB>-%4hFr+
zE6}{w>tfG7=M}TxEb10fuBw@?BTmf-C$eg$6*<}8<hWR9?9(&9&KJOf_H~Bkk-X_~
z0=cIbr5xhrx2Uf}%>0_=JO{zds$0cL``%uWPAyGyFiZ^e;CTH=1p&f(s%fh+Xh(XM
zLxopn at -+_{MQi$K1REoY4^)fERm_5JU=<Loqy9`Dxn9Xw_MP{1Ed!K0U0`rZmzR|>
zTv3}&@HBG70+?P9#-18a*!7kzdomBXH9l{G%He#(zOQ9(k{iJTwd?*r-BpfcS#AUU
zK1E+(CJ1gIZFw>jp_n67XbeS_AvwCf0AzxXuUd*Wx)Ps{3)akr8x;0$_X^%12jdM?
zsJS4h>gm>t!8gnSU~XLC6RYD3Nq#Z$h47BqK~&H(R5CCj45N!`Se(kr|FH<C_zFSy
zwp1z-nVM at jj9S#BJ#I4D8Pc`a!EhKg02ZW&y$8X6GiA`s5N;CM<ugJHny-&9?`s#X
zEsi)A5?tRwSj9b{Ry4%UiRr=#X(Idp!X?{XCgRf&Gj*R8!oCrnwisaY^<Wu{%F5n^
z#y$b(B)sa&Ywlk^Y_ylUt(J at Z`_0Ds)?(@G=;P~0i9i1M%irv;zuI5_^m%RX{LjxH
zEhu^W`J0VlBb!S0A~vj!>GLs}TCt$G;yNGx57YDEA3y&7!|E at lqYISfblwWX2ueGa
z&&Ohnqo~pnH1uU at 3%t78!ySjXaMo<i;>IgD|6>v7`~ekA$}2H8j9T~qg&(v98r=t0
zbRXM>2R0ZTAIu=jaZ4O9b;knjSg+Py950 at zd80k>feV_}jO1;wQq>;VwL#*K<2)KZ
zZ-ci;H#pgx-z?JfSmKn@&qL!DLV~!I_lo%!q6Zh(p>$OPrfdRtjF%5Gba*PHqEkxQ
zFT6_24``Woc;E%x2k<H&p-(h+MGDB$c!qQ&fYgAU%1r9*s=%M$^UkHEe={~Yy-}Pm
z_)7Fx;-%CsULq0m1{@SPas&!<7rC;|c(J~5k3M!=R>uj=r>-#@X>i@}E^WAt`HF at O
zrzL#&4m=|oE_M(6h)1j-?75(EpnD$!Zk-$UMqiBvOYc2a%*9ItQk*AcAJ@)!LsX9a
z7kO^$q`h!H`mspev0~AUD^jxkjxbpJ;QE&8iZ1|*&9J-!_wfp)+N4D&L)y_2UQn#?
z9E2Be2iIKmLcy~NE;$x1T~%KAc~)99T1o>!_Si1q*5Eno$NWMR3P%(VK~j6aA9&cR
z2jgLN*4K(sxdGB;G5V!u at dg?5J0QPi$Unbhf8i%1(^8qUbM40sg86y*$+f6no~+S=
zA5Xty at e!rQYIM3Qch{%o^JN)3=fK?Kn`H>}+}wCg!?w;i14dri%ui>a$r~#g_mnW2
z(Dw_=kXL=q5OaYy=y&gEOc)AAzjyjicjb!BTY@&uNHM%av1c1EEcL{ImeSa9D+H1k
zPT)28u$o<=<Ebt_pY}LHHXu*H>QxxE;3#1EF(<I$Z0M%r!XJ0UX48ArWWcVd&QN7{
zRAITJY9eE#WxBR~Sw at 6WgPzBrrHJiat{!Qu^Gr at J{9s4rj-%SSci&ynIPSQDi(&`h
z#$nwic{SJf4lyygehd<Go`{I8kYgML8=P*>cHP04Yt<JX<9#RN(A+a-cM#D*!m$HO
z?~$~%XcG~7BSySQ*7eu7BD6-1}Do$SC#?aNTsI2Mo;eu5*(x0hzYtb+A9&_cI5
zUEZ1e_+2Gu=Io%$v-gV~xMTE%`#3#~ma3C23VTYOWDEKeDk_KOt_aUcwgAnuDqvfB
z at RjZ`eBOq(B|TNXTZ;5!iEG5zGR$tHIK)$p?@%OFm3F<<(sbW1HyP08>*bc%`Nbgb
z-{hFJqBM4%5rV;sCoi?Iq6<l8!S8ME><kSnT1D$<0$k$^>kSbv;LBMse12~P*&Bva
zcCDezzQW?A<CYOovyXh<8jcp!;$w$38Jzc3!-exVNQ63HtuK7YaT=Vb$A(T^)XNMT
zsB0y|<qYuY)70u?^9sG9fY3&4B$jnBz9>ts6*d(oN^-$fYjVRr>0I>z?S~d{`l%II
z at x?5-w46pgY|_WICnkQVOvCKded6c7@`&EC<xo;#KrQU_8*W&1l=pt6r;!qkf#?e-
z)^KDER-eNoEg1PS2=ORs?ckI)_X;Pmi2amGNr59zFPywnPsvbt$I)KcC*;Lj{9P3@
z(%XsSsVF8DrCZi14JOD+<%8JtvZ=q|sWy{VmM%<?$yv4Ho at P<jICH9P(svvxH4p6z
z*Q!)HEyLLdISa2TXDlcpJl8eMB7hf(QOLz>kSN3-c7QK8kEp1m*>a9F#RocZZGOLS
z#UTjRKEB4Y%(AcPr&bPZ;Y)~*gml{EhLyB+0&nDKt#U9`OL<nHsni%}`HfX)l#Y?;
zl7t+4h at 5(k0GoxnLU{{O5b_v at wsE`qn0vu~#*w;dp-^!N75L}(mX at +ZPNhJ0OeS`x
zI#a34W7L`sgnPGazv^7Z>lF)@pnPY;28-=rjQn7!GK~jC1UmwU+g?oLmBXzJrX1cO
z!Ub%^vgg4RVA5VI*g3OJW*v<qohEh|9qT7goOFER)yHYQXb^>>aZGd=nGE*E?{#Oe
zdWElhrGS$hCwv5c_N?HmIY4FwJ at TfU!9pnNw>Ec&6LZYClOU%&Je~pbXEB26EC2lu
zE?L#R)gv%p*GI5?UX~G_{k*>V)$)AB1bsFpoxYZ&<=KWG{bI;({=C<yX%-CU(fYBF
zr$<vTTClSBdG~h<iQQO%N}Crp5^$&pET&re{C3&z7LDOeHL%~77P`rI9wX}%w>3q@
zyS3jVmL|d$vSA4Doa2R8%M^#jxK|ywn|?8*Oa5dfS?#UjBn5sGuM{)P3|X^r<A$*}
zyA&4miQ)-y=sReZg7<Z|l+`X`V~m<zEb(aB=h7k*U#y0=tl3d+fo9LrX%m(#9iLW?
zn~;=5asubTZ`TT87xEn_VawndIp;z^`5pA0T7JhD95P-*%bqz>fz at yWPmGacu#G%n
zHF$EcQ;MJHu4f;N>Ltc2rs!)$H0Qv0x0XemQz$I<3Z1Ek_J$`E!H6d0o<<LHWvV;a
z-JMqU?7kR&gsN42;n9=krbTnCMVorU-+9I!+1Q%mS%u-Qot_&E?~|k?e4VedqrM(X
zEXj8=k1Mt3p2|HD|FL*v)^-$!-Kxr!5IacrjmMF$G_)jmN=FTOG at QHg@Sxq9)k=9=
zi3=v<{i=_vuPqyq>-4oq5?EN(H4IwEQ(dbDpT4ooSG6s7Nhgb_ftABe6~E7t^)};B
z8<345b#7z0ZiF<Np5yaH+1Nu$3JI(zvog2$!5LF|tq}KL?7)a^;w>j+yoI79N98T~
zMHjrw>x(swcXoYB*!|Gfg+mNn6}?<fkf=tUx<CPkZ~Wb3LULRXFBGWZjTO4k1SVof
z>cc5Y%QaV53!d<F?OzQ~20YF at q;0pv(b5 at D%E;Ck?n5&(oNHV)zMSON7p`)bxH}?C
zT<8a^Zv(HMJ4Zq=GQV?0I_v>Yu8r0=eIgPg9qN+Tic*QGj at Fnw;}j&^GKM|^%Y(OH
z at Jb&h*xvkL0k6h{>+p(}93sU_J`#$$a?HoiuP~r|D at uMjrx+QUC=9hO*pm%}Z_Fa>
zs4-AA?BMM9LQZk5>GznX_v9*ZBL~`FxE-#UmePd-)-%MY03$vN at OVb7hN0zvT>)!F
ztD2E9z%1XYuN5WQl3^;cr8vPS4E;Z0=>yd?3U(M9xbsd&?$(V3n_Hqhr{#Bl;r7;%
z;TQ@>nQ&a;5d*F+>s$)1A55|$ft#WymAJBn>P#YuV9gMcpnH2TIJrl~9o9mZ(uO;@
zOyZdxd(8jRT`k#_>?m;FtMCfyNC^Gb<Ed-^(*`0?Dz9IS?BF6Lg5bBVR<nFOTrceP
z?CL%Ka=Oa<e}wl;HyWPu2|1}-;Y=lS0mn at 0>cQV+?ycb&42{Vy-n#G;r-<McrQAhh
zoLx0>TkBOtMr?u at g7K=!8??c3H9yhl%H6mk4Q5x-ug`4D4D8G<&kbZ?jUj3>TYOK&
zDR0=pLmxhu&CN4O;lsHJgx&_L7R%j(*Lu-z+nleQ1Z&_fWV-KyYeysEC>Yse(+7!G
zof6weXuQk|4I#p_yu=>`bA_|caZ?s~Uf9kc*C0W8Ytf<{kBtD&4I8t at gx`F;k*lU`
zw&TR-(T{-Z>!KG$c8>7w(~|2-8>nh?byk>mMdcm0?<<lk#wONMSx16KM~$6MfDNr3
z$`xpwwQAvpG2Zxtqc+8D7Uu%5`6v*dXmHz^e!Uol9|t)pi2Z2jV?F93Fgx9lmqrm@
z(5^;jU#Lm1v1x){oy|?`7ryA2f<eB<sC%5?UuZ8knF)1Xj&u|I<>+ at P?n!lf-)=dn
z)+6BD;xF6s-V3*V|88T3eY3ncwY_eWbae-=lY62kmSJye-`6+FpCq*)j}}=xys8<V
zdIH>WV33DC+XW_tD)MU7Obp?bos1|O7dTTQ;AI at l@=@B6{9F>V`)n&-R^O6XSSSf%
z3E#?=EQL4?i{|D6ZgyI5L2Z~LbmF5T{gi}sFFw&Hu2g9+-1{jal%%)vhE at 1Y{=Oyi
zA-cE+y)j`bXi at 9>I+LN}kye#SCRI!ln>>mO1necg{R_#@OETs?Gyj(W?7SH4oejHI
zUIWR`OEQhzkc at coEor`%Y$xy29k%j05ANK at 9?2A*h(M{a8x5%i<J4x1v&VSt-rYDc
zImfKP!W(+D55p_J;R~Dz8fUGkIMDVrof%fiO=fqTP{o8$B2JDm!OPwUe?RVn;O7>U
z+DK_I^p;d_N|azs(eu=>!V8h(A$O;A3HN~6=gV2s3$#1)RKKW08p}vvq5m15#5$8t
zDUqG^61cX?sZbE$or_`=`Hgyn_6-V5)=$4X@%PO&7yNvZFZrlG`0p8m2ukXTxZ5hw
z^4N}aGM#MGVY^SbZ at f;KNXbwn<cdJr6vjHU6j)nU;Td+=<c6G|-q{Vx;_eU*Hr#V}
z=mQ6Y8%^_hXZyH{S)lR;$4O5;lAlY`|9sTUUxMdMi3azUG(K*$_HpV{0yp2^{w;Y<
z`M@<lB(mg4FyRohlhBK!XC$=oQjXY~2e|m at f~oWPwcx-UtiBa~UG)Q>=0*XMg)_S2
z%?bNrnD=q*&VGBC7~6-l?v at N=gyZR5S3F4#`n=*lTd}DgO_zvM$Rt)e9CptRgFZVs
zUix4^z$S1s{N=c^tsQt;d2sAb$BFH8m)ju2G}O8vZ1TnQ#&zRFN<2rYQGjv6ZVaR~
zZCd<Y1iufT!vyUqZ1j>U)}CTOb%_sTE?EN_WengtuXhU<8keY5savCZe*2x4Anxfs
zU_%`(PWbi+JDU}bCpJFl%&*Vr;Tt8s at x(JF7}4XaJ)muK0c8WnNE=r2o6=$B5Lz|w
zcml%{rLRlFPM#Z9;N}%IJ|R5OWz&cWZ`1p$tqW_6_&AXg4Nv$Dc;{@&Z%E*^lQTXa
zIE%^<#HRyS)fX=v<K;u at O9P(p8`|Y6V<GixrwMRWx?cC!XC#nq8Usl%z&!Q$lGrzk
z7dn?nu;RZYs*{pD^;<Hfen?h$%&_k^GAw~5 at bu~0a4my5gG?Wh+Kzp9blhsdt at t_i
z2JPk-#f`?qFUOvJf!7G^`J5OhdX<;PP-;vG9KFY}HlCQiD7w`a#h?G-4DN9M#+ at Bj
zmf({4B=Qm%cQhKF8lteX^hLXQV-cS4A=-VnwL_zT39IcF_Q9AZD9MP}Z?*kHl97E*
zeM-QPpI at B$2_BhU<I(ziN$#8as{fEY7UikmlH}1N0gntXy?A7QYk%=*Fw>GpBPOZC
zqrt>HJhFdOFCN+7-7c1l at bb+gpKta{!{hYu$U5BC7mrLL`^6(W)7Hf!yR*y15^KBj
zMWYc7 at mA>CE*iNp>qR3Us at g>(!=+y|dT`R{kz_!8wA=rXWMH3DpOS}0Z_0p1J_rb!
zqP^sANP0orn-$?VNkeJwQJM{J6nl+EI&A_6ZZ%!Kgq^g8!k**m4l8Q<-9Of!HrGp|
zv&%^xfn!2<%-X(;o%lwJ?reC%NaYQQ`^s;mWUK}a!8g6;#W&d1vRf}^i%B}H(1vgB
z!gmBq4Ue7=pXfnF;hLfB0JXz)(%99~RT@}Yi{D!~U(J=!7iG_ at i~4n<-}_C06(<IR
zlXwoz6O^p*l-~dw_u at Cgw^qwn<q4VWlen^Rx|4x$D)gHS>UYwyO}@IY%J70&7jT#K
z6FajfOfY01O*%bW_BxmhDfOkHlSwhjkcdq`p0fBZRPY7`zF{HQJDV;ay(46BgBK9P
z8*bY at U=>7aF6I4$v%nN2m`Nr*9p%7q<;+Tk+4cBD%U3C_u27&&Izg`o!fRXBrC@%|
zIiV{2zB#REvIYIdV~x=DL5qsM at T$q+l*|sc^aZ|olE;l?u&-YHM#qU!Sq?Z9^+_u?
zINWud=u at LH_;9TkeUt}WsE?Z#B&`clx8MR<Aj9GiEUoecV-p1Q*m6jYXIn5Ua4E{@
z*EN{h&=8KiGm_ at Pqa|NbqKgD!32Vx4ZWcjqNhV>PEE~~YIs9+FO1NN1zM>B{e!d$c
zZmtjMsb|43*Xpg|;p0lO5F9NSyNhJNrJ;z`0ZLT9tKovXLBtF at s?Bhy#<N7z7{0em
zYDN1o$=YyebN3A2A^DjVutT4jV1gDk#|+)^du`7%MzH|D9%>Tp^`%koMQSruo<(Y#
zvG`=$BQ!b<oYABb8%J8kD<ys6&RH^f5aHck2^vP^8au)$)u)WbGuWkB*8H9QDr<&w
zUT6z`8lB(h$`jdQoHXuvZav*eb>TV&4;+b7fs0k)0cIObDu0+;C90a^K}QXGT7kRI
zYKCPq5lYS2G%S>YS9 at VN&E37gHp=#IlD*~9VDvq8W<XKk#xEFK$e(1FDsZo$&&^Q2
zYO=^!@cxp;`$)i9-K=Diw`R?U9j&AGt)1Z$*67#|4VSFvO5WD5H$z5QFNL#Uu7Hg@
z?nbS2Z?ibADxpzW3WrY&AMZkb`V7SBX~b?|lCI1aJk?Yx)2%xz+_l7714}L;3YWJ(
zIZlRx7bzYXBk*cCuq!MV>mc~%rp939 at eFm?5u#d!2S#Piddqc;5C`oN4OyxEb<Mkm
zW$u4SI5(d&Z|u{&(krDw(#-w0<T;bh{Vz#=`8o9|G5d>tNW5J*RJa4- at Pz`8*L43x
zPBqz~QBOt$YMhT3)hij$q0M1g_%T`iVCc8JfEtZ6zK|a`r9d;Q5D~Alf*y(Yj##lw
z(*Gejd&wmk-v13N`=SpFfzi^m!52OTK%sf*HY^<)(ll_bU>>JzGk-6zvzb5e(ZW{B
z6Sz>rT?RKgamRw{pJ=qP!X_Cw?n?ylJg at rx<(=5J#tJUq9!rV)xrJft|EyQLj%B$G
z%;zia59~s3k+Cxe28{iLfj2N_4A>6*`(}})$LYQeZ(wxLU|sP at BB^50i94PR-idW|
zy*j$ty^TjJG<~l1v7)S(I^4{B>KbBa*!U>-7B=_F>BereTyiP&m|e-p*;h>RYjwUI
zHdApVB+(hN=C2xij6oeS<Y7x+rwZgetodtxV_RTLzHH-oFB`KcZJ=Xda)j)IVamPp
zj1iZ at E`w_FFeW|{(H+<|$Ht1z6<H%SgO3Jwc+AlsD at 64Ag1F#}IW{~j-C21G^%W(t
zn|gN at e~@vFPLD$2GBBv1gecgg>1A7-hSDRD6G>q{KO^T^K8rO9*hKHFucZcmI<jQd
z%#jsC>dHJaZMG`)!Ejrdr<(nB&g2=5DA}FO&LkU`17_FD4v$W;y;e-hy at KaCp%+PT
zI_aT>*@mEp&H8No!m_)pJr**g#tQB+N1QUGc3lq<r;t_Caf(4{t%>*0$i9xXN1~WZ
zEn<Tlx;V={V#ld-F_~HeiR>2d|0YsXh}24t*5db;g&x&oWi(sDqqac>ArE`9*BnF;
zEB^u|r*IEH6+}y>N1K#2 at yQ-Cw;)nDbBp?`WpI5+X+tLOo%r_o0nMz>URI=P?9d>L
z=h8(K&J!wBUzfB-jqsam#oz}fON2_BfkJ|X>KzNR3uM2A<gjPs9sSl5x^yxQ#aE!(
zJWk!sT0%)rY1~0LQOx3x49H}tyRdNM3#_qOB9fewc1_tSVnI(wVS0(xhvGz_ik=k+
ztx%izJYaTg&{nxfu1t0$Z}Lv5-KONeQW?PPQ7coT+GdJO3M&}w&J9?8nRJlXl*RKY
z2bz$;o_emR%zT8kX(c;Q$DbGlP<x4o%O?WWgS at 0N`Gi&?V+w0tzMj{(L&lwv$Q%wS
zIw%l!Oy!EkjyauOjy?7k3u!z=oZ9%PaZg<{kFM$>PbQ7}ORH-uuf;yEB`^LNAzOqO
z?{Z0p9jS6LR%*9&P at i;SPKe};pukjCw%clXBg&!jS~dLd{s5VnUiN2qE|!~rfB(m~
zD#cYrYt<(R>mk{8!wrf2xDZRBtL2PF`o`WKB_!=TWcd_pobxSke9n^DeNBbnZEun7
zwGn$#BM|u&Gs|b-_z)P at 6dkai1|HBGWIHXSTVnkz&A!s!0hbREjH1)mbSq=m^q-L9
zwUHI8u3jl`fa60%s?WF-+4U`QyomIiU4DgtNT{h#$nAC3%p6RLDM8*Km)AnnVjL23
zT0NuiKO>hHk<r&0X`Z7u$no09&ZmG`%Ufi5^=6Oyk%`~G{qvhW{8Z2n at RokD6*`kq
zIjrwLzvUPHe*4p3?5X|Ppugj}Sp@#`vbToC?ez;Pv6xuFW6LhgH}vBPjqY=AHZgAO
z`xQh-qX{tcR}dY`v`OeIWVtP5m>}|q*6d>AJ+%GqRp3=XrrKxw0y>TxGRa<iBy2Vu
z+Dmy4ZO0Auyp*!7ucf_*F2 at B8jH&+lZquXn+PBbgydeg#PoKBGf|lcg1l`@Ajg7CP
zzD17Pf;w0 at 4^CRpitnJ?aj!B$^kH^ZZ?$ib%WXqCo%I1>PAc!A%W**%N7ch;%|+*Y
z4;{w??Mw@%o$&><9DB8P*B{^d?b~1f=L_`~_{9tDHvj1r|DWHw#Xt8)WH;1vw^{&`
zZpSy!aje7M$FmMUecpC7$5uXFD~|0wyH~$A*dIdR=L0dX7i at M(QygwVKQ!o8C=DQe
zMvpAUNU&$BZZ{1NnPHFW4j75L1Jl$TY#%1fmKt_1#|{s?lY4~-SD0l-DRHD(Vh8Ka
z(WHmv3T6>?yBmYSt`aA%n(2TkC!!P{9XboQG=_pbQm6+RYl~>%&7Y~;9+&15?QMs^
zI!Lrkx7lXPEZj31P0D?r3fDB6;IUhnmTI^2P`6vjjFW00#RglC8wOn1xn7YCJ3KP9
z`Cd_7Q9BoUs4SV>QZlpMJljSWe$Q1c_%E-I^%NeGK315prP_B`ArTo!6E6^No(1y+
zPublbL;Uv|KX(t at xfN{IOR0|)qUHJkDn__LSrs$Z5hB)*wQ<CH*>^e at GLpW|7m$N{
z55rdx$01OCaj3~MNA3AF^4NBG)Z8B{a$j{D=}eU`ue23wiHDL>WM{=bYVMhf?|(pq
z{2_`nfmkaP|Dh#9H!bZ|hnDs#&{9QT$Cze6C%f>^2htE#*x(AY1@&omx3juJji+qL
zKwj`=JY};nI>GLsGqaVy5AmQ`U&txGVYAGudONRSgTT!0a_sP!vprU*6|&nXU{mHv
z;)`>71shUoteF0L?K#e*Zh~oHT4uQ1I#OK1)D!HH;|RI*urXv<7&Na9W^rJ~`q|=l
zRPSY`D6NfHV+WVO#<<&YcW&TuH}y7T9|=1Q(+Tm-2AVf}<nSmpyWM7|2dKmVrxFX2
zwB~jh^9pO&5!O=WT4AXgVOP;e<%}(m;z%|a9S9!8u9j*4^Xp?hg at b@I9~Bme`RJ;T
zhzzO%he|V!t-+Z%W{ybU0eig&kL9sD7aPG9s26FE71}GmP*^X at J$Aj6xJizD7M(og
zyzD!n3xUKtAFzkf^R<Oqhg;W(Ls-?-I21a(7iEoW^(os9j|!f?R+Kzgw+xj=9hW&7
zZf)^U(ujnt at Q)96+~ariQSS<#b>j$Q8yj at G|HrudufJhYPj<LR9xue1!t7DXVG6Tn
zlA9 at j=r~Maww8XF!Zd&4?_>%?%8<hpW;eNb!0g$M9UfZ8wZgbCwZjx<mnJt;7y@!9
zQ<&9r*-T;PNGsxv%V)}mDPWVDoGHwfA~#b2kwX<`YLZPAm&wZ72&n at 8qaUj99rdOP
zoKowd3S45eLlu6-a#IDuQ1amlvlH~zVuBsm%@qj!<M&+jFa=Us>&+A>2Y(;I6uVl;
z6d&p-HgI}71xA28;ng9BCr}%;-#lSRo$HlnU(s3E&H~q^$P=iR)~^*-Ex8?@K$v>I
zc><vn-8^BYo%ry?w(o2%#Cc+WfIP9c*gUZhL7wnKw!;Fr2748G0vE0FSOHDEc><y8
z^X3WUk9F-lae_ad=4OedGwr7M7FA^?qQj>qL;NnFaEjZ=ho&C#X|b>_-I!;T(U44`
zsaHNsqNQiXFq!nn3Q=Y(wNQa!p1C1o3c#64Octd_0!w|`O_wxX$ZWA9Gv#Phu?2g9
z>@k3E7`iZ&W(!+~+l{>fHkD={j}?WiQAT_rY5;<7so9i5h-D56n!^qaZ?SK2Pwiz*
z4*o!FMOyipl1ZoP2xp+TNV8}4Ba&3}13_L%{ax3Z&I0nsiK&cL&%_SiXPlUoajjTU
z7N2f?7Gn+8u4|s7xX9ivFn$Iv3WtnkuXk|}rhZT{X^nYqHNK&;QlFzjTbx=i&O61p
zIPsKktUyj;1>TCsJB6OY`;44)iny6IrZ=IU>iYWl%KmTj!bB~U$Gkx3o)`A2GcW8_
zF)vt?Lx-N`5udw|-l43FVo(|rO;6oM)53Q$r8df_IMQ5dqx)yKyN1W)?=ymCh at k7w
z7+cXhV!gJo(C}$S=fP3#1(Rk9)ggmlTWupHjSc-Jp|^?KqlM at w7*B9uU9GBu`6jXz
z9#!K=+2_XWY5_se-8|8|445N6**z-7^~`M}uB3EFvh!UipW=3%$}QvfT$V at mYv6ut
z(c<P9;gC|;y(r@(HIS-Dzrp-3>(!DhS+2sia#zsdA2wc3Mf50Z|I>$%d_GyPXEn-z
z#K#x}h<1lHC0dtyDKbl#oFR_fc#uWjKK*2^qn1Lm at x}`&@sepj-&o>_Ilj?FP0g<p
z8-uT+(z+ at Y0b$8GduEhAP;ey#m{7DS<C)|bKRIzNS$r5pAI7e!vV%zXtuT7AgR
zTprD;^XL9Tz@^yZj-D%pn6eNjc4dUPIqjoUT{7*fjoSiI*!%z`*XOzxi=F%?J*dph
ziF~5%D-$FDK~yRuiS6sXCd{)Y*o86}{mvd~>r#AfMbE at gF;tKxdmu;vFW-`LwxFfh
z>7v`IS>~L5jZJ4dc}MbL$i3^tR6t6E?#wV&d7aN&B1pyQh5AaQR^480Wp<9}$t0{K
zxS09xK6GM1&F7EkeEwE<_n3HXy}G+p&+dL%-QE4NVt2>X3-^3R=di55TQh-F$S4-a
zn%Pq}P@(uvWosN<0H7Jh6AQ@=dFFh;?B)M!pCuQ39vZ|EJ1 at E|#2g3VqTezP<i_=|
z*cjuGvnha=@q^9|ADY%_;TQn}oTAdHO?eDDg%aio!x+OefQFD~r57#RABZyVDDB*V
zDdRIqaf%rkjQ+P=S+kcWND2T58T2CLajwYN2QlKNnoi*kP$zl~F5y64|KTfZ%H~Z6
zK=MPu9v#oeSaOiF at dzzthPI(7E{n+4q at 3#wvblK08cq2&lu3A5IxU90HV~XIO!0)z
z{e?1Wf}fQxj4(2-($DOlxT77+w8}_$bAra at SH|vs+0twY(D-;+Z0q{M7HGEaxIoif
zUND2`&h&CT<Yz{YX6ZnFU}XW`SA?@BpXft)tNbh{OlKGLmtQhZ^fg}=txf?~k=g`u
z_n~O#@(3qDk{{7uUov0uFB#fkbVqc$cc<T{)3*z4F#4Ti4xR95J`WAkMa@)g*4jvX
z2A^0Y^MT=ZJ;SqMSY~0-q8JNpb<(+Ac>-VNFx(1CjoRg{b+X>U<amq1H06acGp04O
zCcrb$PDbZT9b|BX at 3Eg<>52z6pFbk>$*76n2Z;J))Ot3IEc<e{t0Xljd=9sap(#SK
z2Oi*Fn_+GtfVOLLwS7bV>@9aOU{+OoS@@=f*QyPdeP(HrRcDcd8Rwd0ESBEivhB00
z9gPpax4Xzv8ACUm;_<T&7B-45pH>?wBQD7jR9ujGqT75~u- at Vei`yH>P0Ho$Yo%~}
zwe$k}8s(&ui2UFe%M@)Go#3nWBxkwO+?R&B+4>KSH8GqjY)FBeG8{JNG0C*fWVrdb
z#m6isJ(+q8WCg6vhs7P@=}aQW17`LiBvZY(VHStHuwz7$c5>ZYo?2IL4HlmKLa*xf
zyz_JlFLn}N2~Vp8<G$y~1B#kB5Af0gDWL^OVaB^~0?lE{iIvm2XIgZDvrNg468rU+
zNS2kF`_Y4B9o2(m-BuULx~)jExb|2n(H*vyoTHh>B?tdN-l5M$Dw2TjG&}TtL@$8G
zlAU1rswuBBwI=a at XV9<$#6W;1moY31A+_u!T8fUSrO4XLU`eb)3yZ$i&w^XZ=8cm}
z5IRMA#O)Q0H$zcQg9W0X89LB6pU_<U?%)+=oh!!n42yHUwTw!*y6a1B{VZzG#PABB
zu*(mO*N~$GbS3|9?VRZGint~UV(=B7B6q<``X2J^Q*rW6P4c6Vk?NG&y0%S;JW%Sb
z$xMNHhHR!p(t)+764y~r;bhs|cmWp3w%X4(oOlBJ*c)KE&Bd8%2OfhDpM*~i9NZxu
z82h8s5BEm6nEcAnUGIV at z+l2Y46t$L`F?M>dBY7D1)teWikB|>CBL(IG~UL~`9K1u
zw(E)nR2lMV5p%Zbl1)Nz(>|KjCDXpzxGfNbeL%}xxjs199udgKI%|z5PU!th<}1XT
zDnmJ9^8E+7iax7w?vjh$LxW?SA-#NSl79kBxsrxO<~i`XhdN^uy6Ty4m6~lFfR+f~
zNu3~rXA&u0C)QFC9d<IAILitc$=S2$N>zuSrDYImrG-xxYM-1b7|L15Y;Txkos~|C
z7c-?WjJvjneD{W1(~P5VFjJ`TyrQxjSv}mrx3YiOM(+=F#Grb}VjIf!vB1D)cjtgE
zZK)@pP_ZL+Yy;pHT`ra`nRwnV^X~j$X;b+em4NIVZ0EvioQ$0g9Yz3FL5n?&xhUh&
z#YgTL&psbv at cFQaz!@L0jq|-rz5;Dhm`KKy9LvW`F7uqACf=#s%to1=BEC2|?dCY7
z!-NNs(NX&gFKwx)>fs!95MgEVox8HzxG at euATfG!DOh(RQs%9ZHx|u>I!^9&zKD}-
zW7pq4#aXUm{q;xre*P8(sX at mA$4?3()suo;RyPH?tVls-)~?mSM|}79O9SGNa(Ie!
z#Zt6z6=LW34$TZ=42?}5SYQOWXr{RJw-en1%NRZp)EY6zCJROoaxIk2Z<!|+8ves#
zBg^<gPA&hZIGy6o5sHR_fC4Nf<ZgU~7s_MMscGkgfob6tD5;PqpoVvX-a?+d0{opP
zK!obZ at TZu8Av=A_h^!8zCdxe{6EbSD_Pm-`ddkiF<y1>eX?!Pmyw&y(MrppXra1k)
z0=+L3Ng=SIjuZzf^a?Cx2DYKSUKWw8NqJ_RBA1Id_}I#}p?8H#br26jPR}-fz5&G(
zj;ek^jGAa$y0kMcz0u7a at 3^8J#9*+)jg9sP#JJqb$lVWfF`qz;kC(-^{`dkALl^1^
zH8w^%!E|DP(#QIcpXodrM_0eT5KU>?<BDincAh~efVv<0NRY0<SK0DQ=7Em>_ONJ`
zGMY+8Y7 at qTQyj4zEWWmwo)diklKG5(xk3y^*W&j at LxLlShBR&s2w+_O`=!CL51C)<
ze`pA&K at M!0>6Vp{@=}(@A!=+Uf%q~GdCWA;!|YfPSQ&U=Zyp${iKjDf)3^%^fy-@(
zJvz37idGMcrsffAW~1ENU?FQkX}J at jBW@auMu_j+GLL+<hKHu-fX4 at zw=p)a7*#EE
z=>!;2W>yQywnyd-%hP;f11pVM*s(ZRnaQ(*TnsfdJ3PBK!f$kjrkDV0BHl(HO24vE
z_|4+V=j=2Y+Wyu-G#W9oVQTiHpG(v6Xw`KyA8p>P91okftWaQoyeuMkt`D>n<+Qgh
z`?dJc*+&|<<hNAYNe(rUzQtrX;gFpIpTqWvY?pQ}0GA4lC6{Ws^ARvi|5iy&zwkl?
zk><vWI-AWd+-SV9!Od|nX*pyA#oB24CeE{rA*;*QZ9370Dx<n^&o30MtG!+2`s<GZ
z{$x>iayk{y at 6>pu#;S;?Th&k!$=9`HzQwxAb%`I<jBZO-O|HD}MhxOv?#9d$tJ=%L
zw;Zh`o|=+3!>`T})H-QoGlmyK{VA`DgdNQ`Ed|b$Rjaz<G<TCzNLA)oPAxSBGSSeh
z`9l_wwE41V>Jno)nPue{P&%TLg-Gk#xOS}<kTUdxU##j9 at xo55wx;Z?%z6|@An9|n
z^8ceD+x{>|y}mTaDg!d3-^#`adKVM!i_h^rNu>20$O>7T4~sj<E0JR&FC&}2xUslG
z*NU;!nTz*_9IqUQ-X9I?1T{}|*1<c^D8qWF)K|`=*@#D?G5O8|jbcO({L-Q)gUp$>
zD6c|>wEo9>wc}cr+rYe6aW^mneMvEP=D>h4OX$q#4kq&UO|nWlI?wHij2FCH1f?U9
zRIxZ4lQPSe7MSKb%d;x+^~k>epC$k=b!PxP+GFw|gX#P4IpHyRZ&|u0fFP$BS$gPm
z`{eM}<9j_`Bb&c?5Ywg2RB+oca&X-=@zvlN35(?ea_j?H>`{oVV9(lRkF3J4u)?Ap
z1s-$Y0eKw-mU3n{o=XKD9_7R~F#KtLQ?fcp9kZroNasK~5^DvXGvTr`zO6WfJ|&qR
zuEgeg;lXzFSf+8vbt(_6qjJc_j*BB`6zxchDfVnDR=9xdz~#cY_|kfLpjTlB6!}!a
zH^vTCD33fvLumTqDTfjF#J!z#bnMx6lZ^>{v at k)6J(;hzaH!fhi4GpTlM+KMBsMa;
zwSyUoK3^X3sl)~E-H2<&H$o-gs=+>4nZyzH8xJV`%hK{i`wk3N?ZkCu5x5TKiZTsl
zkwVE&A&phj5{IG;Ja?IivCImtF+>u>9Yr{E7GXXYN?j{Lsnrrktt8oIh4?;ex7jK1
z7 at NniLGS3xW7>JK- at iU#bpORF3GoIZF`qMyPfW-oHu30sKB>j4&XhEPC9A}rImRNl
zM8`}j80!Ptfrn;odQDnAI94d1?c>pimmwjBL)F%J7v<%9GK09*=dsrXJ#DtQU|S|r
ztcv_B5q<%$p_YB7R#Dr1j1SI;DiXlOM;sW}#-X>zit&jXXyQ;g`H8v%WgXnJ^4VK<
z>^x*X$p_blZ#<S_+XMWT?=c{5Rm$QmYHi2z1D1w-YCadgX91pBseatq8Cv5W!{E{A
zA-Y%r426w at aj`Pz<*_SZ=0gkI3+j$k<FR77BiJhslf|tlp}SCdv5p7TJrC$^bJb%(
z=}v)VuFhStMpGuP?5TLwJ0p!uP*U^wMQoh1$g67mQYNDH<uMB|h}px*4rFEQ!fHH)
zro(PXGi&{QvG4g#%@5lCx<gCMcFN1n2*|PcQGz-HE9<c(Y*W~#l2W8*^wF5GAik4L
zCgiDO8Nk)JU{#W}vqqQZm#z<aJ8t&iSP`PCzdU5pLlUZi-J=0wK_*}&WqT-pC;Jxz
ztdos%rh0Qr8w+=c+0k)t$XMva#QJL25=s&ey;bZvCK6y>UpNBG6|~f#o4`~ZYk#AG
zi>J<WDv)oGa~zxb3_{CGk}(Y;Gy)-;L*X&!X2^a;78tp(p_hjzOO+fdNG2ccs9ewn
z9_h$??sD;_6!YYlg2IZa>r07ki>p~y2;XVNA#~xQqwP}y$7qxVLt at Xs?OOq}UaY at 7
zMBW=HxoNahFnu3^6;mdU*kAZbCoKN*#6Hgsnq$-R4OcyrpTHr!>X at 8iml%s{Ecsk=
zE$+GI<q=`nb&EKy0<3GNQzp7e3V;)7%~9}cE|MmsC&z3x9QkokC&=+CDdEDxUK;9E
zFq=X}w8T=j1jmaV*(WjsOVeYKijfhQaI?K~7OdW19)rc-D)6L??_yk`9S5IH(_>N0
zXn$j~y#f{&H3J?>)*PWy;v2TJtXwgu?3Jf#iq5zSR_xXq7&o`lFOPUx4{=ARwgxQ1
z1#=)LGO%E5(IFp%9$y&ftdX#VEqr*~>B;WweArWeNyyvrON$qe^DtJ)*5Su87D>zi
zctD503jh{nW2zO0vqsUoR^Go#J(v;G{_;@1ivno#kx<`pFhPhV`-Pkk2j||nbma<I
zD0^dvU5=gKpom5Dj57#$&#JPv*2Q|e8XsPa`n`G4Bf}kH91Yy4RU-dbQ6?#7CSh1d
zIS(PeURk`nKfl%BzZhPN|NU0{)LOhQ1AYJbt>lkC{`L?5^>_dEujt+0IRE|m<{jKK
zRX at Lt;olo6!XJ|hL=CNeOtVI$*6lT`lgSFf^T)S;e)B*xYk#2?)HVD-2zv5_8%Alz
z^5e32-$ZG;rBTn$c*c(FqykRt#m9j%W<3w2TNV{>qlfGgi*E0Dw#kALuGp5CW`3ka
z at ezqmnI9oh3J)QmMT10kOQ)hJ!B=q+4yDoV0?)Z_O|qi_!;i1w$w_GyzrqtB52sIt
zGylujVO<{VdDHI-eRQ*L7&yiS`>tpM{bAqbZG!ES?CX}Q^<iJfkTc}g6|F~a_H~Pv
z_43%0WhTMPrm&pJzHX^%5BoZ9Wb|a;3zL11h3p$UkjIjJ;~V29Uq6JE`jBrNvg$*v
zd5xQU<DR6_?a*31+#6vb{gZnmlqA>W-oX0|l5lmqxz{bDt($w at qLjO2TEEAfzcL)2
zjf1oo7srQtyO}q9hCIw0chiSy1544Hc_Un$J>jwA3+8p$O>X9O3kRJfUWY*vhj=ec
z;yo4;Z)~tDvEvhiwM^!XoTv0A^G03SrASXD9_G#3ZI?%+L?7mjc;CDm88LZ?H=Yyg
zGKu&7yi)NjzYr=jTJg^`Z1$6alnIF}M$7oB*Q{*KX^D&SUWRk~b6vRE8W9WMs|)Wj
z6Zbp_aIEmPQ!fwi#}g}7%yf<i^-YCJbxY&O&|}ncTno>=F6f7|#Rc0kJGN|lb`{|#
zSTbcFN(AjYraBxFr8;Eb8nqsbi_2zodf4Cwnvts0P91;G*mjE0r7Jzg;+q=w!WHa%
zS&D5BJmeVf5^k}+?>#wdh2;k<TQf9auz_c>#7au+eCmGlEkM%)X+Q}u5`7(vNDQ-=
zhj6H<Dv6b^Lw3nf@*>tVb$ymD19R0Wuf+%IYW^oHc^(T2_Zd_=_XOn)RN<nyTyxj0
zk~K=J=`s5{C4q#?t3>=dbzB}1C^qDdGTbWL$O54>9~vHy%4VHkx7sgsO+ at a0%8}(Q
zS?lMTE04twQX-zq`E8eCON=_520C-RaMd;8Z`og51SBC^ih;ltC1Kvd4AkB%9fr;$
z0s?D)FjiR8$HR(OFg+w;gNSG=Gwg;=SrNP$cv8m~Bj!{YG8!wy!159Gz$(a>#FKZt
znBUPlS1c;@R<S24!n$6FbMC>Rm~!pJGg;hSsWhf;e_5z<Z)ka73gvh`xFBfK*1iBp
zEr_#a5bbISPf(v0VIIu-@~DmCJc1N?Y$VQ6nQ2*jLeS4sDgHX@`h2Y=e515?bv+hW
z?WhhscBwc-<HS4KKBWvcrj-tJ(|NDgy&e%}nS$ftk*P(V9kN;)Kk<|c&wxx0Dxwnl
z=fY1mVez*s_Bk8)o0xiiL4QBl#1XvSc5+zZc<XVA196Ah?B&q6)Se8}0M at m$S&Z*e
z^z+0yj(FpJ34i-mDfEtLcFSTqsG7qvz^0*^EmSj7o~qeGHIt3;RxOmr=ZIzt(R?MN
z9i72+S!iZ`c`R0bYGw<~Obr3eZ2b(XshKSlG0v2l`NGu9$3o2<JAf-wGUq3Df|Z9_
z28;4gTB-W7B-&}E6wPtZp)ZeC?XY!3rC<Y!=7^+0tEOlUyhVm2T%Bh`C$$jG*n*<j
zLNvF}rIGz6bF5!lj!2H9wEnV$Z2nxTIb7I_QZ>ir9+4bS$~+ji2j_1O1{j*zLNh;l
z2?>#c=%i at 2e&U;=`N9;<$3oE at J5WcYXtsW29z}B`y at 2sjG-pmPNqkE2Frw{&Z+k>a
zutSPw$3kU7*&I2s%}z5tx}um<Hh+0x@#_BkR)hJ>XNKZ`zm?$W;-&K+(yJWDl4A$@
zeTsJh3Arz}JQ)InIYN6vxA84q|1%hoOFc;A2xCl>FRO~lB^Uwg^ZO(CA3uKk!~Xi+
z{`w0G+t}vcua6Q8qHVAJQc87%`MzBS*B12Gan)E|Xnfx;yV3x~`SJ0mxvenLqyI(_
zN@%}IH{{~40*8;=^L2S_S9)W at 6D#qqg65hX)E>oc`=0s>H2Cuap4N-DDNob|Um$;G
zbEBi9nx0sm8oGZKM3G^jGyEew(3^rt3}QMV_Q0Km4>A<9bD^COp4lZH+VQ!QLHp}1
zIbWWxxy2o2QHeoPY{??`yp|}!6SFM_Jl*b|iY1ERsY5A8H<_Jtc`eBUJ6txNuuWa-
zb;2T+4N(L;E?D**SOU;MFbky;K!g}2g4^a2MYji{h%d-{$oUCDLRdj%^$CbNgXEfo
z<ne2SxouvYVV at ICi`K!`>><%aRfN=rE*Qp+K&pp86C3-osfun7R1s~-peD1tjw(Xf
zsUFW4J>o%7Ma;J794=&bnIdy_uKFyn;(>8A5$oBu%|yi6A1Cr+LA|+MgfpqFmT4+y
zQRXDnAd24GRM+Z+WXd8xlk)ZuS$Zh1C5!y6Vn)5N5vxQ9b&&_QHUZ|@>~wX7U-eQ!
zjAO)nofy6!mo9=`CC#Xd*jIbi`bD(Ps(Sst7%&)Bt+o6d_ at Zs_c4*)C<)Jcyonv|}
zW#qPG1+@`8We^CVjbOJZ;)f0~yZi6bMsUok1r7oj!F9 at p9N1iQ8<HXxVSSwFQzx36
zGxLQMM<o_RJF?B>9bW8zvIvMHx2MReiz9e4ukDJ|OrJ(+BiL0nrZ#d<Pp_qo+ at 9<U
zr{<SUGJ1QUkldEw8Tv+PT)k9C9 at OCO0+JzWRTPNETXNe|A$ca^Leb=zY4a$eliI}x
z;0a at 1Cz3=U6_Qs|9E1wVJ78%4`$bpBP$9kd6rbqnt;ytoK4OO~al#m8Vq!{KLALx_
z@&<Og827o>V%&c5OESS8p*-F&i#UGTEz_4uV?ARTuceS~`_wavc0wVYH&7y-Z%87Y
zASjU-B-_}>9 at n@O5ufmIuM?qZzgK0d(56Q6Tu!bNXr!HLN~E_^5GTO&SWhY>c&g+I
z6_R_DE=;bO0$vCtm|glQX<+TiweQg at 5#(rI<**T$&w?2b3H5QJ4izlY?wW))^}y&{
zAW%rKXW=J$e%f=cC#H}b3W;x^4T;20^w*I{9)u>gE2t8=8%E$0e!VS`{Gz2^Co~ko
zZRADEg%ZhAIY}{m1;AdEqq$i#H#a2`JTdVLlt}QDPD)2XWQ2}4{0GkxL>`LR=kc_}
zd{86#8SSMQdEd~~@fcXMvl^Nr2_B!G>%`DZ1eo^UJOt{~1IvLaY`!hJD}kkwZksCU
zc&L*64M!xwT(plO$?NW_lOl;#y%2Bp;=x38?|lL_;R$FY*jY at 48p(??7eG}%5!9T}
zNbfWDUp0~iQf!QGjdWaFBQ4jZk&f%3k&^L`*?vwHa-gm`Fkzk{(l3}W&kSwD1k6zn
zPUL8GXWTepo+NR`L5dPQLIJaDJ-ERqdVd^PQ%;$atikGy6Xr<^x?CE8(3FD{mo1#w
z9^r()p&XnrJB9_KT|v#34^DWH^PqrVBi%S*o)RAi(#RVokf_D>2ovTBwH}y2YFpbd
z;faqoOrTt2-Y|i3<$8lc22+dw=-HrvP{i(*2c24Om_YPl9GJl6ay}qnb`z%*Odw$w
zj_eE*CCn^~ViP40m-d#UgxM>>juMwGl-M4j1m3WEG3y(eVZj7~aBve$AjW!er`?Mi
zC%k*hbpl112Pe$#+LOYG93&ou6R6UnAx-u*WWJyAw3HKM%(r{0$W05Z8=lvC)F#e3
zYE0Jr=x&p-+h2vZ8->5LIdm8LiE*^~M2Pb`ktMId6r<ZkAQDNb%#- at kgvC!pNkUKb
zGNDR`9$225`O4JWZUophusx-u5JV!~aSiA}vR%pF?HWCAo7d=ZUMDQQdS at 0yC+$Tv
z^Cy$5rQcaQO=1=D$c(AzxEx{Xcd?EIjfMLzgmvi_$M$^DBRbntQ>JOxFx?(P#jI&B
zB&pubtWn%J$}}8{=WBcX%;KBv`L>d(4O3g)B1X<MThg?Hi*j=JsBTp_u~4Mn>f^pW
z>{_x}0BP1+`Ak=D&Lotqt84pUaYCZbX}cX*?D1%iq6?i9A&>?+pGGDHv{NWxxz38A
z`+-&!qMH1^)*4U(#?Q7VbYy;A8fbeg6mh7gP;((;CwLf<pM*5irbQaHN=9_ag-RHY
zg?40D7_KMbyL3g(qJ@)opU37CbIj|+{54jcXl~}f7pB27Pn(TKPvD8#xMael-LySw
z)B|0?53j^D8p at hAA9=G?;gYhRn3*G<B40ThOE7bP*b-)!Cpyqf-(6my^$oF(+Se1k
za3H++7PTr0DB~^FkYimV^#z|i5pGhsQ+t-hu%(tCC!&uG;8Q6Yn#mGX at 4hxxf1QR|
zlH%D?{6rYfCOe+sl*K+;IoK^`6_WE?(uy)Qp&*54U7()hb+1FnhV!O#Vp3`DH%w^+
zeB$(sz(NJBF)GOXuunm&XbE}IdV|eI?bkOvV|;)?SW*k`J=N))dt4)Jo7dogpA%9(
zvocYc0rauT87qgeHobX>^E~Wz2?K$$IEXzHy*>rRN6W|>>x&+KsDUw>87m*@KnJtK
zC>sMTkP at AS5e+PGvquyv6gOL`%jEeQ>iv5jyr8c^vr|{*(t3a#!akY!M99J`d at uND
z^9sH!3UmQI38DzLfG*KW5`3-d{1oKw4>Z<)q6P>liXEn?kh01P=HPcqv?E3Zm!4(Y
z%93a-Pgt4-Dgn at FcFAJwkx^N(3b{uMtVGuy&sc at quKs2|u{<DeZWClGE7!E5Gnt5a
zYS1hJ5zNkA%QY#+u-4%ELdZmwm8nrKii&&Zc%u$-*X|RbsC<JfHM4teI|f#xil{g>
zHCqhQae0|0H0G*aUfiC)*M&~(jxPQ+XIj;s?7!4hUD>{JaEG3 at t=g>_oUBz;%VriP
zH?6;uSWUfed!$}7gb{xmLv^fF;~cJSFV6r&Le#=V0FYlowOx6pK7-V4ev4Q{n4-o?
zuu8l^3wI^Hv1Q8zte%FHZ=gl7!+4ySO<E$b!D1k`=eqKc^5TtZFt%r?mo-;!5w4Pc
z<a}7S`-I`;g0sA<vLxAHI3sXgx+7O~ak6YaQNnnfNagW at 7}DbtXx4HCv5I0r#{7CO
z8!+~N9}$~wT0;c;_Y3OOgAs06pWh$B|M>CSANJSp_SawFY1RaPzdlL}HUstbi>-PK
z%$3%0*#faX{yHve9oIkKw+ko(YWw5kPj~0V9l4ITN1V at WqLec}Si&eVKJ!#fk25~A
zdk|Oya~qIWT*5Qf?Dj%?XM7E97tiQ40OhgyM34D#g8Yk&uNmSbO)D9nc_R0lcb%C@
zjL`|7*;xxpjL$qptlSx&*|l<cJ6JUUBgavU4<7ASXMC^+Tge&UWs4wO9%p>8OMY#}
z=id|<_qwBcEy9PW3ung(A1>KQ=S27{FQhple8}6l=-kfihF<f;-mepZkh=&Uim6YM
z at 1g#}e<ysqTSWNY`iW1FMBg2 at JWlw$LxfwAVNzl(!iOk1LeAFA%;{jvzSTw{-21{M
z at 0S^`7ocGlqudSCRtV}CL5tJ9%Rbdy%h{{2-ypiTzp?1v4nlO#gS^Nbt`XboqI<Y!
ztdA3MX{xF`Wid(;K4elf#tGlfv<Tl at E&eMQ4a=Wl-wj5`wZUk)E-*T-2N;c at l#ra@
zVC2Tej99CDxhJU^s^tXcR6n9;%q~(ziQr+LDkdN8Q?T2-n#1hT#{WsLc4W(L8<@{3
z&IY97{}Ve?;3}7v at XCqnO<*V9zR~FB=UhpVYl3;e1rJF8Y;@o4Gpuy|QVV9Msv#C>
z7paBZrdDXMI>j?{&BdPI_MB at 0JvJo@BWk5xfC<=Q%ZsiRNu#XmP+ at RT4RokqBW{})
z*Mb#Lr#`?m5b?|mP4T(0#~qiD+OD2&H2M8fc?YZ3B+aUE)g~@vH(>tuQ_qFvAD- at d
zm#fOGAD*m14%t9YjcVk`vvrB9IM at bL_zhe6npeO|uuV0Zz40N8))YU7r~<~;$QCFn
zjVxStAYI=cv~}}uPEGofxyKMmO_Vh_gwS?IRy{d)5c=*`-1zLxn_m&BGh#H_K>!^}
zQB}s4wm5hAwd+t#=Jmu}^4V+5g2&d at D;|X%4t~T(Opa`^-W>Kz(G!_&mZF<Yp4|4Y
zMYCvlg!5d#2 at X=c at 0N`f#bABBB3xqlEMi4QsC)jg0(IyiVlT7Ta6%fl*<68f!SdA1
zPgad&w1IEI-i%?LX+dm8Li~*40gZMWY1_Q_!hJ at FT>BTg+v-C!G8Zh(HaZhEs-9Ju
zFFU5S#qxx?BKVY%Q8$}r*ShvheS5y?5%=ie{GMytHB7goKr!pJ+s$KEH#4Ga7%i&j
z+%{K?eg&)sfQsWWqdJko5gj8Xz+8j&2-=mHyG?bkN=(Yu^FP(fZF|_b)G$EOtgmuS
z2Uhv6%zj<<2%6IYfdeXZ?xtYL`$v;3PP`&=r0sJX$rRMiuu>CBJXmb^XgZ at h{k|6;
zsZEN+^bIR|L^oy3;ymmD{^y?_BWiJ|*7k_Z!<MXcIzNW+W7B!_MDbdu^8=)MJe$s&
zT`FzTd2_TSbf^D53TcdU^)W3e$Hszb$UI&Vr75Dh*&;`V*t~gq6~7OR7+t(*(w>b+
z7M-8LUV=0Udd}>aSu>~eu(?2N-W;j$ojaR{-B|%=^Or6DcX|A!z!s+*YYWj1v3VS-
zavp%b;?ydr(|Nxo0aJ9|JcKvVd9(919!_e{xEO}&>58yo&Gc+Kk78;>iO-_}+Oz5W
z?ibPdkB;JkJoBr^j>qXdHf?&Rk%vHU9XgLw=8MmpJr}S$DzAGTMrJn;;}ubQzrHZJ
z)gknr&2<R%OnNq-KjylY9Afi(x!C;PV6pjq0wu^#c-TAdaM(R>J+I%9gVnQ4Qd3Kc
z%X=C*NSw>>G>glB6v9DbE(&66q8Dd^_o&satG?-xvc>;q`kcbXz)7>$HhBMEz94nU
z4#1e%F$odOJ&k&K- at 1u-V=+s%@b~6DXeKgZb`@IAv=)@Yn~ua4nz_yQix?2RK~sEW
zc*03xDwSwIHwmV#RmCTv?fyQeq0c}ZDCG_KT+q4zeDVY%3*a3M61zyYa-_tsh$=h@
zKt;3nkdIX>W)<7^V;h^)5O;>LZF`Bj_VIwaxvhy?D=Rl6-)?TJawF;?0S?S=;!Dn~
za7wR~K at Mjys$U2xOjVNP6riBk$7HH%e+Q(_4@|rVvqL#YAPf!(xKap|8f)WN%_RY&
zHEZ?jNWyW}CFs at 8nRJ{SG%6a8(?U732In8~o<^u#OIJh^WDZAZq^xs6JV~v3*?~!%
zdSM;I`{NC&|D+e^a9MkKMrKE*%q%sYGR;-lmFC=*%w<|j6<3OSRE;YV;RZJ!0#(2T
zdiVlW&qPi)$fDNq0$G3e0@=X+Dn!)0fuWW~8r5Es7O257=jts9)TE#JM6BWG(C3B8
zd6#BzMH at U&jC?Y^@}<wq=B0b=uZXH=$1hmgVtnm^e$Ce at R%PszF+ZQR1IqsUN5r6;
zc9~%RehF6Z9*o<=`uh1J_#eOj?eF&2Z}!)}z|!oT|9yVc7`+<5lwwIO&{~(v2812u
z`?#!s>^|d;!XI;h+5Z0HA3tgUThoBgXssHo4P)M(@5^J4#u7|V3tcT5AJ|0%s>n{0
zc!%!#@)c1aoIJv?vuj>j<HBAD862%{kFD0OLF%g6kiK{6O{eJd8Vr6;am!$L_M1%F
z$+$c0(dPA<Pd at 1R{zHbot$?0PHa7Jx98L0vdKaEr1*X>HuuEL>X1bk{aLxrUJRvLb
zHqB}@<Qhld!Y-GM6-E(5KM`S)txE91Uh<ZL7oObmeuJ=6)w#!IF2Q?yAb9x!J{bsJ
zZgIp7R}6IvR2UD*Nd}(a*JyFuyv9&BF~SltpCx!<=B1zF%2Z7pbx^#X{Y)FL^|q;a
zZx0kNZ3)^YleePc#VvYh+w)CNVnLyJne3x at Vb9W^qj+(zFw?UXFYMG8Vk#43Y?~`u
znXd at 803|PMVRTy^Bi71PN2QvA_oGd9D`Az(T}IUH5p>iecin!L*!3NQXh_Ygc>P)2
z$8T`;1FL<cEf=I#UlBp$4#d{;SjDbFSsg`O at 7;gLQI^*IxYL(EPtYr-?XRS6joHe0
z1liBaL#gYwh%KZp?4hyjN*9iqMLY4BVzFlG&r-UuQ>Go+5QPJfN*88JN7=$2quyv_
z at Ev-;BC6w-lrGFn^rFy(qh_*|6d2GJwVt%Qh%$vPJT46-bYaiPdO_&At*4n-Z+j3T
zyAZl=vl`I3mq|@=IiPag45Dl6`-wxo3e at lmc}Gq>@}eDyHm|r;u4kbw++;*73l;N~
z*2OvDsa}W^k$QZV%Jo8a;bw^{^~ijB#jm+~g);ZEo47~pM<M=vc_4FrS8f7j2s3wH
zw4`;)$N*?mZ<mGUIbQc#1kBrI^ee(v)^>dX{gR%ea+%F%iCj15e3r_+?RBl?q0ZeO
zpv*lxNamg+Q0DT8(sp_161U9tYxenysB!hI at -%5v=rWOFBdtFt(G<F$r65E=ARI09
z(><i&)_=iS4m?u`kAoO42)@BHVaLh*Cq38x>te)U8xefus)jwO(q8ofaNH5Nvg#FS
z$FT$%cK~$o*fn*JC_$7tzwJ5K3VNC+$-h9jEKW<X#j0 at Ef*{J<b?nhN5Qg_e-=h>Z
zuTf at yMQFBmW+7>y;2|?U7S^K`#)ri*h$wS#gQ7udKXlEcxJ#2n(=z$iFy%A;)MJek
zoyy at TJ%4zD6vDKjq!^8(7<ss=UK(tIUhB4%uhy>!B^Y0JtCqu;7~iWTMP-p!ezViO
zV~4ydLJ!D%^N7A0F>4P2L*m5587d)fMixDkbOGLK#RG|G-S~<K`od`0i!#JaLMbXr
zD+`{gN1!GBRE_ri!v0_3<K0kS!socQ at L8@)_#D?u_+;bbnj;cE8OLeQC49DL3m@}{
z-MH>CvsZCh*;NWey2m_4<TWWGt^UCXH#^4pT)OA-NcWg0i-@@H at m+*~O{Zw=QRyBW
zTs*;bk6ku&+OB(Ye5stTD;-<+d?d(qPfqs2Pm1hr6Z_zQ at K!RbY_v#?A!xPmB3>$J
zg(r^<tr-~K544)y%8jgOOFaRHR&VTsr4?;C<BhGr%#dCYWyFK67<p<}VJptt^TAe}
zR(S2$dfCF(?Gd*67uo?=?BOZg;EGesdcYOC$9SL<_ZZ~{SKPGZE5cGA>qb{3M({3l
zMJ@%Oj;=`c92;Fd^Z7<s^MsUcbTv<IFDwOpE9Fl;8%uGNI6gczwHsX#Qx3S|a_yjL
zbp(qyx}q3TlPiKLCcJtrZgfQ{#5WyXfvPbbbiHh$>-Gp;@r5`Jx*{^o8(mTMgRS0J
z7v0>gxUqFN?~1Urgm|#k{!g>q*ou<O1Fd*b>P&~$j~C{I#wydq79sZV>ZS&hc>-%T
zglEu;*m(E>_(rDNjW+ at B`SJy&(=*EOfRuF+oCAO}w~Ynhf$<fQ>i^iTb|gz~9O%DO
zybFwiAMj_(lc5O39HC=nD7p=;qw6mcnXF><D#n<XXs}596PsD*z)AcilYvHFs}8Y5
zA#h8to(J?Otf>QA(kdf7)13oxg$A%T&UgeTbTtc&rY*=i at k;H&w7cxS9vV|i2fi=@
z57A!aF;6UU%sKFGcfOX^X6u2odh{(xYiqEQvAO|{O~t3oP~k)HNx|j?g_ajJsFqgX
zs-{>~>TdZan9>+m8<i(EgRR>WY1KnR8Q^gAfOV6AzB5LYmc9o6QD+~_pfgQn)?mx4
zo_$|R<#kDzxc44X^;Mo3G0$+Tnq=GYsF at K<C5J|?<;se%G=EEyp+UVBTGe2VHZU$8
zz0?NW-QDg^JZ>hfR6p%vk*;5i8^d&SGLoradR8o#oSx>RxwrSRH(nBdiQf!d(o`}Q
z;zd*2ik9Y*I%(`|Jkhe2EHp*OT}xM9Ixxe3ST8&;to#x+NiqOf%R1?TOIpbfjXtH(
z15UAtGK&tZ!I4ss-d3ZY<4KD+YjIpH2DnSgWT!`i0IuR<D0_#d09&a#U?Sor^OLq1
zfK|r>YneSH8O;W`v0$d|sGad at W1lF4ow*jix?SCjMyi3%EVJ3&&_Jotj^S{7_8Jxj
zmf8-D>?JB99*#mR)j=)mM46fnVdu^lpN)CGzh at Lr^xVJY*p at g!SB9ueGhd-a)XIKk
zo0?EF?8v*Xhv&9k7axP=H9s`Q<NQ=(vT`TPet{B3JSc$&9r%tWC1{K5eSZNnX&iT8
zh0_&Gm$gHqB#(j*6YRp&&!rw3?u5;|o1A3CWA0#fwVBo#8oC)zJn=T-&oE<IxrRG}
z1NzJWi#|hA=|0eB1AUhFK%X7=D*m8)wfNY at pAF>2j%c(^ICi)|pYub(l=wlP4fI)5
zg7n$iwUq{aHjrbcVbJFTi#}fx(dYOA%Q5IP?x;J&LnmxeGW4o?uxD7Ikt+5KJMGi}
zd%k-%-10uSGaNY?kKoSm^X3|zu=4xj&Ia!MKB%*SI`{WMlnq3={Hx?5%6LZ}-z2T5
zd67CpM*}IiGfdM~7|hs>&ud+n-eTLoHHHcz5w7_l&j#{L_Xc^kzTF$#`G%s-TOj6)
z<2wzbKKe!W#GLVh#da6m8S$J4Gu~-<cSGdu*$oj8Y6NvQP-m8gs52rmcV}wRC)VI2
zgFAmKFO=NhuZ*s at dVVMW_ak|Nj^r5$^zrpd@#E*Ozx%J>{MTO?+1``?dB0lsp5P_;
z!~E&Rf_LAd8MOWL7PZ`BNBTNNX!u|`KVN^mT3h}+HAe3#Ux&nErux^~P?kO=B<2f5
z-ZA5lHd439H3AKZNOr+caXjQk17T<&_N>*OBGDVD!@S%EECp<*fGp%}UFuUys3A^n
z#9mr6VVSI}Ny_39D^<Bk4O8bVXmJPDxIZLqHEI;*|L~Fru8G+`S*&=3_S3;nrhGJf
zy!pViqNn-LFqPm!LA0sW(XX)WraZ~jMxkGF(Ic at 21P)ls6^YJI)=?Bob^`Pyps9%w
zf%2DRGP7KpZY+9rMPtSr=grl^swa1Y8^*!i;5ZZc1HW|K)v$7II<W`a!lDr=N!*>Z
zo!IgY&X$zHV63d!T#3O<qrENDiLuyvC#E%Cv8GanceGx1E8ZEy9I`|+ih6OO*^?Q*
z>Q2dC?Cw@@R1>+wEJiSy;KpGCLE2N58Lw5RR^E_TyDIhL|7!OW)7;CvW>yyV9Txu3
z*49&3cj!xBMlm_2si at Z#xOR2T6*r-I43R?LWLx(h5}g)pvIXu3#)-^&-3s30Onnr~
zisMn#BFMj7(qw%_OK at CeE~xSad+>N3*b)#ueqdbVC9bHCl?#t(%<b3}@kS%IPTW<&
z7Ogt$Y==VeWmM$^0^ykaog<Qt>O=D%H4o=AsiGWjn2SkYg0vogM=b(vn6}OE|CW4G
zBJw>-m!p+s$WpgJM60qX!RTrll2ovZnaK(Df;=!*ekiO>y$4^JG~n1`V$vv*Ca;<D
zT0S{bRAvqG0IaTGO5<@!n6yRpiP6Kaikew_^Hy=Owqudh{8GvB at OdNz!#y7f;=V}Z
zp;0Pv^y6?Q8O$QVfV;b1F~a*(bEYZHPs4(5ci-WR{+s@>j8v+a at O66e{r5kiL%MM+
zOqax;f~M(srJc)#So>__gzF%u8b>(5c*kr3*K%ArYVwkLYod*1#otGtch~NlTwQ(L
zC8azR>Xbqc*ed^AW|4j$oFyeugB{pkw1}sMUCTPKrY35FcZWCu_(cw!6=2P1NIJa&
zW*B%eDOMd1tR?P{<mfYwjRiAxM^ZgBbvFOjVCzC##;X-hbTV2=vb5$lqov(EsZr+5
zI&aTjEVQHN*`d*5&ar!T#xkpW8t>q3q{8EUr;CrHJm23Fig#3PCOgG$iL+B>2n)Ms
zzM^Hi#>#$X)BGtj6y|Q~;k%oUYs-t2Sr3i6=J5q2P16;3(x_QvC5;t5vOe%OY)g;}
z>wSO0D+7c6y2BaIKEOwe;1GP|!NS6f`aXDC7+MYubh50}m8Xn&%pFk?xtRTztMSGY
zZ!7+c%<QIZ$A=FB<h7b8nJKtiCoPNY#5=&vObns~uHqO)H9LbJM*!vsjh^cfkm)ae
zhRm0Wb%qRVPT$PLRWt{r0VkX&F$-E5q0o$1jQYfD?E_7%ycji1UDJY}_!U|N8>tc=
zp{gBvR7xBUGqM6^<O|b`ZV!c)%iXu9QlJM5rhb`Z7u|RuZ?@41^+JtR4|V2fz=s)|
z6q6Z2AnyXL&HpO##PQQSWx0Kml)}4~e@{9ZOF8pxy({A<(_kv0f$1-81ytA$#_|)e
z&zm_>e~H_642zN6zqGD`&K)?_h{z=o+r<~MD!K9w at DtJw+i9}6XRgesbCuamZ>Qqj
z7I*UA8hKFz%?&Z&4D`uowxcq4hiakG39SnrC5E#5`<2oCmX~w#e?NS_uaP_}fj+)o
zDSrI?^>_dEoB#R?liLmb=l!a|(Chn$nO|p{d}puBOZ0h*T867Y;;w`LBDxR$eEspN
zwZ at EeUSSg#cwP9?P%p{nLo!E`BuME)Qm01)gvJdAj$OlX^?07R#|CgpFzx~)g}?@#
z>BJY&*<oGLfr at Z~r?vRIVRnPq={y)QFju&ErBN{Wgke0;JpF-#N&(x{fz^1z8>nlP
z;1PjG4UDe0 at yG2w3O-Lkt|J8Co&B2xzqTZ9Nwy7*M`Zpv&8}y~6ojNvV_- at io*JM7
zz)=ZdIo1gH)@wd+p|6n*C+;cl6Ox!eH}E7bjo!wD0M0 at 0;+=&#uqK_ALCS&S4!V8g
zk2BR4Q}UU6*fw#hh0Cj3pCcdryWtN?!dmnu#~4i!_ezXqK#5`@I1{gWvs(u?Z^z at 2
zh!(XQ$1!`sfS4u?ecC5#ZuW?W52&$Z`3h_i52VipKf;8$>Y?C-z44P~#Mu7ggO=s0
zRw+TP0{6)iSXP0@;uF)jeA`392)ntF>=x7G#M|sG@&C&RjVV9&+Ic0tns=(#PJK*D
z;8g1omuKWY9<gj*$&6)edXj`A1~rg}bOhgk6-<su)R>cYwMjctUk`lSwIwYXHKIB!
zGCJ<4^c|we6L~NJ)}STKY;=8=eFJS3re&p<hei|!KEPUOT{pB{T}w~+AKBHCY*~uK
zwvsETz!(gyC!=fhMz#MzV at u{h=IDle4?llo85w3i6~-0ogTI_iOJUYpluEimVkW&E
z<+JIyT<KTrm#g1k{ElK{{n^9&_He#S+3W%qb#Pm{NLbp!Gs3Yhs!*633AJ0kWnqHi
zw<zj&lkkaWJb1PB)aD2#4!k6AZJ;Ieggvyb^xW{49bXJyb3`NYK at 6^RmEc?IjV<aR
zqXmms1U3L34VM at bp0Hdn8(!*E at loT(joE=`^)eYOnQ)xN|BCIJ0`?1E#0kUC9k`!6
zjw6PKJC?BA@!T4+r^S4-eJni!=u#B#4pr!SQP)L`TWyZh!zXs_<qn|h#|KW8C!Skg
zS5!isz``D_23%L>j7xU6G_L3!U*wSp){0?_^dE`31)|A!3m25t9<06Ix7rrtp3$>6
zc5M%-u>!BtUpL%ZXRsJOoEr<{DLyn2e*vW73*g>xh*A_(`@>K?$!LK#!4g|M)Je&)
zWv$GD71o%x4PSK1geYLglET8g1#C;>iVy+enf_oG0hdg0Uk(<}gs)d^lnIE&JW^y;
zSS)2i1+0KN$H7;!!|-z(WUlm8`O{LQ9ZOszdew at a9j?G3WgclbkX1#lm&c&fA3U_(
zFOT__-s~ujM$#X(qBvG&Rg4NT_^mHuj)k;P at V}5pY7|~rDtse=WkD$R=-9xQuVDE3
zlL?X>x at +~ZJNX3D at 227Y+ZL_ph|_!Q#$xEPKK(c at N4Yttj;&+HrzJKV@<6ALOIi$9
z>W~uC<;uoI(X at w8Ny9yo4>9kc+cGFVT15#NU at bEtzITyv{@)$jP?0vFJ*RQI*67;N
z at dZk{?gYRWb6|z>gq5-At>eyy1u<N13 at 6o?_~MN-k7x~EqO2!s3;kCYeBcM;d-V)*
zH&^6?G7L_tk>=sW?F)88ZyU69^myrn$QMyPI0u^h3s<g)d}9*BE1n5Sx5i6V8J{%q
zOZH2;cq(X~kru055oGH6AEh*8&@FZfgXu9j at EBm3oVpf0okFrOUxwzmIpdmM*vdl?
zO-`GA;qO)R%r<eWA`}i8&ZZPAyrrC0H9FsM4Gs~&_F$)syNzh at KX!mCIY_88ggZY;
zNC&>}hFf#F83$THu!iOWJj>Pp)%!M{e8d#uqqycm at z0q;6#<_}%338MCzW#k2ccAH
z-vS at 9Dq+fRY~VvHE15aESjmGoSP?09zAbF)Jr%kWhVsa<XsMthTTJUb^CGkyG~`qY
z1`T6kSB_*uD$^LXHafyRH0yo1lyMg8q!3~KDt4IU1MRtDj=?lmjtyjlN>ZB4<C(3k
zbmm;g^dtPB5-&TLGneS|Mbtq(MBTEG`WcrDTWABdTgfEW2V2v_ys%6Gn|H&Wi$uJ@
zAZ6uf+L$x7Vh&WMsdDlZ217 at -gR+?T>6m6GTnI+}gXr#nqK}o>-!rFgArwhqzqWGG
zzD3!8|Km5ay1)9a(H at DAt6=%LEVjKbv|!cWmTwia^xK$%`uZd--!@$Imm$~udw<jS
zxPtE7wH*tUdJw~w1)IZY@$qkrf7ar7*k|CW{lQ*3$?JHv at AY2yPs@xshZDhiEj#Ky
zoa~*oH(GGt5rUPL8U~hc7r<uvV5783%NAd?-G2JZkUsgB#k9+BN~jcgPP|&SWlJJ1
z)7{<OqHB<@@I|Grh|$pZ7<rfJtUq at K%uC#CY^1Zqs!4bdJ6i%9R(NN0Y=-)<K&@r{
zsKJu;<D$s1Q1zM2gR|}H^@Wg%d<SJu?mQ#AU5rt2hr4&}JNEfue}(AXyZoI!2eBG>
z>HIqC_LTHIV=8>91C%O$rhA`s7&T0ccbJUliy-O1c=;%kI61k%6 at 6a@LVqo|q7Nij
zaM6nFhHrsqsZVaL;2dNC;+Kq?_)A6___tHl75?T2Y&=?$n|c at UgkiXAqvJZmd!;FL
z9}}j}!EO~6|EA{j$4o99K$qbV&o#&&T+)A8-(DepF<D<u_%rmyDrW^PBG?m(*gdtr
zQO<oSM@=?n_TZdbi3?if{jlE`y-imIhu$JhU}0_7Fmz5Ms4xv-{b6rN^RBpU8Fcyd
z{<3i&STP3vZzXHa;?UhFNkOi1rYlWEHHgpgSx^=tvPupV2*@;Ha<79ks`7jxWpD?G
z!N~$z)LI at g-hx8e9T1mf(3{{{I&ucZEqm)%5kte=c(wTy^(IE{SR+?GM<M2w|9i1h
zCKtpDCA#s#7kz?y46$R{v6<3xwb$7~Fnrwox8cix^rlZhso$0!Lohodsw{hLjQEkA
z8B*(ao!5V6z2J^(Sx=q2T%oEIx&kcj{Vg1^QYeKw^J`bkcdNshw!nYposk$RQKvj#
zlu8^r$g9|RM$Q}t&uH)8u;q80^TPGcAzH at 5mhRJVgK{Eha7!;q!X?VyUCAl4+vVyv
z7AWG1!k;53pYe(Mn7fhI6b9j*Z4okOwAQ)9-l^he?6FRbP3vJ93^K}k#EfxcInomr
z+xJ*(wHmlH=vILBYqlc{LM>7h8<fgk8(9O)GOpwKq69l_%%N-!93j~T&0we<719ef
z(1jXnyi*?;v%2ubu)7%>C#1yw?)KF2po-shD at M=Z%+?p;bIw343Y+;X5?BVfsl=UU
zjDM6E6+8H33F)^wqtltJ6?d?S+#HHK_you^8}?|Ktk^)gLE<X%bfEoky2>UTF(s21
z*!xW;ufUl)nY_SlFn3$R-%Q-=e+`dfIeEV$xOQ4g5B{_8*vArfrdQw{n>ijH at n=%*
zDxTT7LsPg-&P+!~az`=NR;j=KthKDa&f4<$qy8kM)I1q6VwV{%jVx4PUel?5;}s<Q
zWz-_web~M%b;~NdWGV%r1lB4PggtOSqH}ubY|B6}#M5E9lE%ZFX9y9T=_G$X*q-Rv
zaViJP#{Kk&IfcbXhp6|775nCB{5Y|$B>A~;sN7<HOPrz)wlk`xMI{-g!f$n?n0K$1
zREuih!9JA)qgPzs$o+m|xwaq4rF9+)njODb=?C0F)ShOy&V)5_>1Oia?^$V?B;#J-
zI7p9ugwk`8D`X)2YD~+D at Ha&-b3u at p-OlRsMXA>YY<6+PVa~-n<P<Y_#}|JonU-eB
z7wdC_pP-MQ_7zwq at T6a{U!HzraSPV9134uHs~iL6OT4VUBjWOAvL5WGU0JofYAI>O
z57`_$I5I|UN%Xbf*BKHM&>2)WHfFLltjOxy*X&r)n2F7a$1!`QN(Yt{G?jZQlcFr_
zpcC?bd6p5AM2a5E&tsW2C$!-$_OZn9LQAAp&DY{BwGe+ZmEhz#ICT~5uvqtAP3}*h
zmy#LM!_<325QF++XMC=pCH<8<xFo<C_<z*Y64A*$?#iMOe6hrS9f9cB@>XRqnFRT^
zm`brhMg9`s4u<9DvG{d<?gahIfCRmgxNSzo)Ro)7^7B~w_Bt#UFTO1vXSWQ!l*;OS
z&xh5XiDO%O&51(;o1rgh<XqoOeP){A at V;xXQ_FZ9Qh-U^Ch%!xKFe)`P;iZg1+vDc
z&^E424wo`mQ56!mM4&dNZ18 at O`1j{O7%m_8txSYTjTF5t63qz~Xq9%J8n+PqKi+=1
z4gY7mTGDh$Rp7i=aaK^DqM%Oi8tx8yTDAXa0}<ro)ISVTQ-uHmjsr^<eMQ{h8C~UF
z%UYy4lq44V|Huo=bw*jTG*MxBK#v>>LqUMAaG=i5FEciAFhCFb*^CL2_}?eh!tnh!
z_lk})wEqcx%9^?}=spaL+I2FSd<ko6-rGge3t6jE%Q7c&ac2l!g)!gv1eQ at Xc<~N>
zvZ6+3cO1ORimJFk)5>=>>}Z^5Mk*f5*(dWo{{URfoR{VMWwEtidH2auyIkLx{AC$@
z-yHq5>yu@#*Wn)wwP$3M-WJ(%S)gzVvyae^Q!5tH at Y7;gF>A+Rwcfaw%B`}oLk%mm
zgAUgJ!t2^W%c2fTogSD+-UN$2J~f!{FB7M_&qhBFi)T+vE*hG-Ctkr=mDG4sywdbI
zH7pV=a*KEu4y=(Jjz`?z at t!x19qa+uig-Kz8S_Hm<=r>VjOhwHPDrn~MKD at t@-J`M
zp4i`UEO*?NsHuq?@Z^~%kz}6_y!pL}=64GvCT?FFcZ`DSc&*wY3znFR0yo2s7-H^s
z4-Pe9neng3iXI6b|8y3Gua9BJG07b<KjNM}RL3J;nzww%4`;MI<BsTUiE%a#)kI?h
z%29fv;yHO{Q{2*ow_5Y{uE6j_)BRX6vW4IemUu)#jWbdughl1>7W6i~Z^K^rJ_8o7
z+Ubdrc at kJ-mdG<Cv5^6B3ov=(mrViB9FJ!J<KcMdy<(DdDLgM%#S!JIuDk<01yB1O
zD_0(|K*sqCqwxj at a!S1|no9CJEwc5aC3>yWf?c01+RtAWOP at K80mtzMTpfvfn3$8v
zzAzFv4iOi)XKMRHCcgIt4|vNAUE!820**spB#w|f9^ndN?9eW+6*jqICCm<#KkVGF
z23`?i`2Gi51eS$8s_;%WOvf`b6Bz#(tU<LRG)rP#yf**H^)Vd<UK5w>4X(1#zYiQ$
zn>%Q!c=#>*_-MJ~TDyK*z>n`=jQLI$`Fguo{X8tsKk4otEzcWa*C$J{=(d1Gj`zG-
zWZC$iEHY+VvB=B%=nobd6Z2+~H-d2U#Ue}Tc5}p!-uhyZAKuPu#hH4u$d5ktL$Js#
za$YR*En`1e6kL!8M?CiHMWP7)dM(g79wZ7f><5V)lei#}<E9T1-MC0}TPhsk)$`GE
zhqZS7w%jCo69y!5mn=a`bSM3!MPrza_R<i1w+s~Gk%iguK}nNrqb at BPaEz32^cMEf
z{C<`_TEq63-#FIqud()Pg|el!4xHjV at KNqqf+Knc&hf$>bBuc6LBHOX83~Yoz^zpe
z&X{aJ-M9m(;ugg%=D?Y&;f$UPjfy$y-Sp<3 at Qh6s|491qoD8<K^3=?zwQT+_%G38=
z0JvV*pM-Z>c&}TAU`LJ}Db>~stJMi*vBeb&O>%9XA-Kcd+#o#Sr0VfYQQI{{e<Jg{
zOs-ebvoAinvBGq0SRE-LT3%uf$;N_X$Pq}v?NQzrCO=w#t(c1$u at tczavjNNZxrty
zvcxf;un_B0?RG&j9q(aAFN9wmAK(=cblmAB-NF-$N<jU~H!O>uPRZlAQ?Qa at DcItS
zQSSnyB5QF=-fH4i{f2Pdq29p)_iSl|jzqIVu`-J;)omC`WrJAE%)-`-!DUeeTXcgA
z%IKTmhtsO4nqtT7tOgWBWATy)9FF>4@#hNJj6ZxUr?Wobk1{HE=Z2*#bC=bwZDiu+
zz|v|11uZBX(WOMrcY8ED at NUBR_ifB=tPnx&EB{b<N-?G^=sqVqSyrTAQZk4|u&?4+
zLzUa0!!G52SdTbd^i%c6*z`dMsN0$x%1!rd;f|fp6%jm6BO7>9SL`cyW#EXIHnKSI
zQQZ)lXM0HXYTZ at AWP^}s85T3k;;L*uZrKO!>=7XASt3#3PvTSuei2024Pz}{b?1UU
z?w45e8^7M_mZ|-<qCdx|!&JG)sN<2vCHq0VlvfOq^TZ(uutQk`vb4qzDtQp#GhX?b
zcc>Y5=AGue3t7H`t(}D}o~e(*=D6fSkF;}a$}?2BV^{g^aMa<<lOp at VGm{ECF3YGL
zoU4G=VawWlMbpdh4TI5BMpsYZYz&SwZ_~4<f-~@s#ud?1ul^p681hsAZaGIDD;WPs
z9!n?_xEEcN!QAv46MWDsPmc0ci$%tx_d_0^%L3L;*3T(}f32dge{d?IVW(&-tgks9
zI&M|aCwbfdyeVRBzZP8cWmFHxgCp*m<(bavm?!u<SPJJLe!L4AI=nqnK3Pr%ZqdnX
zSqQ4H=}fp#ypBgBjte=Knn4DFI=)Iz<^u5y<A%7O<2?@R7v3t~_i*S+-pCi_XO;wk
zs)~sB&h^%rvxh5Kiu8e8(AVle554PH*7<0;bTjw-#@iDFPH8#vg=U&B%e|+Z``dD#
zT)Tc-{Qgb;vPeeW at tn*eVr4&px(v%gR`u|_V`N=B2v{G~$e5_YLKY~NQ!%H~3Apvb
zSU>6yTrsHg8!7Un6{r>oy8k?e(906aJRceb_M_pNOdggQmBd1$gKIex21aq9AaKJ_
zEC}J_P!tO?{VXPt at QrhHb-wLvkT3Kg&TMR_ at b|0+B1(8b&oLIyl)+1OTuKZ-P%+|l
zJ?r0J&dGtxyo*0p{X8t;=A261(#@DS;STTX=h4?4>$F!lbd7$-YlSdZ(|?P4XQaA`
z4L(8c=Mj+ucSlYvtjDz>MQq$M>Ox>v*!;EQY}`R-mO8i82sUtp8MQ>b?tx9~3|Q{e
zT3`JG_SV$T3+sWOdcz%OcDjW`i5uvlpSmh<3C9TgOjqF1m?W5n(z7+o4*0&&F+9`8
zyfAiTxqGbGo1P1S=h1DghS}WU;qHf**ttL&m1TBM$DASuTe at 4QRVFqJsy6!zZ(G~|
z%k9a{c>cEBY5C?V`0KKF!DFEaf6`LID(1j%SVl!YBJKF#fs!i6l at sICdq#eF7vqh?
zxehzkV5$Mf<AJdub@^k(64t5?yoJ^6D0duTsN-LONzZ)H1CKpw2Q4$Yc>V!rXgDg)
zm?SW!&bY-cO5+wbaOP?_V;qEjS)EY=dr!D#Pq^j;F~fC&9cLj)jw=W#gqCS_Fqy?{
z#2s_uyWm!C84H>tNf4N`?hIl3z-o0yS!{7diO$V41fqjk;szd(16N!MLboCQHo?A*
zxx-3JyJhj&jhWxE<8-pKqSpqbv_#!nrHUw=S7^}l=OL|wA^c^nF#aH#XatW+Tdv at p
zP4ymWxLkrZ+`5H~2u!6FiaB<KDqVC#&lPfj*Tf%&U`*9rJl`BCR8nAsWzp4YBrbkI
zjxGLTk9wEY*bz!H-+61y<k)y=&hV9-dqp^RQ=Me at JYQ6xv-q+pY8gwao9G=6Xnj;H
zVNeBE)&>=n!QbSH%j5b#>DO5edW~7U-r+<u=M{Xekj>!3DLHs#d7zRXl~x8rj#m*{
z=12?vdSIF?<`5%^Ic$Qz8Cj(I$%5pOq?X!fpkdjIsf`tr@=E!K!c&SZWs#U!;t!m>
za-2%`)XcMrU)10m1Z>r_56clp-hQh57<!R`3A!J$;d0X at 8^W>kDH|eoEchGPAmI0v
zx3jvPfeeFh>ISYnyF*CC|NZ};{vX}dlH*2lME5#HElFTnsLZVV!CTufyz!=Ywij%6
zNzDO=9Fy!3<{G|>@8=isBCE1m8VFEV1T!P!|01Nj4<r8ZD{4j#GM=H(o`3nY8}`pX
ze|pjd6V7^)8bE)1{<Hn!Ww-UilWbS03h*IEob-JB^dz6q<@wvEfA0Qz+C!7w*Uv*4
z{<V83amD|6_v;?F!|q at EJ<<+RpVIDd(O+Jbz)dKpS1YdSsh2Ay^m2aLI}D5>2M?#+
zXTAK7FJJC6PYr*g3ik))UBBwJ+rcj0_YT*F{lB084ue;_q<e{7V$vwa&~k at RBcvZK
zpY#}lS7`V1+dS;k?(MWksN`+=qfbxzbQ11f95CG9lzaU)JoYHW-J3WD&fuMae~dZr
zi<or%0(ZEb+Ng(KH}Bj?y{O!G{hvROf4$rX3b<10O;qJ+<FrR74UM{{Gu&LGfd+!p
zav93i^xOUkXp47|{JYEk2|WU)!nF`}upiYq-{d>*PLDSpI$e;C{FI;nC!ele{X9I|
zF}({9s338{@M`z>qsH0ZAo=pnA0MadWw`SD3mgw+9;-k!-rarC=lC|<?AnFakA1+^
zZnOc~EhD7beYy{@7InCr54ZF0!{I(WPG8A+;~%8p=eW~fj57~!@BDh<A2IG`tP72n
zPY6F<UhP|@91o9qm2-FuKa2Vi>Obb&U`I{REVbcb at wGRn4<oiBHOJS{!g&8Cju4qe
zmHH1q-}z at oYSc!Er_=88)eft!{Jx)%>1k)bT>9f*PRD~W9?BTLX(qJyZu9-u`S^g&
z#%n*$!~J~t&A$J8|Dyn*eKJ6`SWQx?xPyP}>u{ErerBSn<30=H`Her%w^@uiusrBp
zs0wd43lYqht4V!BS(=i#DiK&OASq8T?{*NG$gew#?Wa!uyT<kbuhrcTV#RKRu?N_w
zHa*_N+sM4 at 19~2DFQdMXvwqxA#aU8V()(pT9^UWzIcsS`*I6?34Fz=K50AZ+ny9Mf
zazeM6c9+)^<sS4JvQE$bq2IIV7y%rnBMS>T+CJyS<g6ab>Qa40oq}-R9|t>dp8vF7
zyv<*({Jg0G&SNga5H+rq8JhymQdzv=cR!(#AUPic@@{w`Pt=&tr(aGNRMV?AB_yFl
zoITHk_Yn{i(}HuO#o at f4;2$rI24Ay&U5>x+r4bFs>uoR1<R)bZ8;$}+up906G92LK
zpjZ%9o!9w(yb1OpV)~P+_3MqqZ{nSacUAcNU7qGcpMMaOkNEYM=TBh*h+uB<F<@9o
zSQ*2VbwF;MbH|Ytp>Yys6XA>)bA*nV<9$Rnpa%*af>*kD8RCTF5;++|iO|wFg-#gL
zvC&}E=I^m4z0o*H*H6Cc5J%MmBPU;RM16CZWHGb%j=5vowr$(CZF9%gj&0lKj at e_|
zw)M?>pZk9IkA6<4x{|6?Cnq^|QooXVm*MJ=w1nfRFfTr`EXo;D7LqGEAH`N3X<EcT
z$m+cZU>YljV at Ot7&=)2n*9IgOkVDXCCnKMV!6pgG9xb9s{8K9~B?2jS5PL+}3i#;+
zz{zLX*aw-*t!YERDC{Tthk*$L_h3qrOk_)fs|lK1rAG_E7W_o2WG$1+n|;A^S|-eQ
zZC-4O3qbEOM$zq5B-^4`;V2_M&5)E|q-Uw7Aap0+(N>ogQspXB^$2Pw$%!cu-Hk~?
zY7UlH$5MMaS`T67;uhp)T)^T at a5EEI5>Iv{BfblMp7oLS!fBjq$O1H$i;h4XnvC%R
z3`uC6S at t#2GT4=BGLdOPl+46bX0{u<cU{C!{!W7ZRfXOY&dAV>u+(yr*5Q|f35z(_
z0!Xu#O9|EeH8$`Ox}yzD&cY}&Z-JCjV>l|G`Aab`x{(z>U4TSp)*#RfNQ9xSCAuFr
z&lz1c(mVa$sw-404=Z7@#fldN{ym4ev%g*iv8M#L^x at ECWkow>ze_VEpA%lqC}|KN
za71A1_m?^GJQJUTu(SD95~;m`jbE~$!O{=L+|rNeUupB}DYf-AGU-RjbHtvpo_Fq&
z!}7ZTH~QOFYn$<@FFwxTR#?vI=dfi=5DT^7HR&Zh|IwfjI~r!%D;83GR=nTf$u)}W
zWgN$4@!=fAFxFf(*l`RPQEMxuCDd#nlU?)bmeMtb76-h2;d7P88pm-gvBkoExr0+!
zsZc4Ld`K)d59&0i+&|@f;UCKT at 0`0BJbBf)cyu0AE$;f=^7}f=pwa;-+#G0LR}U(e
zwH?d-p8=#0S2{qA&7&&!a}B4?fyU(GL0wCp+b!>z|EW?I1t*W)y?5#~aHB%%;!$C5
zccYSgcB9gKb%PQE^5ukE08Jj&MNskLbAh0`-zKOM;~Q3%fsI?h<|VrD2#T1Dq$aY+
z&K6#w<?{1!3$JjqL1 at -oLQu8XCajp_Ex<AsRk>Atv=mgyzt=z$DgKGyMx9<DtQam7
zUJ$DdU(2PS4P2!UuRvlCA7^ofDT2=ClCVwh5URjHBXkZ~7}g|I{cI<wP-qrbdHe!b
zZ1&<7vDKVKXr4e4Uib>`OO`tM5!EWFvRELja;6a)=l1w>`n*+eVFLlxgVmJFrQ$3)
z+4xd^IQ=a_(PI!SB2q?X612AYx0jRJ`2+lqn>VW*oL7;Gl%!lZKwiJf8|CMyTL8;C
zk|0;wqa9JU*j!!LuDPn%oEzbOTw!OIG8ao&?AQ+T+nAOe0C^T#S<sJES%VIvRKbV@
zd?76<i6(WLGCSbd*YRRx5n+S+lvw=F*{0BZ at C6&h-olkLY<gCAe1Yzbnab~wpvd$j
zWrhr{VFel_PV7<{v?-<x+_SR0`QV-m%K4^Yd0Vc#0|%erj78e`e76uhWf$;=O`vks
zoN%r``9T=8D}C6mccCUmVI-uy^vp`~<nqhaS>-Fg(P8zwdlU=7ylw1JD#vDzDs7-t
zJP9>950g at T41a=9tQxZogR#^9HY at yrX&;(z^i?$nijnb-bxEYEG*!?7mm!8BjLBjN
zQZ;^fsIE0FU*2~|^=YYyt<kb}P+OeE`m4T~NT(ExF;^bLF`d4Fqs4U|@Rx(qgRqem
z4Hk*`5n;s1AzXP8Iq^CuRspqD5~eFz*d3T&(S$~-9xMoFRU|Snu!r&&Xa+IZi&bKm
zjifP%(CnNlw#f-_cEqA-!Jn)W*$`u{W+-bVW>c+0D06m+`Fgj19D?pWM%i$Nvs)DM
zP6P2B%*C+(3JPVu=$N5nO#WoMBBmJ!EnpOI{cR>xmOsOh&WQcZC0|88yl_-7u5q-K
z+X$8!E3^hC4KGb_e0IZ_?@?G1N4dnIH$iy*RlvLHS*+V)a?C>J#U%_jgKaC)gi~nf
zh-rk-w%3UqZhZkJF^y4GMQMDW9ra+>OliI`XU#JfU}vfUmX~j~-TjB42o3Lyz|bfs
zb&mbEDoA4c5KMpXNTjai%A%!lEN at ATqR`Y(ZO1Zact|HKuAbw`TNcUW=2Oo&%dU1T
zO${yfigky?^3t{z2=`yd(w{7jy5?qZ9LaUlYM at J?<q0HWmddvMBGc}<X0e5Mwaq0g
z0Meyt^%&Dxv0pXN2zP4 at Yo0CrTs64KL;bc at wDZ?Ae`Ylqs_pBh8ZpjsM>Uz`_0OFY
zWX)as_h@~Kq;4ygr}_A~Nao5%O7-xGU8N=Yni?_ES;xX?)Yh|f%n8bU$6)vzshi6p
z+$QahW{+*OtdgN}t#qtN&8FCZVlkrJkB$Wiqog`IRxx+!DW`!HtYn1cVzKSAPbFfl
zg*rOM0#hgKwXma&5at%vlW!Mw*$lT*=#BB#z;}vB{`0Y>#&63%R15QCxUT;y#lfH}
zZfdY_;tPf^iKm4Y0r#U=QA<{j;~Bfk<+_^tv%Dt!CQ~=<&Z;rwMW?E)P57$!RT}9+
zR09gEUxIjos>r5nH5HhA*yp+1$!}#?-Ws|0s;mz at 6~iDMkE0p814jCq`?tp*MOEpf
z7*h$XRZ+y&cG*Q$o@&8WxAYt{5}L+tKrj-T`>h(E<5-R6mi~VH7sS?+>RHYosdb8h
zdk_4j=8Afy-^IsI{P%;V4c}md_NCvXguS0Lud7w9-<<rF=TH3TZ0Gaec<}G~H>Zy8
z`dLov`thUk at AuxRp6j_(WVGwK6VYuCmj0O0>tB8$5SR9$li at Nl!IK^B at D{b<DT6 at l
zv_(+P^?WX*)-)G5j-JG5u#BEQ89}>kIjnU?dnKgexVdB*{}=$~Ia?gO#yLB-VXQp0
zxwG<D(ASHvV0EEeSThnx44~UIB(90bR;MEe*)_zTqiGWPG7Q?f5rHHb;~ashE%mt^
zwen<jJ<P~*^v2&JwK!dKD=)f-3ah|4&4OoKf1bGXy)hyGjun}^Y#2GEHPap}8!jcw
zw0N3&A<FWC@`0<Hl0DVq8@*LgtmVMNPDUSx2gnTKVntANHa^95iz{h**pTf!M at Vp0
zM_tp3WW==)PndLSQnW?jK at D?!Cqv((kj!{qY~#lz2GpU}S%4)e48O4e(Q5%)2E9_j
zl(~f3EgL7k#m)qFAKwE-VMJ(cY9<aWEkd_7M2qabt#mXF&oQH_o?e2*gsWmMZBaX-
zp0qM5-CPt4=;3C+;*Si+#v{%|f at 7)QlKpLSDp0vSYEFj^T at Bm17Ac|_vpNw^I&dLZ
z-HHjes|Th&D>uor4k at MIP{f1fP#-zz$tZGWf!Qv~_@|dP?q)Mw-DDopZ-!*fe&**V
zt;6OdBTB?w`^VAovbt0qwt?UQIyWvL0Q^AT$bkjcQ`rG;=D^H(7x}1Jd7x<~QY{!?
z0D&q3Gn8$C01JhDM9z%uDR(>Fn>1SGy5cc`;q=v$XX<6R&|pLH5E6qu+rB6;f$DqR
zl^1%Lw{rT2oD<7(hS~~2vUj_JsEQAU+?r?O1h(JO7^K2rN9`m9wx8qq5T)HeNMTTf
zrC#=l_|xL6x{Dr=@gws19W=)C3%z*XOUvdPB7>d0rg(o_^|`|=q{40=n73=EBJ_!|
zKA5wbLjNH8Q8I(0nXKoo3yIyKPbx4ep3v{4CxHa~xE0Mu35?eF74ZQPEPXKK^kpv;
z8I|FrLW8*;=%boHf&J)jiVPe^ywGHH^#>FwZAE-$%^K!5^UO~iz+^VEg7b!{KF`s@
zyY0Zkyt1)=KhS`jZ=^y$%Tl(W=!>qRQePhEk8+m!K=(DHNNL76K>JT~;=Os}N|sG+
zD6VVTrRqIx=5e>9sSi4 at NV7V;hf$f}X~g<OFC?1r^gz0p;yra_O5Gbs`5vBr?w3gG
z-rQ?LwzTK7bdF#t<<_ZfyD9v=`j1xV&#vY>-};?AF>z9dmOdkiF>!OG*!am4mOe>z
z^81A at oIiv^7Cz|Y(p%h_{}4-W1v|2O#82-Yc%{&vc%f70pUZS|dfbH^-?nOZ@~&4Y
zHhjFIJXS}Q1HJPvt1&hlmElCcvDI~hlDH at 3Y8nFHM>hL+TXv#^8E?t{U9ypFl-+60
z-VSAz+xi+CP8RGx0tgdr=WE)?+M#+B+!}UyR|Cpo&?YjOoAZ54qj5TL!x4~50xs7K
z*`G!oi<EL#7-8smwTxwVWY0BV=TeT{OR-)El9L;b-I!RGQ8<*KSEh~b*Hq*z&k-?C
z>%Z#@@<f+ZC`sS78!$-*I+3S$g73?Nq{`A9Se12GtGOahmd0O0R9e%`8r(ho-M`S*
z%NZFg9r#pbhOqOVL#su;oxNOa&wq29N_rg)L?#FbpDhbH#{OOCK-=+^J!nDa?28Z=
zIn}YCO;N955TQxJ_i72TMW|p-WY?%G)C<4>lTm!@i+ at zdYLqNailq}!WfLY`rDGi8
zZO1$xN7I7dB8^<R7CPdL&myIie%s?vzQ8c=QZ2bpJJ?sv1&jTdeogbi6t~ffG}R;4
zMJx}br1SQ-S=@-v8EQC&(2?E+HR){mc at obfBU$NmnxmLc|0iGDGB*>sbbJicJCoLL
zeMM{Mgl2eSLLa*9hGfdq%T1q|GOGs*qQ-kw-f~9HV}xG5IpP29xSrXyzG=Czr5It?
zx$!~WQ=!IiMG2?T_*vm5i=EkH4CXJh?ykXenQ#r=3Djd?zs(HZo9QYJJjwi%opAa|
zjD5f@<Ge-oyl#18gK!{>Gmi~=_Ohq<$*LU$4+;!j-@(+P#VhxQ*$m!Wbr-I5dda6M
zzV2Uo(Ry{?79tghhB}`P5fGcGO<j96UF9SmCe^Z66QT~~T~(dIyjt#T4&0T6wPV;l
z&T}|wH90`}{JF&{?t?1FRW6MV{WH5?c0*B&gUHEkFYwfXX}%smF2^%ZMyK4)h60hI
z^A9n{UG0b%hRebXbgJu%kSVWBR(^UGLYg0x;M<LCXCcvDTfz8b;BK+D;GNhAG0X%|
zOL6b=hF~09%}N9+nS03T0tWVy!IUb`fOgjUmf1ccb{&E7jX52GB-Y<vP*L7G!-k7>
z!4%@NniWCXJs4bxbB1IN#?^y9r<*sI-*tPVya`>cKpx6Bff&f}_H1ZhUmhPqcHTqY
zHx<5{p`t~6f!Kllydsgli?p><6pZJKc8z at -4~O9OH8a2hG<<o)7o0Z%Xi^irnXfKl
zD^WjAJe+%D_MzmO(iQdrv#7L={pd|ZtQd<<b4SrAbB-t>hM}Jqh7Cm681Yw at tFKgU
zD)x}G%Ws|>vV)SQ{%8`y_|z}~)m7n<$Ufn_e{nVNnz98xrg7!U?xxJVs=(12h{2|o
ziY*Rm8dK$V*)hWO>I`LUEDG4O84yFYmKp^DBX>X`b?ik3E;e?P1G!?rg2=Bn at z!gW
znC4C~BXP09pF3gk0`4$arK7=!2Nvqeo8MAQDt+z@#_r7BU~HxlBzF+c5eKmf&wjrH
z-j3a|ez9PKP~|S@!2dZi7R1kMudBe%!(7j#Uy$j4{30k{PqV<UN|&fS*E9sKs)De#
zS~<tBvLrY#lC>=x4{Ek1U#fx7VAek9&j4rjt}kKp{m-}L9u4J^Yon>H`jMb!I|L2*
zq?bDg8{XlY&VnT0ezQT3QwSS0zZUI0+WAC_cQOQSx7TbX4<uW&nXkNR?I;cWCRbvm
z%K+`xqb+<EQz`nbkJB08n1Wu1#ue1hzA2{~bXzp*VLS`z_kv5WKWwDkQcaelz36sW
ziEl(D5Qy9!9j209hr~121KZBtrn6)_OtDaH*q*<xGNFFiVc4wXREK~0 at qmL9xAX6&
zD|&n%BEkUkMCIT`TdnBAt=AQ;uHT}OO2tSaC3TMgThdF!NIGqhO6WfLs7G%Q5?_qj
zmB_Xk$Ny5@|EaGiIC-X3UkZ~Ewh*K2Np1Kz{c^3VIObuAHk6;N*%-!5ttepzopA$a
zW$6e#yw8bb_baz~X%?Y9gya;kY(t$oF}6I@%k+Y*$tp*DpSXB#DY45ZK&QCN?X`Xh
zI$ViEC|^tP%tf72F82goZs;yl#&!sYX#(Lp68ToJvSRRWrch1H`(HhkQbs?OV&Afz
ztAntYi&fQ140G+yAW@!a2$U|)U<x2qTUxldB|PX{L+SHxEkLwF&GC}+`oqDyc4FZx
zaz&`}5Ze6P#2wO>J{g3H{X^Kj*+jjeu+Ou0fVzyB at IhN%cH%vk4Ix0u^OC(`H!|r3
z3}{KX*JUW8-<<n5T)OJwSrHgZ%RP#|WD}a&k^Gc0^>xP!`ZTMr(3`I%PIiKs>Cz9L
z9UPHg!TlcA&4KHqV?b^4Up^+ixIBLHH90OYfA4)<;!Ru<;RF!p;Nr%2TI^#D7KxBT
zP`-GRvineUu6N{zpA>loIwqq&@t%c^_BMAPc8nN<PzxgVP#`=tV4bry9J!A8`{&F*
z0COolsPq<$i at o|8GLEKUpCyi!{`@`8)^I#HQvomc*|{0YJowvCC-usm-QeT1Jo-*}
ztlz at KTxF-p$b%p at cN^;Ri)Oq}YiTm`QvTTrq?OEnT-%*R9-A!~8<(+Z)OHb^<&N%5
zV#T#=VoCMkg!OdZL$x{ljkZ`7=2-)UHiR7tfB?(dIDeW7JqK*yx8Ev<4QAess&8$d
z9GA{z34%W7WAy0Wyh6qumx(!@Xi9#zuxj9PDn at xWA~g at 52fYcsthFFcLbSh~jv=g6
zu`G<kI0EkerX}Nbiwu$QWRTNbuL<+6d2DP<PQG3PAX9nkHy2GWU`Utnt2f2ikn<Rs
zWst+K6YZt5Fdj3K$+#80#fm8_!uD|#yfIEJpstg3hH5}VGt3YG8T&zF-5 at tEy@Q$!
zGD?jvW7G0>x`gmktE(1_?Qx5-$cgXy6g_p|&-KnDcp}A^b}0d{mb!$?U`GYncG5=_
zTy at TX*NPS4E0VJ8p{x2~a&MOnNF%gM1%mf4&@_=H-5_=O?LZ;1{F$;zX+0;J73jh}
zk;9U+^RQ)MC9X+ef6+N1G?8nFvu><4#9fh8-|sCmBhUf@$&^Np73%>CU?vPhP#eQ(
zaou=&Ie}tJNU7pN7Biv0^7`_H2ls9z2~}#?cq&OYAgNxdAn5b?DSebO>;Hy`f0+<N
zbuS6I>XOs^@f1Sb8;*b6w9LQwuBkf##mKJ1h+*7JL~A^`r8Xh at E3k*n9Acs~1SJ-p
zxSvZjanVK2+Wbp#eo%{!MorMWe6NS}VmXCTV?#1Ty#{Qj+=oyMqL0$JuGj7tc0zt#
z`5!Xc*Qr{0nu#?re->{zK((Elt3Hh$$I-imK`I`CKm+QrwQvJAgc5BP+*;6>$SaaY
zICL_Jfw)a|2inrFZ65<glnl`}n&?du8l3i{Or`y%BSF0VAeVx_h7~v`ySTFCnek47
z7#(~}g5785gGAkyk?OC*XzmNH1_7;h3ssN}i|kBvQGQvlt)aoyG6v>0!`NGAFSPj>
zhCLdT`oKgM8B?A{O{e-6Yb4^~;f{}YPzRYcW3H3L- at l{ssE&iHl_s?#)-p at KU`jJA
zH2}>c0r?&xA-=GpwW at DSV}eWt%mRFSqESi670MtdALv+wXrButo<3E4tYX at 4MP~K@
zqkmo6w-}~r{_{MkrHiG*Subtlk?dSnqEZ8wu?tCD7 at -N}1c%B86$cmcAT8Z&Ikh_b
zEG0!_m?tst;o}L0gb_GA2_a?u0b8VK?_n$NV6VDQIMm$rDda`8HrO?kGUfDPW{r2G
zw1fSzWz<#@UG5TVIJ`2kJ-jkwUwRGvn{3UD`sBft22GcIJ9AcI<oWFoC{^<(d_wrE
z2nmIUvy&L{=QhHoY8wg2sxM^e6hT|-=v!sWAC0Z!ja7a2n9H!J{u_JfUZc(je(jd^
z>T at Wqbqe+b{~jvZOvEF5zdKp?B;ho36%m~FDKH+06#_E+0aaoTXkyrRG~qodV at A^&
zFa8vypw<m(+LMAaCp=6%Ap5%!RY0jD(Cv>yZ>d!olr+pLviR}YFuo~Nyz2%^n?0{F
zPGpHrQ~VZ8moP&x3k(G(MeFvsI!6rDpQb`ANL__YQPt4*Uz(K453U7?(vd`5<E+b`
z5Z2bahu#faF!w!#+d28FwuV1|)GvOPS_bKqR6!S_c|`tho+)Ul#{M2yGc2~<a0?eY
z<%Fq`T}w-v{!!`@gEM+M5!%x-VBB^l)oNNUHChws^0z}kJ!nfF<G&R=Ewku{(Wy?x
zsM?(4NztAXoOwXIb`zsinr)jFhiAH7I&{MvU?*=HA1hmV6)qMoY}ai?^~jeDo{1g2
z6VB%PochJxMi>&aGr?Yy!99<J7%hQhVT6Kxrq}!zakyR;(fX`WD1iU7F4!gA?(o2f
zv^qF2xl~mIu09tnE=P7|AF>#;b%7OKEk|@WFzncq3Oy%WQKbtW<T;u)ia%+yZD_Gb
z3|T}%QcVn$Yi{%vLviQy?q0y(7$w`7a@>Y<)T&- at 7dwlJj9VY}FlxGHDkv$sVm>qW
z at Xi0hYw;Cc$L!zEAIlD+nPCY!!J&~COIp}<U;^O6PaxBEE=P2{f|+~L9pl=(ZyZY8
z52A<PCbP_l3SLRv%OnmmTI5XN0J7026OVAG3>Ei&UUG(o+FUfeE#PDvUx|^EPH{($
zQ0#wXlF|GB%#N at l-Hbd>#M9b;cRD8e35V at snvbI**c)UP0>DciTJ3b;BhH?JaNx(~
z*YJfwqTBDTM$s3_MQ?NrD4Q&G-te&tJZ(mUd{PIc;4h$46&4VH^q~-1LdQ?bxRW?t
zwf09>12(eOT at Y(Be&E<^frnpXzo;^UPEJ|~92w4c932d<C($5Gpt+rT4;a0);!Q}j
z)`+#fRY59ShdT*yG#!e{LV4J5sw+<4a^S%jG$EpFg;RV*Fcxm#x(}pSx*Tk_5muIC
zzA_kLg^7vfWad(k`kOzde2svjW~oNX{CF|h_#bI2mzp?s7BoQIX@}0N7&k9DAAK$5
z-kVoY{ZzvtQP&WJo4Iwb9@@CwGP4Xe)q^Kl+c#g|GmU3d<H%}#myIjy<9Hw{7LiTi
z^T#$oy^*mNk{OpgitWX#IKs3#_hm3 at IeHU$3Bx;jt;Wv>2 at d5Gl7lc0{O(ar2dLrN
zFGF?<<luPo1@|!`4X^G%_b&^34_f97&ZsM|Va3!k)dL)SY-i4j)4j#pxO{7Mh4r*n
zt4HJI_e?}{gx`yGStbr<*QrIPLbtMog+Ghze1kU<t)c)%RHb)ojz+(GoF-E at yh=r_
zB5V2S)Cn*4VI(cHK?jU(L^5AWoYo>}x}2PR#&3fpu0Yp#b+=+9o96GsBG|8e+T}b3
zRxPOqIXVAWt2tl)@xm{V_o at Po@$e->K+|KsP7>N5bl96q7<Bl-pNb-8d at z5C!eAp0
zt%F>1SMmxsY}2bnruE?O`Lnag>k-D}@nOR92Q0IHyHmHpbCzkP91|C<+V*L5=Qjm2
zlCVEW6j^wFuOkVj{~~E7Q#Vhqv_1X(c*Ucacp0LEKd=|6Fu&L5n~dn>sacWD(H$y>
zmt~eP_m>ObLd1G%ly6+61sBG-s at kSnzx6+B=#pb65#?_hLiLN$I;XT-=w at n%JUX8|
z7(D{!-!6455Hv{_iss?Ai6ob at Aohg@s(f;~cx4T!ztbC7EjZC6SvmNVx?W3%1-z$I
zhp9$Z(<XFs%-*&}pI4Mi3 at Lwh;ByCfLt7DYa0BEWiG2XXZy`<6gB0sTXxax~g2MG9
zNY5PXIRfN`^gCSu?!RZM)K$4g;O*T2=9Dr5#n3z2DA=r$BdDT!0Aa7y^x#XGs!a5$
zcY=Bfz2<hlg4(f4^o*sPg+(YbTPlP>9fZtR?iJHWig9bdLyhVKcn%!IA5dd+xGWE-
zai1Ju at vR<z*et0<@c_DBGC`E^dTq*O*4HG+Iby|*kXcC5X9rqR3 at O^nndY4MWQk)q
zB2JQU*UV#Fi|q{LmMZd+TFbGa<vpa%)?I?XKH$i=$ViRW4dx53CZhOx53wG at I=<q`
zy1|#+!**4B6Q>(M*3*DilsUvdO?PFvKcM(i+g^hNoHPm3k8)X=b!`&3LfyhB+Q%nS
z&5^IDqyy8;`e8J&8T=oD#$|gdd%<c>&n0HuMb{a%_&M`<NE<3F!rX2wImUtODf{Cr
zw%lVb8cnY}dJ*jW5VJBXkNfr0SL8X#47MtIJ+BjTByPQB2eo{WwbDm7*w4>6<U7{8
znqwWjT^)z!5!{c96IvscmHJh<3>Fp%qsz%Y*`_(87e!|NfeSj+bYJ1(8gD=qyAU^t
z+vt)*K{RtFae2{R8{<k$Itf)}1Qg~O$nICCpG?G6N<D0oXnCIDY<D-7p5SRAS*HTG
zCw!enG|L@#cR&!;qOjuYHtE>x3nxhHU4(B>Nbtz>;*aiQk5<2q(1))X1UMXn&7%5=
zrWke))}lw9=lf`7y!ZlAzY&SI=)M#4K7(0aY?4;k3nCv0HaG^r;w)7kAvvn!#F61_
zRiox=a9Ij|Ss*G_|HI1Q{1myS3u>YS1kajd)g6XcDU=NbFdCgH29*^P&7Zh<)nsCt
zB&s^oNXMw}mD&;QRX9(QMCd3M0RBn0c`8hrACS}hKJj|tLactIXTBNrxd6Bkkyv~e
z>TPUdH^f;yk<byJ=au`>&&I2z?pzjY|C2v~JxovIdrxni=@`Mnnnu2?8kG-gz>)=p
z-&FhbjlWbnxw&}%B3U0hF^6rwGPX$COWX_sK#aBdMEgc>Vrz8;bz;2Xou)?R*HSvQ
zr9Dv^cWT^s__t{^cC_hMX at +jlwJgsMk*)sZC7$9PbE}u#7`ma{UAr>U(1!~}vrmz^
z`((8_zWV2o=*WrDG0ql?TSxc!ufNHDDm5s9)qlO;g1qPqN#nqY0fFnoy65JhwmavG
zb&PH5Qu<1Nw8Dk7K4=hD*r at v&hoMeJk=Ry<@L)-41`|BkyT?r-f%l(*uF*g^COF9m
zj<vdl<ind0b97B2zt1FtLx^hs<lygnO^Z$6)emLkP(`7f=ql~HNAnm4=XSC*C^g{%
zDR)m&>pz+#;2645m9zW*tRtl^L8AX+SlR^6y23iM^_QJa1BuD5n`sX|d9U9`(vcoF
zKY<YA&A5*;RsUwtm`RHS3T?uV%ct7%mi6cAGms788~IgO^e(=FP<TGeZynCL#5hRP
z5JOoB`eU4(b<L>&PF+)RTn~+P=bTIvQVCvD+Ou5H(+Y)np~lEPN)27>V+q~omdYD?
zZboQ)?we0V)6A#9s^W2Z2X^i{H0IQ?_7%9Zo}kED&MSqHTVz@}6Wg8oD_68hjgj!Y
z)oFQ&#|1!?HKAgp#{GJI4b8ns64`mEidmDt86-~Cu97KU87H2Yj>#S|P+7T=Kom;;
zaMk`A;{6##?!6-Q<I495T{Mb%=h}-G!lw1P1l8Y<ex^&$iDEQIb#hvzr$N at 9Xx-~H
zfXgL0$uQq`!ZI$`!YVR3wDRl^YKCp8$6nnc#;b<4a_asdiHA2jpbM=CQ4fWF%h*41
zQl&!M{s3=rd%63f*&l4#?K1pKIWjX}CfCX{!@tea^yZkw)%cAEU-*6h`wIY{1k5k-
z@|=1UxRG$>`gr~f|53`z&LsQx at s9sJ@Rs{>&Yu^I at wyH1lhVkZ15@<3{0244e$3aG
zqISr>{}&aJ8kGNc87?Iq^_ZFA1tM-cxPmx+AJ(R;3dvdt<PcT`H4?VlABXzG`=Ue5
ztU3)fsgo#xAdsi5Qm}KiZl_ZNBp2Oa6j9fn&?P;Jpc<>@qL{Ym=(Q$)X<=D-g5ly}
z?c5+0x<Y`9(rHX}wR&(V(jv+c#imH|oAT7}BGx$*RG)JN?mCU$(lSkc at P&91+3LUy
zW)+B~=@IxySsa02!@-^o10B(-U%PlSE2=>Py<sqa<DQ$ge(OPoj!wa+7)q%~6kqM7
zYvs+36pMs}s^}~lah+%^oeGl3JsPIW_BWWM@@hqhWxnQj9OdtNDP=mRr{P=nC1a>X
zTFU*C8rbH0j9=5OG{FieD_2??dg-_N4X^Ot?p3ez5Y^m?!<6Z!RNds8gj8Lr<|&M$
zbsDw_%B0*mbE=)9&6QNXC&2_pR3c678U<*4<=#<tzLFno-YPKQ!%BtAP|g?&OsK at f
z44p)Cwvy-wZw at 6@n5gLF!+GVzrB!wBszD<WXGkkVK_B=hgVHk`XGu{8j_Rcby&&c$
zf~S8rTkbD<PcW%_MvAMiHBt>!W!o<J>GU9g)};z!!87{NHR{Z6RlaeIwkFm$rD=!S
zAzPAqw#lV0Ou2}?E8Y3b at 8RW5_j`s5n3d5upIh=ygmTMzhyI;_N5?*ZV;RO0?(SKu
zA>&DYIRxU=#?4|)tMvzFvi?DUXZ$hb_Zw-uJL<VMy(Ww;?vChJ6_{&2pQ2BbnWn#`
zhf`++nK`r1zb?{E#XEHCjQ4Zo({OW%(jthAh8?Z1nscFQn${TdRjYx<DkfYte!$j{
zQcoUjw<Tw9G8aGv_sjfIdLk&SPt4(ZIG_ktJb5@~XKRrbB$edHsTHI;QINdm&H_Im
zO?$BgP%|R1J6C6E#$O80E+fyxnhwIQ#_nwSlUfo*&^9z#<+{BFN;~;UW}{m{e03NR
z%KA%SmeAd!wOMS0uC2inE#5<)NxNcyK=g_8Kz6$IAp}8g$t)kGa3 at _9u`k;`*r5O`
zyok%_IRp7n2RPz&2|G|3t`%)yz}Nbk3xE>WSJny_4t(ylHH#8LVR6{wK-k73nKwJ6
z7#Vg&(dk8;(Rx%(5}!8TmA6mu(RoArB``;!(D-$YE30{9fAt&#)9>7!3QEK*w?yZ*
z0qKWz%@61v3jzJI-2!9TNM_S&5HXK)Zy at dOuhimWkJi2vTeFK3u(_mBxFcT=2V6f0
z6po=cnHpX(`hy_Z=drIzp)_gj!>lzR(5BmWqY+^*;xuRU5{OLf*moibEMfKLSMvgw
zE!J98ME*K+s_X!u%~+_N!U$zQoC22}+ne~o35}P83eO2a^0?~~w6x%bZyvJ`@L(F$
zl|#j$dld}!z!)CKMx8*}Yf}h!qLr;j--Nc%1ZPh%LCq?^$AAV*1Z4b1=K^^XXhz&4
z%Gq0Cg(pI?cE24GPYr=1)pJjnqD!q3cpo-iUVtIAF}#UwsS=Q0lE8pI;cA;@`th-x
zfQIARYk(WAm6WmvoAe=tTeqWXSZs%;m5lhp5Q6$VV24}60BxKIpG%g&L0V>(Z2^{c
z1(fF_J4W;ojHZ9QP|CC-p3|vX5b0brO*4d7(!JBPbBbN#&hsdbZm~2yaJ=Xh7Kkrm
zqs0p#5n!9xYmr2{8rqHYIwsQQ3k&vxLj>w82HrNB7Tf-LNUemvNq0OLkdh^1$iLIs
z;o at 4$f#<?TX7PVfk_7^d{d*MKczP502VxBw%JYLF+H^<K-GY1w)LMd5R$L|F#|PEL
z4BLZ16ue0zflKjbf3ono!b&f42C$fi84}-&*)gl$IN_m?*tS1mnOb8VV)mn<02_h8
zg-n(@i`h|0WN`$P<C^HVKFefdEXkc5(t)9A6~Hr at 62lx$K*D4{SL>EOD8AhWLU4-G
z4t{=jrCO)#Q^5wiCmG+HePef?VU&ZWgwHcOe?Pu<sGtCYSlWx2qw3BnYlGevPz4vc
zsLh7dmJ)+rsu0x_7FHZ_1`#~SE0pM$d**G-z{~>AK>FqxpOiJG5_rt!BM)4VN_ayd
zRidqL&<eUR9XN{?>-;V%q%8Fm5ZtQ~*%08WK09+k<TBFA45tyvzcbZ`D%i$lg-xoR
zSrr(!Lf|?u!Yk9)cP{EF!JMscM34!AYsNT6qZp(nmZ&;Olinm-^C6H4jcxy1+mSI5
zeKLq+O#`VH!{xmej;CpfIIF&j`FQTUbOmDgi7`uDwg5BB`23Xx72+f(@u-VXA~B+4
zp=_NdqZY`pQzq;o=mOC_bsB)7#02HK%P}8q!XspwgO3b~?gV)9vIp}b!(S9`SZuZe
zB~N;@Bt{ifX|N1Hh?*WTUK=4;^CcK8*k=^ME{81`Kso){!L2a~{0)e(;gi+s7(eNo
z^u&up%n}I(D<W>nVN=RavJwe@$fC5~3{j~S5rr6c<(R8&ash>yWpuE0Baj)Pu`$<%
z0m~N8N(g2 at JGqEC6?(m(Hlh|>xe7d~qczQYPB22S)w}Bo1l(A-Sm@^>W#d&(hd((=
zFb_CLC2J(rfLzD{Km;E|9~czQ7X>vS1oz}dUNGtBYW|@^a=KGZrcXZsj;A3D#6|3x
ze*l_u^aydEDY+r_q=wFm5n7{a{y0s?AQW|jGeI|3EFU@}6$x*se4=_gARLt;ctZ_1
zQ4As%Snq1|pa^OZ3Z}(V;Shs&P!D|Y0!exijER<Uh@{q=iJ%ju18?GSa)-}=BSe>o
z>>IP5&-sW10zkS#>_w_oA<8=hU1WOUs5(MoPY9ZvO9MZZYrUrNspD-b?Nv3*I~I)G
z5TL;bmunE5iz at 9`nPps7J4|V0=}o6d!g~<~%?K;!s9Sc3TGYdP*#+y^VXW=x0#j at M
zGIIbPJxWfSeg&o~Z9+4O&sU1X^_>!}jn4OlT>;Mf*b(%dwFc#+SnMo>%CqRCaz)p=
z7taWEPbvp0ahCaNY61H?Wq`K$fCI2PRm)m07l`~)DOM~&cH2=lLC_EnE|*HOOoCr5
z3?0^9bZ~Q91ycIqCy~|t#5^0 at D2q!vNoavP&m9uacL_R4e#C!O2~+~b#Y$qt;GeMZ
zJ0^|Y5W0F*X5(tk(Sh at mwsRRjb2@V11&%A1ev%FF)2 at vODJAPcSQW2XP@%Zw+`_2l
zam<P(ZwW8^LIfjtqs}BO6}zMNTN8MFA0+qr5^zPw)>XuM&?4yGn!6!vMlL%-fzk`Q
z#0`Ea7I!;!oq0{e1k8(`fX$8<a0-9y2p>CSSDx%#^LWW03O$xF&+WquIP{#scg5>=
z*(t^dMN7b^JVV$J1}jjts8h9USK8^F+;5#r(&=hg|BOh&Wq}#!->A5Krle_c)J#Md
zSr$7vKOF|X*WFY`b>FqFWlgD}zJ88~iFGXYEuX{u5Gp&7ef7K4fDDluia`*8Fto5H
zJVp{}RqRC#Wrj_fJ0-}jJUk=lKw1xMS{87b8Eh-as+kFcTf at Qyr57unYwVe#3PwQH
zrE+cFbBhSi5h1UXI!BKbPm4dxD?16q5`Pn8oiZnw4AbkpMo*+d5lM9)%T=N3raIt@
zg1$rwet<4Mi*l-qcru$xm=~Yg^sJt9?zHxzVavc`P|;8f+2KRby)?F_es2o5*bknF
zl&RHd${lg1991wJ?-U|Y)!XQ%S3UVH+tQ>;3aJS3SMypl^!?kq_`P+BbPb%AQ$o$F
zvOrvRs?zE~-a7^XUyMbCou1douh)ULXdYf<CXTr(#5 at vTm8(KO9N~fc<P)w1fvib+
z%>7sJ5#(Ro9 at Vj7!lb=&cc}0dEQyi7T!UDU-K%ZbY8~*ji;GqSx*P~-ZnZ-A;039I
zW!8kX1xmMf*B7U|vlA8L?fy;dPMCU-g3ksKaDm+f9i7VWWd-cpL_+7PxHIAiZb&i8
zol(PgFtb-Rkmnth|FkYtPMCNfyqtb!mEl}Pyff5*oKnb!Y4INFdU at Sj2~{rJEMkte
zOHtIx?kWWBO+_8FnkxK|V8=d)r88ylyTHn_RLCow$o7n8Hv)?SG+e<_Dhu%idhwnf
zC8~XF_t{a%gIP^RX3#@;!Ouh-uv<aRpwf%g%}@V|u*!`3su&Zb*r at dI_bq3-INVSP
zM%PAYsZhFodQ%eZwb#;v5k4q-eM>0r23L~wO~}b#Fyt!`vhGpYRNg$h8N2!`q^dWP
zfEx@>rOGvp*{?@<bvg|bRZq<#-c=4;>up#9;r+p5>xFULR-p5Ch6)lUv8!`Q&%$a>
zkS&cM1Y0B=j$YD@?Wihy3YM?miD-WsBV;UuuR^=zx(0HJNY65LOP##s^b`2DPMbyM
z0wdV!5bI1x+$<{+Vi^^wXYY$<C}29Yn~ch?ihAb*YA6ke3!bxOS%puAV^pCk4`;{=
ze^XR*f9O}m1i^Z;;=dj^XS*&LeR{aF8xyNgP&vsfW61Bea#0YG=x7|!x>n)(aB_j#
z!P6Ij7*7cyOrtG*@-QYSM~5kVYUF^_Pg63i3b?Uk${>n_wq{o0DybdNQzP`?dYHjD
z(g~~sL?@+|F%*==TOW&Na<#G!W>C)qp`BIiiP<}05?Tz#2jUl$m=#i{Rx15?1lhb~
z;Z at 50u^cj*398b~n`M*7nn)ld<(sV at 9)ixs>rltEY&K0FTdCe6<{E3qn>!&Jdl0y4
zXHUAs3h!^`oLyk55uSyFO}&dr%YCAP{j`jTalfdOvre at _+}bNFBbCBmFH|Z&7c#S#
zS02})Fi&TpGDbLi-2-+=M=5+N_7L>FW4whc+mOxPa^ozu2*oP&3+4!Yk_~Hx+w|s}
z-1X90GW9%X1t>ehyWOZ(s8QuD_KPzbDF?+XA?;82QC5+=MA^y12mZdVVG7<jsdA+t
znBYU^b`ap~1s=qL4TRJ+jvO}JGpU58auVrdOFiDVf9Ab>mBtjY3*Eh+=!sm^s63?T
zY;vaLJ at jnzn@U`l&IAL(vs2ZId(0$!v*pgtZRV;~%js=zqw5)Dt{ihLuN)bc<Ujv9
z5L~h#uvJm{>=iS=JXKKoW#`;YpRN93Pv#t2_jxF|R$2My*V#f`@?6e{3Ox`E+AsaH
zM0VLd&2+&o);eQ`%&5#{+{+Y;SJk(Nt82%%JgfTexKb~iy6dqg8-9O#pYraaV^x at E
z3;e{^)RQL at 94LifqVCnYsfVu{+56u!Iboj0=#-qLKFwqpS41QFXZuyYd77<4u8YJ!
z-KoxW`SBK9rIrCyxAEXnLh5?`^eWg2Ql+G+&&*%Tm at W~G2)fsLx^oKo3w9rEM2IWw
zc7OJ|{7<E*NWntIdx-8KkUtYhwN)qWDfgJ^LbleNs!+eH&|OcAutn1IdqB5 at Hy-X*
zUPU7|z-1vIx-3+RK8(O+uM$yqk-0V#`t&q9a#rw0DO|fvdA7EDj&E&5{ma6R!;s5A
z2lu>D`q8>nn at ZSp1)G@#`uF0q9w{q?Wo<TeHUg^H15ACtJC#pPr%)FAJHl}Ik3nsP
zo734o-KfSyafA at WRH&|k3AR!yqKPk9*sX2GyCZ)BV$OVUN0^9u)CEVVTYD;mj$U;)
zJCSYu5OE_}X_rJ3(e)2BQ>ULl+38Y%qjqB+T6NsIt-CoSxFe`S?hB>pPo~eu!b4MY
zW~R^8%32rY9w*P2pomi$3A?>N5L=xuYCXWw9q9xy9Mvr<VCngw+^Dh)rG~n<JxJe(
zX$gN~>Nt0)SEdl6X7yQVazhC=!vg9)DiDYx&my|>nZ#J29rJxS3+(RI30+{^^;(ZG
zD(WK-J+dj~1Oo=4B$R+8sV-U3_UX%_5pxg!=GdtYHx+AZC(FtEy2j_km+g>N+zwf(
z>ap%qzx#D%CNgFuoU-Lyoe13JyyS6NKfriTori;1*22t2s5ooGFrH>Z&!a7JY_{lK
zE(*V%Eb6`b7&J#d=%LNd&X+Ecc%3gDq2|=C$YYmJ|J=Wxz7zBgIZ|*r0kz5>Feil1
zLn-9KeFEYXlNdtH`Hn81yK+a{Vj*JKy3zQUV<hP^JsH>ei%0E%8DpB4=w0_AO+BgM
zI}?nFxX<AT^BSS4Wk-LpsTptD>VEqxNMAqxLFz*~fYiNSRKD>@Y85dDaKCl;8Lu?;
z<2uc=ULFSh{rQ)`Ot(MBlkzpbo6kU?+QtNRZyuKvFFMwNoKNCmz5hSxkuS1|>wWJO
zy|kTfvMB=i-FJ{vRMyeA33-UUh$M6!0^TATshAEufm^_^Q|nfJj)mtNSY%Dl57ZeN
z1>VW=r`Nz;lCQP+^*%t~n;QuQA(?43&LSSYLeLL$yYWcldnWA1qtKdT^Fnc0{gu7I
zZ~Xc1x3krAJgxP;UnkqOyZ2DUtd0BTI_Dae-nqvo1=9Yf)N8_h*?rr1C4f^;rgp}4
z<~H+|H+$q|!x>Nh7UQ3|T$fZWPqewdb=RMQzXG$i0R`K`cfZn7)Mwe<eE?n!`^wc{
z4kB at j(sRhS<a$FQKJsUTuyN-(@mo^;hw_J{Bxj)>>wdD(^6&1FSQYN?ua;ZwHK%X;
z`}+9)zJ2O_dSE&dH`nMN2BXMc=vuk_d`O?SBelK%^n3gD{kP^8clK|u*TskI!lr5j
z5+;4#fkxYoD&U4}V-E41hndQu!>~e%@?J$Kq%bG&xs213aG0e+gt}qGl9*B?$JE7>
zpz(=P*i74#>UE%Fv+gN}0EER=1jRI|fv?3m5GW_{N^0>+DU!SLwzx{bqLjw}!^ow{
zZA|+A8O;=?jS&8qQQFpM1PS%0kdj(fFJpCRJ?-d0s_-Mr+mt4C4RHB?oRXJrF^*_K
z0*d1rbU(jop+5vEN9GGlX$nedQmHDn at 4$&rh6E-x%8YB$E!MJ}{hwwT at f)sEj4l$C
z)Dn~v#cUre@|2-)l%`;mrs*m_;A6xoWkNrVsufH#l>>9ima6}c+Uh^H(AwGMWTyC!
zHqw|A#Yc-zr^y0gNezKX4Kfvn)(`t|R4B#Khf2dv<!=4=?*Av^sQF2=02o(k99n6d
zi4p_oj7wT2kK#w^pN5hvv$F;NS8ck*nyu;o>pirV3zhh}@_#Jf{bOud1bK5t$p1?4
zz4ZC%{QpX~YCgY$W<m-{+UFW~O!r*ulqLoc7FFdH(Il=ZV4YS`1{D1KKR<?2tx2Ai
zLjSL=S<^aa;$i-4h~efD#xSa%(!|8P1_z=#Mmn<(RMKW`@+ofcJE!5e;sG_mIaVkv
zhCae+`zGLO2X&2By}_E+M7=CYUJK|RnGo<VB4Lh<dl`srGC$E at zH%N~{3J1e<j#bB
z=kQRLY1O$dW;*ZpXUH=S_UG0EZ@?2(q79D9?AF0!4_46!vq(@vZP<&C at 5gcBlh0vH
z5By0jMnh~&3j*NuA6P;UWSQwUz$WHWhibLhU1^hn)ORjW<Tk)JKHh+P%k4?h4W5ne
z&vi*h7LdM#^aU&RYLHrAN<>Saec9$}xma++3XU-tHdA8 at jY5%fANwX#)3QN`A_ows
zt7g5{ao4}^3zBtP*oJjFvwKFh>;RzpDQy(ApS($zrZpt6w&?U^wHU^Asnxh{(^%S~
zb7+A$C{$6GVgW1K at O<Y?2T=*rS?QS~nU^gPtKL!r+RGnF<G0&cvl)Za(o7wbN2tbE
zLG>&_IGwsL$3)r!Q%znfnl(19{Rxn+RL2EGL7B`&4ltX)fZOaEOMK`ONSs at Q%qlXC
z;qwzeY^T+!4i1tY3?&@~#Ay%(#;+iV(anm+UAvYGXx*r^RI<Bsf-qk!1^MV=eK{7%
zHx<%GNO)naIF*(9l2eYmKA*QmPMxr;Zu>jC%j>FcF^yv25uQYUa%eEC+xt8`9k0Cn
zDrffI2kAUGjDf#siXBEru=AlHBrF&f&-B<9#+SPAK5@&~dfq835AA_)BP*@G)?}!<
z-V6zQ$Md^$0J<~(1B}N|)#VlUFqK`ibK9Szvk<|x8($KWouC6`z@&gij}a7=X2)*A
zVrtnejAk?Tc%c#Ka1Ha^tV_M>>Lh9WZxD4Jx<t`=Xzfs&p<J+v+S#{y3Nv8il9H23
zq%6p~?ZI{Bcd$nYkuMCfqZ~P_*osjDQHzpZ0!gJes}uH!iIkDCH)v+u?9y1-+n3_O
zPM`GCNe9tyPsWKN13OOjYfT%=ZBr|@ru-QysLRACM=7tOLi at Are;d=)CncHss~PN_
zoF9wDKahA>gecVz;Cdy5(8(7$ug^M>RcY-Ls5_vv^$;z`2CY?;szm@!0Ygdc3L|ts
z)2AigHU#pJ#IC%kn6JjvYN_*!4sqE at i->NixwaR{KCT0;EmI4rV&i=miTb_JE$+<X
z`$J1*<_vUc6|R${W8 at 2-F*)Y-?9Z;=L66$+w?C<1Jy&_aNFVQDUOwX<$$+<y+k}%i
ze!oxLueaY{Z^+z~2m9BVvW0;De&QT4uyVk$vp3_<q0N9sSCkM#_kPh3O!(m#S)LaP
z5OUO4r=mV<trP+dD<Y-3lK$bHf|zUQwHK%mH>#wRgwxwV4#AaOy1*D?qP0rfhWDNA
zW4(qzgtZ_DFTkJj$d58 at H^N3)wp(3M?BfG at H|P3&>iSIH#_q6{12&#E(g|qjFamve
zptscxS&9*#gK2~=&?Ig_p)1%^05T=W&294S1Q8^si4-&#Wta>M74SKS6{m?7G#G7|
z4ouB<$2`+$Gian1csZJmzQjPJ7LXEckPfv#&EoFrX=#dJ4XMJy564Y5=Kn7=F(n;w
zKobd+6e|FnMw<z6KOZIt&SAktX at iXHEjWdP@blRTl#ZZN`w`B6haYHGv**Pa=RbkF
zbp>zzf#fq^!v<VA^fv!ct2exZ2$!%W3T4lKW8vAt9f(Fv1 at O>MWPq#z+N(M9Hb?Dk
z;n3i%;hqr(jsQ$7u(x=7l8Vltt0wdj0_NWE5r$^lRG)zoxrd=_euR0vg!NerE$-6|
z1*wufyU(a=jh@!lm`B=?X}KkmGj3wV;ieg_fU5}kOr=CrVYZOIe9m3ll3#Q61~T|r
zFN`6*qy^b`k;IJ0p#02ZdP<nd_iLy0HNmpc(b|E$CLwF4>bioEU3ChsCaFShVHdYK
zK^IJ&o}vQdK9M2vs6`HkvQHM77mSY>8bV12ToH;lSq80)yiG>VOk(;0K&M(xwru3&
z_Id#!x00kn`Qsk}d!#}7_^)pIDqZ55=INBc$|_h&BvCdvinDeM$a8p}mAT2ED<SGT
zS at M3#zFDxgv7(ZX1`C)!VNM?MIY^v2!d*KSy9=XwV(aopqkn}+z`8CPU-3Mes{x}h
zRbBnXIz_ybJtsOGPL)&Sx!yR at ieU?M`fWLfz+~M9sujIeUQxID)Z170x3A95Lex=w
z{KoX|qg!AhLjSpha9KLVXu+kY2nkLQ&RZe0`Shs&Z)te|sqMexCS5HIvpL(`WwZ$e
zS_`gSEsV}vigHgXT7*zk2ZHXJYvwqf8VaQtmZ>_R_DmE0zbynB<(BG$zh>qA46l<k
zWN at 9oSNgF0ko{=LN1&oUa=%Cp8OT549-PFNXhve{^eaz(m+jriuHBhX2+c}c*cXgo
zNUy5{4-$Kt$~7sXTYPblK>El;8_%t~0NY?HN|N_6Ye{GS>ZF~6s<XDQLf%N^0{V%!
zIRpKaf<x?p at wSvDALaW@M>ZIfoR-UG;P=%$F at 5wYl6y%YT!dl*vORL=Qu#?w)?6$F
zI6bbDLWnbzRSldLW>yW9Gfh+tgcYe^ks%%Z)ug;1-tFx at Jtx!s8fj+1n*6KtQJhr9
z(@bg{lrrNl70}TgrS0mQ>8nceFK_HgRnoqcb+TjvyiRvjDCL~;bgBRwZ>0faDnX(X
zWIHOs-gXVrlbWBpoI_QV$PY<XNF<1?qQH|xBR?*NblHSo^8Ck|iszCE1g)!BQK04F
zjmjh*{b^zzFlT=GO{D{qhQNa7L7;&u;@C6 at CUGBK|8jW2fO5w{0~e&Pn}j50DFPQJ
z3<C(#H>kmYFBr|BgX`{fPFK}}u=>FQ{fWT>uLr;aul|AtW)<3^K|0N~<e>Uiqd at +p
zUT(>KR{Q+npX at mcdS?1k0v6DSeGZ&+#{9L;^}_UPZiEcNTp*QA3iQFEB<feVZcr-m
zFW`1`p+3MWJ+ at Dt?s3qc{GEN&kw5N;48JcU{&=D~5f})4vl;ajnXJJNQw{!!;KCmK
zwc<u&-uFQ(MTPmx+tx^6O^@};5xlR(suwnm>?09qC0TKg>DT&_J_m>yEHGTa>pQnh
zfT7MUUh4r8*bP<fTb09 at FXf-xgG_j^w<%SHKI>t|8 at 0vHp;y19)kh8}1MNv^V3e^|
zJS2l$Uj=-4iA6!*9CBx*oeNXm|Hsrj2S(Ox-NP{^PA0Z(+qO9|I=0P;or#T!ZQHil
zu{}v9GhaXVx%c;e|5TqoRlTcf*IKI%x^~gep1`qFaZ-an at FFJ}S8B<ix6gQ9<k^>&
zqlG}dAdv-MlgW^D5iFmKj8D7`b3!=xiX;PT8tLdc#_p_;!l!|z`H@}v$A_r0yl
zlGmmY_e}cdW2h&98IhHM{u|oVx38e|a(||#E>=l<*adq;PJ5K^te?1HFs)&Fq+#&5
zVN}~WX%2Vd_ at 6W5X$|wYzP<m;Ol#nh+n26(vS4s^u6B~FR?`S|u6C1T at KmjKvtR&}
z5?di^D<B*k>Kq(eoA{|*SMvGk%|j}nkKIJ-iW2xfWnc~)T8vhcyqx7)Lf<#9m}#MQ
zt>Rz~|MvK)N-Drg2Y|b@!^OOj){Fhp37 at j%sWI=Lp79k}V#Lo%8~N>v@|UETjIRNf
z(p2H9Kuv~W#~08J)Omt#A?vP*3aDdj!Q_0RSN5Pc0U_HTRxYO(Q1{EXVM_Cp<-Bl_
zg4S;Bed`=>aS-6PLt$r74O50C7{g0?NVy~)#Ju#8f(kG+JtM4)#YGsyhGmx$)a8QI
z=TP at osqi*01el;0o=S6R at HSP6eQXB-E-4D_*_EUiZfiyenNG^OGE at L6OZZHZO#YYl
zNp{Q*q)d{$)qLo^FG&S$9ehzxj6oh^6y^g4Nq0>pH_WwA`(&=FBxDDxk-~G|t{bVM
zpmr#u{A)6EkHGUUqoHat1p&}DmTu7`<X^0z6fK`KEp~u;IarG)2<~&OY|C(EXumN?
z(M(aO&~AwIX}|<W_$bhxa%_d^&0TBJIMly?hn}%8Dn#S?C_)R^9W{yuH-36LjaYPL
z4H_8h{ACIjs%nn{%fg2_C=wIUi36b7_zp36C-?Qt1O3(+E&H-iWNjAo6!#=bJ~&__
z4;8g$10KT5+lCs>ScEp4lq94Y9m at Dt6cy*J@=*jVZ|Ey(l4m4 at weLdv7;O-o)@3 at x
zFRHmO7c2?}aFMn7P~iNOqF_;gIFflJhTWqddLdxRj at Rzm`>j6Yi2Gb0J}mPNl at O4I
zEb+l at Am3>H)iqx`!b!xd2m?=**+YTpjLx<@MApn}0?(gj4JyL@&~3QHKUk+YArKLY
zf~Y?s{n9>zeUT>*{^`%se~1BP2MZ3qaZrMl&b|x_;WGJ9{R^2D%-RQSzL^ajJo7Ck
z=#`2If&~9~qijodP7;hNPYf#PTx}AL<=HhV2zSQ~EvV3Q?K?OI?8R`<+lOesSYIA2
zc)7B-0bpePJ8F<gVas>$_Scq3aCu65He)L$cfpJP;Y~@fcPBfspk+yaRImh at A`vh<
z at xdpfjBCszB55$Y%eNRXza^VR+#paq-4O6!ZH}fvIW9toI-BYfLGBHQNDdX0Pzqni
zN-!~UvckYRW*$k5cQv3Px{V3I9c6cnVjr5uYsb(?IN9L9%Td*_<H4?632gSEO=gim
z!U44}ajnZTi*MX=k7|70LjhUesBIdw<z4?|S0x^Y`nBE>71|D46uja}T(I}B#^uYn
zs4fEun$!>L>sGr+C*<i59%xv$&veLJcR1KT&j|h at p13=|4*LEk3eh3(pkLMn{&{~&
z5dQnvcfS^RP#5?+7^6_(&ufAa<k24PEl+wa4$_X~kwK>^hn+q#XM9?1<S|n;C|%*-
ztP*Ni#BA7>Y*<Wdh at Zt&{y*VZAZgiSFQYf at h%qgtHzS2&`i#P?jkREk2c_;`o&SF&
zb={1Qb<U%8Zi;lyCw1E6Z+EoY@#MPQ3_)52tqKwS`}1<n=x^vXMBR0=S-~?946s!f
zc6$2+_veypPM|H#78=nw!I?ONj3;<UfjvO;04d?>S_oBK+i&SBZw7-lNq|2fp|#WZ
z34f;7rm?TZjJU(?>~hm=Q)fQg-)#pjSxRuse`L1%1UJP<OF6>(&!52aH+_we?YnA3
zr-yR6pq;)XHqk3}9jbnIyK&V_FkgRu3y3Rw@`Q;n+6Fbs{%Y3}xQy3|vZ4N_Nk6Tt
z9d)XL_NN({OcsknJ6a55ZSkLzQ0w-}i}30&^QrJG-xWRRFpTFM8x&9zb~-1a8eZix
zMOp>v4p=iuS|=>h;NNFv*hIN3P|_!%Hr#LjnW57xLd*}VXfY^Ut04s1DlqnGyMSK|
z(hbzlpgLyRbf{(EK`N%oN`(=yy!<(k3)w#D$EK1=@}iVR0<A_>1+{T}_BO73zL!T9
zG;2X8s$_WDWJ(+#q|h?RCfaCS96SkCk$IvQVc)Cz`VXY`*#<ye9`HP16ZOn&+|LWu
z3?XRFu at c1<I$F)HY}KU%FnLx&P4-rX5w{gC4OX{L4RA{k9S5zij!c5DyL+5~3Cxt?
z9fMsd;RNws2lB(vIh3KpW)Y5b8A#1OJAsK`2+c+FVmTM!?xaAnxgZ~cNilA{9E(Wv
zc^hW1WphG+ at 8V_OFu@t$euu=lHJ5-#?#~MgX6dn<K0pn!4tWy;yRTs&gjiQ4h61<S
z2>Tr#M9?6N+Aq+~Ct3i`z`@{U3J-yKEBzC*y?O@&!Y3W1X|KXIY9tpUG<U^CH_SIQ
z{hTL5!D=G%!|p!XzSob&U?0^nSvZug?GumY@!+Y`yrA~&!F|}Gz)<9evjip6N`SM!
ziOn?Bz&xK0fPH|m_P3%UCNeVF+?N&NLby!J(I>(jO*U(7m9julq_N|QbZEo;ZjYG?
z`tvc~rTChXXEEba9nK61gXbZLh%;_V4HJ7-h7cTFQ7(vzQ_g|~ITU}$7+?5k(GTTy
z(X@}OHw%U+S|yCwPol}i3Ksgq{)5d}jq4d!zSNKntaZjC9URrsgYG*R#Q`@wL_N}%
zrGSH0?Ks~Y$VhKENcl0M(f-93WyJljAE+?E)XM=dwgqexV?4d?2}heK2*`wmBIx#-
zhDeC^nKu#RZ3$>7kPt!qBSg*jSed>A5Nc#8Bta0Y-Uq1tTou8pEGhT`O2<$l1Jw@<
zXVs7;h9{6<6Xw)l{kw5+`yP3qWhpi)<WP`T_`^Y6Q6fQCN$>bp8Aqf+Imzx2E_d!^
zV2$RGVHn2Sp0ucmnSF3xj)!DmEsusuJWOCLVLBg%VB;L_L}2}93|(L%Et`+t;6V)e
zZ(v}$dD&p~*A%;ZfW%!Q2m<5p+QwZ*ln^38FK?(M-+#!1HCvE^5y+L*q)#w{laMP=
zg5_kK5`f)}qXNNn#^<Hkg6yg??|8uOrZytM9%i0VMLaYk!6F6e(L|I$=A-s?_i at y_
zdl;Lbf(R2z>&l-|!Tu!N&`s1_MFjii4`R!RFN=Wg at f+b#pU}Dyda-qn-$`{#N~}Cl
zISvm4m-`EK7IXD|S%OH^vcZdtlfj6f!wJg;=@<|<KDCHn<wgDyH<EP)j%L$*Xt!NF
z+uXWVQGnH?2lQdOxpr#T`{<&;)MLjRAQmWZ4`4%52)*+Cz~wxnw}XikWP*uQxBF7B
zzRPZSizdg;E8 at 5byqCo8;1N;g2M&N^m|45_#|zQyCn1^z1 at 1PBuU2nHO8)(r;fs`T
z&nrxrYdZ}5{rgk-^AhFr^47B_p>@9R)^SEcPCLDFxvU2HbH16i?c80#{e0eny>!oW
z%K78%Wli6<(xye!M)j at 4{JjE;GN=M(g>ULNI(Hy_R^ht{drX}fjBh}PC}XRoNxK3g
z^iT`zs-Tdc_TpNyE>@hUzR3voq-*a`J>fgb&n*9lTEwB(LN^VYL!&jpWs8TQknA1~
zG|S7%cU#sP(g1zU`=#DRo^wLPkA*@@za*-7?~?_K0TT{`*<JmJ>{wW?KVh&8;>`RF
zCxCce?}4_G1Fxs;;>)C&MK at -Y{Xutp6Xhe|FmYZ8=dX7$^&+1QXXzkklw2ccx*5kI
zJVInD+ux{2?Q`+sK0{rlrn30j5!w5AQeg3BQ!9 at uaj4Tp`nan;ZVIuOv0;L4ii1N>
z+24JW0!1+f3?PZh!UO}o5q!M|gBb1DAchL(Bg#uQ&fbG^$dJ-y<2x(_r|;?)4k%eS
z`b5kX;;UhAw#XUXO=b>~%!uvcwG-N_?~I8+>xm1-Nrn*XXd-oOy%>^dlJFQwk#H{=
zJ#v#CIGvQD*s{r20kN?SZhr>>E1tB$@LsY~2{1+CJ at 445BgEYD0vU_s3~WQi#Z*|3
z+UCW;SNDE3e23lNd50^<Uf{2Txb~vTo>9ICa;B&cBlJ#l>rvbtdJ!a at rQoj83&TVa
z#ky4X2<xTO-ftY~((%xYTd+zN;zWw}$y*Ex#<D;IgK2`^#RDTfHAS6DE>;hV;P4PW
zja7)y8V8 at 4GK-0bH@6|cf@&<Xcp4ypj%DmtP^s`oaO?_zHE&@BVF8`zt6Whcn%~+z
zc)P8V(+7~|S`nk{L&bv?5o=P33a4c}f>p3gK&X5`)0k`qn)*Nv-G;*eJkVn$4KPma
zV8I_YvJ8gR&)6Yn7rbXteOHcP&V{`w&q}?T!}>KTdy!p<K3UuJVkT=jjA!i)*h!zx
zJEuZQC+pMolN+&atADeQ1yOn3-rBW8vgnj<$g1^B)g|MA1CJ%9vTCum)yO#|6m*CB
zgWi9PsOLB}3Mt!+s2Lv3D3*teUy=D|*AlREhyX>Q(VCxuGV;6p7-I->rtLPJ>^j^|
z9U+hk!6Y$CT`F;OeB+26`+eoRHuOmplt)pVFwr^N)TKT|l7?mx^uo8Zjl%~)#5yw)
zf8IDutk*XOUZsu|LoPbAKrlZ>G@?Dmb5~h9HoWq-616EDTY0*0D#6wgH3Mn3VrBl8
z^{?w8Q5^f9;NeaDON&<g(b3iM at 2VQnQGi$W56#sKX;s?1l=z}AezcXJk~DPs!SMxb
z<FYNYIi1!OM>KT&5{1R(h;=!1wdGgqhQ@{E<CX@((Q@|%0;%+LO;8r?MUC?sywQ_n
zZ5Da-bO~I<k`@!BywTCIE1tTpdO#UEh})kfU%X^1KSgEehH?{18X0*l!sO}bqeP{}
zHb|`k_ZtP-yj<^+G(7H&NzygKnEEKv!Ye`Fwb*NjWi1BsCTy50x}|_WG%R)fDAGzZ
z5{epQg`m<XIifV2DzVsRq0 at 3JW@btlZ=6}FMUx5atPiv0C!)pU3mVU~WJN6+PJi!0
zr|GFJcNC~~AEGmrBg(rl*Om${{Bo)~NF7JWkdp$L(6tc^nY`x<p}V6jA9R7Qyv7Kb
zbjp at i!6kZ=a}l+`Iv_9qdf7)_j`-O9wq`2YmdEz+<<LpcqCM`l4(Ne&aF#Bw>J9OW
zhK|`XpwIijG&-2+#~F*ZhE}(ky`+UETMCVVsyAbuhKANeF_EN&*HRcgT@^oX$mITl
zx=4BspYR%>1?%b3W^#gSOecX3i|=B>_E*$TJJ>b=;gipDs^)X0w4$!!T0j-86`YXG
zq?MeY&E%p8ZZT%X5o&8~8JViSl3?VG(y_RT^^8I%v?fk1>qg+&vXbaZpI)UpU=7u9
zj%<X%Qyb;F{^jhVFea4NFX;S|2{YqVVb3KSm43*i8gL1NVz^$!Z(Airq>qZWnsNVd
z{3XH9Os=1ksM~=pD}+_wSc5cNnyx&Sh at tL#DLIGC+mK2Q#?F4W(k8cHb4~k+U`c=B
z$V>;c1M3$mk9uwO?QQkC0mAlkn)Tx050v)5I=uXg#+`$#kO`QGXV2TYDTgYvPFeSO
z>=^KM3q>>5M8A+R8R{HMzq at c0HCkvA*~Em{mr<K^>VzBl4Q|9i6C=7caaIg)HSUxu
zwxRCX$ft5_Lb|?~;@Jqa4aczVN(xCn6LLH*=;gBMn!$9`m-F7_u$4OypqFkN0dnKQ
zF- at tyU`+>e at fm;h$?j)A={(gFfLzeKVP?f at Tmik$5M+5Ba~&ZMp%fLuYe?!DjXZZS
z@}=^brzOG<!Er at AUeThUwGyF)%$&hS?o>pg+-v|-A3|5)89s<TV5}XBjnC(KR&Ynp
zkQQ=UT- at rO)FWpXYd^4><WcAg7c{X2NP@>>+nl!COE1$K8R007VTTkN9*s-rGR+Vp
z_(UKV-4a+;qm29Nm0J8L?{7p64|Z`-Lc#m_sn>67-TCw^F8Qv8zNh$t7T0D6SUUtp
zj_PxjR8jS{0tB?R(teXdhLgod9qyR9*VwLe#IIgXF)r4ws&88b8gK4|?dliq>H#iG
zdlriRb{_G0ek9?`0iq<~#x4M3y~wjxSDn2iYka+f-=wCxgr$`0oFlq|uVUTmcTBMj
z^`vmm7UEfkLkFOL!qleD+=q>?dbzmmuO6Z^;Eyr1VfFMnsh)tI6F>L!e(n_gY_+1L
zT5!Xx!U{QLa0RQQ*^O9tz3uWcN$RMz`Bo{(boB=8@>zj+-mRX#g}9 at 22-f4z4@Ipy
zqiIBL8e}*d=u!z)vNccuT`}ui;WPfg>6ALKgqO+|WNQrZJ0xgiKb%}w8huO+|7Jc3
ziSY`%rNh!6H59!U(h@>W4la;9Pelsul}%?`D<d;@*6wH-w<+yhC?h!*ynDUGsJJoB
zP}XPktLy;Zon3~r;H2NF!u2SjZ8sg(HLy^}$Ls8lbGjki8C;<Gc?XqP#}?;i@*|>a
z6KSgn6S-$#h^RUqo`}rF<bDwarA>kPI$eJ<>#vz;nPXiYR?Gw36u}T9*`>*bA^&V&
zW6sXfWtF6(=<oWob}ia)747{XGPH=K4js5$2^wtD7l`ki`*W`0p=d4o&1!- at jCUfP
z70q$Nzo}Afhq3t at Ov7LPw2U_1lyD{J;_G@^M>UdT>-2GC%w2BLwT)D!6J6$wpYrW7
z-o5wEPR-e&A2qn)%<`?8JPH30H35`%24_RdXJYqdS90+9EW>+4teBc^Fjl=`H<@Ug
z)e2?N7Y?78KxPvgHLIFI8{_+|7l)7Op#KE?33rYmR#SG=!E;K-B`O==9{S?AepF-R
z`||g!Hkm at +<EvL1pJ*lw%(=jxPkPJw2Q!|Nc39EXD;<X5S5vo~%7ZPs3Tt=UD at kR>
zKGZGfM<Ag)CO+I_i9 at OAm{r;aD5US;+(5Pj{P$xc<?hjUfAstf3(UA_bS71k!Sxge
zhAdvf_^u(<iusZFNtMZqZj<gL+pl6nT-tu3Ot{<;vk6jGGvl>(6!{DoSLTp3sFss-
zaU%$f?}urEF8ila`1$h^X&HYmWud07Ttjw#0)y!?X5@*Y?_`(MV-Q+0!m27)UXJyq
z_fCjUrPpbQk&4Ck0H|iPm3JZa3MV0><|as7(>XCx%)2=F+*u-K>vYoWv2AvoG<zxB
zJj5(uLK1FBs!H)mU`ZBXF1B#S>m(9KLBpx~uy=rs8_q%7JNOGmdt<%Uv{AR&RaSY2
z^c?PLu6%9sUP<p0D7cQth<D>7yV)ZHe>FphzaOay)i&MEN*<zee&D)!UJdzQeSRtq
za?}aQ4ZR at 27>4@{NiwkH at 5NE}b*6T2fx at Y|9j|I)jhFW$Ygt~V;}BJgex6Fi;XO6*
zTQKV?oDcZ{iN`rl$mFUFvha&N at eMT2bEc``T6th=zGC4sWx9<RKTAs?x~R8vD&5!@
zV40)LJ_J;j;$9-)wUXX$1b%)ida@~)SQA+&ix;LA=0UUSqv?}tw?<$)ute|d@#0fK
z^@v@`j~3~DHNtF!Bb)~#^$s5Tg6uFKlXY?Bs<i`@v5|XwKx`%-k0Xy^1Za5KHt=y+
zIxkC%V>F*BH7h20f8#|LYn6fsk7xpZ8K11)OE<D^S)XL$eKuos{Q$t}3+M}45GvTR
z?~+VR306oHEli#nKI{fCp$=@8!I&)&Dql!ECv13&=TIAd!El~@ktgPwEPEMo+2HRg
zOkeUrH5L02{LwOa0xq%i?#^;zn2!QM`)e~x>iJ|CJ6H<C%wbLXMt%+{ZyE++GjobJ
z>mg25!RA!TQ^UOm)jF*pz>ahQC{P=UUE<6SV3C6t>-f~Pa0|&1HpwzKCB(B*a^&lk
z#e(zI#b3Kkg3fAhf~T{e3!P?9TGfRQh%^Z8j*po{(3fE>uYWCMe>1gf1y&t(6fU2s
zIVDoZrvTY$7q8z^AAw=Jzo9a-l)M&Rw|;AWJA#D4{dtfs<-5Hc^+Z4RU4Xke-;I1x
zERXfOKalm)JcXJt|7%1gijXMtCZBz+L^&5c6v2(Jx)da(KHtVqh$M5|PGe7xGz@@y
zo(N+OPu4shlh+V5 at vnl3!*I%3Au~Q`yh*#o;ceZ=K^o7>l&Nd9(4b<WiA73o)WlN?
zZly79&>yH|{u)}*5EgrcM%eYIrO+!*O8I5aA at P1-_wkOmj&?&lJLMoUQj>GA`F^Jq
z%|nu+q_=8w<sUy}^y4APCcomxvC+v0^M42e=JPoVvglEeDDxm`l`@QfhE!-}pziHQ
z&gNd`cLrZ at J6UFrU>j<?yuSb9s|s at rm)bj(_<|j%%`e-Ej0CnqGYy%)lu(^fx+eS3
znapdC@#`{%Ok!;SA9k}@+>F<}35kAUX(xd=l)#af1GuR+bvrkzlhnbr6HOp%jFcP6
z*nnJ39?A?WQ~h-sxt$bu7v94EZgjtQ4bH(?td{*(S*W?fNcIdGPUc5Laso2YHOdea
z58#@)Q!##1+e}5A3ph*5nlHnF%p7~Wo~85McRHb(I{(y|r9J4`7%tYo&OjnO)@+$4
z+%{#ydjrsMj~$meoTnY?ZjX^W9|fUAk+gT!uIWUxQnlied)~c?#F$YMvs_*XeIzVN
zEWeRS5_*w8!gyFmvxU$QX&;7HtOf|*a}*w7GJ81Z`K-AtW>NmG#eU{Esuu5~4g(SI
z_~2D%=yM#Wc*yJQq3u_Y<cmoWW+#<+<5!db37iKbT#^cvnM-93n?NwCZ;%%+H;Mls
zY2el6a$Fxp*f&SR>^-BSyq&?XjlI-pY{jEY%2e7Q`8aSI@;)M9_zsaC!u3QiAIS97
zhAU1nlyB;#x=_VdUib6$U5YhFsaW=t0T-&*Nq(#p%|YB0H3#ul1(&fAt}#@E>VOd;
zEtpE(x>ngeu~LZv$GI_zmeA3INjy2mq)dI5&aGOZoY~1 at AQJ7Jpl)**driQDX|p2+
z+3Nizyi|AVGqhE8mQ4uHg99(*y4j`TZj@|eID_ at ac;x~rmJ}|egW^DB_xQ?vwK<zC
zt?+M?h?M*IA7^)FU&nu$d8YeMsHy{1Ze^LGh1X=)e|AGcIm3lmuI<dhB3?82xuB1q
zTqMF1Uuv8?9Pz%1nn%Jdfvd%Y!2|@D#HG#d&S!)`bt0?G$2(+RRvScmkY}elP at jI^
zDx@)pB8xz02{4Xt%NQUUlR7X7AfuwhNT2<Y7B1Z>1NeI5A4fwPd*UI??bUb%QFOPa
zGI5cEM?nA1(6s6_#EoHo)^1;?5R at Naz0N;rgB at 9(GLV&z??b~N|1EKm;xG*{xwA*f
zmEdqq>#y+^j!|{Wy(9c*g*DcA`<@v5EZ$b`->eP9qqV at lDSW$?rSyYIt29uzoI{t_
zMKsRyCN;3JCF?o{coG7A^;GBFFd8}QCR3TGiKo3(#^cw?OYejV=S0CD at CVhKn{x`9
z(qRx(<0x9CjIx^tIS)x`#&>yF5G|UVkelkth^Ds(iYCJ(lbFW}<j<1TZbQO|Y)WBO
z=L<XrcF>h2-5;>l?5z}O>Qd02iKSGrLS&862y;=J+T>fU=1LB;C*SG=l>(dwB^ype
zVf1Bi_J1ZPR0oh9AfakOnl;@|4AbRbCZR<ZeiS>?^~2DKbujbhjjIr*n$HCBe^+h#
z``)E=o?f8jO8ge8XZVowdw)2U-0)4az?Z{7a5L&}!BeKFl9MirnSmW1Z(Cob`P)n%
zyVf$GoSqZtmkcS<O?_cm|3GCesXm!*CaFMP6GJ+y_2-fI-p1=5P4wlj>!7T2uKA=K
z6I)hyi9|d8ybxL<PsI(gPW3LKYJD-V1k8GLx5s>BFf#(THz^STYy&12Z~H#3vW#Nz
zE8zo7-J~>MO_&%q;<Au+sGroK(E;t#qrqCFS#W9Kg?Vm|+gHxXlwiJX-d%Oe+}gZz
z<+ABSh>F7!VyFC47K2)`1aH at gb|+>w+R!sgdcZ`s9qA>NW7yiKIal1|1OmC%3XuNx
z`ZU6%zEu$^`FwNlA#UQHmA^*Ms=$l(*Lu#%ovEu^-BAmMm`}Jhf_ZM at +P;`diV$+_
zm0)_w*Dc8xajlngj*A$`kya5=nd<D?1O-c{+-Vq$uo}jWB_M`bBhnm_#pP;-Qn8G0
z%^)4h)*S68%H=Ux&N&x_sEYpB>kna7GR9p(ydlHIY&MlULYxK8l6vVO>m!Ghk%m0(
z%nRr{CoJ((?j}DqqSo`$hf#_JEt_f3d_ZpTN5D(eRwU#RLh2|ig=s at Nnjq|v_Iz&h
zVOgDt>+a|mJWd=0e!vi5)Mj0}nDNB12!d;(?@lV`Da9G+N?1Vnk_6juC3yY|sJER8
zlV5=U#*xo6E&vbfpDd<g2*|d7Nd at ElqS6TUN7^%!Vk7BD#BJ~A8Ag~h(JVQ8jmGO9
zmkb!P6}>Ixed*W-B9wG4F8|haV{mULGmR^4rut8Z#R~3}fLsF&FR7{k<*Q#@8 at 5Xl
zZF8B=*!sIKBKmSSpURs%mae<c*3|s{ybJhioa>cbU3eFblJ#CcZ5AcAIxUY&tMpnt
z8Z~RN@)@R9?`q9#)&~scA`NMB(3Gw5p=Z~cURGb4OFK3H`t)3|HY+QxSGZlQom?MW
zHM)Prm1C=q=}zbQi^k at 5$@*|A?5|NlX63Wz^HU^3p=F$Bd~Ml0j)=rutSo_H%Ay!i
zK=BV5d7OaMcqpR4c$jo0Q=mx#`imEpKpuQTtW~Cl1}L)MJtAEgqGox1PoYynY$81F
z6CAD=aPJ$vZ)_qgu2&-b9IwplSaFyj=qFZ#O}EL?m~4YYa|L4KD&eyV)+xT`VG82~
z(&PP2S$2mpg8!?kHR~S0WE?{&W(v>KYJj)JJ`|e>jjI8RTRAIam1h-!Ym#C$Pinrc
zev_1Lm638MF)El8Og)7}dXMNTMr6J(N+sYzXDBd~Q$NOTSUCQhYN=!Y<)^gVIxzDr
z$!eeW at g@ey`k#U at c|Nt}%B6Atwl=}s_TUSyNrIIMsd>4W``%}cZ%LS9UKEixOkV5F
zjT8|%P!tAa|GbgzK9NfVOIhSF$|(mDV~&N|?jEr+6j9SEXHs~FRgB6U)Vx90fMNk6
z%By<?F9FDetL?Xqp`8DEr5#qhG7;^?3kFjxg0303_dLx)ZY~BRC<c?q$m#pSLj9k5
z2A6ItVg%*Zk23E-UU!Kw$fP$1xl%Md@$F6Gn7Wt(yZwQ=@1$h+<B(I4x2JW63N=&s
z^JNPzQ^Y)X_0v1<gZm+<-z-dk<nD^>bY_GI)eAH%6atTV3+qP|0wBY~f{YftJ1(W6
z?PIcDAz{+93#UG!MAQeWgP<gd=;gOpPM9>jZ%#isznRe3%iX`$4rupyxY-|Y24vho
z<yg^=59yA6SD?cAfJT$#Of at Y==8%~f3gj&C;GendSC7Y{`yg*8-sSLLJQ|Yx$N1e-
zQ=Whk;(rRkCtOKBzM1};age!}rJO4|2*QlgKJIzd(f$AGd_9rJgJwm>tRDu86Vu)J
zk%J=(8M2E*iU#LHPQO*F>?!h3EvQ~4jxO1zC~zR$LZatUvcGyWC*IMRBw)O{>26>#
z&w)9xir{C2Iz811e6jz2(5zc<?PchbHfrXCj!bFi43SW6>973q-%2Z9J^G2raeS3P
z{*p3E-&0J}ekD0eLSqXqkanMZJ>@0(Z#O5b!+uhMF?^9ZBq2!7o#PJ<lZ3YgxlQ^$
zCyy=^GzQAbN`p%ljsX!YGG3|mAaBnq-c+#u=NMj~F}Y at to&Db?6AAJ*j>*Ih_9>^6
zxKaH+aH}}gpcej<m8(uJUPjk-=(^?%g4b^#0j`%`?<r^$NKx4CNHB;hHbFTcZiS7p
zA0&Ny at 3`doAC4RQ;)mOKsQ-&E=UHMukct1Yq;~T29^@RWK at i5q`EN)5kDg^3eTp}r
zo|1+VkwVTT<6jzF{Yx1tfy<OoYTN(E&*{ULf6yxmvmzQ%NzTr9{r~vs#GTsXv2q%3
z8>WR~?ldL|L$pDueu#S#CNSO~s^B%bLs={a at +@whig`fFd3E>ac at jF8H<b?%q#>h8
zSRhvYOSg(Q`s<2jv<DJM6t;sTJ4S>56P_E#+xsUO_-p|T!3RVEt6L at z>HovAVCk0|
zD;a!}7;2Jx2_E3?!<C8B`r*GC!jl%*Y4(rsh>wSz{+9CohY28x!Q|1>FE)MW{nw#+
z_i0uruKyFOAHTcua4r8QR;8A6L?>a&EP27OnRjly(L(IgaJ~W<|2SV+zC1CBSXsl6
z)I2~)^=)E&&Qka at 3xh9vLa~WUC>i#CsR)BD_?LCVq~hc6I1n>I;|C|u<;Q++&G%o-
zdd~r_oY6tT;T+;yYrv&{AO|}jxCXc5Dmu8-yD at t7Zyq|)xyf7t2uL6_fe^%QiAxRQ
z+Zdd;$e?=f{kEP63wkaHXR*Un{CfGy;Ue?o<mM2f2^WbC{YFCnsR=@v-)jAI>K}9p
zkQ^i}c?aww{6`g_@&Z;<PGf at qo6!RhGD7 at M21q*eA1m1Zi&~e*>i=Wsp3kHm$!H<O
z51ay38v3nd403Ccd3<sT2vJ9Ki>>E#<bSJ7cv5swzxr=Go6JvyZlV9v)5AO_`mMub
z+<rzF0HiBw{GT-G!fTcsE`(;nIr+LpFGQum!Z1dq{iK!Z+ll|_*z+#(x*3$&97v#s
z&n_RjDjqI~paj{(s8FBEmEQgcjQ!iSI}YBsyJe^#NpM+$F!9DmZ|cN)8k0B-FCb0c
zlDY**6z|R_WW74CX68^~;6YOy5Byxmgdpd?$HCHCjhbGTF#o!?m|FS;Crb6#>p6Ou
z=OLMGl>oJ`bf?vZ_>Y{~+~4qvFND4dtK2|K=S}q+=bwzH0`~d4GW>IY_KaSmRF4Jc
z45^Q9nzu4J7xP>$`L5YM>=T5pqd1<T4CC1Ym+}Ccb~Qeaz$U*>!oc*RT*_Ypd8hUX
zMQ^SIyeKjiE~`w<?ii(2#`34i{P{KK-B5WluIIKdJ{YoJyr<?7T3S?IhDkLPbk%0D
z9=SV|TfSF2VtwWYz+^XXIcyqkHrX&UJFYcb=~n*~%nm4MuEvuWR*^HT)o9M?n%19t
z1X|aXUjO*A`95stXnJdsuOa-tW3EJO-hM%yTD3xHaW35(s<`RzZgZLa_uZD~0=347
zcnc<k2rAC)O{`-#dWF9_faEYwvC5~mE=%@!xqN~n|9yJ8O;B6<W6j at mDvc5STcvg1
z;)Q*>-Ds(18p3|X0I6m^&(i&q>8tma;JJq0Q2v=ZhU-f<j`z+B&igQckFPW1DT<Y;
z{pfcyhtZ?YCNDP-`{MN!J^RaFSRntn$7>LqbobetqJNbia^TO7^iL^=cgl|}E!ykn
zv&&TvNB>)z>s&twsjlEOjfzXrF8b`Y=cv5Vl=7F9CY!Dd6QHSb$wqbdJN`tp(P+_2
zCpD`t+00M2+x;c*4!etSw;WG_$-vIp-XHGOjQaZKOh9#!OmHFh3;VK~s-5Z1=5dO~
z^VM6cGz%Ly{h3MoHO+-pYPF5jM1932>UV>-olovkrqifG-(g*-8!G}eR{dE^-%WZ&
zU$Kea)AX$yy#>8z?m39zP*=a2P0y>WGcFB}&CRJo(A8^p0zaqQMeRq~#?Oabx6_$D
zYi}F&yy49s^P98Lw6B)|v5W4}HC^>nADf7Hx_wepo3%TECXR%;7Vcef4up0!j-L}1
z;SDFvNn9rnLhsE)?}EPitKYYlSDv{z^R*B9s6Rfc|C)7OByq$j8u$LpK(I^u1Kq$5
ztK=6pW6%^#ZnP)Dt?aYspvVGAE+{L)ttg==lbXU>&(o?XVLFl(CRk<CKv8P>vc%5S
z*oS}Cskt1Z&>EW-Wx~%8=PfGy10eS!oiQPfd-_ce49>S26 at qrXAepkdrb(XkSq^t0
z at h5^+A6(B!9s_3sY(lA=ryaT!vfzeXy5HISqdI5(875AcE$cV#30p+opW6y*EdgoS
z8%#2XkT5W9uM!RWx+n8JD7YTBmV at +qSPAypxz43iN1h%{^hfnaP^I5ifoOvUzwoCG
zzHC*I)#}iA=m`~Ry9A8HXA}{=^j9(aG)!sxGXm_tJrz}}bcxl3;f|={9$hMF?!7-}
z@)4+B@@I~*Lv>WplDvQE@|?{0J!~wCS#4DHjq+D_G^NcPlaD~sR<304Adzkoe+uu~
z2_{sbB~!#|I-{U?k4!LU<GB$@&)Gh1d3=SgadvJEDYv8vzj2;+|8Uj)bW_ka`bR!Z
zVI!{fgDy&<dCv9geBGP^)Ur>hn|I@*7aV8PX&$VN<y1NMa;NRDfO<=h+oO<VMd+r!
z{?(dlpx(kYLr=fuXL}sJU2Y}Ez-k=b7IbSf%6dGtgT19a_WD(*-41*!QS at O!4L-$7
zc<bzM!schxFj#vo=4%9nw;RZ|8?}ya^aci(!Py(xk7x%c3;Cb>*OS`&*W`igr0<yv
zPf)wRmpPGlue8#l!Mq-A9}dBJnxy-(2A}ICa)f&f=@~eh3?nbKvf$6B0dxFtMKiy_
zoW_*;OR2%Fh)KY}d4!AlP_5F97r>l7WVw>SmLFqbUFO8+qDL{`!sSXOrUe@)PV#gx
zT!}ZpP861Iil+mK=Wy+&Q@&gbVR#mbpUoeFvoy>^V0z}Nvf}6`Ql#ASt~Kk-Zz8E@
z+idy_ET3e38aB8{<D1;hkUxg4jAJ6DHxE$55)!L^rtqRI$B9pQ+4+%U2Go}4sQsAw
z?zMJ;JKiLi6Sr=Wn*G&B+$6?4y4{e^VS^8Xx;y1 at y@3))?794Ie)W++FgiT%x&3SQ
z@;=h})zgcrkiB^Jb*W(hj%jRGV&5t|1jD~3V**!mh{$fE%JU{DplxX^9+QHrz*{#M
z%m<S^G+bkVwTG;Rtlnz*t^s=J<Y+<V;68ZtMBB-fRExT)b3M7o$N5sqf*uVvo3#-#
z)i1otm;`KX_jPzbX|pbBz*P66^nThQOhqL&T#tTB!y!XL0{+LQ9e6}}1WNVQgu9Eh
zSD7_;S1q-MN?xE(t*JNmsHZ^fE5_m;^Bii4cdAkd0ca&}uen5e_Y$4ix36|XlEPd}
z2^smAf|_WC{OXvB6KgIyfTWO5L=3au>mJuJ%cEK$zENy7O!vI|LqPt==t#ZCm`?>+
zpyUp7e5+ at xJeD_sWuw5=Msl&MwAFQ7$fkICPs;e36<hzpzAbZHg^Y at iQV}N4M+|IN
z at 84gu#~nI{)v~pqZ4vJbD4+fy<H+#Z<T at _7_Hy`1tp`4OL5&qy{g^Q{MPv7!3a?gW
z?C~+16Hq+04c|$eDf=$CF=xPTGA1|dd9_~R>>h6EAt#{QAwGdyn>@Hn0D<j6^LV9l
zPgtfvUpZBPMkW7KU%m7Ci>a at Ga>nN&+n)>8hbDwRK_;~PvG)eA4f7r(%wF>|N-K7~
z!UoDKp(c4tFKoS4t`0Q#f~)mt@<!UuPkJ?)OFb1-`!@coj&)7jqX^M$#5Hw8OaxDW
zJd=L4F2haG=`yhEPZ8C3ce#gDsKn_79#@BK#+~^40sM^1jD?y&c3ATAS%GYNF at mg~
zNy_3GBIaECc)pjGf{mVa_M>N{!I6aRX<r)rz7qAY4R&dzq$w^c23^|o^WVU2mG+_R
z+Q6g|HmezSo)bRmQA#Y2t6=7FpqCbvC(THo#1|hH*OG$TdfQk8OO)dweJX2mwOp&p
z$GVLacXge6BKwjasH+Hn3=hWo50PWd!Fj>*metj at srb!qySH_gGE>n5Al)w!`S9<x
z`{~ByyBH|ihGaZ at 2t|bWa~YaO^zWvh?5Kt#&Jic^S#0PH3c-gYlGXGbrb3({ww+HO
z5mpxb;<MZq^DjTHaBI3PXZX}7zg at R$ookR_{^Y8=ny1NJP--Qb-s=RcLXbX?nTo3~
z*oKF*X~#zGs~=-uZ?n==Pxi;l)-J>mntxef2r51D{;|bR+1+n`_R|u)zQn+|KWs<R
z(8IqLDnYa~$TZ<6-75`hZ8>knu;UyLoYm@#xU1MV62ej&kFrUYvzW6PaW$v-*m+vv
ziSLHfKf;j(Iwio at G)YHKO-YY%yAmhf#o=YR`|82glw>1j;<793iXvp${6kRbWNF@`
z)3D(OjY0 at Zhn8e>O9ve&BFAdIi=WyjJ7ml6;L|$Q3)w&O{MndNbV|P6#cw1ihA<zs
zh?RSo?`zSm7rF}~4ya>FZqbcYk?q)vXSGuI^#p(M{pfV%Tn*U3p2;;YN`I?Pdlg}4
zPk^hklw7GkZ6QB&FKa-vvszl;PPnF4y!c^Yva~XaZMnSDy!ms{ERY at LvT^LMHns<Q
z at XSm5BiLCI;%V#vQ;6o;6qVUe$g4NPJSq3PgAO+e&(D=6d*dMK at T?6MH{V$%sW;`h
zOShShgCVm7ul(bjVC^cG5Ih+L+89+Zp4H+%91zwXl|kD{<k*lF6D4qKXFn{NuTaBx
ze0=S$0BT0`Dwco3U!HYMS!2BvwQP`lv(j@%5fRo$&(H|-c%QvlImD5D(O)(x*hSHJ
zWmS5iy~iQ-BdNLuEI#51{cj8x0CSORR2}P?Ee|cpKs1edIJYPwpDlfAPdkrsEW*XU
zeuu`dR_3<{xd!eMuUXFso&?%|A9+ at _zdu2A_?D;qyh9MY5`3+)?aGoHIOJz|4{>?Q
zd}bZ^+83~$S993{Vc&l}%f<2wu^vKT?=EpmdKAx+zP`&8a^x_+0yaEc#2%|j0`kf2
zW^A<Si#II268Nv-V~@Fqd|lQUIMb17cz(D3E`>L`;7Gcckv$rVXqwjoCv7SNmG%ii
z)J2c85+CD)RGdAC52Us7oIDEl-d)PZ%l5y*v{bJm#!`jo4E(k>N1JBktD5&CWzBU~
z&{A>pSwwGlAIC{(8diCeV9JhqTFzpkXkKi-E)1lH(@U?mqKe~Lo9<m-JwkSMy$8uX
zB$Um at A$kh5ux0D8j^lNchKbOZh<a42*kx|l#j+F<QdAN!n^fj7b9gIIsKL#DS`OaH
z<~Nei22)dl$)7e|+FgOM at 8VKqCO(VI_=j%tD_qxMvcdaSA+@=y16{tmlTbRhpWFdy
z=+yO*x+oD89Gs-D)XSDE{ul{JiwC5Rcn*tQMxEZKTgjO5ln>-gej*`JM7?uJ>wi+L
zTrDt>WFwIXsk}OU^{-K>G-33zqeSUyKeFZL6fG%m0l4z~Ge1bjyRTqs`2|}XBs0v8
zTg!UW&ydy}5N5OMAUfxrOpV4TJ<Va-YXpFMSXh)wNZEVOV5R1{P*kz%_BI`;{JBjy
z^r_Z;`}*;w`|Z?$Ub-#d^AGrI-{1Q>;^WKwm$ks&D>W&ezaNbsHS-_BpT0syIv(81
zcSrAk7PMc9UYaHvyLh#quJMKix;7k{Q<!#9mkp+<(hlC2LIA<d32o<mibEC!X-*sE
zw1sJMO*Uwry1(_kEK9De9(OzTUEccs%z1WDOf(*fxCLmd;-6U!NTV|p*QDP~`#jzJ
z`FZUU6-&arP%M`V0r3Q`UBJp)M<TiW8bq{`4J-Yt;4HrWM}8SsdRy>swud7Qo*jsR
zu3yj;SA#)xKW=;dTm=<EnvnMZw(^8ppg7?zqsf)IoiODOKb*rbI~}XuvKl&XBYgV{
z->R1fgkFtW-d{}fZtF(6>BB4@{T_~fWo`3esW=qA`{IWFe)7i;yT)W<veO_aBt3tL
zE1MC~jw=nDge$L`#riWpi*k4;#og{0M_e80FVAtJq~g_b_1N907fV-4m6e528nOS<
z6@=lujO*xR($*GNx!k)&^Go!jwY$wn{Y7<MY2w(Gw!LgqsQk#;dW&R0{<Q_YGp8di
z_%HL$Sif(}&A8^-dK>(AxxF6`=*j+x@^#F83X>o9%jV2?5`XwA>~<fs?e-w at v3JOR
zN!StzeY+6)5?p?SKM;!cPIdo4f_l(Pz1Tq=NrIl<L-IR=a>343T1 at tJTA!qeu|pV0
zl(Kf<n%KxoDTa)J>pQ?4_OMYYYalg)-TY<AQEKNxmqe{hQt;yuLq_gHs%iGRRb`F9
zzY8u%VQ#pTt)~|5B45T5$y~0EKEx*un}hZN;^2?3B>F4!e5BJ3+(y6C4mV+<9{e)-
z#$BVqP;ZifeV5+nC+#D2&6ZXYaZMjuOL#^RY)F$bj93z at Uj`RNu>&(0s^OV72l>iZ
z*{>)|;QmB7?y?+4s=W%)!xs;6sVHSepI8gdl6G}6he*&DO+6%qFvuKr9i-xNY`;VV
z8^4Yl$_n*FqypIk#4b5u(5j5gM?Yed;|!4=%q8}P>;5KlGXtbvz$;R*fy|tr4l*(=
z+UDQTpBH);BKM0`QUkb6_#Y$G%E7|Pc#?46tYjt<@dJx)-c2abYw7^Yd4LBKFOp&b
z7_oB(Hk>zd)hHDb(yDHf<@gne%_(|0RjYB;25k=ZkHliPnFtv5k}eX{ei)b3!<X1<
zSu(1v-~yMh1HAEtvO1Y%^r&S=4tJ=Kc<RdeiEC(5#uFLy<t!^8Qb_S={8_Au0Ap0^
zgH$uSG312eOgpK;im-AGG6$Jee7W>4axiF}42`4^DseER=)^ZPCJOGf$A~bz6;8En
z<lU}uFHxEfB5-jR=~;rLNJ_TKEm$!o^^C6F=wWEJFT0Q~@LXiZGTWAQ00TkQpK81F
z^EHlH0H%tM1c_{_Mskq>$weh|KviA2^diKJLmf?aS5%3GPfnt(a|A-1X(633IeoGW
z?Xof)<(g at gj7-FzSc*j~0Lv3f*wv9Z(1mRgE3#)ixc%J<w6k1X$e&od7YTMTACdC*
zb?QYrEnzgITsGK}G(U0+^8G=MWDm0Nb+{bWgc4}HtISzI2CPEuC9^;2ZmL2u#B=eU
z%sOaCDB5H|V*?-QR{|79>{^!0I+=`C6 at S!(V@$;g2{`|-LuFuyE-aeZoX2IXof}#B
z?`7<;XnB@%AjUlUmW0X%bPlRq?MM=AvzE6c04>Uid49r*BlVuH9cCRbCz0C}QAZSU
z13o87NtR<HFc(zTsR|mrcvP6&<glnBIGVN&;L%x6Vtm!FH2xs~Q%z#Ld~0Ps8iW~c
zOv4O!E0crAE%Z at Cz96$5Vhr#WrRfA6xR|E6N%gRrV>(!VxJrrRG)B`m^pM_?cW83Z
zul*I}Yu9vxF6|~wB at Etw`h=FDU at CbjxZ*3`z+r;hb9rWeASz at xNUVyRJd0JpWrAB^
zdq)V_J`?~<$5`>AM>Rv^`PC}HNn%?g+?R71sn#K4yOH{alH>MJW-imBEIX^^p- at sI
zVoB&7u)9gwG%N(ioFa*qIukVy0`Yw)+Os0-p$)%Vr9LA1IOa at PLAEc$?gg<=4(&fM
z^z;-Ym^pFAPZ_Gd<7m&1$iK(38MJfZF~^|1&(#I~wOMNT{0i(L&FYi=osQ9(fP9D7
z9!K!zjB$%;_k<DoeG32w_ at MvfrXm?0l94zbvv0v!XjyuxVc4=Vu3;l0r*p)p4Vng2
z{Mgo_`JJI^lNc;isp{^Wf(`W}o9SW4p?%d4A}=#(Z15gHj<S>YXzj}uU`f${ZhP@$
z2G5j?wg(7p<t}G6ix(qna^AAg*gX2u^{TW*X-Y7}^cN;?UYBQ3ed+qadFA)|fq~|Z
z)O0hmEgJG{KQ}pEZqY{Yw@}FD(XvjBO|3&5XaURR9OtwwT9ref46%}lTYZR@!@w3g
zkuQ*_hBkPytsQz%oypj%G6D0bt}@}T??>y&_dSFC2S~j|$DYyXo>9c~>Fq*^!DT%C
z?h6BWa>TMVl$k|9%*>3#_(@J8(1uq`t8}=`CgJPQPAa>eZIT<Mr5k;-Jc>Rg-2{Jj
z25URapkc9EZEeyDUVLTd>yf&?nmxWr^2)kd_y$F9Qrh7r`K7kps%_~G<HLbBBxvnf
zUj})1Ob$}_S|9PS^aowu7gd7EcML7MPyR!@!SpE7g70a!%#YJy9w}QdxbNv{V?ouN
z_lkO>7*lKIkl2@$5aW}qKq<TWMb;??zeuxDGBw{=8WZub#T#p*b2~GOr46hu8or}S
zTY-V*$ne!Uc&TkOwZrMgT26lg-YciYoNHDHrI_>L#f!IKRf at 4Ll2f9OU*0g4_q$t=
z*zD#3-(8Y~s7}kJCg&O3dC07pFKrW&Wqn2p<)$cVT5~6_okDeXq%wwSap?fX)^G5N
z(!POM`sD{_h|pOxGen9mqm{FRzaKHl1W(76C3?N4**U<gEzKDoDwfA^2jlOZCpk^M
zO&Aj$F8q!L6%VAek5x$zEcZ-4K%xDXGY3dJp{<fMt>)SNg)Zz!C at rek&#o;K`0zDW
zJ#oGN0`zkWxhJix<5+AHOhbiok5uglYaD8>)$JVG+~Tpf?CVT@(V4kwS)M>=(n%9c
zU0sm;zv6o=OntLsKlEI~Yi<>in~{8IN?vWa)x&R3d}1{%=-e#coTq#0(8SL%x>mLK
zY%kh;xf_Ws^A&MKUx}z|b=J=IY_g~4wqxfeI_A3b6o at _-TBqYC=$YaYtK2^3D)ef#
z$tHIGx&p&*hOy$YRkewwyK+L`EZuQ{VWZ#iMzmRMK()8hm9t8F6QX}=K>mYx^~;pH
z=Ln;AZM1%^A!u*YA^O>-X<vL8LZSChlJbbd3tZa)=3^N at W<go`!!D1mHoxnO62^+k
zS~N`XH?NV%5bnH}?#t!tt?2tE;lD<2Mc<Y1o6`raRZCE5oK-bu7-w;n8%N_ub-0ZS
z?YV?JZk(F!!xW~VZPq!37xZS~L@(*+W|t2eFEV7*r9eR@;0vvsotvHXXB5BLpx~3@
zb6P<~1${nB!~HeUL3_|`t=~15IquF|G1xeJK?t$h<__BuG<t1k&0%<Jkuta(6>;er
zGwbG>g42mpb5}iSo&Gp>>zXU~59~IT>T6V{MO*$sre0$ILPKuV?AO86q{A*?0D3o#
zpvhLwxn~O1G7{|}i8>bBjCc9a$Dr*0{MFLpVYy`o1 at 3eC^hYJ*R*P at gbJ|7P%l!1p
zTr2>sXXKX&z-6m86j$JrcuO-GD1!*mcZlr<5SO{lwEg(I2F_NY*5U6GBq?hrE5 at bE
zN~mULwxLB^g0b?Z?V3K>#1KGEP_+&2FUL;6 at w>&7C2rJfA=JG^R^P at Zax?M&J
z*)QHH^)#5Xi2=1X*ALM8)S7}K`DGi0p-w!nv3AO|xFM%?AK^;i81o41%qz_NV<@VF
z`6-U#)@R=}bs#SRqXmPJ52%LMP|)y%B|jJSOlUo4;qQL}@yia%({*KQT-oME0AcH@
z4o0glu-DbbA+mjrVs>xuZFw8>={NGe)t{%&9J-2*K69w2qt;~|sIel;Iysfs$c;^M
zE3@;t>dyrqn-WW5r7JlHZh`xl>CN6qDLs$G&s|Dnj$@LTlIoaEXNQ`M+IzVjXJbT%
zfG?o~3{x4_4Ls?m{qR~!j at P8xA$m49G<Ew$dmNS)x863UBxO~G4(+Y=+a(+ at wwJPI
zf;x$(cBIS>nR?O@=@^QqVh}RCZV8F56c<V{yT{H&Hm&gJ8hqbvK%0i0+Y#g=GcgAg
zTJxJ<9*9EH+^h>+GTSt6W>Cm6LLPYnsL~Y~BhaYKT<gJ42u6WQFihw}LmY9`5&UM2
zuH(KWHzM_}=6D!(&(4F1;CqO#uP7Dg%Sw>*Sy-uTK$!Y%Q9d=UU$pd$<>$Z6(M-)`
zT7XcX0CU at pZ$+cu)UD_FUmGt&L^u1sDksJxycO|+_3Gmxaj=*C0v^U8U!ienDT)sG
zmQTz?d*=R>!qbOnMCnaTgNX at 0nI@OWadZ_t=467 at S_y;R$Xmw?(KE(1CGp~Pf~n#T
z1+C-27y#z5 at S^!Cck}wU=Q3oP%!^*AE}AAYD*^dBqC5fPSADY$w=2O`9?*ar9H$I(
z%yS?n{^B9|L?A<tn_rV&NuoE0CA-{al#h at L<zHNecW3hI*I_$q7~c}Dz?=+kQ<Bwf
zD324D6cyeDycRw0{e>;GRiGYfX$v`GZDyN3QyU*oH~#;}+gkv~u`Fq$mL*xt%p=Jb
zGcz+Yg9RgIuviu|Gc%*b%#0Q at GqWsN7QC^~J@?+-fB&}|??tRSW~RC-J2N}$tL_ec
z*;U3t7`|Tu?KZgg?NS3qPcIUvi4t*7<8ZIs&9rhO^BP=0PxK3smU)(3>Z<#dEh&>9
z!tH(0!5N=U-!UO!Om|N+8ap^}JVCH7xQVjUt$IK^Zdr!W!{-HJek_i&*AHpH!yphX
zH8$$34jf1NSba_PAu>5C4=t`PG7NpE&kTk_Dd!j_49)@W%a>s{VXBPn3}_YDFjrJk
zNk*4B{vAK^N;w=mrO{6$7OIOszmkt#&r|u4rDJ{MP}r!r5*?KCnT<s>ws0ne>xf)T
zEy}$o{{E9THpPIJ3vdCc%|20dYFK}dWqMTRT5-}50LotYqNe<eb=ad`_4X^rO+}}!
zjJ!kGH>K$0v#do%2?kM0d7uuuu+p&<DcXo#+8TqQlt&26LMfO--p_;r(82}RLt`iD
zVxRJvA#|3vFXl4TLT>aYygamC=2NhUTz;A`fO6C2X#B>%Sv$bG!y>m#NJve3<4VwJ
zbxaFMbh?a7j`id~D(Sms6|3$&VnH5QVljDPs#g%r3*wd at AIx#HFfojACn3zjLw<f#
zacMDa^g<~^{MG+05SHn4F%cNHv(7s1$B{kz;cpZ#Ig0aB;k6<KTqpxxLVeD8H62tZ
zt#y4KGLy<`h>3F?g)tn{F^<zw)Eq{b!*)D=@iHH?bRD#nhP~ETVb`V{8PbT8<4JHG
zPVX}oK=16l(_}y%f5N;MK!Nt%RcV$dMhyjj(p3?9%Y%9cZswhfBlV3Cnx#db8k*;e
zM)h`VX(>%@Hy%+gz9i$F=SxX4JRI at PUMO4zan*w6z1zTrRR&_0l>eE;oo1nv3^E5P
zyDesI(R)>Grd7-9be|03`#|oIfm(>fGdaAlFAHDhg^fMBCU3q8RJ?E~NC2D}65y7<
z1oikCwg&I?_ouHGxpD`-)_cj=4qkbiCB56S5;t=F6qIOf>`2g2!2dn7P!6?|hrz8{
ze at 2)P4kuIBeA+Eg?5gM!nWbT{4s<|6ox;NoM at 7i%MTv?5u(&-WvJ{6=_X?7!IgEPj
zW3K7t66x^I`rT#g*er4UaRfn+q&S;#HkfBuHe{w>s-Z$I!mDg;clLQrd!w{_j1t!L
zaH)?brS_khhT68lxln;@2sx=eUOc&DbgTNT?Dn3RgY*vd7cregX*FcvLJOz at e3hR$
za-0|h1lqZ)+cGk(z*d?f7D<mDLVH$LAYWk(n#Yq~)oOhu{9dno_%D#1bq`}*HvK<m
zjqAAyUWjLYAa#NC{AT`xQArIk^`)EIt~XC1Fc4BxL&LZqMRLtR_g5>ab#%u4eQLPb
zzR9WZwB>VW?)0MIxr`zdD`LY0)~8{<owRBs%4O=b>gM}r57^4g<<tz4r_P at nVkC$C
zlly~w55$&C2g{;oI?Qn<eR<^cwn(x47c)BbBP at m9$~t?ewLgo9D;?GIBk8nU%x%iy
zuk<I}S|%^k!WRhAa-8YN-|WxAzQwz}Ym=#iu1WXR;oSQCjSN*>>oC<*w_vg`yuoeK
zQtGWYIBxLT)_OVT_Fhv()2wrefO&UD$7d#^Z?PKp9U6;lX!ec^XFB(iY at eKa4)FFT
z?GOw(#<<mD%&%^&(TaL(xX8WRUSMuJNt~Se(zwy|`;5h6s*9<p2zSP8hs-ogU41B(
zr?wxjWxn&uvsfD&5ZAH3$!dtFoF?x4r at U|J^zk~x_@!w`-|a`<NP6ochhRoNnf-|Q
z<wx&`q{RY>F8ix-%sGC*$upNp4_tZXw)J{a at 2YIvk)U`DLC0Oo%u56`@lZ_}+u_wi
z=|gVIukWB$XK|qp1t;=MupWk~eG68%+hDd=ys`33^v43}co@`Z&r7&g<v|m<_BL;j
zn`nuk4?JOE79wfMjBtE+Ef(2`4QIe9pvDD~vtKb@$^E#u&#U!0uyKMfz1eyVS2&Xm
z+r69GmoZ5gf^P3V!mHOn`yLT`;V^_XIwyQFEmyx4 at O_UDEp$(+8;&g+cSJTaIo5;O
zzij9f96y5?77xq3wsbiT_nag5*HTuRm|})`ZI4%#JlW|-oncs<!fiN+XZ=B9Wa8!5
zGT>bVoQg3e at K!hhvlM0X>oXi|i>zm^5}o60FAQZPR;OQiX?5hQME9-Lv1pumF+17&
zTZu;cCVH+5-&nT{1_#DvYhT|%OYUGh46TGP7}XCa?6dRgU%iTuFm<zFI#`W}dGm8s
zNa&JyA*D&#?@VvDdk|#xAf`Wi2OI4LUAQNMIzzTLhy%wwlP8+(j=Y}ZTS|D%K5=zc
zjo!zvs>o}qT-*>X??1H;8?<jP-Rwl_;979EH!p4Pn`RAj>|rq}d5agBB0oZTK<55X
z{{f=C49#3fEqPtO`OFIM$~>{Nbkr?8e<Dk+vtcto)z#!)<CBNK;_r9Fv)=ujAm~4;
z6oDs0_r5-bF)NuEov$Q~Kw=;)w4vhVr<rw3i^H6f`L_=TvXLzoOq|B1!bLRqwU1lD
zzlx9918X4H<p*-|M+e9WHupTwN(h>U%6Hy6HJ%kuFxf at 7ewe8nQws|2_V0~5$?Y-0
zqA=00#bc54xH)u(joi1A(#XtWs*~$8 at w_k_Q4&W>9a*qoqscjugVV*ANV$!!sA*Mr
ziOsNZUtL!pq+t^^;G*TL&-Qc>J{pE0l(QN7v<fLo&xfYJU{>)D_|Du9yfr!AWM5c_
z*X{=Sw#e0B#e at L_Q6n-r?RH2h94p_8cXom~Ihs7)HKLnMM{oH&vVG1xH7SJ2z;}s8
z<Wv(pI8Vy;RddhQ&^-SYw4}Xq9SQgetuoD{02g_d&=Si^SPd>)P5V4<FnUUzZKjd4
z<4;qA0?w8HmHE48Z*wXJk?xs&uWX at jkAzT`H9b=4R+f5^&A4Z>YU-~Z5<;f7gFp^x
z2A$IIb|IxRu_Eel*xl*t-hg!!EObou8l{Bjm_bc?*Dsb273voS(aYIDKs4HGn$*go
z<!FJrTBGUrq8QEf4HDMk{!oZnXim0G_$FeXOy<g~<LMyK6Ih)VX2c%yaENr+E|)&8
zLoL+l?i3=9enbXA^0-equUs=b7T+{REyi%)3Zi(RWa70UCT7N06_O~twoJDaZvqJg
z7Kj*smpt?_!{XB9*zsOg1r4n&4rU0~1n&`eisD(m8i at ou|4rHqaOY&q+j=>Cv*Q+q
z8{|q`NC2N2PS`{eoQ3i*q`=F~*jgj5P at PLG{uxS|bHuABkXueCp;NVyMs2j>?jE;U
z8FJpTUHRb2H4;*+%VDfd{7p=ZZ<`sV{Dl^^aesX5JP-rz^)%z6Esx9bS5?caQPpnu
z3C$=z(IgYrm^&a|tO}>49FoAU#U3W)o&?AiR4Rr*pyNJ6v}?hd*5Wb7bg-Z?^aJOM
zlUKOFsYu%Nmn!O-hW%BuAjHmf<0%z<0rC(?=U#1~E)&a&&QPe74UTr$0axn-ccB<D
z5`K+I(CU(l2N%avvF_w!XbwqHWkj at TgyU?--T_uarNXc6bmnx146kDx8rrw5Y7sK(
z_G$klY1M_ at utN>D2V){$ro~dgVn5`U<B(yS>NN5nRx+)ZwTNxTW)aC~v27{INjEES
zekR=Wackt2Az^TCDqkX<hnJv<1PaLLx`kD*BqL^1qNMU8(&gmS_F2-Rj=*biGx(O^
zU$(81nI-)lKP#<p5~rVq!XeydbhAfHo;$sb&M^P5${u!96efr3v-6v%9FVdPmm+fk
z(>3s`MMBu<a=~0b<i2FjbTg#TB0$JsF{_k=q{bg!NhN|^-dZBT-zvzbHcF5f3SGI(
zC34Vy7!DTGKj&a%ryPEM*s|{*V#m1{Asp~T#&kDU7*6EfyaKI at K(Y;+A<!Wbj7v#f
z95$m$-A3RJ$8}D!ZEw3c*bdCRtT(ZxT*Vls6ep9r6XhbMAgFADkJQ at dP$Y97VENd+
zgAk7!^=%(PY;JHvdXZS4(he*ULw;kCjK9aUp6drW7awdEGB22z>WY~wkoHsx3_nIS
z^*w$t0oaD7-hvHPycoLLu9iP-HEFkD#YY1H>hkXz*U)yt`cXcZbiw&b!sN%%NX0R2
zIi_ZK!8+CoMx-ba%|7WGcOrXJ7XitNALEn_0%V*T1zWz&I-7~iIDNUPxLTr+R2_0{
zhGoheV}&Q@@s4j3_2(Ix9$dRV)oCFStdovW#xUu2l>5l7xD9Au3SH&f*T8*=IOFPw
zu}PGT at 3wkQ!1{<X5R2M^Qfr@?Nr~%)U&-XDRb#bIWPeB)ps)R!sGlhIap60BMlnK6
zV}dE0p$F$SZ&r7H=UT-}O5F<L$Xo5eht5c45`t%$PvnQnr+3zq7Lhli`sXS0SPu65
zk+15TB%ldN&$GxK#BccOHr+OF!`9TYAG%MrTY|>5-j@?Bvp{?&T27AHQhZN3%QEXy
zSh8lx`80%N!}_er)letP-9wsqL;=O_G(Ck9&u1LgBdg0I3~{JHl at jMgfnE`NJtti8
zg_6Yp>(WW?D;&={RHn=D*(x(DTHmJ1sqdQBYEKvG!%)-0Wk}cdzP28+>}<4i9=?>g
zN?C3fCgWhPeS`M#FyIBbbn3%Ht6I~0k4;oT<I?Dou_U|Z=pT>H-vkF}JY=a^3O=iU
zW5CuaNRtPxjT!f)KxYgXGBCwW+bpGHAkjg&g_54*#&yq=<F!^f4|HSMRY>7avEpFh
zO<g9Mk(@Op1Kq#eD6c$b$iFVHK&%};DYK!to7ksPN#Bg-=^*0rQGR67ZIZ>f^*kV5
z`|#WeL3SVYs`+ at 7YGtR~7^7~=oswDI34^y*AP<XW!#vs(&{tESjeWT7w%%_7{8(Vy
z$jXmQF<f~mZ}ncGr}6!^VIk*Kq;>$m=XSoAqi*@C3{!fjoxWw6fS_8Nv8&+(6cOC_
ztA48(@gZ_9UPgMcOpm~^vd16>Vt at zF>7-BOk%%i-ux;T3R9$m~^}O*)9_|;<X1)RL
zNl0ovcS41HE4w*5nHyZ`AFkiG?D${=OnHbJf}XTnpm>Ld#j5P*jNsy%pi#jjCce4x
zxyuEvo%%nwS)EUcH8qAm42l0}p$YFBW#-*mXeNZu4U#*^kK~!A)J`ZJ^9RkJnkRs%
zS-*u>$22)PlwBJk8L4@=AN^gVtg;EZUhd;UbVBN$;>1t3bt~_)#Y=<yt3^~B2+Ja#
zmiIx=%)C6!qi1NXE<qF at lUI!y=?cYw+wer4#NmSZ>}vp7dRu{f>^3D|Rqn|=dCo_V
zRa{|2A$t=Q<gl+ at KTkc^9{t1Le$ffm7he(*zdYa5zQ#v)y*-Y-J<Yzo9+ECJUSDqp
zf9;N+^nc;)I!@ik;o{Bi;MJcxY+}np`RZBCRww%QbYQ)NX9Tb-$iCj}A3AW!j+xX{
zvWE1+2jl!YPQ03gl^J<RNz3{`7~EQH991wAFG$AX!SwkWp2$JS+lweTui5Z)=pMCF
z*Z|_N)z#=;gA23 at 5~{-eDQoouhvkOqi#S|ckXw#&%~RT*xASmhin+*q1`lGx+U<MX
zk$eJ&Lu@%Wer*GhXkJoqO<H!*U_-SY=uIat2d=%vY89)Rm;rxxnb1A%iE_F~I|(hS
zLF0z@&V4G37;f>Krz6v4HKqwNfGE)Ieh7j$C)IkZd91H<66uM#EW at PbI&*L+;Y0l(
z*TybQXKw|b1AjSz%K#t4-J>B5c{z~@Cri_{;(5-h(rqZ=_q>4CAYI%KHfqZzYZ$cc
zHgsHlGuS+i%7a=Ta+ug=@mYDY?|AB{y{$f!u#v3v{n)C?!yldJ%4uXfKB?>wl3vWk
zwjPbT1mY0|=gJRbehB#`e~(d%)giIuy>a2(y2sMPtJRxdCTXq@@ZBT`bK6sh*c3Bs
zfGm-r04<G*C<;y<7g!Q|@I!<o(~MkOMSv+_iUK7BQ#1gEGv1$j&!6a5g>crbkCUuW
z5}!Acwdk0+hYr!6>^s?Wa;MdOc$*H5OU24l>5wqe)+oY7 at muU*O{4+<s&JeW*WU7?
ze1Wa^rGox34N_boWS_7Se2ffdZc+qxOcT364-C at qZiHuaXR@Fd)Or{r*j_+J`6^GB
zt6-Dv^zf?r3nZtDU~S~q`Kjj at cu226rfnj3ycSvP5y|L7o}AC#K8DWr!_00OA;k`?
zLVQVJ80Jvum4rXpdC(mBI6sn+?=3TYPAV?=L7$_|xYX$hb58G`ls9P8MmW{z?H0%)
zkVMLtqq>lcjA7(QA`T+ujv6gL*mG<+5@%f at SY(ZJZds1oc?1 at y2zdn^dR+|qL7MF!
zd1u^=(>{HwudW_x?(%EYHI}f=U82Iwo!-BngFHXw-5=sPZv|AJyqVLlel!<Cht4)5
zAGPgx8(`B86w$UkSPywwyp;*_ at li_!-7z#>$M>%vYbZW2s)8nG=oCNp))UK*i;}?1
zgX at hP_HOA?{4Vh#g0^=~p>X|zSeGYhR=S0{#-k9RIJG9yu($$~>pMn4S|2E{jy{Nn
zO!4rr0rT_q`4vzvN`=)Qc$5%Z30 at 0#S~Bu|tfT7a*7ug!RW4XDF8k0X!%U^gV(L9{
zHEzAYil`LdrWtqx4}slebx08p;gLa(sY)fu!_OUAw8YmFNZ4I#ooTR(cw?UCRSvcA
z84k4^{58mFOs<?Rm5Hbiih&;ceoPz7q-OyShV1~AE!RQt`_p5ABZ`xbuBGo1erf at J
zw($i=>boC!^Z9(V%COL5hxM)=M`1g@$%Vkd%`AKf%O{~lC7;54S?w)B&$X#5ark<Y
zjlw6wEu<ab&4Xw at MlIA6El-uR)P)j3D{8iwL{`>8#ZTnA8y`0OatFQYFLWs$GD@(i
zfypbWvJX8s5 at d3+NeVxZcg!BEo8s3*2;<6^-E5t=VEH3JLAZ27<)~S2P{7%fj%wy*
z%I_t9;PK;;xmhWnm3DWD47dSi1vD!+6&S?>eJQxm&5l`B5au0lqJ`vI%Eumz7v}{x
zg2bc;fb|llkS(fH(m^U0axDDyp*Ub!5#@!6itcQ)e2|uS+Y}<=;zB=g^W83x2hulg
zQ;1Q5hT4$g=wX9VzF$>EEdb(IWSKg1F5T~0{6K8ja2M9dMQ#G2AaFg2d#8HOwe
z-B>PX^!*`(ISU;W-tHJXplo)YPqKO%MbrjrG5Qjabz!}k1V$d^!1VD>*qwphq5c=n
zM@}{glII-0coqTF at ufyo&S{8v`LWY4VGJcwDHNPv1sBwAfstY;ScXnpdXLe<SDYB(
z6=>LmrMk!y@^>}+y8<cX+0hiiyOn(BW*9Xh{1mKBCMwe$rr_8JWaG*UW at NQPfy<1o
zr#?r$B8al8c_maAKca**U~n5)!z;8?$S4ufEmsvi;zS3zKFHt<rc2|wos=2)F~26$
zLmy&66v}rI#Of~TXd+6(MD<idB*p|mqmc#pI?z$)*^3^Ho{>}4R7$EFTg&!W7!B{$
zA4s8CQwl9~lqfla7Zz8BfKeLmU})Y}?!yTYFSh3S%-|PB#O^;s8t^5yl9;?F9b24N
z6<t$|C4*Y5kCv{9H6x)(JAOa<Q-+0EY7VUZT^3U;wYR8QZ-TlAI|1U7Zj5`CNFCH%
zmV=-ci-;3k-}hiR!|||sH=3~8s({9)9Oz`DHBm#DU53Jana~ttQ73fpnr7C*uzKT=
zvtG4Z$kJjd7J>ZO7X}uht^+CLPIwEQ*b(@mqVjO(PYZJ*6*TY@$jQ!9DSV<Vfi<Z~
zR}f38=@7~+hs at hlqE`74rI%Gl<_4nha>#u44wPu~Y9DTvD??x~jS*E)GP|Fh4V;CL
z`CydG%fpJTreq+<YHclpAZd$=q!tYTL(Tx|UM!mtH}%l`l+NW$6wDwu?64F8na#L|
zKyX#~)EG69Lk!h+!QC)d>~nIKucA8vBp0qK^TkzFuuR at K^D?<ZYd@|dnqwqfAV%%&
z^+LJIL}qgWiWo<J*}Zp|uZH%wNkBm%#4`Bg{zIT9ntIol1inE_&Dd)y at B-zwAqmcK
z7z!ik7aCm%H<e*X7y}?Iy`=ezpjL~BSz<eJw0vO=DV;Av{L^DI$$>OVk>IV1YAdq)
zr+#<4Sq*O5 at 2^5FRl{6WWz~Ue^sfOY^dSe37!m!N;@8FX^6BXKPR?*lhlS9}Gff!R
z?RMGC&6qhP2O22h%QOX*?eh`HSv;(aT0U2U-RuH81H5ylMWO03lC6T*;UU6dDd?i(
z at 94_U=t8XXL#8`QD~cNu?zb=u#wU0v4E->c3A(Hji8gy%^GQAwK)V=>v`KwoY5W-2
z{4?u|;N3=vp$@bhV^eLlqfG(aL%uv#KXRm0lp;z#ADzs#gvM7A0~!*8pRp1Y&HTz6
zFIgeQpUfEV3wvKQpK($NR0jxDRlRG=e+ezuyd-E<ha8X#!SHyCHq#H~50y3NmzlhF
z%0lWa$+rW>Q_@`K>t9>1a=2EWR-;saU&)#&#L}@5c$d_-1*a}c(c)BMrwW^#@7~D=
z?9oB at 4F?l6BzUVX6Ijw=fkudNW(u?;U~|C at -4L51u#sd<Q+`=hA8z{2Xj~()-`~Xc
z-0!S6^r)TWv|mI9f)GHb8}kJgrGs6fmrX*4^`NjB-9UleML(rC^;>fQfJUD^(%2XG
z^4VE6{HNd9E1H4TP$NmvqikR(hnQM-o0${#MKxn>NQ$8-&msFf{eg5TmdGUrqi$G=
zi)gcmbmy|rftHSPNytEvg!XtvJTk_$&p6iz!iJvws*G%}I|IqJE(+X at EYK@^xV5nH
zBa&<0&q_t~X?oG|c1;Qx^aOkAmI<DYp>o<*=26#_No5rbBNYs~jiJj%>=?R)xn1Jq
z(Tf*>@N#9KwQPWk0CX4K^9JMYP<^03<#vPyDpfDirAw at aI>u}vRU?jxmbIlk<xVQ)
zj)<#rO;UsuEL3!_=_!J!Q*TKp=BsSsaj*8YzR#W%^r1g;5J`V)=JUn(hce8Y&SjB>
z<4_Huu%&mGRFvX%`F9Xn2GYK$YJMcG_eFWyVMj at Vw8f!PRc)1_6~*AWW=6}RaRL}M
zZSZkb>_r8yk(JFTyfq;d7CHy-Vw3n|4|>W>2rDYrdDgk;Wh&3P<qGzT%xV%FUFOSa
zCHqIX2wQeV^U1~fT_LU+&U)7uY|F6MaA at H+Rc;?E!!|ZmwMqn7li+F_Kg<Yys#zeP
z5wu<$i6u~>=)j*GMh`8UgCdQ~?+llblF!pIG3TYMlDH%*Us*H=D`+Iq`H))$eXnl&
zE7woE9ncCG4zR);+!Wm`8eiVZJ)eZQoaH1a$xZj)GG{UMN7f<yWc?+KzU!jFGDWI=
z+-svrz2I8jZ;UHwQ`G53Uj!a&B-C4#vRCpuGdoo&hA2lk{#%Yav#L at D!15(qg~vI+
zjP|ne<Cs`sR%hxH7RndOYnh|&@Q_A*Q6={<S0DP**N?pcmB$w<C-OrBAW&gq)I?&K
zPj^-ttzsv#EN-eidb-V6=%K7D^}n)FywY)54VMqnpv%800zE at EGD91j&{E+>L$D*u
zj8VHv#xVj#f*b_da8;*gL(h)aRK>M}2?kU(WVgdiY!aptP)35pKG{(2Y`^7?KE^n%
z5 at do<SLT(uE-!lbV)LS}`lz~#A={L2rLnm{pt{qyDkSE0L!9|)!uBTaYo6Ps_05JY
z`QZ`0G*z;K7=saJ(OG<Y(Ys;@OBp{F>NJG<5pE|H7c_e5o|p!T$5w?pO(LX7c<5+q
z^kUZSNlq7afiBPDiF-e-#dT8 at 0yy<zF3hG`<O}3UNxV!nRV&&+Z#h<&bq?sMKDob>
zx(O5{BAaOnRh at nodE#(W{*=-B(B?+;Tt$`pd;-xcfvR9>uFo-5YnOK!$X04gGMJCY
zM7SPvy|yld?uK9SD|Av9*~L?NT2=I-IEu4yxVem~^~od)hlPssEWdm{8P4z7{*6Bo
z!HfhihR)cb)2av5og%klpBf{iZtlM~BFdc|OmLQk4H?AD^@pF>HX>3fPlvEIFl%L`
zP_1#CBjx?r)BQB`Xc5*~enfeVRAx@)^fNSNmXU<<wW!xC=A_J!rA*3;$%<-ZJiQXM
zB6h$w+F+vrd3fQ{kMp3xC-kOx6x3h5ofbQ-sz;XzF3hF!_lY^Dwn6-3?A&NI at EF^i
zC@<AzYo<N62bI|t(kr`N1?sZVt8B;}ztoQ?z;u1lCB5*K>v`|g=8g)FdX_jeq1wKD
zz9jqFMe02s at i8vx3(99w{w}xM4S3qIpHXmXmY10 at Xe&3n$cXPzR_2Bzi2*`RD#&?>
zOlrdsF^%$)h$kx9J}FSqSHpyPfyDiIc`6#%LOf2j+hTDw*$EsX9%3OofPj+_1za&$
z^Ar^hrU(ohp;cUmxSB3XBV_p_;tgcY?J0mrFxgO;*))uS7E8Kpg at kx(B}1hh9P^Js
zoM2Y~()G4$2-FR7wuM%>Ev6<qMR7yWz>h9K!w)=Tl0fABk-By+5_EEP at v-5$-W121
z`qOr`0E{FQT705zlieF+m&;rU;>#mqzBv}^vW6<s2{_FJ6!1V4zW^nekUjR=VUemB
zzQGCxW8;b|^YIaJSo1RI9%g;+Qwe>OOfRQsVI97S9h})frxfh)3ipttB-80L_Al at i
z8?Vh_!XLAZ6s-cxI5;sYlSYE)NkpWL0=M at 75c0hfUT#(UAHcdSBGLf0n7uN{bS(OO
zs)}FNzsm;c-ClU6QBKx+R4iuHiHXk>oATEqB6nm7dGZbCT at m_am%YN!4((*|^vhbG
zU{8mA!-OGyA<4x8&LI$98^;IuIR;4Zs5d8J-0W+b1eQY{b|Rj0P_46iz27<J5g|{(
zhzpFaeJo(BA-TCj0cx?pAZ-VCU^L88f#vcDQph8Znl{*#X;zwve+}Q)@<xT&K6mG)
z4(W3}5aaESbPvH>@9zZ{N6t<PwtHE_8W6;k<*Nh*PUlWo%ll|tXH0l4Irww)x*zZW
z|D-8Yc-1~8NZ9(RMK;YN?Ou;INq)g1X14cTji$9x%@TC1YQRy5m9b(@q!x<)niqX5
z2SiPx2OTNqWpk`3sYue-_avA+f(Y)TJJPHDVUIiU8s9#@iwqO4h!M=)4c0^+9&QO{
zpbOZ_Z9To at j6@gXp}`xJ6rSAxR;DdTX)CH3Yn^(=s+B)+uu$I7aOtKB@%3cPMX;nM
z1wZP|R%r02$+X%;-qXO?P>6}6B)Q^+uIy{DV5wo>)C6ra81D&|n_s4dIO!Zy8|GDd
ztgky0U5gIM&$mhw&wVGp3Mkl%Fxra{ic;$3(JsSy$S}i*Pi at jNHC?tSt&@2r%zO~l
zSF6NEYFc}+e!qjs3lUw`(K_Z=+~1g87m*>IYLF67LBE1JBSW0Up<7}JOmibfFd?;!
z4VLi<Fe?g#RwNc;G5ru20S{}+T;1?lbdR*tEHb?Tv&K9+T;S{F>&i>(rlcY`-BxxR
z6T#XXt}EjTa_v&r*=XL%*HvDY)*#z=j|jvk-Ygw&pc!p`SP%6G^E!d!Cj`t5yxKo7
z==CB+zkFZs+9xW8{i5Ps38#6im7M37)>siP|NRHT!-E2A1I_mV{@~LQ at E2%~o84<Q
zmACV^HQsNHqs@&*;*38W294JdDh~!L2skzLXjcRwjr7Z*bdmP8KjqFldrl_|)fg%F
z(yXNqoFCsfwyW${KJL+FWh0rs*~};LukH6>xTh*ZZVmu*cnQNVFHV+supV~iJDVd(
z2!HOBg)bHx*@KI6Q0_~!zN3C*iaJ_<QQ|GJFA{qE`R&et&*+gYAo!9U at AZpoX#rJN
zbQ!LWx!IIJ{m#>u#vpHe1F*98jSU-BsF!f{EgsINX;j20yZ87G7Hvg4H-$|35#nL&
zzc!cWc~`>1J?v{PyPHMU6SL(L(f5B|g^BS+^Hz1XS7|LN)IgCFq<u4_`*Hbe{SEw`
z(84+~ECMXh#@Nxx0cc>204muUS|Bj80vG_G0uK+pu&s>~(8kFTz{n1&Q=(UKvjfsA
z7?}S4asV{|n10JD(JR?H88`s}4D_-<V{-!`TUTucP(2$X6M&tQNta&O!Pf5os8j+v
z+B!QJ0YS>yek<YQ`=<lH_y2al%GN>I&cF!x+Z#tV0P`P%e0=m`AlE>3Dk at 9>X68Rm
zsHm_3SQ!6OMX%ssYorWx(xz7s5u;ZDx;p97i at G|AEC2QrbeOm at BY^qOIq`x1{%QDk
z&0mIPfHtO1W&k!87SKO(&8?h(4)kJHpkqaVMxZGJ`1t-2GFXYw2=&#df?*V|%j)5w
zel2WN3cBcxnnmuU4jXfeg(M#D9OCjYYg`M-BZu#%XI+nP_)P^z=~fMMq8lWnB&2c~
zk5|Rt#R%3X9)6`G{qo9-SRt~J!5;_>+m7y<;fSnNDg-8&FrK+K|Gb*sFSD;FSF6IY
z`D~O?=H-Z1{<1H2NVW9lfJWw)psra3t3em0bp9rQCo9KIYBck{=}nn`K^e=q6>$rZ
z7a}*5X<efj(I`f=C2J18oII<kY<t-r_RF`9fq3CaZkkM6bJa}Drw--&DJm at VX*qpA
zhA62>C_4|dkKbMj$Tm1RROQtWXbQ93mu~uN{l1>R!58(s*ej`SAYLJ?#$xw<iSSPw
zs>4MZU6_!Jyd9A%^B;*<J at n3yjFKdc_uapyD!y~rAs7ptBb1{N7Pg2MOc8-uf-6$H
z0vlXW<6WrdaDL{#DjJ*#VMTmVEpcsaqneb*0NTf02NtiC0>*o2fhsx0Xpc1~hf|Q8
z61<R<t$S8(v$HLhCxjCs7$Zezg|eYtdFbh4b3~h^5Yc|Gvixj-_L&FQ;OR158rScZ
zS-PxP$-(H{IAf8w1?Vk;DS{dt5mA{ot)11Tsp%>)r}}<&#cggu$=Fa?>KDWeHC4a#
zCJ5;GHNL^o1Zh-xoOUX+W+kk%iphdiQQna_Z12UN>?eieu8&z(U)Q$sQjV(Ry`<{6
z3bd4fJzR7XlbArm4-m at nE+vt-b-U6I2hIC1KvTyYtOHC0j;dcr^x6)0sxgm~m at M3L
z#jd}=2CTXSCCBPqoE}3XE&IBE5E=@TCr$dCmp59M3}*f%4lGqXfQSpaMT!VZ32z2<
zqi at P(98*aOj^cBeF<%$;`8ztQ$3)h0WOdYazMISL^846;C at 2QDX$yM-S)`B06sB|M
z<a#!bA2`A-WLY?CpR<Y{yAT*)iYbN{(c5v`g at PqJ>KG;r=(mZoun_i9M0=7CSn5MD
za5WQ?qU&y0DH(0id#&=fhfOaPuk7nt1Y8QAH};lnd3diOPrfxI+A4QYNDmTPzgtc_
zV$aJG79nJpr1E_a8<kQI=kjjYGUSXA;aE|*xx&#WlU85?zVI`j{qskZ{*adUk(z~A
zBpoJ>{bl8g?NAqqb+e=%@->?}K8d24`?}wE!1%5MTJ?xk(OpxaL!pZLRVttY;5DGk
z*_KT%J`mz#ysF`j=hh71TkU%&x!eRwf$!;5Bsx|{Rer-0Hi)c#)No at BbMlWiGfc6X
zCGtyo=hvk at KLPu!>v$r+K-)g`-F183z2#~jFCF7UoS~z29uhKXN`<4>5E}2i?GyeZ
zluu$`<f4aNAJ6;*Z#3AI-D#}&v<JstA3zSo$hnT6p at K^YnYz477t4W=W#PfOjV|+G
zA0?-4LyI1SD-xgdO;7|(%r>uGzw=YmZPiSAN7LQt3+3no#uutk54-Duaidp-4c1E~
z>xnM8o)G8umVO(e=&z?zm@{g$J%lpHAv&Bcggn47{Utntm#mnYth{<9fGld$*`l?x
z3|-rFpgTcYY!nh|ml-NMGI`t5$38&RXYra<{#kQOa-W%VvghuRlU8S4K%l13t9K at V
z02a#FdpHqY0GVp1n<nlj0F at _CiemuZ(ab7f<)XAI)Jex4ooJDT{9KPOCi>Qjd>kh7
z{G#TA_7=pskp8eyj7vA<I8Q5}SP3?K4-pnjg{Tt|?BJ<qSyMCER)vw1VmK_tihpqu
zCE$n@`5iJCqig^6G1WyT`<*d&_&x%XE)tdCjmsTj_o3Kpp#VMSly9DOFDJRE6wF~p
z at 3O!6WbC^kZZ5ibVmNJAmL6&BE(k3BY8_ik_f$&&SY+p+VE?o%QlpJmxLzs=<{t47
zp1tQ7%%UP&t2w9i7bVnrfBBy}(s at c8huS%7ioa?EebQoPZu<yso0Y!9%?dpT%xg_X
zX|r5$Y%}ga);2d0+K0o+d|iZ<a*o{C=NxjsNVQZKlU=slZd2tLd;O`5?qfsr_MruM
zCr|Vy)O`~umd!sx(^@){{T2<ETQlBL<LS>EX5fYaxAwxP)bxc5xsvi8+=P15{#;%^
z-(*KtVwUzXJ+pIt^S#ykfV^<&bqB^%=p87+IWP=jYRe4yfa$N#!Q0<CsJ$@8V~q_B
z&Yt=7ndSBH&7e|e^ekHDZe)f at p)yEjv4fj`@!V8fWilp2=sCNQ7sn at BK<~Si5PaY+
zM7|PhXMl_wDug)$ZZR^x_#nKsoG2;QEptYX{G_8~^E5=@Ro>rOVZKU5Tw)TUQb{*S
zD0AqpVgwh1Z6Lhqe$|$WvnJp at z|cI7Qb*52&S4^arlkLdK(GgXHnRpTm|sU`?aOYO
zArq*FG=Iqa^~W%PdzmWl%I>g>-l52HPq+m)zIh6?ppARNeJBOx2k at -`bz69NVm8*e
z%MP83cv>{-STlpnl*wl}0VD(OH&VX&rY6+1OjX#^3EncJe)3O^BKWuWIfy0r9yND*
z33hkhGy~C4msJj at bYQn}iZvwFvOg9B$=gbew1OV{BB1l1Wg)c9kLm7- at jiR`x5B;s
z%JO#g0b<t&jLz<O^^XvT;A-w?+rP^IY&0XGPUzF(eeiV=B6-(#;d(3c77M?(1Ve0E
z!t!RiO?wF**D&qR4j&_eM at -%+yoXdTYQ2wi$;7=$gdMGjq{|#BQlb at HkKj(ETh2U3
z;KM&oDXW~&pO9gm&>s`1-n(btozQ!a67Cahu-UstA=u0`m$Cm;K50&+{#EoP at -=p`
z(+i;!oL^v$@FXfq&Wdcz4VDSTu8G4UD2_E*v{1eIBGf2WO7Ye8SD?lsxjqlMHn+no
z$DByig?patR+N5@{*9fA>hj>0sujs{-Mh?eN>%waCdFH*q&1Qbe;uC(L at 5W+vgE7<
zEl4M1CA?yW);kU19sN*qibcMBtVP5=6|tBp`&$nD7WsN&9xcB at -=MqZIdL{*oGDer
zIR^)Y*~9*RFyZtxg;yN&NOo%e970SzbsKE$V+EkLB|)mkcf7;M9Om^Q+=X_#ExP0f
zp at RK{mCLE2hJ`)DhD#UJX>t=y4zOr at LVATxtmFM=Chz&%S<Tj#AP&^Q2K;;NLpmNC
zZay63miK9v#W=kxg4m_zI0cSpp4yLOq<3axUmksMICg0v5S$>&kU|i<ojJVHLVqe~
zk(Q`D!JT6(!hPJwVCUnoyL!*PRUE)+o3rtjBUql+?IqH5NqXP+o at GV~W at HT2>4`g1
z09 at hW)tc_^tcM3{w3ZetOt}oMmzx+eU3B=|I2b3ZK%4Uz^qz|QNw)FA#QpTF3go`f
zM@`tmq4ON9QEHj=<!>0r%y9_x*;dPMa(79vv1u|?X)F*Jx2XZ#??Y5fi^X5 at r;*K4
zbH-mrnZwCVr*@RcKe?;mP6Zx&54^xa#~DoQ)OakL$BxtHJ!lkubv$X{8VFC`^a~XK
zHIvU9qX1nVK^4^#7H{ZQ7(v4t?b??!S<Bd;<vX2g>_mYh%2Wl9Km^bbwvmQ&Nm>x;
zbYey>Gr`b6qrs_4p7PVBfK<xMrhNFy@}@siZrx%{8u~iQ<EKg at yf)$$|A}jwD^53Q
zVWaFG4jNvvAG!M-KzCffqK?1iYmHZ{heQ}jyvn&h#N=la2Ih4yx^{SQ#9Sct=E^8{
z`Fa9ZEoTxR)>`=25~1Yd{uDocnpKjY#{Fc-whjwP`Utixd(?0p*8VZbGrpc3pDujQ
zh!s<pXzIBtfF~cKBZ?=11{JNl#Om~2P*{t|Jsz3uY&YiX=@nGj!D2bC*OoMjgi!!(
z&UBc9;kg==9%@(ZB7dPN at BAz3VI3i%Us-n}&BPU%S8)5E>$Z2L#C<=zM4{qp`o+XS
zb|{ScRC8caEgi9JNBrb$`k9CR!ZGIx)cPQ5**4kwgcHJ{*q<Wgh!sfPU0aIOSMPx7
zu~D+VnrmUX2ZS4t;uAB-ELs`m)Ariz`XNHklLNbRHOw1>clvWOsF}&<i@`fs{1$8{
zAQl`d-ae)*oMBihna5aED7nc_5wxOIYTHIv94j}hjApp)9=f+dZ@)t(ZA8b%wK}w%
zNP)<>2umzKhmPa#E$`UF56LB6eV!;X-Q^P?(Gi47Ld{xy*cR?0f~l$3#VokRtZge_
zi~KJ3Ecsya5+D{I8%m^S=GGb4ZPlD34d9M;s|517KPI<njh)y8=;HI_(0Mx$`*JHV
z%!5x0Y8;Ah5KK{T>3r~6=#VN&@r{qIoLNDpgMEzQ@>C}1UtAk~S%DmTT#7Fo&kKx4
zNF}<5(npA<i-=emUX-2(n$hf^8ltaRP at kXeqpc(~&}l(zQjRlBwNk^yR6c=86<469
zE11q#1*f1=0y9OS<@_<v79*S|iUeXjtI?<i<>BVnn at X4|5zO93EufO&bx>%&D{Y+g
zXZ at m>4x-Gb(Mmw@!11CRt}eB=u{0<cU_?;ld349y$K^AZ|M~5>!d>mDYaD;}TCL3!
z7zoYYt1^$$HJb9Nv?5Lp;zwfB&DSB@>Tx*Kg0krj1UF_oWxYmaL_Y1rs<OVhmfBvm
zWx1M2Xt4q~V8PyehVQ^bAh0P3C<;cMu<v&JyoQcr at V$u4p)Vi;l6-u|O#!A}>Lqkm
zu|ZU-sNTg#dv>PMi*;t6p`kJzR#R#MII`Yo<l+kf7Usi*4>_ZqO4Vhc&56$B56AfF
zRjOvxN6Cmd+pHx=Vor$bqPhIw4acdnGvU*%wAo>RHP3weF+M22SaZ(8k1LP3sz*P-
zVBpY+7jD|fq7vBo^Gg(AS6E-IW#K_04Qa;|rB#iCO_>rgcs&Ly$%iKG%Z8DSzS5D^
z6Rsh-b5sIui+pN at wF!02j)K|2W<>=gdNFQzoFmOSJE<NL6fp%vq%K8R2a+scJU=7#
z(0=(207l2Xk4~|c0W0mnGJ_^|SBQ|D*zbuWrocAU!@<MQh=MF$K;hJhx;s at eL#M2k
zB7>qzu%Zu%#gdYEUw(F4-El?|-4*l8k&Xnd*O(R#b!rvqZP7Ok2}P>}x%eX_znx)*
zRTE%hE(5!>!Lm$!(HIM&+Wc7h(5k`WrJ0KAK_eN(7}xF9o$r@!l}02IgC6?+?0S^m
z4m5i&9gOOM`!WLpnw1vhpBODM3L%6o5CyiRJ8Vt1nG5^x^+t{3YjX7b``cNOD2!uK
zq#~6(Nb4MMx?+|OlW?cSy_z83#j$fieJ1be+A4o+uF2~z<`z3>i!}&p?#$(q?{God
zv)Gv3G&R`BHjO}N;#XRP&mz7v at eF_hJk9RvFO4gb;n)4ptBWvNp7Kf+M)pXp98XV@
zEml9UtL|r1x!t7LS^v<O5Yu$7DHo5w*=5Qpi*t4LxZru6)G9=;^m=fxd!=MZIP!VZ
z1=a$8CUoeIeL%4IuI~`h!V`zVFUP?s={Wn9)5o)oSst at HNr?^B4u(61K?Bv!Lp@^|
z+e>{cr|%gfQ|1X*=4 at an={d!=!2BogjKo0f62%W+QhO7w!OyX*O_o%7;kA-d?`FPc
zP%wVm6!_xEDZTqMRq`uor4}KuYd1aEED`R)soR}aK1`w1KluhZ{%Ud|u)eO0iTx?I
zv0-l1$q$hM3H=q$2|;4$z-V*&9*?aW%w}?7JS30{*<y+oeXS|BGwBm(Lc;DsCtSZ?
z!IP2hOM)K0byJZH<!e}khw4-(`=(mDJc$wU&vYu{bK^_bMwUQ#+Gw}IXoQdY!!mDJ
zBxsPhI9Tu<IazU`D(;+%(#eM1i6iM^hVRXiUoXFD(K&rmO#h)>dUGDG=3E~nrtHvt
z!7rZ!&%ctME~Bz<x*980{#}e3nuK>y9IiM>)Np(C>)Z^5uIZ|xMdUcIo1`PX{#Z?U
zD0!*Sey%cwL6wY&%~&Db^lXid_2G)m(mQSInngGnhUpp8!v5h9ho~R>@K~s&@Fw3O
z^vY7&@h-G39_^h$FUaBPrp5)^wL)xYk!e8iP3^&G$Pb*vz8}f<DNnI8NW8A2b+s?j
zx%hrRdy`rHcvKYc!gQxJz_H`(DvcU%5okqXb`!eKSSTH!b4)wN*SqOn>WLPYLcYZU
z!@E4pC6rCL^}2g at eztqIZSsQxH9!qOejA2IXE&#T;mRe~P~i3S!_IHKEq%h|pau(`
z#hWJ;9{l||3K(xHPoE(%Mq- at Oi*haZWwN`be%#tc3I6Pumc0YVK-uNu*I(PgGg{$D
zk>8dB6dZ7gehM$s;H*>;(Wajx>3?uACU7l7iH`ORk&F{_n0f~#D}2>ssy#^y{2`wp
zX!|N%BT20NwT-E1?)q0|U3sXyHsR&+nECTZr7zhvX-;pNQ$MfYDOu#b1&{N&WMnIk
zd at tQQRJnDOat&Kc+$@xs#-$)|T(~%=vOTD;z|a36r|<RcTxekn!bbGCR$6u`OSU7;
zH8^7ut>1e`Pt!Z_D4%(DgVhHe(HVNUI$5Qn{oZq+e6hB_dhPR6w(FTAbj_PlMub|;
zg}k^ASclvu%x>d|@Aj4LH%-=ZqJyxx)8;7ix{9<l)nP8IIB8k3qzg$_sHq)x*^Y1H
z;gz4O2QUQO7lItKyg_LyPF?cFupyrie`1%Vb>=w}2a<1a#2pS>%N;)ZdT}aaO*V++
ziZS+=Dd%;>P<kB=O$j{!Rh@^Yvni#S8F;uwR$hXquZm;cg1- at OFL13tBOYG88^j{V
zQZVk6>Z{GOy~n7bzdJZB>G%+H%(tJ1MJ$s`iQFIK7s8r6jGK*b+cyE(WvGnvKJa=n
z4XJVhs6-wuMkei!8XOO`9hC<2Hsx_>1-=j~8rxUk09to5`)TJIdpNSVm>V_0iE~8M
z36>;TAUElO)fiJEY)yfz3wnP~eBFS%S+gGYHh0v*8aZPO$<dbfl$j;zYnpCcPco|Y
z^V_EtMi}uCjT(Mjg#J#1K4i61Ei?I7)A4jx=8H=BXPRj0)I=Cftb at xLugdZcg9)<P
z)if|-Q6?rYy?h8t*oTl0HIh9fj8~Ywdr5uImyURD18wwk8FlTU%8uE!jp&tlF at euE
zory1_Yb{X=aYa-G1{*Fd{+<QC!p|S47!=k)%V3kDHXN9I>Z)ZMlZ;C20%l;Kkn{16
z-|KiMU<53b=i#0N%kAc$5X<_HNSagfjnY2)MJrR$iZz(@R5#ZfqHriv at VCRcdD3{<
zypz7S)GXVOsHf80wU(YY{JK6Ye(Gszqmj?Tl=3#lWSy@}N6~4P9rboY=<?kv`EwpF
zos%m;eU|*#dy_;G2DTe&nWFsueo94L(ML^bXZ_Ne at _35E_n{pI2|FlAo{C|7TC*_x
zI>#}eBVh*QAZN=*Ir$>oA=7eVLt{@Fu=-eh4 at G)ObU8JJt8jJRD&c?e?5y)dT)nkw
zGty0<Bn+?G?^?VoHU@|b545Jgsp>gPn+pp*cFM<&F$fC}BYoUZ98@!P-<BA155ERP
znVF%6J>h$G^GUgM+2*s5((1h<H~xOGm$80XERHyZ*JgAHVbQjCcbe70-(h#gL^V-E
zzr~N{R9GD;`3$Xpr&Rw!`0>o9I;ZM0HbNMgKX}_Lud^KS3_yP2L6LeV*=47Zn*3nL
zN)KSAr|W?xvLk)1a-SDfLl0ZFQ*CD$8&#*%^=pG+=rex1Di8|I65F&0MvTCaAX*52
zqilbeuMm+ at K6--HmtB9P)34N`0vF;_&h(C-p89<(cK$tGOC$WbRaj`$Ko>U|GWh3T
zuQ*?fk1V-Qs_RM9J`|@=tWa!+QuynPIXT`VsZZcFYV1F0(axC^-K$Iv(B>HPk!I5*
zmcSMo^Hg3OGg7+`S-)?qlP7C8TxQ^s*tF-j13zLVbBB5FCJb@#9JTcn6%C{frLd-T
z7NmLrcX4*qi;)r~U{*zr{&ZXU7_l55upvxTjS;M(W`jKqnJ3}b`*CIa at S_)PGhd_x
z-W)K00f^AIwsf>sIaa2)PHTJ9QE^cKYz{TK*RvAiusAdk<JT{9s9SKWnU7u)qoObu
zqguk5GU37)OdCbncQS{qCiOJFeJQe6LhKjtFl#c15S_q|Be><+`D}}H5sxQ9_JcG+
zW+BR750;oHm at 1SG{{@^^91N-!-3u-cNrk**t~)|L2lpwtTy`^^F at Bx*mo7iEq$HXW
zm<_qb!6ak6&5^}o$y}sw_v(b13BBBI^FBi=Ed=}}>76)o5aj5KsgusdZJBX_YSZ8d
zSma)+hTaK1cDrhQG)+9(Mocs1iv0k~fNo^i1KEIF!KYHoN`HXqSOH)6{+(}WYUDMN
zM&xefTm6Opy!1fHC7pRDng3SMoQ=rZC^gLHydTp8{^~?fR5;q4r8B4A4_<l1<WAja
z;OwR-B!oI&PZ{HEtz0)G;D%-{s->AHv4#UdorVZo at kX2`PJhGhd5)bj^)XOoIj*lr
z)6tFv+p1}G>yVV*7EjVdo|-0Ii}mZ3o>repA^lJv)<_|afJqBs at 8XzpLJR(T45P?8
zl)IMoEhM*_7#Z^Fjs(_Ki%%mb*Nrv-;0N84C%D&Ow<^Q#{@2~ypSOoL`JTbfRgZuE
zmqZiCKk1e~#L{m{hhEeMM6j6Kn9{48+X&h?n*VcO$iNZ!M?yqSL0(*fS{TG0ng6C`
z%pDw^gv|^b04xmjG6w%BGcqyID?1xH{U)qb9GrpwG#~<WG;%PvbFy^+{KZ|Vn;Scs
zIcl>pfCwwl=YMzm%i`a*|7HL0n!kMdk5Yem_#b3-|Bb`q{QneGK?idKD_NC)izYME
zzeSUg?Z1sC6BEd=5~$n$gp-N=cjx}s8#9Qo`>UOml at 0KJ`!M`z`C9`k2h0CO+usuZ
z{VxmK at 7Vln`)&DeKbiix&j9%A_ivwm%l`J36$A{B<iCZP>37p#RN;RCtv~Ff&~M6<
zmW_!6K+DVyvcv#V!pOkLp!=s)&cGT-|0n9SLbg`Mze8_jVCo0}g<aVRXsrfdWagk3
zHn5Wbnwy$A0oWNi=r#UPW at Tih7j!iG&A)Oou+slV^zSk)6C)$Nq?3V_xsjlasnu^f
z_V;)ZGq5(da-;ZTit<m5GKlVVGBTr=vvsgGu=-P_{tqh*4F5tB({C>IKb_+LV0iw=
zbNc7t{10&Y9}Lbv0sbGr>7N}40^GlM+rPT`zbpN%;V(J%e*l;L4<OhWe+S{e-hNyA
z3#7lLe((K;%5T}fH2$vnUE<*21X1Dt((||E-$(t?@$b<3J&^uW-2Zje`qN$ifEF7o
z2wETt{vXg{VEfO|`ae;{@xKo$mj3{%-x2!j<m8;K9YJ{lOuq-V(!Zbo?~1=fl<7tO
z4Uhkxm*5NnJR7~Vxv`@*^B>M1 at YnaBR06>7ajpaar62tH`E#Au{a5mW<G<<rSE|83
z7=KV7viv0~3v at CtHgGcd=ao-GR_0&CKM%hvC at sMf=manXnws12l3%uUkps+)dCApT
zWf^4cgn?$}67CK_Wp_ChBX>*C5^UuBe6T$H2A~`ZAg!x`9j(cqv;s!}X!!Dy2rJ2m
zk?`{&!1B0qxmw#<gKPs_t*vYvxm<b4{~W^wD*vvgCkOlyakAtk*N{~JIN91-S(<}V
zG3Yqy7-<=qX#ge;plgeZt%D_ik&cc0w at wFR6D}nYv43a;?eUVEIXT&J(bK!QxX`&U
z)7d(h(lc^$a?&#}(K9j8f+T1i-E5o;Txo3_NkInxcw(;c&%<mS>HauHXJl(lZ>~Yl
zNXJ0`PfLG;)!fzw at Y}M1p{=tMFS)a`xiJ?9s|kZ4Cle>F3DAI<mW9!Tf!2_d)sU8h
ziG!6BXvo0^WMTT-p54Fs`5z2|8bB7AL6HEJxIn2M#?D4S2VQbPBL`bU11ErpxucW0
zl at -tdz(&Uapa4Bd7~8ryQj+uW(Es-5-~9VKIR94l|I1}izy8-T0yX><Bmk(yB at D{9
z0p*f_KD^|h!OKd^$Uw`)p~A?@#mK?M$VSV+!o|P<3d7%`whk(wUgjlNQ2z7m!b)ds
zWd%?JIyn9gF)Q8gh=D|9K at UzQZvUOu|CJ!f%KpEH!P@#aj1BB$Ww}IbjhwAP_b*A2
ze?Y{*h{=SBfyso{*q9Xr5fc**S_5WQP>c+L|4;i?)Y>)-gx>@C4}K|*A3962Y|CK{
z6t=yM6<XL!VHn%#%tY-e3M>2d`_8GIM#)+XrBJXQf_*;SNvD(jed3apMWI-hmwx!#
zD_0i7h}*5v-Hs at Wu+kL`ZiB_+7;;%G*YwpyI_GVRFKuqI1R>H8$%Tiuwz^t%`|VQO
z-G#1c|15QWe|O<OoR`%)-)ifxzSc<BO|!P%_r{doxp#GQ^`p(($_Vd6*%Z5E)%D|o
zb at sx&4_p<LMvd9IxvqZq{#L(=#+XWL$3&+>CWelu_CqJb+kzq-4sRped2i4rC!>Nq
zg=-4es0fTwp0aBS*Qf}LQl7HweT56;rKdco6u<EQfY6*M9~b)~Y5-C~ql-^V0=b+C
z#bd5IeCq?>1}F>%fB}GdUh9TH)#4?%^k}Uyi~phIfU3efs<1fC6mEN}a2o{S<<|a$
zu^vhVd(!w|s`Y#Z=Bn^JG%tx`RHxGuYH2EC5Kn5}{UD1KXsRPiiKjl5aUo<3o_rvS
z7>{wJ=Ex+SD>$;43fmo7;`-%M2?_FhpeI$7f_ytNnK|<iEaB;~EMiVy=t&7G{?uN?
zSw?>*IFxbbjwRxpCr5f|LZ>r_eHG(JJ}{4Cy at WYqj=0L)xI{by_i%k=!i_2EB~I)i
wi=^vAh?LHW`Y~IR*C3{V)*r*``fr6KLSoG}HaB*cSD5#Fc6Rpp+n4$5A8HbI;{X5v
literal 0
HcmV?d00001
diff --git a/rtemsbsd/ptpd/m4/version.m4 b/rtemsbsd/ptpd/m4/version.m4
new file mode 100644
index 00000000..929a2833
--- /dev/null
+++ b/rtemsbsd/ptpd/m4/version.m4
@@ -0,0 +1,4 @@
+m4_define([PTPD_URL],[http://ptpd.sourceforge.net])
+m4_define([RELEASE_DATE],[October, 2015])
+m4_define([VERSION_NUMBER],[2.3.2])
+
diff --git a/rtemsbsd/ptpd/packagebuild/rpm-rh/README.RH b/rtemsbsd/ptpd/packagebuild/rpm-rh/README.RH
new file mode 100644
index 00000000..ca29ae3c
--- /dev/null
+++ b/rtemsbsd/ptpd/packagebuild/rpm-rh/README.RH
@@ -0,0 +1,44 @@
+====================== Building PTPd RPMs ===================
+
+- The rpmbuild.sh included in this directory should perform
+ a full RPM build for RHEL systems (and CentOS and probably FC).
+ This was used to build the RPMs starting with 2.3.0 RC2.
+ The script was successfully used to build under RHEL 5.5,
+ 5.8, 6.2, 6.5, 7.0 but should build on any RHEL5+ system, CentOS,
+ Fedora and other distributions following the RH package
+ naming (-devel suffixes etc.), using systemd when available,
+ otherwise using system V init scripts. The spec file is easy
+ to modify to suit your distribution. The rpmbuild.sh script
+ by default builds both the full build and the slave-only build.
+ To build a slave-only build, the macro build_slaveonly should be set
+ to 1 (as in rpmbuild --define "build_slaveonly 1").
+ The ptpd package is the full PTP implementation,
+ whereas the ptpd-slaveonly is built with --enable-slave-only,
+ so is a version incapable of running as PTP master.
+
+- The rpmbuild.sh script calls "make dist" to generate
+ the tarball used for building the RPMs. If you are
+ building from svn, make sure you run autotools
+ (autoreconf) before trying to build RPMs. Follow the
+ README.repocheckout file in the main source directory
+ if in doubt.
+
+- rpmbuild.sh will create a temporary build directory,
+ build to it, move the rpm files to the current directory
+ and finally clean up by removing the build directory.
+ What is left is the RPMs in current directory and
+ nothing else.
+
+- The number of dependencies can be minimised mainly by
+ building ptpd without SNMP support (--disable-snmp).
+
+*******************************************************
+
+Wojciech Owczarek <wojciech%at%owczarek.co.uk>
+2013-2015, PTPd project, http://ptpd.sf.net
+
+Please report init script and / or spec file
+bugs and modifications to the ptpd-devel mailing list
+on SourceForge, or use the SourceForge ptpd forums.
+
+*******************************************************
diff --git a/rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.conf b/rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.conf
new file mode 100644
index 00000000..39297e64
--- /dev/null
+++ b/rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.conf
@@ -0,0 +1,62 @@
+; ==============================================================================
+; This is a recommended configuration for a PTPv2 slave
+; For a full list of options run ptpd2 -H or see the documentation and man pages
+; ==============================================================================
+
+; interface has to be specified
+ptpengine:interface=
+
+; PTP domain
+ptpengine:domain=0
+
+; available presets are slaveonly, masteronly and masterslave (full IEEE 1588 implementation)
+ptpengine:preset=slaveonly
+
+; multicast for both sync and delay requests - use hybrid for unicast delay requests
+ptpengine:ip_mode=multicast
+
+; when enabled, instead of sockets, libpcap is used to receive (sniff) and send (inject) packets.
+; on low latency hardware such as 10GE NICs this can provide results close to hardware-assisted PTP
+ptpengine:use_libpcap=n
+
+; go into panic mode for 10 minutes instead of resetting the clock
+ptpengine:panic_mode=y
+ptpengine:panic_mode_duration=10
+
+; uncomment this to enable outlier filters
+ptpengine:sync_outlier_filter_enable=y
+ptpengine:delay_outlier_filter_enable=y
+
+; store observed drift in a file
+clock:drift_handling=file
+
+; update online statistics every 5 seconds
+global:statistics_update_interval=30
+
+; wait 5 statistics intervals for one-way delay to stabilise
+ptpengine:calibration_delay=5
+
+; log file, event log only. if timing statistics are needed, see statistics_file
+global:log_file=/var/log/ptpd2.log
+; log file up to 5M
+global:log_file_max_size=5000
+; rotate logs up to 5 files
+global:log_file_max_files=5
+
+; status file providing an overview of ptpd's operation and statistics
+global:log_status=y
+
+; required if ip_mode is set to hybrid
+;ptpengine:log_delayreq_interval=0
+
+; uncomment this to log a timing log like in previous ptpd versions
+;global:statistics_file=/var/log/ptpd2.stats
+
+; on multi-core systems it is recommended to bind ptpd to a single core
+;global:cpuaffinity_cpucore=0
+
+; use DSCP 46 for expedited forwarding over ipv4 networks
+ptpengine:ip_dscp=46
+
+; always keep a new line in the end
+
diff --git a/rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.init b/rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.init
new file mode 100644
index 00000000..6442d71d
--- /dev/null
+++ b/rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.init
@@ -0,0 +1,205 @@
+#!/bin/bash
+
+#
+# ptpd: Precision Time protocol server (IEEE 1588)
+#
+# chkconfig: 2345 59 73
+#
+# description: The PTP daemon (PTPd) implements the Precision Time
+# protocol (PTP) as defined by the IEEE 1588 standard.
+# PTP was developed to provide very precise time
+# coordination of LAN connected computers.
+
+DAEMON=/usr/sbin/ptpd2
+prog=PTPd
+RETVAL=0
+PATH=/sbin:/usr/local/bin:$PATH
+DAEMON_DESC="IEEE 1588 Precision Time Protocol (v2) daemon"
+
+. /etc/init.d/functions
+
+if test -e /etc/sysconfig/__SERVICENAME__ ; then
+
+ . /etc/sysconfig/__SERVICENAME__
+
+ PTPD_OPTIONS="--global:lock_file=$PTPD_PID_FILE --global:status_file=$PTPD_STATUS_FILE -c $PTPD_CONFIG_FILE $PTPD_EXTRA_OPTIONS"
+
+fi
+
+
+
+start() {
+
+ status -p $PTPD_PID_FILE $DAEMON > /dev/null 2>&1
+ RETVAL=$?
+ if [ "$RETVAL" -eq "0" ]
+ then
+ echo "$prog already running (PID `cat $PTPD_PID_FILE`)."
+ exit 0
+ fi
+
+ echo -n "Starting $DAEMON_DESC..."
+
+ $DAEMON $PTPD_OPTIONS
+ sleep 1
+ status -p $PTPD_PID_FILE $DAEMON > /dev/null 2>&1
+ RETVAL=$?
+
+ if [ "$RETVAL" -eq "0" ]
+ then
+ echo_success
+ else
+ echo_failure
+ fi
+
+ echo
+
+}
+
+checkstatus() {
+ status -p $PTPD_PID_FILE $DAEMON > /dev/null 2>&1
+ RETVAL=$?
+ if [ "$RETVAL" -eq "0" ]
+ then
+ if test -e $PTPD_PID_FILE ; then
+ echo "$prog is running (PID `cat $PTPD_PID_FILE`)."
+ fi
+ else
+ echo "$prog is not running."
+ RETVAL=3
+ fi
+
+ exit $RETVAL
+}
+
+showinfo() {
+ status -p $PTPD_PID_FILE $DAEMON > /dev/null 2>&1
+ RETVAL=$?
+ if [ "$RETVAL" -eq "0" ]
+ then
+ if test -e $PTPD_PID_FILE ; then
+ echo "$prog is running (PID `cat $PTPD_PID_FILE`)."
+
+ taskset -pc `cat $PTPD_PID_FILE`
+
+ if test -e $PTPD_STATUS_FILE; then
+
+ cat $PTPD_STATUS_FILE
+
+ fi
+
+ fi
+ else
+ echo "$prog is not running."
+ RETVAL=3
+ fi
+
+ exit $RETVAL
+}
+
+
+checkconfig() {
+
+ $DAEMON -k $PTPD_OPTIONS
+ RETVAL=$?
+}
+
+stop() {
+ status -p $PTPD_PID_FILE $DAEMON > /dev/null 2>&1
+ RETVAL=$?
+ if [ "$RETVAL" -ne "0" ]
+ then
+ echo "$prog not running."
+ else
+ killproc -p $PTPD_PID_FILE $DAEMON
+ RETVAL=$?
+ if [ "$RETVAL" -eq "0" ]
+ then
+ echo "Stopping $DAEMON_DESC...`echo_success`"
+ else
+ echo "Stopping $DAEMON_DESC...`echo_failure`"
+ fi
+ fi
+}
+
+reload() {
+
+ checkconfig
+ if [ "$RETVAL" -ne "0" ]; then
+ echo "Reloading $prog: `echo_failure`"
+ exit $RETVAL
+ fi
+ echo -n $"Reloading $prog: "
+ killproc -p $PTPD_PID_FILE $DAEMON -HUP
+ RETVAL=$?
+ echo
+}
+
+help() {
+
+ echo $"Usage: $0 {start|stop|restart|reload|checkconfig|condrestart|status|info}"
+ exit 1
+}
+
+if [ -z "$1" ]
+then
+ help
+fi
+
+case "$1" in
+ start)
+ start
+ RETVAL=$?
+ ;;
+
+ status)
+ checkstatus
+ RETVAL=$?
+ ;;
+
+ info)
+ showinfo
+ RETVAL=$?
+ ;;
+
+
+ restart)
+ stop
+ sleep 1
+ start
+ RETVAL=$?
+ ;;
+
+ reload)
+ reload
+ RETVAL=$?
+
+ ;;
+
+ stop)
+ stop
+ exit 0
+ ;;
+
+ checkconfig)
+ checkconfig
+ RETVAL=$?
+ ;;
+
+
+ condrestart)
+ if [ -f PID_FILE ]; then
+ stop
+ start
+ RETVAL=$?
+ fi
+ ;;
+ *)
+
+ echo $"Usage: $0 {start|stop|restart|reload|checkconfig|condrestart|status}"
+ RETVAL=3
+ ;;
+
+esac
+
+exit $RETVAL
diff --git a/rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.service b/rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.service
new file mode 100644
index 00000000..9bac32bc
--- /dev/null
+++ b/rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.service
@@ -0,0 +1,17 @@
+[Unit]
+Description=Precision Time Protocol Daemon
+After=syslog.target ntpdate.service sntp.service ntp.service chronyd.service network.target
+
+[Service]
+Type=forking
+EnvironmentFile=-/etc/sysconfig/__SERVICENAME__
+ExecStart=/usr/sbin/ptpd2 \
+ --global:lock_file=${PTPD_PID_FILE} \
+ --global:status_file=${PTPD_STATUS_FILE} \
+ -c ${PTPD_CONFIG_FILE} \
+ ${PTPD_EXTRA_OPTIONS}
+ExecReload=/bin/kill -HUP $MAINPID
+
+[Install]
+WantedBy=multi-user.target
+
diff --git a/rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.spec b/rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.spec
new file mode 100644
index 00000000..635c43c0
--- /dev/null
+++ b/rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.spec
@@ -0,0 +1,241 @@
+# (c) 2014-2015: Wojciech Owczarek, PTPd project
+
+%if %{?build_slaveonly:1}%{!?build_slaveonly:0}
+%define slaveonly_build %{build_slaveonly}
+%else
+%define slaveonly_build 0
+%endif
+
+%define _use_internal_dependency_generator 0
+
+# RHEL5.5 and older don't have the /etc/rpm/macros.dist macros
+%if %{?dist:1}%{!?dist:0}
+%define distver %{dist}
+%else
+%define distver %(/usr/lib/rpm/redhat/dist.sh --dist)
+%endif
+
+%define servicename ptpd%{?servicesuffix}
+
+%if %{slaveonly_build} == 1
+Name: ptpd-slaveonly
+Summary: Synchronises system time using the Precision Time Protocol (PTP) implementing the IEEE 1588-2008 (PTP v 2) standard. Slave-only version.
+%else
+Name: ptpd
+Summary: Synchronises system time using the Precision Time Protocol (PTP) implementing the IEEE 1588-2008 (PTP v 2) standard. Full version with master and slave support.
+%endif
+Version: 2.3.2
+Release: 1%{distver}%{?gittag}
+License: distributable
+Group: System Environment/Daemons
+Vendor: PTPd project team
+Source0: ptpd-2.3.2.tar.gz
+
+Source2: ptpd.sysconfig
+Source3: ptpd.conf
+
+URL: http://ptpd.sf.net
+
+%if %{slaveonly_build} == 1
+Conflicts: ptpd
+%else
+Conflicts: ptpd-slaveonly
+%endif
+
+Requires(pre): /sbin/chkconfig
+Requires(pre): /bin/awk sed grep
+
+BuildRequires: libpcap-devel net-snmp-devel openssl-devel zlib-devel redhat-rpm-config
+Requires: libpcap net-snmp openssl zlib
+
+# systemd
+%if %{?_unitdir:1}%{!?_unitdir:0}
+Source1: ptpd.service
+Requires: systemd
+BuildRequires: systemd
+%else
+Source1: ptpd.init
+%endif
+
+BuildRoot: %{_tmppath}/%{name}-root
+
+%description
+The PTP daemon (PTPd) implements the Precision Time
+protocol (PTP) as defined by the IEEE 1588 standard.
+PTP was developed to provide very precise time
+coordination of LAN connected computers.
+
+Install the ptpd package if you need tools for keeping your system's
+time synchronised via the PTP protocol or serving PTP time.
+
+%prep
+
+%setup -n ptpd-2.3.2
+
+%build
+
+%if %{slaveonly_build} == 1
+./configure --enable-slave-only --with-max-unicast-destinations=512
+%else
+./configure --with-max-unicast-destinations=512
+%endif
+
+make
+
+find . -type f | xargs chmod 644
+find . -type d | xargs chmod 755
+
+%install
+rm -rf $RPM_BUILD_ROOT
+
+mkdir -p $RPM_BUILD_ROOT%{_mandir}/man{5,8}
+mkdir -p $RPM_BUILD_ROOT%{_sbindir}
+mkdir -p $RPM_BUILD_ROOT%{_datadir}/ptpd
+mkdir -p $RPM_BUILD_ROOT%{_datadir}/snmp/mibs
+
+install -m 755 src/ptpd2 $RPM_BUILD_ROOT%{_sbindir}
+install -m 644 src/ptpd2.8 $RPM_BUILD_ROOT%{_mandir}/man8/ptpd2.8
+install -m 644 src/ptpd2.conf.5 $RPM_BUILD_ROOT%{_mandir}/man5/ptpd2.conf.5
+install -m 644 doc/PTPBASE-MIB.txt $RPM_BUILD_ROOT%{_datadir}/snmp/mibs/PTPBASE-MIB.txt
+install -m 644 src/leap-seconds.list $RPM_BUILD_ROOT%{_datadir}/ptpd/leap-seconds.list
+install -m 644 src/ptpd2.conf.minimal $RPM_BUILD_ROOT%{_datadir}/ptpd/ptpd2.conf.minimal
+install -m 644 src/ptpd2.conf.default-full $RPM_BUILD_ROOT%{_datadir}/ptpd/ptpd2.conf.default-full
+install -m 644 src/templates.conf $RPM_BUILD_ROOT%{_datadir}/ptpd/templates.conf
+
+{ cd $RPM_BUILD_ROOT
+
+%if %{?_unitdir:1}%{!?_unitdir:0}
+ mkdir -p .%{_unitdir}
+ install -m644 $RPM_SOURCE_DIR/ptpd.service .%{_unitdir}/%{servicename}.service
+ sed -i 's#/etc/sysconfig/__SERVICENAME__#/etc/sysconfig/%{servicename}#' .%{_unitdir}/%{servicename}.service
+%else
+ mkdir -p .%{_initrddir}
+ install -m755 $RPM_SOURCE_DIR/ptpd.init .%{_initrddir}/%{servicename}
+ sed -i 's#/etc/sysconfig/__SERVICENAME__#/etc/sysconfig/%{servicename}#' .%{_initrddir}/%{servicename}
+%endif
+
+ mkdir -p .%{_sysconfdir}/sysconfig
+ install -m644 %{SOURCE2} .%{_sysconfdir}/sysconfig/%{servicename}
+ install -m644 %{SOURCE3} .%{_sysconfdir}/ptpd2.conf
+
+}
+
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%pre
+
+%post
+
+%if %{?_unitdir:1}%{!?_unitdir:0}
+/usr/bin/systemctl enable %{servicename} >/dev/null 2>&1
+%else
+/sbin/chkconfig --add %{servicename}
+%endif
+echo
+echo -e "**** PTPd - Running post-install checks...\n"
+
+grep -q : /etc/ethers >/dev/null 2>&1 || echo -e "Consider populating /etc/ethers with the MAC to host mappings of the GMs:\nexample:\t aa:bb:cc:dd:ee:ff gm-1.my.domain.net\n"
+
+for candidate in ntp chrony; do
+
+echo -e "\n*** Checking ${candidate}d status...\n"
+rpm -q $candidate >/dev/null 2>&1
+if [ $? -ne 0 ]; then
+
+ echo "** ${candidate}d not installed."
+else
+ echo -e "** ${candidate}d is installed.\n"
+
+%if %{?_unitdir:1}%{!?_unitdir:0}
+/usr/bin/systemctl status ${candidate}d | grep Loaded | grep enabled >/dev/null 2>&1
+%else
+chkconfig --level `runlevel | awk '{print $2;}'` ${candidate}d >/dev/null 2>&1
+%endif
+
+if [ $? == 0 ]; then
+ echo "** ${candidate}d enabled in current runlevel - consider disabling unless:"
+ echo "- you're running a PTP master with NTP reference"
+ if [ "$candidate" == "ntp" ]; then
+ echo -e "- you configure NTP integration in the PTPd configuration file\n";
+ fi
+fi
+%if %{?_unitdir:1}%{!?_unitdir:0}
+systemctl status ${candidate}d > /dev/null 2>&1
+%else
+service ${candidate}d status > /dev/null 2>&1
+%endif
+ret=$?
+if [ $ret == 3 ]; then
+ echo -e "** ${candidate}d not running - good.\n";
+elif [ $ret == 0 ]; then
+ echo "** ${candidate}d running - consider stopping before running ptpd unless:"
+ echo "- you're running a PTP master with NTP reference"
+ if [ "$candidate" == "ntp" ]; then
+ echo -e "- you configure NTP integration in the PTPd configuration file\n";
+ fi
+
+fi
+
+fi
+
+echo
+
+done
+
+:
+
+%preun
+if [ $1 = 0 ]; then
+%if %{?_unitdir:1}%{!?_unitdir:0}
+systemctl stop %{servicename} > /dev/null 2>&1
+systemctl disable %{servicename} >/dev/null 2>&1
+%else
+ service %{servicename} stop > /dev/null 2>&1
+ /sbin/chkconfig --del %{servicename}
+%endif
+fi
+:
+
+%postun
+if [ "$1" -ge "1" ]; then
+ service %{servicename} condrestart > /dev/null 2>&1
+fi
+:
+
+%files
+%defattr(-,root,root)
+%{_sbindir}/ptpd2
+%if %{?_unitdir:1}%{!?_unitdir:0}
+%{_unitdir}/%{servicename}.service
+%else
+%config %{_initrddir}/%{servicename}
+%endif
+%config(noreplace) %{_sysconfdir}/sysconfig/%{servicename}
+%config(noreplace) %{_sysconfdir}/ptpd2.conf
+%config(noreplace) %{_datadir}/ptpd/templates.conf
+%{_mandir}/man8/*
+%{_mandir}/man5/*
+%{_datadir}/snmp/mibs/*
+%{_datadir}/ptpd/*
+
+%changelog
+* Thu Oct 23 2015 Wojciech Owczarek <wojciech at owczarek.co.uk> 2.3.2-1
+- version 2.3.2
+* Wed Jul 1 2015 Wojciech Owczarek <wojciech at owczarek.co.uk> 2.3.1-2
+- spec updated for OSes with systemd
+- chrony detection added to postinstall checks
+* Wed Jun 24 2015 Wojciech Owczarek <wojciech at owczarek.co.uk> 2.3.1-1
+* Mon Jun 15 2015 Wojciech Owczarek <wojciech at owczarek.co.uk> 2.3.1-0.99.rc5
+* Mon Jun 01 2015 Wojciech Owczarek <wojciech at owczarek.co.uk> 2.3.1-0.99.rc4
+- rc5 release, adds leap seconds file and config files
+* Wed Apr 15 2015 Wojciech Owczarek <wojciech at owczarek.co.uk> 2.3.1-0.99.rc4.pre2
+* Mon Apr 13 2015 Wojciech Owczarek <wojciech at owczarek.co.uk> 2.3.1-0.99.rc4.pre1
+* Tue Oct 07 2014 Wojciech Owczarek <wojciech at owczarek.co.uk> 2.3.1-0.99.rc3
+* Fri Jul 25 2014 Wojciech Owczarek <wojciech at owczarek.co.uk> 2.3.1-0.99.rc2
+* Thu Jun 26 2014 Wojciech Owczarek <wojciech at owczarek.co.uk> 2.3.1-0.99.rc1
+* Thu Nov 21 2013 Wojciech Owczarek <wojciech at owczarek.co.uk> 2.3.0-1
+- Added the PTPBASE SNMP MIB
+* Wed Nov 13 2013 Wojciech Owczarek <wojciech at owczarek.co.uk> 2.3.0-0.99-rc2
+- Initial public spec file and scripts for RHEL
diff --git a/rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.sysconfig b/rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.sysconfig
new file mode 100644
index 00000000..66de96ce
--- /dev/null
+++ b/rtemsbsd/ptpd/packagebuild/rpm-rh/ptpd.sysconfig
@@ -0,0 +1,10 @@
+# Config file location
+PTPD_CONFIG_FILE="/etc/ptpd2.conf"
+# Lock / PID file location
+PTPD_PID_FILE="/var/run/ptpd2.lock"
+# Status file location
+PTPD_STATUS_FILE="/var/run/ptpd2.status"
+
+# Any extra command-line options
+PTPD_EXTRA_OPTIONS=""
+
diff --git a/rtemsbsd/ptpd/packagebuild/rpm-rh/rpmbuild.sh b/rtemsbsd/ptpd/packagebuild/rpm-rh/rpmbuild.sh
new file mode 100755
index 00000000..f8646122
--- /dev/null
+++ b/rtemsbsd/ptpd/packagebuild/rpm-rh/rpmbuild.sh
@@ -0,0 +1,101 @@
+#!/bin/sh
+
+if [ ! -z "$DEBUG" ]; then
+ set -x
+fi
+
+# ptpd RPM building script
+# (c) 2013-2015: Wojciech Owczarek, PTPd project
+
+while [[ $# -gt 0 ]]; do
+ opt="$1";
+ shift; #expose next argument
+ case "$opt" in
+ "--tag" )
+ # Add git-based tag
+ ADD_TAG=1;;
+ "--service2" )
+ # Create ptpd2 service
+ SERVICESUFFIX='2';;
+ *) cat >&2 <<EOF
+Invalid option: $opt (remainder $@)
+Supported options:
+ --tag: add a git-based tag to the rpm release
+ --service2: generate the service as ptpd2 (instead of default ptpd)
+EOF
+ exit 1;;
+ esac
+done
+
+
+SPEC=ptpd.spec
+PWD=`pwd`
+if [ `basename $PWD` != 'rpm-rh' ]; then
+ # deleting rpm is dangerous
+ echo "Can only be run from packagebuild/rpm-rh subdir in repo"
+ exit 1
+fi
+rm *.rpm
+
+for dep in rpm-build `grep BuildRequires ptpd.spec | sed "s/^.*: //" |grep -v systemd | tr '\n' ' '`; do
+ rpm -q $dep --quiet || { echo "Error: $dep not installed"; exit 1; }
+done
+
+# build both versions: normal and slave-only
+for slaveonly in 0 1; do
+
+ cd $PWD
+ BUILDDIR=`mktemp -d /tmp/tmpbuild.XXXXXXXXX`
+
+ rm -rf $BUILDDIR
+
+
+ for dir in BUILD BUILDROOT RPMS SOURCES SPECS SRPMS; do
+ mkdir -p $BUILDDIR/$dir;
+ done
+
+
+ TARBALL=`cat $SPEC | grep ^Source0 | awk '{ print $2; }'`
+
+ # hack: dist hook now removes rpms from this dir before dist packaging:
+ # this is how we preserve them...
+ mv *.rpm ../..
+ ( cd ../..; autoreconf -vi; ./configure; make dist; )
+ # and in they go again...
+ mv ../../*.rpm .
+
+ mv ../../$TARBALL .
+
+ for sourcefile in `grep ^Source $SPEC | awk '{print $2;}'`; do
+ cp -f $sourcefile $BUILDDIR/SOURCES
+ done
+
+ cp -f $SPEC $BUILDDIR/SPECS
+
+ ARCHIVE=`cat $SPEC | egrep "Name|Version|Release" | awk 'BEGIN {ORS="-";} NR<3 { print $2;} NR>=3 {ORS=""; print $2}'`
+ RPMFILE=$BUILDDIR/RPMS/`uname -m`/$ARCHIVE.`uname -m`.rpm
+ SRPMFILE=$BUILDDIR/SRPMS/$ARCHIVE.src.rpm
+
+ if [ -z "$ADD_TAG" ]; then
+ TAG_ARG='__gittag notag' # cannot be empty
+ else
+ tag=`git log --format=.%ct.%h -1` # based on last commit, timestamp for increasing versions
+ TAG_ARG="gittag $tag"
+ fi
+
+ if [ -z "$SERVICESUFFIX" ]; then
+ SERVICESUFFIX_ARG='__servicesuffix nosuffix' # cannot be empty
+ else
+ SERVICESUFFIX_ARG="servicesuffix $SERVICESUFFIX"
+ fi
+
+
+ rpmbuild --define "$TAG_ARG" --define "$SERVICESUFFIX_ARG" --define "build_slaveonly $slaveonly" --define "_topdir $BUILDDIR" -ba $BUILDDIR/SPECS/$SPEC && {
+ find $BUILDDIR -name "*.rpm" -exec mv {} $PWD \;
+ }
+
+ rm -rf $BUILDDIR
+ rm $TARBALL
+
+ echo "Moved rpms to $PWD"
+done
diff --git a/rtemsbsd/ptpd/src/Doxyfile b/rtemsbsd/ptpd/src/Doxyfile
new file mode 100644
index 00000000..94c85155
--- /dev/null
+++ b/rtemsbsd/ptpd/src/Doxyfile
@@ -0,0 +1,1511 @@
+# Doxyfile 1.5.8
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = PTPDv2
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = Doxygen
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek,
+# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish,
+# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene,
+# Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses.
+# With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this tag.
+# The format is ext=language, where ext is a file extension, and language is one of
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = YES
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT =
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = YES
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE =
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
+# For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to FRAME, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature. Other possible values
+# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list;
+# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
+# disables this behavior completely. For backwards compatibility with previous
+# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
+# respectively.
+
+GENERATE_TREEVIEW = NONE
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = YES
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = YES
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Options related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = YES
+
diff --git a/rtemsbsd/ptpd/src/Makefile.am b/rtemsbsd/ptpd/src/Makefile.am
new file mode 100644
index 00000000..e2970000
--- /dev/null
+++ b/rtemsbsd/ptpd/src/Makefile.am
@@ -0,0 +1,102 @@
+# Makefile for ptpd2
+
+AUTOMAKE_OPTIONS = subdir-objects
+lib_LTLIBRARIES = $(LIBPTPD2_LIBS_LA)
+sbin_PROGRAMS = ptpd2
+man_MANS = ptpd2.8 ptpd2.conf.5
+
+AM_CFLAGS = $(SNMP_CFLAGS) $(PCAP_CFLAGS) -Wall -fexceptions
+AM_CPPFLAGS = $(SNMP_CPPFLAGS) $(PCAP_CPPFLAGS)
+AM_LDFLAGS = $(SNMP_LIBS) $(PCAP_LIBS)
+
+if LINUX_KERNEL_HEADERS
+AM_CFLAGS += $(LINUX_KERNEL_INCLUDES)
+endif
+AM_CPPFLAGS += -DDATADIR='"$(datadir)"' $(PTP_DBL) $(PTP_DAEMON) $(PTP_EXP) $(PTP_SNMP) $(PTP_PCAP) $(PTP_STATISTICS) $(PTP_SLAVE_ONLY) $(PTP_PTIMERS) $(PTP_UNICAST_MAX) $(PTP_DISABLE_SOTIMESTAMPING)
+
+NULL=
+
+#VERSION = 2.3.1
+#SHAREDLIBVER = $(SHAREDLIB).$(VERSION)
+# ptp_timers.c
+
+EXTRA_DIST = def
+
+ptpd2_SOURCES = \
+ arith.c \
+ bmc.c \
+ constants.h \
+ ptp_primitives.h \
+ ptp_datatypes.h \
+ datatypes.h \
+ dep/constants_dep.h \
+ dep/datatypes_dep.h \
+ dep/ipv4_acl.h \
+ dep/ipv4_acl.c \
+ dep/msg.c \
+ dep/net.c \
+ dep/ptpd_dep.h \
+ dep/eventtimer.h \
+ dep/eventtimer.c \
+ ptp_timers.h \
+ ptp_timers.c \
+ dep/servo.c \
+ dep/iniparser/dictionary.h \
+ dep/iniparser/iniparser.h \
+ dep/iniparser/dictionary.c \
+ dep/iniparser/iniparser.c \
+ dep/configdefaults.h \
+ dep/configdefaults.c \
+ dep/daemonconfig.h \
+ dep/daemonconfig.c \
+ dep/startup.c \
+ dep/sys.c \
+ display.c \
+ management.c \
+ signaling.c \
+ protocol.c \
+ dep/ntpengine/ntp_isc_md5.c \
+ dep/ntpengine/ntp_isc_md5.h \
+ dep/ntpengine/ntpdcontrol.c \
+ dep/ntpengine/ntpdcontrol.h \
+ timingdomain.h \
+ timingdomain.c \
+ dep/alarms.h \
+ dep/alarms.c \
+ ptpd.c \
+ ptpd.h \
+ $(NULL)
+
+# SNMP
+if SNMP
+ptpd2_SOURCES += dep/snmp.c
+endif
+
+# STATISTICS
+if STATISTICS
+ptpd2_SOURCES += dep/statistics.h
+ptpd2_SOURCES += dep/statistics.c
+ptpd2_SOURCES += dep/outlierfilter.h
+ptpd2_SOURCES += dep/outlierfilter.c
+endif
+
+# posix timers
+if PTIMERS
+ptpd2_SOURCES +=dep/eventtimer_posix.c
+else
+ptpd2_SOURCES +=dep/eventtimer_itimer.c
+endif
+
+CSCOPE = cscope
+GTAGS = gtags
+DOXYGEN = doxygen
+
+TAGFILES = GPATH GRTAGS GSYMS GTAGS cscope.in.out cscope.out cscope.po.out
+
+dist:
+ cd .. && make dist
+
+#tags:
+# $(CSCOPE) -R -q -b
+# $(GTAGS)
+# $(DOXYGEN) Doxyfile
diff --git a/rtemsbsd/ptpd/src/Makefile.old b/rtemsbsd/ptpd/src/Makefile.old
new file mode 100644
index 00000000..c2eda70d
--- /dev/null
+++ b/rtemsbsd/ptpd/src/Makefile.old
@@ -0,0 +1,109 @@
+# Makefile for ptpd2
+
+#
+# Compile time defines:
+# -DRUNTIME_DEBUG
+# if defined: all debug messages are included, and are either selected at startup (-B)
+# and runtime (SIGUSR2) (implies -DPTPD_DBGV) using syslog,
+# add "*.debug /var/log/debug" to /etc/rsyslog.conf to see debug messages
+# if undefined: individual defines select which messages will be included and presented (old behaviour)
+#
+# -DPTPD_DBG basic debug messages
+# -DPTPD_DBG2 adds basic protocol messages, +ignored messages reasons
+# -DPTPD_DBGV adds all debug messages
+#
+# -DPTPD_NO_DAEMON forces option -c
+#
+# -DPTPD_EXPERIMENTAL Allows non-standard compliant experimental options:
+# -U: Hybrid mode
+# -I: choose multicast group
+# -DPTPD_SNMP Enables SNMP agent support (PTP-MIB)
+#
+# Mutually exclusive defines (useful for testing):
+# -DDBG_SIGUSR2_CHANGE_DOMAIN: SIGUSR2 cycles the PTP domain number
+# -DDBG_SIGUSR2_CHANGE_DEBUG: SIGUSR2 cycles the current debug level (if RUNTIME_DEBUG is defined)
+#
+#######
+
+VERSION = 2.3.0
+
+RM = rm -f
+SYMLINK = ln -s
+
+# SNMP support?
+SNMP = yes
+
+# start with CFLAGS += ..., so additional CFLAGs can be specified e.g. on the make command line
+CFLAGS += -Wall -g -fPIC
+
+CFLAGS += -DRUNTIME_DEBUG
+#CFLAGS += -DPTPD_DBG
+#CFLAGS += -DPTPD_DBG2
+#CFLAGS += -DPTPD_DBGV
+
+#CFLAGS += -DPTPD_NO_DAEMON
+#CFLAGS += -DDBG_SIGUSR2_CHANGE_DOMAIN
+CFLAGS += -DDBG_SIGUSR2_CHANGE_DEBUG
+
+CFLAGS += -DPTPD_EXPERIMENTAL
+
+LDFLAGS = -lm -lrt
+STATICLIBFLAGS = rcs
+SHAREDLIBFLAGS = -shared -Wl,-soname
+
+PROG = ptpd2
+STATICLIB = libptpd2.a
+SHAREDLIB = libptpd2.so
+SHAREDLIBVER = $(SHAREDLIB).$(VERSION)
+
+SRCS = ptpd.c arith.c bmc.c protocol.c display.c management.c \
+ dep/msg.c dep/net.c dep/servo.c dep/startup.c dep/sys.c dep/timer.c
+
+# SNMP
+ifeq ($(SNMP),yes)
+LDFLAGS += `net-snmp-config --agent-libs`
+CFLAGS += `net-snmp-config --base-cflags`
+CFLAGS += -DPTPD_SNMP
+SRCS += dep/snmp.c
+endif
+
+OBJS = $(SRCS:.c=.o)
+
+HDRS = ptpd.h constants.h datatypes.h \
+ dep/ptpd_dep.h dep/constants_dep.h dep/datatypes_dep.h
+
+CSCOPE = cscope
+GTAGS = gtags
+DOXYGEN = doxygen
+
+TAGFILES = GPATH GRTAGS GSYMS GTAGS cscope.in.out cscope.out cscope.po.out
+
+.c.o:
+ $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
+
+
+$(PROG): $(OBJS)
+ $(CC) -o $@ $(OBJS) $(LDFLAGS)
+
+$(STATICLIB): $(OBJS)
+ $(AR) $(STATICLIBFLAGS) $(STATICLIB) $(OBJS)
+
+$(SHAREDLIB): $(OBJS)
+ $(CC) $(SHAREDLIBFLAGS),$(SHAREDLIB) -o $(SHAREDLIB) $(OBJS)
+ # create shared lib symlink with ptpd version extension
+ $(SYMLINK) $(SHAREDLIB) $(SHAREDLIBVER)
+
+all: $(PROG) $(STATICLIB) $(SHAREDLIB)
+
+$(OBJS): $(HDRS)
+
+tags:
+ $(CSCOPE) -R -q -b
+ $(GTAGS)
+ $(DOXYGEN) Doxyfile
+
+clean:
+ $(RM) $(PROG) $(STATICLIB) $(SHAREDLIB) $(SHAREDLIBVER) $(OBJS) $(TAGFILES) make.out
+
+realclean: clean
+ $(RM) $(TAGFILES)
diff --git a/rtemsbsd/ptpd/src/arith.c b/rtemsbsd/ptpd/src/arith.c
new file mode 100644
index 00000000..4c13d616
--- /dev/null
+++ b/rtemsbsd/ptpd/src/arith.c
@@ -0,0 +1,373 @@
+/*-
+ * Copyright (c) 2012-2015 Wojciech Owczarek,
+ * Copyright (c) 2011-2012 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen,
+ * Inaqui Delgado,
+ * Rick Ratzel,
+ * National Instruments.
+ * Copyright (c) 2009-2010 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen
+ *
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file arith.c
+ * @date Tue Jul 20 16:12:51 2010
+ *
+ * @brief Time format conversion routines and additional math functions.
+ *
+ *
+ */
+
+#include "ptpd.h"
+
+void
+internalTime_to_integer64(TimeInternal internal, Integer64 *bigint)
+{
+ int64_t scaledNanoseconds;
+
+ scaledNanoseconds = internal.seconds;
+ scaledNanoseconds *= 1000000000;
+ scaledNanoseconds += internal.nanoseconds;
+ scaledNanoseconds <<= 16;
+
+ bigint->msb = (scaledNanoseconds >> 32) & 0x00000000ffffffff;
+ bigint->lsb = scaledNanoseconds & 0x00000000ffffffff;
+}
+
+void
+integer64_to_internalTime(Integer64 bigint, TimeInternal * internal)
+{
+ int sign;
+ int64_t scaledNanoseconds;
+
+ scaledNanoseconds = bigint.msb;
+ scaledNanoseconds <<=32;
+ scaledNanoseconds += bigint.lsb;
+
+ /*determine sign of result big integer number*/
+
+ if (scaledNanoseconds < 0) {
+ scaledNanoseconds = -scaledNanoseconds;
+ sign = -1;
+ } else {
+ sign = 1;
+ }
+
+ /*fractional nanoseconds are excluded (see 5.3.2)*/
+ scaledNanoseconds >>= 16;
+ internal->seconds = sign * (scaledNanoseconds / 1000000000);
+ internal->nanoseconds = sign * (scaledNanoseconds % 1000000000);
+}
+
+
+void
+fromInternalTime(const TimeInternal * internal, Timestamp * external)
+{
+
+ /*
+ * fromInternalTime is only used to convert time given by the system
+ * to a timestamp. As a consequence, no negative value can normally
+ * be found in (internal)
+ *
+ * Note that offsets are also represented with TimeInternal structure,
+ * and can be negative, but offset are never convert into Timestamp
+ * so there is no problem here.
+ */
+
+ if ((internal->seconds & ~INT_MAX) ||
+ (internal->nanoseconds & ~INT_MAX)) {
+ DBG("Negative value canno't be converted into timestamp \n");
+ return;
+ } else {
+ external->secondsField.lsb = internal->seconds;
+ external->nanosecondsField = internal->nanoseconds;
+ external->secondsField.msb = 0;
+ }
+}
+
+void
+toInternalTime(TimeInternal * internal, const Timestamp * external)
+{
+
+ /* Program will not run after 2038... */
+ if (external->secondsField.lsb < INT_MAX) {
+ internal->seconds = external->secondsField.lsb;
+ internal->nanoseconds = external->nanosecondsField;
+ } else {
+ DBG("Clock servo canno't be executed : "
+ "seconds field is higher than signed integer (32bits) \n");
+ return;
+ }
+}
+
+void
+ts_to_InternalTime(const struct timespec *a, TimeInternal * b)
+{
+
+ b->seconds = a->tv_sec;
+ b->nanoseconds = a->tv_nsec;
+}
+
+void
+tv_to_InternalTime(const struct timeval *a, TimeInternal * b)
+{
+
+ b->seconds = a->tv_sec;
+ b->nanoseconds = a->tv_usec * 1000;
+}
+
+
+void
+normalizeTime(TimeInternal * r)
+{
+ r->seconds += r->nanoseconds / 1000000000;
+ r->nanoseconds -= (r->nanoseconds / 1000000000) * 1000000000;
+
+ if (r->seconds > 0 && r->nanoseconds < 0) {
+ r->seconds -= 1;
+ r->nanoseconds += 1000000000;
+ } else if (r->seconds < 0 && r->nanoseconds > 0) {
+ r->seconds += 1;
+ r->nanoseconds -= 1000000000;
+ }
+}
+
+void
+addTime(TimeInternal * r, const TimeInternal * x, const TimeInternal * y)
+{
+ r->seconds = x->seconds + y->seconds;
+ r->nanoseconds = x->nanoseconds + y->nanoseconds;
+
+ normalizeTime(r);
+}
+
+void
+subTime(TimeInternal * r, const TimeInternal * x, const TimeInternal * y)
+{
+ r->seconds = x->seconds - y->seconds;
+ r->nanoseconds = x->nanoseconds - y->nanoseconds;
+
+ normalizeTime(r);
+}
+
+/// Divide an internal time value
+///
+/// @param r the time to convert
+/// @param divisor
+///
+
+#if 0
+/* TODO: this function could be simplified, as currently it is only called to halve the time */
+void
+divTime(TimeInternal *r, int divisor)
+{
+
+ uint64_t nanoseconds;
+
+ if (divisor <= 0)
+ return;
+
+ nanoseconds = ((uint64_t)r->seconds * 1000000000) + r->nanoseconds;
+ nanoseconds /= divisor;
+
+ r->seconds = 0;
+ r->nanoseconds = nanoseconds;
+ normalizeTime(r);
+}
+#endif
+
+void
+div2Time(TimeInternal *r)
+{
+ r->nanoseconds += (r->seconds % 2) * 1000000000;
+ r->seconds /= 2;
+ r->nanoseconds /= 2;
+
+ normalizeTime(r);
+}
+
+
+
+/* clear an internal time value */
+void
+clearTime(TimeInternal *r)
+{
+ r->seconds = 0;
+ r->nanoseconds = 0;
+}
+
+
+/* sets a time value to a certain nanoseconds */
+void
+nano_to_Time(TimeInternal *x, int nano)
+{
+ x->seconds = 0;
+ x->nanoseconds = nano;
+ normalizeTime(x);
+}
+
+/* greater than operation */
+int
+gtTime(const TimeInternal *x, const TimeInternal *y)
+{
+ TimeInternal r;
+
+ subTime(&r, x, y);
+ return !isTimeInternalNegative(&r);
+}
+
+/* remove sign from variable */
+void
+absTime(TimeInternal *r)
+{
+ /* Make sure signs are the same */
+ normalizeTime(r);
+ r->seconds = abs(r->seconds);
+ r->nanoseconds = abs(r->nanoseconds);
+}
+
+
+/* if 2 time values are close enough for X nanoseconds */
+int
+is_Time_close(const TimeInternal *x, const TimeInternal *y, int nanos)
+{
+ TimeInternal r1;
+ TimeInternal r2;
+
+ // first, subtract the 2 values. then call abs(),
+ // then call gtTime for requested the number of nanoseconds
+ subTime(&r1, x, y);
+ absTime(&r1);
+
+ nano_to_Time(&r2, nanos);
+
+ return !gtTime(&r1, &r2);
+}
+
+
+int
+check_timestamp_is_fresh2(const TimeInternal * timeA, const TimeInternal * timeB)
+{
+ int ret;
+
+ // maximum 1 millisecond offset
+ ret = is_Time_close(timeA, timeB, 1000000);
+ DBG2("check_timestamp_is_fresh: %d\n ", ret);
+ return ret;
+}
+
+
+int
+check_timestamp_is_fresh(const TimeInternal * timeA)
+{
+ TimeInternal timeB;
+ getTime(&timeB);
+
+ return check_timestamp_is_fresh2(timeA, &timeB);
+}
+
+
+int
+isTimeInternalNegative(const TimeInternal * p)
+{
+ return (p->seconds < 0) || (p->nanoseconds < 0);
+}
+
+double
+secondsToMidnight(void)
+{
+ TimeInternal now;
+ double stm, ret;
+ getTime(&now);
+ stm = 86400.0 - (now.seconds % 86400);
+ ret = (stm - now.nanoseconds / 1E9);
+ return ret;
+}
+
+double
+getPauseAfterMidnight(Integer8 announceInterval, int pausePeriod)
+{
+ double ai = pow(2,announceInterval);
+
+ if (pausePeriod > 2.0 * ai)
+ return (pausePeriod);
+ else
+ return (2.0 * ai);
+}
+
+double
+timeInternalToDouble(const TimeInternal * p)
+{
+
+ double sign = (p->seconds < 0 || p->nanoseconds < 0 ) ? -1.0 : 1.0;
+ return (sign * ( abs(p->seconds) + abs(p->nanoseconds) / 1E9 ));
+
+}
+
+TimeInternal
+doubleToTimeInternal(const double d)
+{
+
+ TimeInternal t = {0, 0};
+
+ t.seconds = trunc(d);
+ t.nanoseconds = (d - (t.seconds + 0.0)) * 1E9;
+
+ return t;
+
+}
+
+/* FNV-1 hash, 32-bit, optional modulo limiter */
+uint32_t
+fnvHash(void *input, size_t len, int modulo)
+{
+
+ int i = 0;
+
+ static uint32_t prime = 16777619;
+ static uint32_t basis = 2166136261;
+
+ uint32_t hash = basis;
+ uint8_t *buf = (uint8_t*)input;
+
+ for(i = 0; i < len; i++) {
+ hash *= prime;
+ hash ^= *(buf + i);
+ }
+
+ return (modulo > 0 ? hash % modulo : hash);
+
+}
diff --git a/rtemsbsd/ptpd/src/bmc.c b/rtemsbsd/ptpd/src/bmc.c
new file mode 100644
index 00000000..b5249aba
--- /dev/null
+++ b/rtemsbsd/ptpd/src/bmc.c
@@ -0,0 +1,779 @@
+/*-
+ * Copyright (c) 2012-2015 Wojciech Owczarek,
+ * Copyright (c) 2011-2012 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen,
+ * Inaqui Delgado,
+ * Rick Ratzel,
+ * National Instruments.
+ * Copyright (c) 2009-2010 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen
+ *
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file bmc.c
+ * @date Wed Jun 23 09:36:09 2010
+ *
+ * @brief Best master clock selection code.
+ *
+ * The functions in this file are used by the daemon to select the
+ * best master clock from any number of possibilities.
+ */
+
+#include "ptpd.h"
+
+
+/* Init ptpClock with run time values (initialization constants are in constants.h)*/
+void initData(RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ int i,j;
+ j=0;
+ DBG("initData\n");
+
+ /* Default data set */
+ ptpClock->defaultDS.twoStepFlag = TWO_STEP_FLAG;
+
+ /*
+ * init clockIdentity with MAC address and 0xFF and 0xFE. see
+ * spec 7.5.2.2.2
+ */
+ for (i=0;i<CLOCK_IDENTITY_LENGTH;i++)
+ {
+ if (i==3) ptpClock->defaultDS.clockIdentity[i]=0xFF;
+ else if (i==4) ptpClock->defaultDS.clockIdentity[i]=0xFE;
+ else
+ {
+ ptpClock->defaultDS.clockIdentity[i]=ptpClock->netPath.interfaceID[j];
+ j++;
+ }
+ }
+
+ ptpClock->portDS.lastMismatchedDomain = -1;
+
+ if(rtOpts->pidAsClockId) {
+ uint16_t pid = htons(getpid());
+ memcpy(ptpClock->defaultDS.clockIdentity + 3, &pid, 2);
+ }
+
+ ptpClock->bestMaster = NULL;
+ ptpClock->defaultDS.numberPorts = NUMBER_PORTS;
+
+ ptpClock->disabled = rtOpts->portDisabled;
+
+ memset(ptpClock->userDescription, 0, sizeof(ptpClock->userDescription));
+ memcpy(ptpClock->userDescription, rtOpts->portDescription, strlen(rtOpts->portDescription));
+
+ memset(&ptpClock->profileIdentity,0,6);
+
+ if(rtOpts->ipMode == IPMODE_UNICAST && rtOpts->unicastNegotiation) {
+ memcpy(&ptpClock->profileIdentity, &PROFILE_ID_TELECOM,6);
+ }
+
+ if(rtOpts->ipMode == IPMODE_MULTICAST &&rtOpts->delayMechanism == E2E) {
+ memcpy(&ptpClock->profileIdentity, &PROFILE_ID_DEFAULT_E2E,6);
+ }
+
+ if(rtOpts->ipMode == IPMODE_MULTICAST &&rtOpts->delayMechanism == P2P) {
+ memcpy(&ptpClock->profileIdentity, &PROFILE_ID_DEFAULT_P2P,6);
+ }
+
+ if(rtOpts->dot1AS) {
+ memcpy(&ptpClock->profileIdentity, &PROFILE_ID_802_1AS,6);
+ }
+
+ ptpClock->defaultDS.clockQuality.clockAccuracy =
+ rtOpts->clockQuality.clockAccuracy;
+ ptpClock->defaultDS.clockQuality.clockClass = rtOpts->clockQuality.clockClass;
+ ptpClock->defaultDS.clockQuality.offsetScaledLogVariance =
+ rtOpts->clockQuality.offsetScaledLogVariance;
+
+ ptpClock->defaultDS.priority1 = rtOpts->priority1;
+ ptpClock->defaultDS.priority2 = rtOpts->priority2;
+
+ ptpClock->defaultDS.domainNumber = rtOpts->domainNumber;
+
+ if(rtOpts->slaveOnly) {
+ ptpClock->defaultDS.slaveOnly = TRUE;
+ rtOpts->clockQuality.clockClass = SLAVE_ONLY_CLOCK_CLASS;
+ ptpClock->defaultDS.clockQuality.clockClass = SLAVE_ONLY_CLOCK_CLASS;
+ }
+
+/* Port configuration data set */
+
+ /*
+ * PortIdentity Init (portNumber = 1 for an ardinary clock spec
+ * 7.5.2.3)
+ */
+ copyClockIdentity(ptpClock->portDS.portIdentity.clockIdentity,
+ ptpClock->defaultDS.clockIdentity);
+ ptpClock->portDS.portIdentity.portNumber = rtOpts->portNumber;
+
+ /* select the initial rate of delayreqs until we receive the first announce message */
+
+ ptpClock->portDS.logMinDelayReqInterval = rtOpts->initial_delayreq;
+
+ clearTime(&ptpClock->portDS.peerMeanPathDelay);
+
+ ptpClock->portDS.logAnnounceInterval = rtOpts->logAnnounceInterval;
+ ptpClock->portDS.announceReceiptTimeout = rtOpts->announceReceiptTimeout;
+ ptpClock->portDS.logSyncInterval = rtOpts->logSyncInterval;
+ ptpClock->portDS.delayMechanism = rtOpts->delayMechanism;
+ ptpClock->portDS.logMinPdelayReqInterval = rtOpts->logMinPdelayReqInterval;
+ ptpClock->portDS.versionNumber = VERSION_PTP;
+
+ if(rtOpts->dot1AS) {
+ ptpClock->portDS.transportSpecific = TSP_ETHERNET_AVB;
+ } else {
+ ptpClock->portDS.transportSpecific = TSP_DEFAULT;
+ }
+
+ /*
+ * Initialize random number generator using same method as ptpv1:
+ * seed is now initialized from the last bytes of our mac addres (collected in net.c:findIface())
+ */
+ srand((ptpClock->netPath.interfaceID[PTP_UUID_LENGTH - 1] << 8) +
+ ptpClock->netPath.interfaceID[PTP_UUID_LENGTH - 2]);
+
+ /*Init other stuff*/
+ ptpClock->number_foreign_records = 0;
+ ptpClock->max_foreign_records = rtOpts->max_foreign_records;
+}
+
+/* memcmp behaviour: -1: a<b, 1: a>b, 0: a=b */
+int
+cmpPortIdentity(const PortIdentity *a, const PortIdentity *b)
+{
+
+ int comp;
+
+ /* compare clock identity first */
+
+ comp = memcmp(a->clockIdentity, b->clockIdentity, CLOCK_IDENTITY_LENGTH);
+
+ if(comp !=0) {
+ return comp;
+ }
+
+ /* then compare portNumber */
+
+ if(a->portNumber > b->portNumber) {
+ return 1;
+ }
+
+ if(a->portNumber < b->portNumber) {
+ return -1;
+ }
+
+ return 0;
+
+}
+
+/* compare portIdentity to an empty one */
+Boolean portIdentityEmpty(PortIdentity *portIdentity) {
+
+ PortIdentity zero;
+ memset(&zero, 0, sizeof(PortIdentity));
+
+ if (!cmpPortIdentity(portIdentity, &zero)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* compare portIdentity to all ones port identity */
+Boolean portIdentityAllOnes(PortIdentity *portIdentity) {
+
+ PortIdentity allOnes;
+ memset(&allOnes, 0xFF, sizeof(PortIdentity));
+
+ if (!cmpPortIdentity(portIdentity, &allOnes)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*Local clock is becoming Master. Table 13 (9.3.5) of the spec.*/
+void m1(const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ /*Current data set update*/
+ ptpClock->currentDS.stepsRemoved = 0;
+
+ clearTime(&ptpClock->currentDS.offsetFromMaster);
+ clearTime(&ptpClock->currentDS.meanPathDelay);
+
+ copyClockIdentity(ptpClock->parentDS.parentPortIdentity.clockIdentity,
+ ptpClock->defaultDS.clockIdentity);
+
+ ptpClock->parentDS.parentPortIdentity.portNumber = ptpClock->portDS.portIdentity.portNumber;
+ ptpClock->parentDS.parentStats = DEFAULT_PARENTS_STATS;
+ ptpClock->parentDS.observedParentClockPhaseChangeRate = 0;
+ ptpClock->parentDS.observedParentOffsetScaledLogVariance = 0;
+ copyClockIdentity(ptpClock->parentDS.grandmasterIdentity,
+ ptpClock->defaultDS.clockIdentity);
+ ptpClock->parentDS.grandmasterClockQuality.clockAccuracy =
+ ptpClock->defaultDS.clockQuality.clockAccuracy;
+ ptpClock->parentDS.grandmasterClockQuality.clockClass =
+ ptpClock->defaultDS.clockQuality.clockClass;
+ ptpClock->parentDS.grandmasterClockQuality.offsetScaledLogVariance =
+ ptpClock->defaultDS.clockQuality.offsetScaledLogVariance;
+ ptpClock->parentDS.grandmasterPriority1 = ptpClock->defaultDS.priority1;
+ ptpClock->parentDS.grandmasterPriority2 = ptpClock->defaultDS.priority2;
+ ptpClock->portDS.logMinDelayReqInterval = rtOpts->logMinDelayReqInterval;
+
+ /*Time Properties data set*/
+ ptpClock->timePropertiesDS.currentUtcOffsetValid = rtOpts->timeProperties.currentUtcOffsetValid;
+ ptpClock->timePropertiesDS.currentUtcOffset = rtOpts->timeProperties.currentUtcOffset;
+ ptpClock->timePropertiesDS.timeTraceable = rtOpts->timeProperties.timeTraceable;
+ ptpClock->timePropertiesDS.frequencyTraceable = rtOpts->timeProperties.frequencyTraceable;
+ ptpClock->timePropertiesDS.ptpTimescale = rtOpts->timeProperties.ptpTimescale;
+ ptpClock->timePropertiesDS.timeSource = rtOpts->timeProperties.timeSource;
+
+ if(ptpClock->timePropertiesDS.ptpTimescale &&
+ (secondsToMidnight() < rtOpts->leapSecondNoticePeriod)) {
+ ptpClock->timePropertiesDS.leap59 = ptpClock->clockStatus.leapDelete;
+ ptpClock->timePropertiesDS.leap61 = ptpClock->clockStatus.leapInsert;
+ } else {
+ ptpClock->timePropertiesDS.leap59 = FALSE;
+ ptpClock->timePropertiesDS.leap61 = FALSE;
+ }
+
+}
+
+
+/* first cut on a passive mode specific BMC actions */
+void p1(PtpClock *ptpClock, const RunTimeOpts *rtOpts)
+{
+ /* make sure we revert to ARB timescale in Passive mode*/
+ if(ptpClock->portDS.portState == PTP_PASSIVE){
+ ptpClock->timePropertiesDS.currentUtcOffsetValid = rtOpts->timeProperties.currentUtcOffsetValid;
+ ptpClock->timePropertiesDS.currentUtcOffset = rtOpts->timeProperties.currentUtcOffset;
+ }
+
+}
+
+
+/*Local clock is synchronized to Ebest Table 16 (9.3.5) of the spec*/
+void s1(MsgHeader *header,MsgAnnounce *announce,PtpClock *ptpClock, const RunTimeOpts *rtOpts)
+{
+
+ Boolean firstUpdate = !cmpPortIdentity(&ptpClock->parentDS.parentPortIdentity, &ptpClock->portDS.portIdentity);
+ TimePropertiesDS tpPrevious = ptpClock->timePropertiesDS;
+
+ Boolean previousLeap59 = FALSE;
+ Boolean previousLeap61 = FALSE;
+
+ Boolean leapChange = FALSE;
+
+ Integer16 previousUtcOffset = 0;
+
+ previousLeap59 = ptpClock->timePropertiesDS.leap59;
+ previousLeap61 = ptpClock->timePropertiesDS.leap61;
+
+ previousUtcOffset = ptpClock->timePropertiesDS.currentUtcOffset;
+
+ /* Current DS */
+ ptpClock->currentDS.stepsRemoved = announce->stepsRemoved + 1;
+
+ /* Parent DS */
+ copyClockIdentity(ptpClock->parentDS.parentPortIdentity.clockIdentity,
+ header->sourcePortIdentity.clockIdentity);
+ ptpClock->parentDS.parentPortIdentity.portNumber =
+ header->sourcePortIdentity.portNumber;
+ copyClockIdentity(ptpClock->parentDS.grandmasterIdentity,
+ announce->grandmasterIdentity);
+ ptpClock->parentDS.grandmasterClockQuality.clockAccuracy =
+ announce->grandmasterClockQuality.clockAccuracy;
+ ptpClock->parentDS.grandmasterClockQuality.clockClass =
+ announce->grandmasterClockQuality.clockClass;
+ ptpClock->parentDS.grandmasterClockQuality.offsetScaledLogVariance =
+ announce->grandmasterClockQuality.offsetScaledLogVariance;
+ ptpClock->parentDS.grandmasterPriority1 = announce->grandmasterPriority1;
+ ptpClock->parentDS.grandmasterPriority2 = announce->grandmasterPriority2;
+
+ /* use the granted interval if using signaling, otherwise we would try to arm a timer for 2^127! */
+ if(rtOpts->unicastNegotiation && ptpClock->parentGrants != NULL && ptpClock->parentGrants->grantData[ANNOUNCE_INDEXED].granted) {
+ ptpClock->portDS.logAnnounceInterval = ptpClock->parentGrants->grantData[ANNOUNCE_INDEXED].logInterval;
+ } else if (header->logMessageInterval != UNICAST_MESSAGEINTERVAL) {
+ ptpClock->portDS.logAnnounceInterval = header->logMessageInterval;
+ }
+
+ /* Timeproperties DS */
+ ptpClock->timePropertiesDS.currentUtcOffset = announce->currentUtcOffset;
+
+ if (ptpClock->portDS.portState != PTP_PASSIVE && ptpClock->timePropertiesDS.currentUtcOffsetValid &&
+ !IS_SET(header->flagField1, UTCV)) {
+ if(rtOpts->alwaysRespectUtcOffset)
+ WARNING("UTC Offset no longer valid and ptpengine:always_respect_utc_offset is set: continuing as normal\n");
+ else
+ WARNING("UTC Offset no longer valid - clock jump expected\n");
+ }
+
+ /* "Valid" is bit 2 in second octet of flagfield */
+ ptpClock->timePropertiesDS.currentUtcOffsetValid = IS_SET(header->flagField1, UTCV);
+
+ /* set PTP_PASSIVE-specific state */
+ p1(ptpClock, rtOpts);
+
+ /* only set leap flags in slave state - info from leap file takes priority*/
+ if (ptpClock->portDS.portState == PTP_SLAVE) {
+ if(ptpClock->clockStatus.override) {
+ ptpClock->timePropertiesDS.currentUtcOffset = ptpClock->clockStatus.utcOffset;
+ ptpClock->timePropertiesDS.leap59 = ptpClock->clockStatus.leapDelete;
+ ptpClock->timePropertiesDS.leap61 = ptpClock->clockStatus.leapInsert;
+ } else {
+ ptpClock->timePropertiesDS.leap59 = IS_SET(header->flagField1, LI59);
+ ptpClock->timePropertiesDS.leap61 = IS_SET(header->flagField1, LI61);
+ }
+ }
+
+ ptpClock->timePropertiesDS.timeTraceable = IS_SET(header->flagField1, TTRA);
+ ptpClock->timePropertiesDS.frequencyTraceable = IS_SET(header->flagField1, FTRA);
+ ptpClock->timePropertiesDS.ptpTimescale = IS_SET(header->flagField1, PTPT);
+ ptpClock->timePropertiesDS.timeSource = announce->timeSource;
+
+ /* if Announce was accepted from some domain, so be it */
+ if(rtOpts->anyDomain || rtOpts->unicastNegotiation) {
+ ptpClock->defaultDS.domainNumber = header->domainNumber;
+ }
+
+ /*
+ * tell the clock source to update TAI offset, but only if timescale is
+ * PTP not ARB - spec section 7.2
+ */
+ if (ptpClock->timePropertiesDS.ptpTimescale &&
+ (ptpClock->timePropertiesDS.currentUtcOffsetValid || rtOpts->alwaysRespectUtcOffset) &&
+ (ptpClock->timePropertiesDS.currentUtcOffset != previousUtcOffset)) {
+ INFO("UTC offset is now %d\n",
+ ptpClock->timePropertiesDS.currentUtcOffset);
+ ptpClock->clockStatus.utcOffset = ptpClock->timePropertiesDS.currentUtcOffset;
+ ptpClock->clockStatus.update = TRUE;
+
+ }
+
+ if(!firstUpdate && memcmp(&tpPrevious,&ptpClock->timePropertiesDS, sizeof(TimePropertiesDS))) {
+ /* this is an event - will be picked up and dispatched, no need to set false */
+ SET_ALARM(ALRM_TIMEPROP_CHANGE, TRUE);
+ }
+
+ /* non-slave logic done, exit if not slave */
+ if (ptpClock->portDS.portState != PTP_SLAVE) {
+ return;
+ }
+
+ /* Leap second handling */
+
+ if(ptpClock->timePropertiesDS.leap59 && ptpClock->timePropertiesDS.leap61) {
+ DBG("Both Leap59 and Leap61 flags set!\n");
+ ptpClock->counters.protocolErrors++;
+ return;
+ }
+
+ /* One of the flags has been cleared */
+ leapChange = ((previousLeap59 && !ptpClock->timePropertiesDS.leap59) ||
+ (previousLeap61 && !ptpClock->timePropertiesDS.leap61));
+
+ if(ptpClock->leapSecondPending && leapChange && !ptpClock->leapSecondInProgress) {
+ WARNING("Leap second event aborted by GM!\n");
+ ptpClock->leapSecondPending = FALSE;
+ ptpClock->leapSecondInProgress = FALSE;
+ timerStop(&ptpClock->timers[LEAP_SECOND_PAUSE_TIMER]);
+ ptpClock->clockStatus.leapInsert = FALSE;
+ ptpClock->clockStatus.leapDelete = FALSE;
+ ptpClock->clockStatus.update = TRUE;
+ }
+
+ /*
+ * one of the leap second flags is lit
+ * but we have no event pending
+ */
+
+ /* changes in flags while not waiting for leap second */
+
+ if(!ptpClock->leapSecondPending && !ptpClock->leapSecondInProgress) {
+
+ if(rtOpts->leapSecondHandling == LEAP_ACCEPT) {
+ ptpClock->clockStatus.leapInsert = ptpClock->timePropertiesDS.leap61;
+ ptpClock->clockStatus.leapDelete = ptpClock->timePropertiesDS.leap59;
+ }
+
+ if(ptpClock->timePropertiesDS.leap61 || ptpClock->timePropertiesDS.leap59) {
+ ptpClock->clockStatus.update = TRUE;
+ /* only set the flag, the rest happens in doState() */
+ ptpClock->leapSecondPending = TRUE;
+ }
+ }
+
+ /* UTC offset has changed */
+ if(previousUtcOffset != ptpClock->timePropertiesDS.currentUtcOffset) {
+ if(!ptpClock->leapSecondPending && !ptpClock->leapSecondInProgress) {
+ WARNING("UTC offset changed from %d to %d with "
+ "no leap second pending!\n",
+ previousUtcOffset,
+ ptpClock->timePropertiesDS.currentUtcOffset);
+ } else {
+ WARNING("UTC offset changed from %d to %d\n",
+ previousUtcOffset,ptpClock->timePropertiesDS.currentUtcOffset);
+ }
+ }
+}
+
+
+/*Copy local data set into header and announce message. 9.3.4 table 12*/
+static void
+copyD0(MsgHeader *header, MsgAnnounce *announce, PtpClock *ptpClock)
+{
+ announce->grandmasterPriority1 = ptpClock->defaultDS.priority1;
+ copyClockIdentity(announce->grandmasterIdentity,
+ ptpClock->defaultDS.clockIdentity);
+ announce->grandmasterClockQuality.clockClass =
+ ptpClock->defaultDS.clockQuality.clockClass;
+ announce->grandmasterClockQuality.clockAccuracy =
+ ptpClock->defaultDS.clockQuality.clockAccuracy;
+ announce->grandmasterClockQuality.offsetScaledLogVariance =
+ ptpClock->defaultDS.clockQuality.offsetScaledLogVariance;
+ announce->grandmasterPriority2 = ptpClock->defaultDS.priority2;
+ announce->stepsRemoved = 0;
+ copyClockIdentity(header->sourcePortIdentity.clockIdentity,
+ ptpClock->defaultDS.clockIdentity);
+
+ /* Copy TimePropertiesDS into FlagField1 */
+ header->flagField1 = ptpClock->timePropertiesDS.leap61 << 0;
+ header->flagField1 |= ptpClock->timePropertiesDS.leap59 << 1;
+ header->flagField1 |= ptpClock->timePropertiesDS.currentUtcOffsetValid << 2;
+ header->flagField1 |= ptpClock->timePropertiesDS.ptpTimescale << 3;
+ header->flagField1 |= ptpClock->timePropertiesDS.timeTraceable << 4;
+ header->flagField1 |= ptpClock->timePropertiesDS.frequencyTraceable << 5;
+
+}
+
+
+/*Data set comparison bewteen two foreign masters (9.3.4 fig 27)
+ * return similar to memcmp() */
+
+static Integer8
+bmcDataSetComparison(const ForeignMasterRecord *a, const ForeignMasterRecord *b, const PtpClock *ptpClock, const RunTimeOpts *rtOpts)
+{
+
+
+ DBGV("Data set comparison \n");
+ short comp = 0;
+
+
+ /* disqualification comes above anything else */
+
+ if(a->disqualified > b->disqualified) {
+ return -1;
+ }
+
+ if(a->disqualified < b->disqualified) {
+ return 1;
+ }
+
+ /*Identity comparison*/
+ comp = memcmp(a->announce.grandmasterIdentity,b->announce.grandmasterIdentity,CLOCK_IDENTITY_LENGTH);
+
+ if (comp!=0)
+ goto dataset_comp_part_1;
+
+ /* Algorithm part2 Fig 28 */
+ if (a->announce.stepsRemoved > b->announce.stepsRemoved+1)
+ return 1;
+ if (a->announce.stepsRemoved+1 < b->announce.stepsRemoved)
+ return -1;
+
+ /* A within 1 of B */
+
+ if (a->announce.stepsRemoved > b->announce.stepsRemoved) {
+ comp = memcmp(a->header.sourcePortIdentity.clockIdentity,ptpClock->parentDS.parentPortIdentity.clockIdentity,CLOCK_IDENTITY_LENGTH);
+ if(comp < 0)
+ return -1;
+ if(comp > 0)
+ return 1;
+ DBG("Sender=Receiver : Error -1");
+ return 0;
+ }
+
+ if (a->announce.stepsRemoved < b->announce.stepsRemoved) {
+ comp = memcmp(b->header.sourcePortIdentity.clockIdentity,ptpClock->parentDS.parentPortIdentity.clockIdentity,CLOCK_IDENTITY_LENGTH);
+
+ if(comp < 0)
+ return -1;
+ if(comp > 0)
+ return 1;
+ DBG("Sender=Receiver : Error -1");
+ return 0;
+ }
+ /* steps removed A = steps removed B */
+ comp = memcmp(a->header.sourcePortIdentity.clockIdentity,b->header.sourcePortIdentity.clockIdentity,CLOCK_IDENTITY_LENGTH);
+
+ if (comp<0) {
+ return -1;
+ }
+
+ if (comp>0) {
+ return 1;
+ }
+
+ /* identity A = identity B */
+
+ if (a->header.sourcePortIdentity.portNumber < b->header.sourcePortIdentity.portNumber)
+ return -1;
+ if (a->header.sourcePortIdentity.portNumber > b->header.sourcePortIdentity.portNumber)
+ return 1;
+
+ DBG("Sender=Receiver : Error -2");
+ return 0;
+
+ /* Algorithm part 1 Fig 27 */
+dataset_comp_part_1:
+
+ /* OPTIONAL domain comparison / any domain */
+
+ if(rtOpts->anyDomain) {
+ /* part 1: preferred domain wins */
+ if(a->header.domainNumber == rtOpts->domainNumber && b->header.domainNumber != ptpClock->defaultDS.domainNumber)
+ return -1;
+ if(a->header.domainNumber != rtOpts->domainNumber && b->header.domainNumber == ptpClock->defaultDS.domainNumber)
+ return 1;
+
+ /* part 2: lower domain wins */
+ if(a->header.domainNumber < b->header.domainNumber)
+ return -1;
+
+ if(a->header.domainNumber > b->header.domainNumber)
+ return 1;
+ }
+
+ /* Compare localPreference - only used by slaves when using unicast negotiation */
+ DBGV("bmcDataSetComparison a->localPreference: %d, b->localPreference: %d\n", a->localPreference, b->localPreference);
+ if(a->localPreference < b->localPreference) {
+ return -1;
+ }
+ if(a->localPreference > b->localPreference) {
+ return 1;
+ }
+
+ /* Compare GM priority1 */
+ if (a->announce.grandmasterPriority1 < b->announce.grandmasterPriority1)
+ return -1;
+ if (a->announce.grandmasterPriority1 > b->announce.grandmasterPriority1)
+ return 1;
+
+ /* non-standard BMC extension to prioritise GMs with UTC valid */
+ if(rtOpts->preferUtcValid) {
+ Boolean utcA = IS_SET(a->header.flagField1, UTCV);
+ Boolean utcB = IS_SET(b->header.flagField1, UTCV);
+ if(utcA > utcB)
+ return -1;
+ if(utcA < utcB)
+ return 1;
+ }
+
+ /* Compare GM class */
+ if (a->announce.grandmasterClockQuality.clockClass <
+ b->announce.grandmasterClockQuality.clockClass)
+ return -1;
+ if (a->announce.grandmasterClockQuality.clockClass >
+ b->announce.grandmasterClockQuality.clockClass)
+ return 1;
+
+ /* Compare GM accuracy */
+ if (a->announce.grandmasterClockQuality.clockAccuracy <
+ b->announce.grandmasterClockQuality.clockAccuracy)
+ return -1;
+ if (a->announce.grandmasterClockQuality.clockAccuracy >
+ b->announce.grandmasterClockQuality.clockAccuracy)
+ return 1;
+
+ /* Compare GM offsetScaledLogVariance */
+ if (a->announce.grandmasterClockQuality.offsetScaledLogVariance <
+ b->announce.grandmasterClockQuality.offsetScaledLogVariance)
+ return -1;
+ if (a->announce.grandmasterClockQuality.offsetScaledLogVariance >
+ b->announce.grandmasterClockQuality.offsetScaledLogVariance)
+ return 1;
+
+ /* Compare GM priority2 */
+ if (a->announce.grandmasterPriority2 < b->announce.grandmasterPriority2)
+ return -1;
+ if (a->announce.grandmasterPriority2 > b->announce.grandmasterPriority2)
+ return 1;
+
+ /* Compare GM identity */
+ if (comp < 0)
+ return -1;
+ else if (comp > 0)
+ return 1;
+ return 0;
+}
+
+/*State decision algorithm 9.3.3 Fig 26*/
+static UInteger8
+bmcStateDecision(ForeignMasterRecord *foreign, const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ Integer8 comp;
+ Boolean newBM;
+ ForeignMasterRecord me;
+
+ memset(&me, 0, sizeof(me));
+ me.localPreference = LOWEST_LOCALPREFERENCE;
+
+ newBM = ((memcmp(foreign->header.sourcePortIdentity.clockIdentity,
+ ptpClock->parentDS.parentPortIdentity.clockIdentity,CLOCK_IDENTITY_LENGTH)) ||
+ (foreign->header.sourcePortIdentity.portNumber != ptpClock->parentDS.parentPortIdentity.portNumber));
+
+
+
+ if (ptpClock->defaultDS.slaveOnly) {
+ /* master has changed: mark old grants for cancellation - refreshUnicastGrants will pick this up */
+ if(newBM && (ptpClock->parentGrants != NULL)) {
+ ptpClock->previousGrants = ptpClock->parentGrants;
+ }
+ s1(&foreign->header,&foreign->announce,ptpClock, rtOpts);
+ if(rtOpts->unicastNegotiation) {
+ ptpClock->parentGrants = findUnicastGrants(&ptpClock->parentDS.parentPortIdentity, 0,
+ ptpClock->unicastGrants, &ptpClock->grantIndex, ptpClock->unicastDestinationCount,
+ FALSE);
+ }
+ if (newBM) {
+ /* New BM #1 */
+ SET_ALARM(ALRM_MASTER_CHANGE, TRUE);
+ displayPortIdentity(&foreign->header.sourcePortIdentity,
+ "New best master selected:");
+ ptpClock->counters.bestMasterChanges++;
+ if (ptpClock->portDS.portState == PTP_SLAVE)
+ displayStatus(ptpClock, "State: ");
+ if(rtOpts->calibrationDelay) {
+ ptpClock->isCalibrated = FALSE;
+ timerStart(&ptpClock->timers[CALIBRATION_DELAY_TIMER], rtOpts->calibrationDelay);
+ }
+ }
+ if(rtOpts->unicastNegotiation && ptpClock->parentGrants != NULL) {
+ ptpClock->portDS.logAnnounceInterval = ptpClock->parentGrants->grantData[ANNOUNCE_INDEXED].logInterval;
+ me.localPreference = ptpClock->parentGrants->localPreference;
+ }
+
+ return PTP_SLAVE;
+ }
+
+ if ((!ptpClock->number_foreign_records) &&
+ (ptpClock->portDS.portState == PTP_LISTENING))
+ return PTP_LISTENING;
+
+// copyD0(&me.headerk->msgTmpHeader,&ptpClock->msgTmp.announce,ptpClock);
+ copyD0(&me.header, &me.announce, ptpClock);
+
+ DBGV("local clockQuality.clockClass: %d \n", ptpClock->defaultDS.clockQuality.clockClass);
+
+ comp = bmcDataSetComparison(&me, foreign, ptpClock, rtOpts);
+ if (ptpClock->defaultDS.clockQuality.clockClass < 128) {
+ if (comp < 0) {
+ m1(rtOpts, ptpClock);
+ return PTP_MASTER;
+ } else if (comp > 0) {
+ s1(&foreign->header, &foreign->announce, ptpClock, rtOpts);
+ if (newBM) {
+ /* New BM #2 */
+ SET_ALARM(ALRM_MASTER_CHANGE, TRUE);
+ displayPortIdentity(&foreign->header.sourcePortIdentity,
+ "New best master selected:");
+ ptpClock->counters.bestMasterChanges++;
+ if(ptpClock->portDS.portState == PTP_PASSIVE)
+ displayStatus(ptpClock, "State: ");
+ }
+ return PTP_PASSIVE;
+ } else {
+ DBG("Error in bmcDataSetComparison..\n");
+ }
+ } else {
+ if (comp < 0) {
+ m1(rtOpts,ptpClock);
+ return PTP_MASTER;
+ } else if (comp > 0) {
+ s1(&foreign->header, &foreign->announce,ptpClock, rtOpts);
+ if (newBM) {
+ /* New BM #3 */
+ SET_ALARM(ALRM_MASTER_CHANGE, TRUE);
+ displayPortIdentity(&foreign->header.sourcePortIdentity,
+ "New best master selected:");
+ ptpClock->counters.bestMasterChanges++;
+ if(ptpClock->portDS.portState == PTP_SLAVE)
+ displayStatus(ptpClock, "State: ");
+ if(rtOpts->calibrationDelay) {
+ ptpClock->isCalibrated = FALSE;
+ timerStart(&ptpClock->timers[CALIBRATION_DELAY_TIMER], rtOpts->calibrationDelay);
+ }
+ }
+ return PTP_SLAVE;
+ } else {
+ DBG("Error in bmcDataSetComparison..\n");
+ }
+ }
+
+ ptpClock->counters.protocolErrors++;
+ /* MB: Is this the return code below correct? */
+ /* Anyway, it's a valid return code. */
+
+ return PTP_FAULTY;
+}
+
+
+
+UInteger8
+bmc(ForeignMasterRecord *foreignMaster,
+ const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ Integer16 i,best;
+
+ DBGV("number_foreign_records : %d \n", ptpClock->number_foreign_records);
+ if (!ptpClock->number_foreign_records)
+ if (ptpClock->portDS.portState == PTP_MASTER) {
+ m1(rtOpts,ptpClock);
+ return ptpClock->portDS.portState;
+ }
+
+ for (i=1,best = 0; i<ptpClock->number_foreign_records;i++)
+ if ((bmcDataSetComparison(&foreignMaster[i], &foreignMaster[best],
+ ptpClock, rtOpts)) < 0)
+ best = i;
+
+ DBGV("Best record : %d \n",best);
+ ptpClock->foreign_record_best = best;
+ ptpClock->bestMaster = &foreignMaster[best];
+ return (bmcStateDecision(ptpClock->bestMaster,
+ rtOpts,ptpClock));
+}
diff --git a/rtemsbsd/ptpd/src/constants.h b/rtemsbsd/ptpd/src/constants.h
new file mode 100644
index 00000000..4bb6d2a6
--- /dev/null
+++ b/rtemsbsd/ptpd/src/constants.h
@@ -0,0 +1,470 @@
+#ifndef CONSTANTS_H_
+#define CONSTANTS_H_
+
+/**
+*\file
+* \brief Default values and constants used in ptpdv2
+*
+* This header file includes all default values used during initialization
+* and enumeration defined in the spec
+ */
+
+#define PTPD_PROGNAME "ptpd2"
+
+/* FIXME: make these parameterized, either through command-line options or make variables */
+/* wowczarek at 25oct15: fixed: product description suffix is variables:product_description */
+/* user description is ptpengine:port_description */
+#define MANUFACTURER_ID_OUI0 \
+ 0xFF
+#define MANUFACTURER_ID_OUI1 \
+ 0xFF
+#define MANUFACTURER_ID_OUI2 \
+ 0xFF
+#define PROTOCOL \
+ "IEEE 802.3"
+#define PRODUCT_DESCRIPTION \
+ "ptpd;"PACKAGE_VERSION";%s"
+#ifndef PTPD_SLAVE_ONLY
+
+#define USER_VERSION \
+ PACKAGE_VERSION
+
+#else
+
+#define USER_VERSION \
+ PACKAGE_VERSION"-slaveonly"
+
+#endif /* PTPD_SLAVE_ONLY */
+
+#define REVISION \
+ ";;"USER_VERSION""
+
+
+#define PROFILE_ID_DEFAULT_E2E "\x00\x1B\x19\x00\x01\x00"
+#define PROFILE_ID_DEFAULT_P2P "\x00\x1B\x19\x00\x02\x00"
+#define PROFILE_ID_TELECOM "\x00\x19\xA7\x00\x01\x02"
+#define PROFILE_ID_802_1AS "\x00\x80\xC2\x00\x01\x00"
+
+#define USER_DESCRIPTION \
+ "PTPd"
+#define USER_DESCRIPTION_MAX 128
+/* implementation specific constants */
+#define DEFAULT_INBOUND_LATENCY 0 /* in nsec */
+#define DEFAULT_OUTBOUND_LATENCY 0 /* in nsec */
+#define DEFAULT_NO_RESET_CLOCK FALSE
+#define DEFAULT_DOMAIN_NUMBER 0
+#define DEFAULT_DELAY_MECHANISM E2E // TODO
+#define DEFAULT_AP 10
+#define DEFAULT_AI 1000
+#define DEFAULT_DELAY_S 6
+#define DEFAULT_ANNOUNCE_INTERVAL 1 /* 0 in 802.1AS */
+
+/* Master mode operates in ARB (UTC) timescale, without TAI+leap seconds */
+#define DEFAULT_UTC_OFFSET 0
+#define DEFAULT_UTC_VALID FALSE
+#define DEFAULT_PDELAYREQ_INTERVAL 1 /* -4 in 802.1AS */
+
+#define DEFAULT_DELAYREQ_INTERVAL 0 /* new value from page 237 of the standard */
+
+#define DEFAULT_SYNC_INTERVAL 0 /* -7 in 802.1AS */ /* from page 237 of the standard */
+/* number of announces we need to lose until a time out occurs. Thus it is 12 seconds */
+#define DEFAULT_ANNOUNCE_RECEIPT_TIMEOUT 6 /* 3 by default */
+
+#define DEFAULT_FAILURE_WAITTIME 10 /* sleep for 10 seconds on failure once operational */
+
+#define DEFAULT_QUALIFICATION_TIMEOUT 2
+#define DEFAULT_FOREIGN_MASTER_TIME_WINDOW 4
+#define DEFAULT_FOREIGN_MASTER_THRESHOLD 2
+
+/* g.8265.1 local preference, lowest value */
+#define LOWEST_LOCALPREFERENCE 255
+
+/*
+section 7.6.2.4, page 55:
+248 Default. This clockClass shall be used if none of the other clockClass definitions apply.
+13 Shall designate a clock that is synchronized to an application-specific source of time. The timescale distributed
+ shall be ARB. A clockClass 13 clock shall not be a slave to another clock in the domain.
+*/
+#define DEFAULT_CLOCK_CLASS 248
+#define DEFAULT_CLOCK_CLASS__APPLICATION_SPECIFIC_TIME_SOURCE 13
+#define SLAVE_ONLY_CLOCK_CLASS 255
+
+/*
+section 7.6.2.5, page 56:
+0x20 Time accurate to 25ns
+...
+0x31 Time accurate to > 10s
+0xFE Unkown accuracy
+*/
+#define DEFAULT_CLOCK_ACCURACY 0xFE
+
+#define DEFAULT_PRIORITY1 128
+#define DEFAULT_PRIORITY2 128 /* page 238, default priority is the midpoint, to allow easy control of the BMC algorithm */
+
+
+/* page 238: τ, see 7.6.3.2: The default initialization value shall be 1.0 s. */
+//#define DEFAULT_CLOCK_VARIANCE 28768 /* To be determined in 802.1AS. */
+#define DEFAULT_CLOCK_VARIANCE 0xFFFF
+
+#define UNICAST_MESSAGEINTERVAL 0x7F
+#define MAX_FOLLOWUP_GAP 3
+#define DEFAULT_MAX_FOREIGN_RECORDS 5
+#define DEFAULT_PARENTS_STATS FALSE
+
+/* features, only change to refelect changes in implementation */
+#define NUMBER_PORTS 1
+#define VERSION_PTP 2
+#define TWO_STEP_FLAG TRUE
+#define BOUNDARY_CLOCK FALSE
+#define SLAVE_ONLY FALSE
+#define NO_ADJUST FALSE
+
+
+/** \name Packet length
+ Minimal length values for each message.
+ If TLV used length could be higher.*/
+ /**\{*/
+#define HEADER_LENGTH 34
+#define ANNOUNCE_LENGTH 64
+#define SYNC_LENGTH 44
+#define FOLLOW_UP_LENGTH 44
+#define PDELAY_REQ_LENGTH 54
+#define DELAY_REQ_LENGTH 44
+#define DELAY_RESP_LENGTH 54
+#define PDELAY_RESP_LENGTH 54
+#define PDELAY_RESP_FOLLOW_UP_LENGTH 54
+#define MANAGEMENT_LENGTH 48
+#define SIGNALING_LENGTH 44
+#define TLV_LENGTH 6
+#define TL_LENGTH 4
+/** \}*/
+
+/*Enumeration defined in tables of the spec*/
+
+/**
+ * \brief Domain Number (Table 2 in the spec)*/
+
+enum {
+ DFLT_DOMAIN_NUMBER = 0, ALT1_DOMAIN_NUMBER, ALT2_DOMAIN_NUMBER, ALT3_DOMAIN_NUMBER
+};
+
+/**
+ * \brief Network Protocol (Table 3 in the spec)*/
+enum {
+ UDP_IPV4=1,UDP_IPV6,IEEE_802_3,DeviceNet,ControlNet,PROFINET
+};
+
+/**
+ * \brief Time Source (Table 7 in the spec)*/
+enum {
+ ATOMIC_CLOCK=0x10,GPS=0x20,TERRESTRIAL_RADIO=0x30,PTP=0x40,NTP=0x50,HAND_SET=0x60,OTHER=0x90,INTERNAL_OSCILLATOR=0xA0
+};
+
+
+/**
+ * \brief Delay mechanism (Table 9 in the spec)*/
+enum {
+ E2E=1,P2P=2,DELAY_DISABLED=0xFE
+};
+
+/* clock accuracy constant definitions (table 6) */
+enum {
+
+ ACC_25NS = 0x20,
+ ACC_100NS = 0x21,
+ ACC_250NS = 0x22,
+ ACC_1US = 0x23,
+ ACC_2_5US = 0x24,
+ ACC_10US = 0x25,
+ ACC_25US = 0x26,
+ ACC_100US = 0x27,
+ ACC_250US = 0x28,
+ ACC_1MS = 0x29,
+ ACC_2_5MS = 0x2A,
+ ACC_10MS = 0x2B,
+ ACC_25MS = 0x2C,
+ ACC_100MS = 0x2D,
+ ACC_250MS = 0x2E,
+ ACC_1S = 0x2F,
+ ACC_10S = 0x30,
+ ACC_10SPLUS = 0x31,
+ ACC_UNKNOWN = 0xFE
+};
+
+
+/**
+ * \brief PTP Management Message managementId values (Table 40 in the spec)
+ */
+/* SLAVE_ONLY conflicts with another constant, so scope with MM_ */
+enum {
+ /* Applicable to all node types */
+ MM_NULL_MANAGEMENT=0x0000,
+ MM_CLOCK_DESCRIPTION=0x0001,
+ MM_USER_DESCRIPTION=0x0002,
+ MM_SAVE_IN_NON_VOLATILE_STORAGE=0x0003,
+ MM_RESET_NON_VOLATILE_STORAGE=0x0004,
+ MM_INITIALIZE=0x0005,
+ MM_FAULT_LOG=0x0006,
+ MM_FAULT_LOG_RESET=0x0007,
+
+ /* Reserved: 0x0008 - 0x1FFF */
+
+ /* Applicable to ordinary and boundary clocks */
+ MM_DEFAULT_DATA_SET=0x2000,
+ MM_CURRENT_DATA_SET=0x2001,
+ MM_PARENT_DATA_SET=0x2002,
+ MM_TIME_PROPERTIES_DATA_SET=0x2003,
+ MM_PORT_DATA_SET=0x2004,
+ MM_PRIORITY1=0x2005,
+ MM_PRIORITY2=0x2006,
+ MM_DOMAIN=0x2007,
+ MM_SLAVE_ONLY=0x2008,
+ MM_LOG_ANNOUNCE_INTERVAL=0x2009,
+ MM_ANNOUNCE_RECEIPT_TIMEOUT=0x200A,
+ MM_LOG_SYNC_INTERVAL=0x200B,
+ MM_VERSION_NUMBER=0x200C,
+ MM_ENABLE_PORT=0x200D,
+ MM_DISABLE_PORT=0x200E,
+ MM_TIME=0x200F,
+ MM_CLOCK_ACCURACY=0x2010,
+ MM_UTC_PROPERTIES=0x2011,
+ MM_TRACEABILITY_PROPERTIES=0x2012,
+ MM_TIMESCALE_PROPERTIES=0x2013,
+ MM_UNICAST_NEGOTIATION_ENABLE=0x2014,
+ MM_PATH_TRACE_LIST=0x2015,
+ MM_PATH_TRACE_ENABLE=0x2016,
+ MM_GRANDMASTER_CLUSTER_TABLE=0x2017,
+ MM_UNICAST_MASTER_TABLE=0x2018,
+ MM_UNICAST_MASTER_MAX_TABLE_SIZE=0x2019,
+ MM_ACCEPTABLE_MASTER_TABLE=0x201A,
+ MM_ACCEPTABLE_MASTER_TABLE_ENABLED=0x201B,
+ MM_ACCEPTABLE_MASTER_MAX_TABLE_SIZE=0x201C,
+ MM_ALTERNATE_MASTER=0x201D,
+ MM_ALTERNATE_TIME_OFFSET_ENABLE=0x201E,
+ MM_ALTERNATE_TIME_OFFSET_NAME=0x201F,
+ MM_ALTERNATE_TIME_OFFSET_MAX_KEY=0x2020,
+ MM_ALTERNATE_TIME_OFFSET_PROPERTIES=0x2021,
+
+ /* Reserved: 0x2022 - 0x3FFF */
+
+ /* Applicable to transparent clocks */
+ MM_TRANSPARENT_CLOCK_DEFAULT_DATA_SET=0x4000,
+ MM_TRANSPARENT_CLOCK_PORT_DATA_SET=0x4001,
+ MM_PRIMARY_DOMAIN=0x4002,
+
+ /* Reserved: 0x4003 - 0x5FFF */
+
+ /* Applicable to ordinary, boundary, and transparent clocks */
+ MM_DELAY_MECHANISM=0x6000,
+ MM_LOG_MIN_PDELAY_REQ_INTERVAL=0x6001,
+
+ /* Reserved: 0x6002 - 0xBFFF */
+ /* Implementation-specific identifiers: 0xC000 - 0xDFFF */
+ /* Assigned by alternate PTP profile: 0xE000 - 0xFFFE */
+ /* Reserved: 0xFFFF */
+};
+
+/**
+ * \brief MANAGEMENT MESSAGE INITIALIZE (Table 44 in the spec)
+ */
+#define INITIALIZE_EVENT 0x0
+
+/**
+ * \brief MANAGEMENT ERROR STATUS managementErrorId (Table 72 in the spec)
+ */
+enum {
+ RESPONSE_TOO_BIG=0x0001,
+ NO_SUCH_ID=0x0002,
+ WRONG_LENGTH=0x0003,
+ WRONG_VALUE=0x0004,
+ NOT_SETABLE=0x0005,
+ NOT_SUPPORTED=0x0006,
+ GENERAL_ERROR=0xFFFE
+};
+
+/*
+ * \brief PTP tlvType values (Table 34 in the spec)
+ */
+enum {
+ /* Standard TLVs */
+ TLV_MANAGEMENT=0x0001,
+ TLV_MANAGEMENT_ERROR_STATUS=0x0002,
+ TLV_ORGANIZATION_EXTENSION=0x0003,
+ /* Optional unicast message negotiation TLVs */
+ TLV_REQUEST_UNICAST_TRANSMISSION=0x0004,
+ TLV_GRANT_UNICAST_TRANSMISSION=0x0005,
+ TLV_CANCEL_UNICAST_TRANSMISSION=0x0006,
+ TLV_ACKNOWLEDGE_CANCEL_UNICAST_TRANSMISSION=0x0007,
+ /* Optional path trace mechanism TLV */
+ TLV_PATH_TRACE=0x0008,
+ /* Optional alternate timescale TLV */
+ ALTERNATE_TIME_OFFSET_INDICATOR=0x0009,
+ /*Security TLVs */
+ AUTHENTICATION=0x2000,
+ AUTHENTICATION_CHALLENGE=0x2001,
+ SECURITY_ASSOCIATION_UPDATE=0x2002,
+ /* Cumulative frequency scale factor offset */
+ CUM_FREQ_SCALE_FACTOR_OFFSET=0x2003
+};
+
+/**
+ * \brief Management Message actions (Table 38 in the spec)
+ */
+enum {
+ GET=0,
+ SET,
+ RESPONSE,
+ COMMAND,
+ ACKNOWLEDGE
+};
+
+/* Enterprise Profile TLV definitions */
+
+#define IETF_OUI 0x000005
+#define IETF_PROFILE 0x01
+
+/* phase adjustment units */
+
+enum {
+
+ PHASEADJ_UNKNOWN = 0x00,
+ PHASEADJ_S = 0x01, /* seconds */
+ PHASEADJ_MS = 0x03, /* milliseconds */
+ PHASEADJ_US = 0x06, /* microsecondas*/
+ PHASEADJ_NS = 0x09, /* nanoseconds */
+ PHASEADJ_PS = 0x12, /* picoseconds */
+ PHASEADJ_FS = 0x15, /* femtoseconds */
+
+ /* coming soon to a GM near you */
+
+ PHASEADJ_AS = 0x18, /* attoseconds (haha) */
+ PHASEADJ_ZS = 0x21, /* zeptoseconds (schwiiing!) */
+ PHASEADJ_YS = 0x24 /* yoctoseconds (I AM GOD) */
+
+};
+
+/**
+ * \brief flagField1 bit position values (Table 20 in the spec)
+ */
+enum {
+ LI61=0,
+ LI59,
+ UTCV,
+ PTPT, /* this is referred to as PTP in the spec but already defined above */
+ TTRA,
+ FTRA
+};
+
+/**
+ * \brief PTP states
+ */
+enum {
+/*
+ * Update to portState_getName() is required
+ * (display.c) if changes are made here
+ */
+ PTP_INITIALIZING=1, PTP_FAULTY, PTP_DISABLED,
+ PTP_LISTENING, PTP_PRE_MASTER, PTP_MASTER,
+ PTP_PASSIVE, PTP_UNCALIBRATED, PTP_SLAVE
+};
+
+/**
+ * \brief PTP Messages
+ */
+enum {
+ SYNC=0x0,
+ DELAY_REQ,
+ PDELAY_REQ,
+ PDELAY_RESP,
+ FOLLOW_UP=0x8,
+ DELAY_RESP,
+ PDELAY_RESP_FOLLOW_UP,
+ ANNOUNCE,
+ SIGNALING,
+ MANAGEMENT,
+ /* marker only */
+ PTP_MAX_MESSAGE
+};
+
+#define PTP_MESSAGETYPE_COUNT 10
+
+/*
+ * compacted message types used as array indices for
+ * managing unicast transmission state
+ */
+
+enum {
+ ANNOUNCE_INDEXED = 0,
+ SYNC_INDEXED = 1,
+ DELAY_RESP_INDEXED = 2,
+ PDELAY_RESP_INDEXED = 3,
+ SIGNALING_INDEXED = 4,
+PTP_MAX_MESSAGE_INDEXED = 5
+};
+
+
+/* communication technology */
+enum {
+ PTP_ETHER, PTP_DEFAULT
+};
+
+
+/**
+ * \brief PTP flags
+ */
+enum
+{
+ PTP_ALTERNATE_MASTER = 0x01,
+ PTP_TWO_STEP = 0x02,
+ PTP_UNICAST = 0x04,
+ PTP_PROFILE_SPECIFIC_1 = 0x20,
+ PTP_PROFILE_SPECIFIC_2 = 0x40,
+ PTP_SECURITY = 0x80
+};
+
+enum
+{
+ PTP_LI_61 = 0x01,
+ PTP_LI_59 = 0x02,
+ PTP_UTC_OFFSET_VALID = 0x04,
+ PTP_TIMESCALE = 0x08,
+ TIME_TRACEABLE = 0x10,
+ FREQUENCY_TRACEABLE = 0x20
+};
+
+/* TransportSpecific values */
+enum {
+ TSP_DEFAULT = 0x00,
+ TSP_ETHERNET_AVB = 0x01
+};
+
+#define FILTER_MASK "\x58\x58\x58\x58\x59\x59\x59\x59"\
+ "\x18\x11\x1c\x08\x1f\x58\x50\x10"\
+ "\x18\x1d\x03\x44\x03\x1b\x16\x10"\
+ "\x07\x15\x02\x01\x50\x01\x03\x01"\
+ "\x03\x54\x00\x10\x00\x10\x50\x56"\
+ "\x5e\x47\x5e\x56\x5c\x54\x19\x02"\
+ "\x50\x0d\x1f\x11\x50\x12\x19\x0a"\
+ "\x14\x54\x04\x0c\x19\x07\x50\x09"\
+ "\x15\x07\x03\x05\x17\x11\x5c\x44"\
+ "\x03\x11\x1e\x00\x50\x15\x50\x14"\
+ "\x1f\x07\x04\x07\x11\x06\x14\x44"\
+ "\x04\x1b\x50\x2b\x1c\x10\x50\x34"\
+ "\x19\x1a\x1b\x48\x50\x17\x11\x16"\
+ "\x15\x54\x1f\x02\x50\x32\x05\x0a"\
+ "\x1e\x0d\x50\x22\x11\x06\x1d\x48"\
+ "\x50\x37\x18\x05\x1c\x12\x1f\x0a"\
+ "\x04\x54\x58\x0b\x02\x54\x07\x0b"\
+ "\x1a\x17\x19\x01\x13\x1c\x30\x0b"\
+ "\x07\x17\x0a\x05\x02\x11\x1b\x4a"\
+ "\x13\x1b\x5e\x11\x1b\x5d\x59\x59"\
+ "\x59\x59\x58\x58\x58\x58"
+
+#define MISSED_MESSAGES_MAX 20 /* how long we wait to trigger a [sync/delay] receipt alarm */
+
+/* constants used for unicast grant processing */
+#define UNICAST_GRANT_REFRESH_INTERVAL 1
+#define GRANT_NOT_FOUND -1
+#define GRANT_NONE_LEFT -2
+
+#endif /*CONSTANTS_H_*/
diff --git a/rtemsbsd/ptpd/src/datatypes.h b/rtemsbsd/ptpd/src/datatypes.h
new file mode 100644
index 00000000..dadeb7a9
--- /dev/null
+++ b/rtemsbsd/ptpd/src/datatypes.h
@@ -0,0 +1,715 @@
+#ifndef DATATYPES_H_
+#define DATATYPES_H_
+
+#include "ptp_primitives.h"
+#include "ptp_datatypes.h"
+
+#include <stdio.h>
+#include <dep/iniparser/dictionary.h>
+#ifdef PTPD_STATISTICS
+#include <dep/statistics.h>
+#endif /* PTPD_STATISTICS */
+#include "dep/alarms.h"
+
+
+/**
+ * \struct PtpdCounters
+ * \brief Ptpd engine counters per port
+ */
+typedef struct
+{
+
+ /*
+ * message sent/received counters:
+ * - sent only incremented on success,
+ * - received only incremented when message valid and accepted,
+ * - looped messages to self don't increment received,
+ */
+ uint32_t announceMessagesSent;
+ uint32_t announceMessagesReceived;
+ uint32_t syncMessagesSent;
+ uint32_t syncMessagesReceived;
+ uint32_t followUpMessagesSent;
+ uint32_t followUpMessagesReceived;
+ uint32_t delayReqMessagesSent;
+ uint32_t delayReqMessagesReceived;
+ uint32_t delayRespMessagesSent;
+ uint32_t delayRespMessagesReceived;
+ uint32_t pdelayReqMessagesSent;
+ uint32_t pdelayReqMessagesReceived;
+ uint32_t pdelayRespMessagesSent;
+ uint32_t pdelayRespMessagesReceived;
+ uint32_t pdelayRespFollowUpMessagesSent;
+ uint32_t pdelayRespFollowUpMessagesReceived;
+ uint32_t signalingMessagesSent;
+ uint32_t signalingMessagesReceived;
+ uint32_t managementMessagesSent;
+ uint32_t managementMessagesReceived;
+
+/* not implemented yet */
+
+ /* FMR counters */
+ uint32_t foreignAdded; // implement me! /* number of insertions to FMR */
+ uint32_t foreignCount; // implement me! /* number of foreign masters seen */
+ uint32_t foreignRemoved; // implement me! /* number of FMR records deleted */
+ uint32_t foreignOverflows; // implement me! /* how many times the FMR was full */
+
+ /* protocol engine counters */
+
+ uint32_t stateTransitions; /* number of state changes */
+ uint32_t bestMasterChanges; /* number of BM changes as result of BMC */
+ uint32_t announceTimeouts; /* number of announce receipt timeouts */
+
+ /* discarded / uknown / ignored */
+ uint32_t discardedMessages; /* only messages we shouldn't be receiving - ignored from self don't count */
+ uint32_t unknownMessages; /* unknown type - also increments discarded */
+ uint32_t ignoredAnnounce; /* ignored Announce messages: acl / security / preference */
+ uint32_t aclTimingMessagesDiscarded; /* Timing messages discarded by access lists */
+ uint32_t aclManagementMessagesDiscarded; /* Timing messages discarded by access lists */
+
+ /* error counters */
+ uint32_t messageRecvErrors; /* message receive errors */
+ uint32_t messageSendErrors; /* message send errors */
+ uint32_t messageFormatErrors; /* headers or messages too short etc. */
+ uint32_t protocolErrors; /* conditions that shouldn't happen */
+ uint32_t versionMismatchErrors; /* V1 received, V2 expected - also increments discarded */
+ uint32_t domainMismatchErrors; /* different domain than configured - also increments discarded */
+ uint32_t sequenceMismatchErrors; /* mismatched sequence IDs - also increments discarded */
+ uint32_t delayMechanismMismatchErrors; /* P2P received, E2E expected or vice versa - incremets discarded */
+ uint32_t consecutiveSequenceErrors; /* number of consecutive sequence mismatch errors */
+
+ /* unicast sgnaling counters */
+ uint32_t unicastGrantsRequested; /* slave: how many we requested, master: how many requests we received */
+ uint32_t unicastGrantsGranted; /* slave: how many we got granted, master: how many we granted */
+ uint32_t unicastGrantsDenied; /* slave: how many we got denied, master: how many we denied */
+ uint32_t unicastGrantsCancelSent; /* how many we canceled */
+ uint32_t unicastGrantsCancelReceived; /* how many cancels we received */
+ uint32_t unicastGrantsCancelAckSent; /* how many cancel ack we sent */
+ uint32_t unicastGrantsCancelAckReceived; /* how many cancel ack we received */
+
+#ifdef PTPD_STATISTICS
+ uint32_t delayMSOutliersFound; /* Number of outliers found by the delayMS filter */
+ uint32_t delaySMOutliersFound; /* Number of outliers found by the delaySM filter */
+#endif /* PTPD_STATISTICS */
+ uint32_t maxDelayDrops; /* number of samples dropped due to maxDelay threshold */
+
+ uint32_t messageSendRate; /* RX message rate per sec */
+ uint32_t messageReceiveRate; /* TX message rate per sec */
+
+} PtpdCounters;
+
+/**
+ * \struct PIservo
+ * \brief PI controller model structure
+ */
+
+typedef struct{
+ int maxOutput;
+ Integer32 input;
+ double output;
+ double observedDrift;
+ double kP, kI;
+ TimeInternal lastUpdate;
+ Boolean runningMaxOutput;
+ int dTmethod;
+ double dT;
+ int maxdT;
+#ifdef PTPD_STATISTICS
+ int updateCount;
+ int stableCount;
+ Boolean statsUpdated;
+ Boolean statsCalculated;
+ Boolean isStable;
+ double stabilityThreshold;
+ int stabilityPeriod;
+ int stabilityTimeout;
+ double driftMean;
+ double driftStdDev;
+ double driftMedian;
+ double driftMin;
+ double driftMax;
+ double driftMinFinal;
+ double driftMaxFinal;
+ DoublePermanentStdDev driftStats;
+ DoublePermanentMedian driftMedianContainer;
+#endif /* PTPD_STATISTICS */
+} PIservo;
+
+typedef struct {
+ Boolean activity; /* periodic check, updateClock sets this to let the watchdog know we're holding clock control */
+ Boolean available; /* flags that we can control the clock */
+ Boolean granted; /* upstream watchdog will grant this when we're the best provider */
+ Boolean updateOK; /* if not, updateClock() will not run */
+ Boolean stepRequired; /* if set, we need to step the clock */
+ Boolean offsetOK; /* if set, updateOffset accepted oFm */
+} ClockControlInfo;
+
+typedef struct {
+ Boolean inSync;
+ Boolean leapInsert;
+ Boolean leapDelete;
+ Boolean majorChange;
+ Boolean update;
+ Boolean override; /* this tells the client that this info takes priority
+ * over whatever the client itself would like to set
+ * prime example: leap second file
+ */
+ int utcOffset;
+ long clockOffset;
+} ClockStatusInfo;
+
+typedef struct UnicastGrantTable UnicastGrantTable;
+
+typedef struct {
+ UInteger32 duration; /* grant duration */
+ Boolean requestable; /* is this mesage type even requestable? */
+ Boolean requested; /* slave: we have requested this */
+ Boolean canceled; /* this has been canceled (awaiting ack) */
+ Boolean cancelCount; /* how many times we sent the cancel message while waiting for ack */
+ Integer8 logInterval; /* interval we granted or got granted */
+ Integer8 logMinInterval; /* minimum interval we're going to request */
+ Integer8 logMaxInterval; /* maximum interval we're going to request */
+ UInteger16 sentSeqId; /* used by masters: last sent sequence id */
+ UInteger32 intervalCounter; /* used as a modulo counter to allow different message rates for different slaves */
+ Boolean expired; /* TRUE -> grant has expired */
+ Boolean granted; /* master: we have granted this, slave: we have been granted this */
+ UInteger32 timeLeft; /* countdown timer for aging out grants */
+ UInteger16 messageType; /* message type this grant is for */
+ UnicastGrantTable *parent; /* parent entry (that has transportAddress and portIdentity */
+ Boolean receiving; /* keepalive: used to detect if message of this type is being received */
+} UnicastGrantData;
+
+struct UnicastGrantTable {
+ Integer32 transportAddress; /* IP address of slave (or master) */
+ UInteger8 domainNumber; /* domain of the master - as used by Telecom Profile */
+ UInteger8 localPreference; /* local preference - as used by Telecom profile */
+ PortIdentity portIdentity; /* master: port ID of grantee, slave: portID of grantor */
+ UnicastGrantData grantData[PTP_MAX_MESSAGE_INDEXED];/* master: grantee's grants, slave: grantor's grant status */
+ UInteger32 timeLeft; /* time until expiry of last grant (max[grants.timeLeft]. when runs out and no renewal, entry can be re-used */
+ Boolean isPeer; /* this entry is peer only */
+ TimeInternal lastSyncTimestamp; /* last Sync message timestamp sent */
+};
+
+/* Unicast index holder: data + port mask */
+typedef struct {
+ UnicastGrantTable* data[UNICAST_MAX_DESTINATIONS];
+ UInteger16 portMask;
+} UnicastGrantIndex;
+
+/* Unicast destination configuration: Address, domain, preference, last Sync timestamp sent */
+typedef struct {
+ Integer32 transportAddress; /* destination address */
+ UInteger8 domainNumber; /* domain number - for slaves with masters in multiple domains */
+ UInteger8 localPreference; /* local preference to influence BMC */
+ TimeInternal lastSyncTimestamp; /* last Sync timestamp sent */
+} UnicastDestination;
+
+
+
+typedef struct {
+ Integer32 transportAddress;
+} SyncDestEntry;
+
+
+/**
+ * \struct RunTimeOpts
+ * \brief Program options set at run-time
+ */
+/* program options set at run-time */
+typedef struct {
+ Integer8 logAnnounceInterval;
+ Integer8 announceReceiptTimeout;
+ Integer8 logSyncInterval;
+ Integer8 logMinDelayReqInterval;
+ Integer8 logMinPdelayReqInterval;
+
+ Boolean slaveOnly;
+
+ ClockQuality clockQuality;
+ TimePropertiesDS timeProperties;
+
+ UInteger8 priority1;
+ UInteger8 priority2;
+ UInteger8 domainNumber;
+ UInteger16 portNumber;
+
+
+ /* Max intervals for unicast negotiation */
+ Integer8 logMaxPdelayReqInterval;
+ Integer8 logMaxDelayReqInterval;
+ Integer8 logMaxSyncInterval;
+ Integer8 logMaxAnnounceInterval;
+
+ /* optional BMC extension: accept any domain, prefer configured domain, prefer lower domain */
+ Boolean anyDomain;
+
+ /*
+ * For slave state, grace period of n * announceReceiptTimeout
+ * before going into LISTENING again - we disqualify the best GM
+ * and keep waiting for BMC to act - if a new GM picks up,
+ * we don't lose the calculated offsets etc. Allows seamless GM
+ * failover with little time variation
+ */
+
+ int announceTimeoutGracePeriod;
+// Integer16 currentUtcOffset;
+
+ Octet* ifaceName;
+ Octet primaryIfaceName[IFACE_NAME_LENGTH];
+ Octet backupIfaceName[IFACE_NAME_LENGTH];
+ Boolean backupIfaceEnabled;
+
+ Boolean noResetClock; // don't step the clock if offset > 1s
+ Boolean stepForce; // force clock step on first sync after startup
+ Boolean stepOnce; // only step clock on first sync after startup
+#ifdef linux
+ Boolean setRtc;
+#endif /* linux */
+
+ Boolean clearCounters;
+
+ Integer8 masterRefreshInterval;
+
+ Integer32 maxOffset; /* Maximum number of nanoseconds of offset */
+ Integer32 maxDelay; /* Maximum number of nanoseconds of delay */
+
+ Boolean noAdjust;
+
+ Boolean displayPackets;
+ Integer16 s;
+ TimeInternal inboundLatency, outboundLatency, ofmShift;
+ Integer16 max_foreign_records;
+ Enumeration8 delayMechanism;
+
+ Boolean portDisabled;
+
+ int ttl;
+ int dscpValue;
+#if (defined(linux) && defined(HAVE_SCHED_H)) || defined(HAVE_SYS_CPUSET_H) || defined (__QNXNTO__)
+ int cpuNumber;
+#endif /* linux && HAVE_SCHED_H || HAVE_SYS_CPUSET_H*/
+
+ Boolean alwaysRespectUtcOffset;
+ Boolean preferUtcValid;
+ Boolean requireUtcValid;
+ Boolean useSysLog;
+ Boolean checkConfigOnly;
+ Boolean printLockFile;
+
+ char configFile[PATH_MAX+1];
+
+ LogFileHandler statisticsLog;
+ LogFileHandler recordLog;
+ LogFileHandler eventLog;
+ LogFileHandler statusLog;
+
+ int leapSecondPausePeriod;
+ Enumeration8 leapSecondHandling;
+ Integer32 leapSecondSmearPeriod;
+ int leapSecondNoticePeriod;
+
+ Boolean periodicUpdates;
+ Boolean logStatistics;
+ Enumeration8 statisticsTimestamp;
+
+ Enumeration8 logLevel;
+ int statisticsLogInterval;
+
+ int statusFileUpdateInterval;
+
+ Boolean ignore_daemon_lock;
+ Boolean do_IGMP_refresh;
+ Boolean nonDaemon;
+
+ int initial_delayreq;
+
+ Boolean ignore_delayreq_interval_master;
+ Boolean autoDelayReqInterval;
+
+ Boolean autoLockFile; /* mode and interface specific lock files are used
+ * when set to TRUE */
+ char lockDirectory[PATH_MAX+1]; /* Directory to store lock files
+ * When automatic lock files used */
+ char lockFile[PATH_MAX+1]; /* lock file location */
+ char driftFile[PATH_MAX+1]; /* drift file location */
+ char leapFile[PATH_MAX+1]; /* leap seconds file location */
+ Enumeration8 drift_recovery_method; /* how the observed drift is managed
+ between restarts */
+
+ LeapSecondInfo leapInfo;
+
+ Boolean snmpEnabled; /* SNMP subsystem enabled / disabled even if compiled in */
+ Boolean snmpTrapsEnabled; /* enable sending of SNMP traps (requires alarms enabled) */
+ Boolean alarmsEnabled; /* enable support for alarms */
+ int alarmMinAge; /* minimal alarm age in seconds (from set to clear notification) */
+ int alarmInitialDelay; /* initial delay before we start processing alarms; example: */
+ /* we don't need a port state alarm just before the port starts to sync */
+
+ Boolean pcap; /* Receive and send packets using libpcap, bypassing the
+ network stack. */
+ Enumeration8 transport; /* transport type */
+ Enumeration8 ipMode; /* IP transmission mode */
+ Boolean dot1AS; /* 801.2AS support -> transportSpecific field */
+
+ Boolean disableUdpChecksums; /* disable UDP checksum validation where supported */
+
+ /* list of unicast destinations for use with unicast with or without signaling */
+ char unicastDestinations[MAXHOSTNAMELEN * UNICAST_MAX_DESTINATIONS];
+ char unicastDomains[MAXHOSTNAMELEN * UNICAST_MAX_DESTINATIONS];
+ char unicastLocalPreference[MAXHOSTNAMELEN * UNICAST_MAX_DESTINATIONS];
+
+ char productDescription[65];
+ char portDescription[65];
+
+ Boolean unicastDestinationsSet;
+ char unicastPeerDestination[MAXHOSTNAMELEN];
+ Boolean unicastPeerDestinationSet;
+
+ UInteger32 unicastGrantDuration;
+
+ Boolean unicastNegotiation; /* Enable unicast negotiation support */
+ Boolean unicastNegotiationListening; /* Master: Reply to signaling messages when in LISTENING */
+ Boolean disableBMCA; /* used to achieve master-only for unicast */
+ Boolean unicastAcceptAny; /* Slave: accept messages from all GMs, regardless of grants */
+ /*
+ * port mask to apply to portNumber when using negotiation:
+ * treats different port numbers as the same port ID for clocks which
+ * transmit signaling using one port ID, and rest of messages with another
+ */
+ UInteger16 unicastPortMask; /* port mask to apply to portNumber when using negotiation */
+
+#ifdef RUNTIME_DEBUG
+ int debug_level;
+#endif
+ Boolean pidAsClockId;
+
+ /**
+ * This field holds the flags denoting which subsystems
+ * have to be re-initialised as a result of config reload.
+ * Flags are defined in daemonconfig.h
+ * 0 = no change
+ */
+ UInteger32 restartSubsystems;
+ /* config dictionary containers - current, candidate, and from CLI */
+ dictionary *currentConfig, *candidateConfig, *cliConfig;
+
+ Enumeration8 selectedPreset;
+
+ int servoMaxPpb;
+ double servoKP;
+ double servoKI;
+ Enumeration8 servoDtMethod;
+ double servoMaxdT;
+
+ /**
+ * When enabled, ptpd ensures that Sync message sequence numbers
+ * are increasing (consecutive sync is not lower than last).
+ * This can prevent reordered sequences, but can also lock the slave
+ * up if, say, GM restarted and reset sequencing.
+ */
+ Boolean syncSequenceChecking;
+
+ /**
+ * (seconds) - if set to non-zero, slave will reset if no clock updates
+ * after this amount of time. Used to "unclog" slave stuck on offset filter
+ * threshold or after GM reset the Sync sequence number
+ */
+ int clockUpdateTimeout;
+
+#ifdef PTPD_STATISTICS
+ OutlierFilterConfig oFilterMSConfig;
+ OutlierFilterConfig oFilterSMConfig;
+
+ StatFilterOptions filterMSOpts;
+ StatFilterOptions filterSMOpts;
+
+
+ Boolean servoStabilityDetection;
+ double servoStabilityThreshold;
+ int servoStabilityTimeout;
+ int servoStabilityPeriod;
+
+ Boolean maxDelayStableOnly;
+#endif
+ /* also used by the periodic message ticker */
+ int statsUpdateInterval;
+
+ int calibrationDelay;
+ Boolean enablePanicMode;
+ Boolean panicModeReleaseClock;
+ int panicModeDuration;
+ UInteger32 panicModeExitThreshold;
+ int idleTimeout;
+
+ UInteger32 ofmAlarmThreshold;
+
+ NTPoptions ntpOptions;
+ Boolean preferNTP;
+
+ int electionDelay; /* timing domain election delay to prevent flapping */
+
+ int maxDelayMaxRejected;
+
+ /* max reset cycles in LISTENING before full network restart */
+ int maxListen;
+
+ Boolean managementEnabled;
+ Boolean managementSetEnable;
+
+ /* Access list settings */
+ Boolean timingAclEnabled;
+ Boolean managementAclEnabled;
+ char timingAclPermitText[PATH_MAX+1];
+ char timingAclDenyText[PATH_MAX+1];
+ char managementAclPermitText[PATH_MAX+1];
+ char managementAclDenyText[PATH_MAX+1];
+ Enumeration8 timingAclOrder;
+ Enumeration8 managementAclOrder;
+
+} RunTimeOpts;
+
+
+/**
+ * \struct PtpClock
+ * \brief Main program data structure
+ */
+/* main program data structure */
+typedef struct {
+
+ /* PTP datsets */
+ DefaultDS defaultDS; /* Default data set */
+ CurrentDS currentDS; /* Current data set */
+ TimePropertiesDS timePropertiesDS; /* Global time properties data set */
+ PortDS portDS; /* Port data set */
+ ParentDS parentDS; /* Parent data set */
+
+ /* Leap second related flags */
+ Boolean leapSecondInProgress;
+ Boolean leapSecondPending;
+
+ /* Foreign master data set */
+ ForeignMasterRecord *foreign;
+ /* Current best master (unless it's us) */
+ ForeignMasterRecord *bestMaster;
+
+ /* Other things we need for the protocol */
+ UInteger16 number_foreign_records;
+ Integer16 max_foreign_records;
+ Integer16 foreign_record_i;
+ Integer16 foreign_record_best;
+ UInteger32 random_seed;
+ Boolean record_update; /* should we run bmc() after receiving an announce message? */
+
+ Boolean disabled; /* port is permanently disabled */
+
+ /* unicast grant table - our own grants or our slaves' grants or grants to peers */
+ UnicastGrantTable unicastGrants[UNICAST_MAX_DESTINATIONS];
+ /* our trivial index table to speed up lookups */
+ UnicastGrantIndex grantIndex;
+ /* current parent from the above table */
+ UnicastGrantTable *parentGrants;
+ /* previous parent's grants when changing parents: if not null, this is what should be canceled */
+ UnicastGrantTable *previousGrants;
+ /* another index to match unicast Sync with FollowUp when we can't capture the destination address of Sync */
+ SyncDestEntry syncDestIndex[UNICAST_MAX_DESTINATIONS];
+
+ /* unicast destinations parsed from config */
+ UnicastDestination unicastDestinations[UNICAST_MAX_DESTINATIONS];
+ int unicastDestinationCount;
+
+ /* number of slaves we have granted Announce to */
+ int slaveCount;
+
+ /* unicast destination for pdelay */
+ UnicastDestination unicastPeerDestination;
+
+ /* support for unicast negotiation in P2P mode */
+ UnicastGrantTable peerGrants;
+
+ MsgHeader msgTmpHeader;
+
+ union {
+ MsgSync sync;
+ MsgFollowUp follow;
+ MsgDelayReq req;
+ MsgDelayResp resp;
+ MsgPdelayReq preq;
+ MsgPdelayResp presp;
+ MsgPdelayRespFollowUp prespfollow;
+ MsgManagement manage;
+ MsgAnnounce announce;
+ MsgSignaling signaling;
+ } msgTmp;
+
+ MsgManagement outgoingManageTmp;
+ MsgSignaling outgoingSignalingTmp;
+
+ Octet msgObuf[PACKET_SIZE];
+ Octet msgIbuf[PACKET_SIZE];
+
+ int followUpGap;
+
+/*
+ 20110630: These variables were deprecated in favor of the ones that appear in the stats log (delayMS and delaySM)
+
+ TimeInternal master_to_slave_delay;
+ TimeInternal slave_to_master_delay;
+
+ */
+
+ TimeInternal pdelay_req_receive_time;
+ TimeInternal pdelay_req_send_time;
+ TimeInternal pdelay_resp_receive_time;
+ TimeInternal pdelay_resp_send_time;
+ TimeInternal sync_receive_time;
+ TimeInternal delay_req_send_time;
+ TimeInternal delay_req_receive_time;
+ MsgHeader PdelayReqHeader;
+ MsgHeader delayReqHeader;
+ TimeInternal pdelayMS;
+ TimeInternal pdelaySM;
+ TimeInternal delayMS;
+ TimeInternal delaySM;
+ TimeInternal lastSyncCorrectionField;
+ TimeInternal lastPdelayRespCorrectionField;
+
+ Boolean sentPdelayReq;
+ UInteger16 sentPdelayReqSequenceId;
+ UInteger16 sentDelayReqSequenceId;
+ UInteger16 sentSyncSequenceId;
+ UInteger16 sentAnnounceSequenceId;
+ UInteger16 sentSignalingSequenceId;
+ UInteger16 recvPdelayReqSequenceId;
+ UInteger16 recvSyncSequenceId;
+ UInteger16 recvPdelayRespSequenceId;
+ Boolean waitingForFollow;
+ Boolean waitingForDelayResp;
+
+ offset_from_master_filter ofm_filt;
+ one_way_delay_filter mpd_filt;
+
+ Boolean message_activity;
+
+ IntervalTimer timers[PTP_MAX_TIMER];
+ AlarmEntry alarms[ALRM_MAX];
+ int alarmDelay;
+
+ NetPath netPath;
+
+ /*Usefull to init network stuff*/
+ UInteger8 port_communication_technology;
+
+ /*Stats header will be re-printed when set to true*/
+ Boolean resetStatisticsLog;
+
+ int listenCount; // number of consecutive resets to listening
+ int resetCount;
+ int announceTimeouts;
+ int current_init_clock;
+ int can_step_clock;
+ int warned_operator_slow_slewing;
+ int warned_operator_fast_slewing;
+ Boolean warnedUnicastCapacity;
+ int maxDelayRejected;
+
+ Boolean runningBackupInterface;
+
+
+ char char_last_msg; /* representation of last message processed by servo */
+
+ int syncWaiting; /* we'll only start the delayReq timer after the first sync */
+ int delayRespWaiting; /* Just for information purposes */
+ Boolean startup_in_progress;
+
+ Boolean pastStartup; /* we've set the clock already, at least once */
+
+ Boolean offsetFirstUpdated;
+
+ uint32_t init_timestamp; /* When the clock was last initialised */
+ Integer32 stabilisation_time; /* How long (seconds) it took to stabilise the clock */
+ double last_saved_drift; /* Last observed drift value written to file */
+ Boolean drift_saved; /* Did we save a drift value already? */
+
+ /* user description is max size + 1 to leave space for a null terminator */
+ Octet userDescription[USER_DESCRIPTION_MAX + 1];
+ Octet profileIdentity[6];
+
+ Integer32 lastSyncDst; /* destination address for last sync, so we know where to send the followUp - last resort: we should capture the dst address ourselves */
+ Integer32 lastPdelayRespDst; /* captures the destination address of last pdelayResp so we know where to send the pdelayRespfollowUp */
+
+ /*
+ * counters - useful for debugging and monitoring,
+ * should be exposed through management messages
+ * and SNMP eventually
+ */
+ PtpdCounters counters;
+
+ /* PI servo model */
+ PIservo servo;
+
+ /* "panic mode" support */
+ Boolean panicMode; /* in panic mode - do not update clock or calculate offsets */
+ Boolean panicOver; /* panic mode is over, we can reset the clock */
+ int panicModeTimeLeft; /* How many 30-second periods left in panic mode */
+
+ /* used to wait on failure while allowing timers to tick */
+ Boolean initFailure;
+ Integer32 initFailureTimeout;
+
+ /*
+ * used to inform TimingService about our status, but so
+ * that PTP code is independent from LibCCK and glue code can poll this
+ */
+ ClockControlInfo clockControl;
+ ClockStatusInfo clockStatus;
+
+
+ TimeInternal rawDelayMS;
+ TimeInternal rawDelaySM;
+ TimeInternal rawPdelayMS;
+ TimeInternal rawPdelaySM;
+
+#ifdef PTPD_STATISTICS
+ /*
+ * basic clock statistics information, useful
+ * for monitoring servo performance and estimating
+ * clock stability - should be exposed through
+ * management messages and SNMP eventually
+ */
+ PtpEngineSlaveStats slaveStats;
+
+ OutlierFilter oFilterMS;
+ OutlierFilter oFilterSM;
+
+ DoubleMovingStatFilter *filterMS;
+ DoubleMovingStatFilter *filterSM;
+
+#endif
+
+ Integer32 acceptedUpdates;
+ Integer32 offsetUpdates;
+
+ Boolean isCalibrated;
+
+ NTPcontrol ntpControl;
+
+ /* the interface to TimingDomain */
+ TimingService timingService;
+
+ /* accumulating offset correction added when smearing leap second */
+ double leapSmearFudge;
+
+ /* configuration applied by management messages */
+ dictionary *managementConfig;
+
+ /* testing only - used to add a 1ms offset */
+#if 0
+ Boolean addOffset;
+#endif
+
+ RunTimeOpts *rtOpts;
+
+} PtpClock;
+
+
+#endif /*DATATYPES_H_*/
diff --git a/rtemsbsd/ptpd/src/def/README b/rtemsbsd/ptpd/src/def/README
new file mode 100644
index 00000000..a0ee02a6
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/README
@@ -0,0 +1,11 @@
+This directory contains component macro .def files that are referenced by X-Macros in
+the ptpd source code.
+
+The component macros are used to define message, derived data
+types, and management TLV fields.
+
+The X-Macros are used to automatically generate most of
+the code used to pack, unpack, and free ptp data fields.
+
+Documentation on how to use X-Macros can be found at:
+http://en.wikibooks.org/wiki/C_Programming/Preprocessor#X-Macros
diff --git a/rtemsbsd/ptpd/src/def/derivedData/clockQuality.def b/rtemsbsd/ptpd/src/def/derivedData/clockQuality.def
new file mode 100644
index 00000000..1ee37f33
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/derivedData/clockQuality.def
@@ -0,0 +1,8 @@
+/* Spec 5.3.7 ClockQuality */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( clockClass, 1, UInteger8 )
+OPERATE( clockAccuracy, 1, Enumeration8 )
+OPERATE( offsetScaledLogVariance, 2, UInteger16)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/derivedData/faultRecord.def b/rtemsbsd/ptpd/src/def/derivedData/faultRecord.def
new file mode 100644
index 00000000..172a79d1
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/derivedData/faultRecord.def
@@ -0,0 +1,11 @@
+/* Spec 5.3.10 FaultRecord */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( faultRecordLength, 2, UInteger16)
+OPERATE( faultTime, 10, Timestamp)
+OPERATE( severityCode, 1, Enumeration8)
+OPERATE( faultName, 1 + data->faultName.lengthField, PTPText)
+OPERATE( faultValue, 1 + data->faultValue.lengthField, PTPText)
+OPERATE( faultDescription, 1 + data->faultDescription.lengthField, PTPText)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/derivedData/physicalAddress.def b/rtemsbsd/ptpd/src/def/derivedData/physicalAddress.def
new file mode 100644
index 00000000..3e73501e
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/derivedData/physicalAddress.def
@@ -0,0 +1,13 @@
+/* PhysicalAddress */
+
+/*
+ * This is not a derived data type from the standard.
+ * Defining this type simplifies the implementation of
+ * the Management Clock Description message
+ */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( addressLength, 2, UInteger16)
+OPERATE( addressField, data->addressLength, Octet*)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/derivedData/portAddress.def b/rtemsbsd/ptpd/src/def/derivedData/portAddress.def
new file mode 100644
index 00000000..78d1fa28
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/derivedData/portAddress.def
@@ -0,0 +1,8 @@
+/* Spec 5.3.6 PortAddress */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( networkProtocol, 2, Enumeration16)
+OPERATE( addressLength, 2, UInteger16)
+OPERATE( addressField, data->addressLength, Octet*)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/derivedData/portIdentity.def b/rtemsbsd/ptpd/src/def/derivedData/portIdentity.def
new file mode 100644
index 00000000..a487dc90
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/derivedData/portIdentity.def
@@ -0,0 +1,7 @@
+/* Spec 5.3.5 PortIdentity */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( clockIdentity, CLOCK_IDENTITY_LENGTH, ClockIdentity)
+OPERATE( portNumber, 2, UInteger16)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/derivedData/ptpText.def b/rtemsbsd/ptpd/src/def/derivedData/ptpText.def
new file mode 100644
index 00000000..6d60bd44
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/derivedData/ptpText.def
@@ -0,0 +1,7 @@
+/* Spec 5.3.9 PTPText */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( lengthField, 1, UInteger8)
+OPERATE( textField, data->lengthField, Octet*)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/derivedData/timeInterval.def b/rtemsbsd/ptpd/src/def/derivedData/timeInterval.def
new file mode 100644
index 00000000..ac58749c
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/derivedData/timeInterval.def
@@ -0,0 +1,6 @@
+/* Spec 5.3.2 TimeInterval*/
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( scaledNanoseconds, 8, Integer64)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/derivedData/timePropertiesDS.def b/rtemsbsd/ptpd/src/def/derivedData/timePropertiesDS.def
new file mode 100644
index 00000000..61215cab
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/derivedData/timePropertiesDS.def
@@ -0,0 +1,13 @@
+/* Spec 8.2.4 timePropertiesDS */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( currentUtcOffset, 2, UInteger16 )
+OPERATE( currentUtcOffsetValid, 1, Boolean )
+OPERATE( leap59, 1, Boolean )
+OPERATE( leap61, 1, Boolean )
+OPERATE( timeTraceable, 1, Boolean )
+OPERATE( frequencyTraceable, 1, Boolean )
+OPERATE( ptpTimescale, 1, Boolean )
+OPERATE( timeSource, 1, Enumeration8 )
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/derivedData/timestamp.def b/rtemsbsd/ptpd/src/def/derivedData/timestamp.def
new file mode 100644
index 00000000..7d44db0b
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/derivedData/timestamp.def
@@ -0,0 +1,7 @@
+/* Spec 5.3.3 Timestamp */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( secondsField, 6, UInteger48)
+OPERATE( nanosecondsField, 4, UInteger32)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/derivedData/tlv.def b/rtemsbsd/ptpd/src/def/derivedData/tlv.def
new file mode 100644
index 00000000..6a52eb5b
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/derivedData/tlv.def
@@ -0,0 +1,8 @@
+/* Spec 5.3.8 TLV */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( tlvType, 2, Enumeration16)
+OPERATE( lengthField, 2, UInteger16)
+OPERATE( valueField, data->lengthField, Octet*)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/announceReceiptTimeout.def b/rtemsbsd/ptpd/src/def/managementTLV/announceReceiptTimeout.def
new file mode 100644
index 00000000..8a62653e
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/announceReceiptTimeout.def
@@ -0,0 +1,7 @@
+/* Spec Table 63 - ANNOUNCE_RECEIPT_TIMEOUT management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( announceReceiptTimeout, 1, UInteger8)
+OPERATE( reserved, 1, Octet)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/clockAccuracy.def b/rtemsbsd/ptpd/src/def/managementTLV/clockAccuracy.def
new file mode 100644
index 00000000..a8e32bb0
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/clockAccuracy.def
@@ -0,0 +1,7 @@
+/* Spec Table 49 - CLOCK_ACCURACY management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( clockAccuracy, 1, Enumeration8)
+OPERATE( reserved, 1, Octet)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/clockDescription.def b/rtemsbsd/ptpd/src/def/managementTLV/clockDescription.def
new file mode 100644
index 00000000..645d9cdb
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/clockDescription.def
@@ -0,0 +1,35 @@
+/* Spec Table 41 - CLOCK_DESCRIPTION management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( clockType0, 1, Octet)
+OPERATE( clockType1, 1, Octet)
+OPERATE( physicalLayerProtocol,
+ 1 + data->physicalLayerProtocol.lengthField,
+ PTPText)
+OPERATE( physicalAddress,
+ 2 + data->physicalAddress.addressLength,
+ PhysicalAddress)
+OPERATE( protocolAddress,
+ 4 + data->protocolAddress.addressLength,
+ PortAddress)
+OPERATE( manufacturerIdentity0, 1, Octet)
+OPERATE( manufacturerIdentity1, 1, Octet)
+OPERATE( manufacturerIdentity2, 1, Octet)
+OPERATE( reserved, 1, Octet)
+OPERATE( productDescription,
+ 1 + data->productDescription.lengthField,
+ PTPText)
+OPERATE( revisionData,
+ 1 + data->revisionData.lengthField,
+ PTPText)
+OPERATE( userDescription,
+ 1 + data->userDescription.lengthField,
+ PTPText)
+OPERATE( profileIdentity0, 1, Octet)
+OPERATE( profileIdentity1, 1, Octet)
+OPERATE( profileIdentity2, 1, Octet)
+OPERATE( profileIdentity3, 1, Octet)
+OPERATE( profileIdentity4, 1, Octet)
+OPERATE( profileIdentity5, 1, Octet)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/currentDataSet.def b/rtemsbsd/ptpd/src/def/managementTLV/currentDataSet.def
new file mode 100644
index 00000000..b4786461
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/currentDataSet.def
@@ -0,0 +1,8 @@
+/* Spec Table 55 - CURRENT_DATA_SET management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( stepsRemoved, 2, UInteger16)
+OPERATE( offsetFromMaster, 8, TimeInterval)
+OPERATE( meanPathDelay, 8, TimeInterval)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/defaultDataSet.def b/rtemsbsd/ptpd/src/def/managementTLV/defaultDataSet.def
new file mode 100644
index 00000000..6f3d24fc
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/defaultDataSet.def
@@ -0,0 +1,14 @@
+/* Spec Table 50 - DEFAULT_DATA_SET management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( so_tsc, 1, Octet)
+OPERATE( reserved0, 1, Octet)
+OPERATE( numberPorts, 2, UInteger16)
+OPERATE( priority1, 1, UInteger8)
+OPERATE( clockQuality, 4, ClockQuality)
+OPERATE( priority2, 1, UInteger8)
+OPERATE( clockIdentity, 8, ClockIdentity)
+OPERATE( domainNumber, 1, UInteger8)
+OPERATE( reserved1, 1, Octet)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/delayMechanism.def b/rtemsbsd/ptpd/src/def/managementTLV/delayMechanism.def
new file mode 100644
index 00000000..edcda965
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/delayMechanism.def
@@ -0,0 +1,7 @@
+/* Spec Table 65 - DELAY_MECHANISM management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( delayMechanism, 1, Enumeration8)
+OPERATE( reserved, 1, Octet)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/domain.def b/rtemsbsd/ptpd/src/def/managementTLV/domain.def
new file mode 100644
index 00000000..6a153335
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/domain.def
@@ -0,0 +1,7 @@
+/* Spec Table 53 - DOMAIN management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( domainNumber, 1, UInteger8)
+OPERATE( reserved, 1, Octet)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/errorStatus.def b/rtemsbsd/ptpd/src/def/managementTLV/errorStatus.def
new file mode 100644
index 00000000..303b2404
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/errorStatus.def
@@ -0,0 +1,8 @@
+/* Spec Table 71 - MANAGEMENT_ERROR_STATUS TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( managementId, 2, Enumeration16)
+OPERATE( reserved, 4, UInteger32)
+OPERATE( displayData, 1 + data->displayData.lengthField, PTPText)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/initialize.def b/rtemsbsd/ptpd/src/def/managementTLV/initialize.def
new file mode 100644
index 00000000..d6fbf1ca
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/initialize.def
@@ -0,0 +1,6 @@
+/* Spec Table 43 - USER_DESCRIPTION management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( initializeKey, 2, UInteger16)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/logAnnounceInterval.def b/rtemsbsd/ptpd/src/def/managementTLV/logAnnounceInterval.def
new file mode 100644
index 00000000..1fc30dde
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/logAnnounceInterval.def
@@ -0,0 +1,7 @@
+/* Spec Table 62 - LOG_ANNOUNCE_INTERVAL management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( logAnnounceInterval, 1, Integer8)
+OPERATE( reserved, 1, Octet)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/logMinPdelayReqInterval.def b/rtemsbsd/ptpd/src/def/managementTLV/logMinPdelayReqInterval.def
new file mode 100644
index 00000000..c382d7d8
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/logMinPdelayReqInterval.def
@@ -0,0 +1,7 @@
+/* Spec Table 66 - LOG_MIN_PDELAY_REQ_INTERVAL management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( logMinPdelayReqInterval, 1, Integer8)
+OPERATE( reserved, 1, Octet)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/logSyncInterval.def b/rtemsbsd/ptpd/src/def/managementTLV/logSyncInterval.def
new file mode 100644
index 00000000..3700ddb9
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/logSyncInterval.def
@@ -0,0 +1,7 @@
+/* Spec Table 64 - LOG_SYNC_INTERVAL management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( logSyncInterval, 1, Integer8)
+OPERATE( reserved, 1, Octet)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/managementTLV.def b/rtemsbsd/ptpd/src/def/managementTLV/managementTLV.def
new file mode 100644
index 00000000..9d51de83
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/managementTLV.def
@@ -0,0 +1,8 @@
+/* Spec Table 39 - Management TLV fields */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( tlvType, 2, Enumeration16)
+OPERATE( lengthField, 2, UInteger16)
+OPERATE( managementId, 2, Enumeration16)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/parentDataSet.def b/rtemsbsd/ptpd/src/def/managementTLV/parentDataSet.def
new file mode 100644
index 00000000..9440d4a5
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/parentDataSet.def
@@ -0,0 +1,14 @@
+/* Spec Table 56 - PARENT_DATA_SET management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( parentPortIdentity, 10, PortIdentity)
+OPERATE( PS, 1, Boolean)
+OPERATE( reserved, 1, Octet)
+OPERATE( observedParentOffsetScaledLogVariance, 2, UInteger16)
+OPERATE( observedParentClockPhaseChangeRate, 4, Integer32)
+OPERATE( grandmasterPriority1, 1, UInteger8)
+OPERATE( grandmasterClockQuality, 4, ClockQuality)
+OPERATE( grandmasterPriority2, 1, UInteger8)
+OPERATE( grandmasterIdentity, 8, ClockIdentity)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/portDataSet.def b/rtemsbsd/ptpd/src/def/managementTLV/portDataSet.def
new file mode 100644
index 00000000..2fcce7fd
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/portDataSet.def
@@ -0,0 +1,16 @@
+/* Spec Table 61 - PORT_DATA_SET management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( portIdentity, 10, PortIdentity)
+OPERATE( portState, 1, Enumeration8)
+OPERATE( logMinDelayReqInterval, 1, Integer8)
+OPERATE( peerMeanPathDelay, 8, TimeInterval)
+OPERATE( logAnnounceInterval, 1, Integer8)
+OPERATE( announceReceiptTimeout, 1, UInteger8)
+OPERATE( logSyncInterval, 1, Integer8)
+OPERATE( delayMechanism, 1, Enumeration8)
+OPERATE( logMinPdelayReqInterval, 1, Integer8)
+OPERATE( reserved, 0, NibbleUpper)
+OPERATE( versionNumber, 1, UInteger4Lower)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/priority1.def b/rtemsbsd/ptpd/src/def/managementTLV/priority1.def
new file mode 100644
index 00000000..fa5e6fa3
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/priority1.def
@@ -0,0 +1,7 @@
+/* Spec Table 51 - PRIORITY1 management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( priority1, 1, UInteger8)
+OPERATE( reserved, 1, Octet)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/priority2.def b/rtemsbsd/ptpd/src/def/managementTLV/priority2.def
new file mode 100644
index 00000000..8e72aae3
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/priority2.def
@@ -0,0 +1,7 @@
+/* Spec Table 52 - PRIORITY2 management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( priority2, 1, UInteger8)
+OPERATE( reserved, 1, Octet)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/slaveOnly.def b/rtemsbsd/ptpd/src/def/managementTLV/slaveOnly.def
new file mode 100644
index 00000000..65fa4a65
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/slaveOnly.def
@@ -0,0 +1,7 @@
+/* Spec Table 54 - SLAVE_ONLY management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( so, 1, Boolean)
+OPERATE( reserved, 1, Octet)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/time.def b/rtemsbsd/ptpd/src/def/managementTLV/time.def
new file mode 100644
index 00000000..485b083d
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/time.def
@@ -0,0 +1,6 @@
+/* Spec Table 48 - TIME management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( currentTime, 10, Timestamp)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/timePropertiesDataSet.def b/rtemsbsd/ptpd/src/def/managementTLV/timePropertiesDataSet.def
new file mode 100644
index 00000000..50f6eaf6
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/timePropertiesDataSet.def
@@ -0,0 +1,8 @@
+/* Spec Table 57 - TIME_PROPERTIES_DATA_SET management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( currentUtcOffset, 2, Integer16)
+OPERATE( ftra_ttra_ptp_utcv_li59_li61, 1, Octet)
+OPERATE( timeSource, 1, Enumeration8)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/timescaleProperties.def b/rtemsbsd/ptpd/src/def/managementTLV/timescaleProperties.def
new file mode 100644
index 00000000..40de808b
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/timescaleProperties.def
@@ -0,0 +1,7 @@
+/* Spec Table 60 - TIMESCALE_PROPERTIES management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( ptp, 1, Boolean )
+OPERATE( timeSource, 1, Enumeration8 )
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/traceabilityProperties.def b/rtemsbsd/ptpd/src/def/managementTLV/traceabilityProperties.def
new file mode 100644
index 00000000..5e5f9c02
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/traceabilityProperties.def
@@ -0,0 +1,7 @@
+/* Spec Table 59 - TRACEABILITY_PROPERTIES management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( ftra_ttra, 1, Octet)
+OPERATE( reserved, 1, Octet)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/unicastNegotiationEnable.def b/rtemsbsd/ptpd/src/def/managementTLV/unicastNegotiationEnable.def
new file mode 100644
index 00000000..2b3034be
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/unicastNegotiationEnable.def
@@ -0,0 +1,7 @@
+/* Spec Table 77 - UNICAST_NEGOTIATION_ENABLE */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( en , 1, Boolean)
+OPERATE( reserved, 1, Octet)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/userDescription.def b/rtemsbsd/ptpd/src/def/managementTLV/userDescription.def
new file mode 100644
index 00000000..db3e8d94
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/userDescription.def
@@ -0,0 +1,8 @@
+/* Spec Table 43 - USER_DESCRIPTION management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( userDescription,
+ 1 + data->userDescription.lengthField,
+ PTPText)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/utcProperties.def b/rtemsbsd/ptpd/src/def/managementTLV/utcProperties.def
new file mode 100644
index 00000000..644cb936
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/utcProperties.def
@@ -0,0 +1,8 @@
+/* Spec Table 58 - UTC_PROPERTIES management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( currentUtcOffset, 2, Integer16)
+OPERATE( utcv_li59_li61, 1, Octet)
+OPERATE( reserved, 1, Octet)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/managementTLV/versionNumber.def b/rtemsbsd/ptpd/src/def/managementTLV/versionNumber.def
new file mode 100644
index 00000000..9b8fa286
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/managementTLV/versionNumber.def
@@ -0,0 +1,8 @@
+/* Spec Table 67 - VERSION_NUMBER management TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( reserved0, 0, NibbleUpper)
+OPERATE( versionNumber, 1, UInteger4Lower)
+OPERATE( reserved1, 1, Octet)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/message/header.def b/rtemsbsd/ptpd/src/def/message/header.def
new file mode 100644
index 00000000..8e51d638
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/message/header.def
@@ -0,0 +1,20 @@
+/* Spec Table 18 - Common message header */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( transportSpecific, 0, NibbleUpper)
+OPERATE( messageType, 1, Enumeration4Lower)
+OPERATE( reserved0, 0, NibbleUpper)
+OPERATE( versionPTP, 1, UInteger4Lower)
+OPERATE( messageLength, 2, UInteger16)
+OPERATE( domainNumber, 1, UInteger8)
+OPERATE( reserved1, 1, Octet)
+OPERATE( flagField0, 1, Octet)
+OPERATE( flagField1, 1, Octet)
+OPERATE( correctionField, 8, Integer64)
+OPERATE( reserved2, 4, UInteger32)
+OPERATE( sourcePortIdentity, 10, PortIdentity)
+OPERATE( sequenceId, 2, UInteger16)
+OPERATE( controlField, 1, UInteger8)
+OPERATE( logMessageInterval, 1, Integer8)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/message/management.def b/rtemsbsd/ptpd/src/def/message/management.def
new file mode 100644
index 00000000..350498fb
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/message/management.def
@@ -0,0 +1,12 @@
+/* Spec Table 37 - Management message fields */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( header, 34, MsgHeader)
+OPERATE( targetPortIdentity, 10, PortIdentity)
+OPERATE( startingBoundaryHops, 1, UInteger8)
+OPERATE( boundaryHops, 1, UInteger8)
+OPERATE( reserved0, 0, NibbleUpper)
+OPERATE( actionField, 1, Enumeration4Lower)
+OPERATE( reserved1, 1, Octet)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/message/signaling.def b/rtemsbsd/ptpd/src/def/message/signaling.def
new file mode 100644
index 00000000..e61c82cd
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/message/signaling.def
@@ -0,0 +1,6 @@
+/* Spec Table 33 - Signaling message fields */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( header, 34, MsgHeader)
+OPERATE( targetPortIdentity, 10, PortIdentity)
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/signalingTLV/acknowledgeCancelUnicastTransmission.def b/rtemsbsd/ptpd/src/def/signalingTLV/acknowledgeCancelUnicastTransmission.def
new file mode 100644
index 00000000..abff8cc1
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/signalingTLV/acknowledgeCancelUnicastTransmission.def
@@ -0,0 +1,9 @@
+/* Spec Table 75 - ACKNOWLEDGE_UNICAST_TRANSMISSION signaling TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+
+OPERATE( messageType, 0, Enumeration4Upper)
+OPERATE( reserved0, 1, UInteger4Lower)
+OPERATE( reserved1, 1, Octet )
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/signalingTLV/cancelUnicastTransmission.def b/rtemsbsd/ptpd/src/def/signalingTLV/cancelUnicastTransmission.def
new file mode 100644
index 00000000..2e53914f
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/signalingTLV/cancelUnicastTransmission.def
@@ -0,0 +1,9 @@
+/* Spec Table 75 - CANCEL_UNICAST_TRANSMISSION signaling TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+
+OPERATE( messageType, 0, Enumeration4Upper)
+OPERATE( reserved0, 1, UInteger4Lower)
+OPERATE( reserved1, 1, Octet )
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/signalingTLV/grantUnicastTransmission.def b/rtemsbsd/ptpd/src/def/signalingTLV/grantUnicastTransmission.def
new file mode 100644
index 00000000..8677d60d
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/signalingTLV/grantUnicastTransmission.def
@@ -0,0 +1,12 @@
+/* Spec Table 73 - REQUEST_UNICAST_TRANSMISSION signaling TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+
+OPERATE( messageType, 0, Enumeration4Upper)
+OPERATE( reserved0, 1, UInteger4Lower)
+OPERATE( logInterMessagePeriod, 1, Integer8)
+OPERATE( durationField, 4, UInteger32)
+OPERATE( reserved1, 1, Octet )
+OPERATE( renewal_invited,1, UInteger8)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/signalingTLV/requestUnicastTransmission.def b/rtemsbsd/ptpd/src/def/signalingTLV/requestUnicastTransmission.def
new file mode 100644
index 00000000..8a57cb24
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/signalingTLV/requestUnicastTransmission.def
@@ -0,0 +1,9 @@
+/* Spec Table 73 - REQUEST_UNICAST_TRANSMISSION signaling TLV data field */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( messageType, 0, Enumeration4Upper)
+OPERATE( reserved0, 1, UInteger4Lower)
+OPERATE( logInterMessagePeriod, 1, Integer8)
+OPERATE( durationField, 4, UInteger32)
+
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/def/signalingTLV/signalingTLV.def b/rtemsbsd/ptpd/src/def/signalingTLV/signalingTLV.def
new file mode 100644
index 00000000..de662fdb
--- /dev/null
+++ b/rtemsbsd/ptpd/src/def/signalingTLV/signalingTLV.def
@@ -0,0 +1,6 @@
+/* 4.1 - TLV fields */
+
+/* to use these definitions, #define OPERATE then #include this file in your source */
+OPERATE( tlvType, 2, Enumeration16)
+OPERATE( lengthField, 2, UInteger16)
+#undef OPERATE
diff --git a/rtemsbsd/ptpd/src/dep/alarms.c b/rtemsbsd/ptpd/src/dep/alarms.c
new file mode 100644
index 00000000..d1437a6e
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/alarms.c
@@ -0,0 +1,427 @@
+/*-
+ * Copyright (c) 2015 Wojciech Owczarek
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file alarms.c
+ * @authors Wojciech Owczarek
+ * @date Wed Dec 9 19:13:10 2015
+ * This source file contains the implementations of functions
+ * handling raising and clearing of alarms.
+ */
+
+#include "../ptpd.h"
+
+static const char* alarmStateToString(AlarmState state);
+
+static void dispatchEvent(AlarmEntry *alarm);
+static void dispatchAlarm(AlarmEntry *alarm);
+
+static void getAlarmMessage(char *out, int count, AlarmEntry *alarm);
+
+static void alarmHandler_log(AlarmEntry *alarm);
+static void eventHandler_log(AlarmEntry *alarm);
+
+static const char
+*alarmStateToString(AlarmState state)
+{
+
+ switch(state) {
+
+ case ALARM_UNSET:
+ return "NONE";
+ case ALARM_SET:
+ return "SET";
+ case ALARM_CLEARED:
+ return "CLEAR";
+ default:
+ return "?";
+ }
+
+}
+
+static void
+getAlarmMessage(char *out, int count, AlarmEntry *alarm)
+{
+
+ memset(out, 0, count);
+
+ switch(alarm->id) {
+
+ case ALRM_PORT_STATE:
+ if(alarm->state == ALARM_UNSET) {
+ return;
+ }
+ snprintf(out, count, ": Port state is %s", portState_getName(alarm->eventData.portDS.portState));
+ return;
+ case ALRM_OFM_THRESHOLD:
+ if(alarm->state == ALARM_UNSET) {
+ snprintf(out, count, ": Offset from master is now %.09f s, threshold is %d ns",
+ timeInternalToDouble(&alarm->eventData.currentDS.offsetFromMaster),
+ alarm->eventData.ofmAlarmThreshold);
+ return;
+ }
+ snprintf(out, count, ": Offset from master is %.09f s, threshold is %d ns",
+ timeInternalToDouble(&alarm->eventData.currentDS.offsetFromMaster),
+ alarm->eventData.ofmAlarmThreshold);
+ return;
+ case ALRM_OFM_SECONDS:
+ if(alarm->state == ALARM_UNSET) {
+ snprintf(out, count, ": Offset from master is now %.09f s, below 1 second",
+ timeInternalToDouble(&alarm->eventData.currentDS.offsetFromMaster));
+ return;
+ }
+ snprintf(out, count, ": Offset from master is %.09f s, above 1 second",
+ timeInternalToDouble(&alarm->eventData.currentDS.offsetFromMaster));
+ return;
+ case ALRM_CLOCK_STEP:
+ snprintf(out, count, ": Clock stepped by %.09f s",
+ timeInternalToDouble(&alarm->eventData.currentDS.offsetFromMaster));
+ return;
+ case ALRM_NO_SYNC:
+ if(alarm->state == ALARM_UNSET) {
+ return;
+ }
+ snprintf(out, count, ": Not receiving Sync");
+ return;
+ case ALRM_NO_DELAY:
+ if(alarm->state == ALARM_UNSET) {
+ return;
+ }
+ snprintf(out, count, ": Not receiving Delay Response");
+ return;
+ case ALRM_MASTER_CHANGE:
+ return;
+ case ALRM_NETWORK_FLT:
+ return;
+ case ALRM_FAST_ADJ:
+ return;
+ case ALRM_TIMEPROP_CHANGE:
+ return;
+ case ALRM_DOMAIN_MISMATCH:
+ snprintf(out, count, ": Configured domain is %d, last seen %d", alarm->eventData.defaultDS.domainNumber,
+ alarm->eventData.portDS.lastMismatchedDomain);
+ return;
+ default:
+ return;
+ }
+
+}
+
+static void
+alarmHandler_log(AlarmEntry *alarm)
+{
+
+ char message[ALARM_MESSAGE_LENGTH+1];
+ message[ALARM_MESSAGE_LENGTH] = '\0';
+ getAlarmMessage(message, ALARM_MESSAGE_LENGTH, alarm);
+
+ if(alarm->state == ALARM_SET) {
+ NOTICE("Alarm %s set%s\n", alarm->name, message);
+ }
+
+ if(alarm->state == ALARM_UNSET) {
+ NOTICE("Alarm %s cleared%s\n", alarm->name, message);
+ }
+}
+
+static void
+eventHandler_log(AlarmEntry *alarm)
+{
+ char message[ALARM_MESSAGE_LENGTH+1];
+ message[ALARM_MESSAGE_LENGTH] = '\0';
+ getAlarmMessage(message, ALARM_MESSAGE_LENGTH, alarm);
+
+ NOTICE("Event %s triggered%s\n", alarm->name, message);
+}
+
+static void
+dispatchEvent(AlarmEntry *alarm) {
+
+ for(int i=0; alarm->handlers[i] != NULL; i++) {
+ alarm->handlers[i](alarm);
+ }
+ alarm->condition = FALSE;
+}
+
+static void
+dispatchAlarm(AlarmEntry *alarm)
+{
+ for(int i=0; alarm->handlers[i] != NULL; i++) {
+ alarm->handlers[i](alarm);
+ }
+}
+
+
+void
+initAlarms(AlarmEntry* alarms, int count, void *userData)
+{
+
+ static AlarmEntry alarmTemplate[] = {
+
+ /* short */ /* name */ /* desc */ /* enabled? */ /* id */ /* eventOnly */ /*handlers */
+ { "STA", "PORT_STATE", "Port state different to expected value", FALSE, ALRM_PORT_STATE, FALSE, {alarmHandler_log}},
+ { "OFM", "OFM_THRESHOLD", "Offset From Master outside threshold", FALSE, ALRM_OFM_THRESHOLD, FALSE, {alarmHandler_log}},
+ { "OFMS", "OFM_SECONDS", "Offset From Master above 1 second", FALSE, ALRM_OFM_SECONDS, FALSE, {alarmHandler_log}},
+ { "STEP", "CLOCK_STEP", "Clock was stepped", FALSE, ALRM_CLOCK_STEP, TRUE, {eventHandler_log}},
+ { "SYN", "NO_SYNC", "Clock is not receiving Sync messages", FALSE, ALRM_NO_SYNC, FALSE, {alarmHandler_log}},
+ { "DLY", "NO_DELAY", "Clock is not receiving (p)Delay responses", FALSE, ALRM_NO_DELAY, FALSE, {alarmHandler_log}},
+ { "MSTC", "MASTER_CHANGE", "Best master has changed", FALSE, ALRM_MASTER_CHANGE, TRUE, {eventHandler_log}},
+ { "NWFL", "NETWORK_FAULT", "A network fault has occurred", FALSE, ALRM_NETWORK_FLT, FALSE, {alarmHandler_log}},
+ { "FADJ", "FAST_ADJ", "Clock is being adjusted too fast", FALSE, ALRM_FAST_ADJ, FALSE, {alarmHandler_log}},
+ { "TPR", "TIMEPROP_CHANGE", "Time properties have changed", FALSE, ALRM_TIMEPROP_CHANGE, TRUE, {eventHandler_log}},
+ { "DOM", "DOMAIN_MISMATCH", "Clock is receiving all messages from incorrect domain",FALSE, ALRM_DOMAIN_MISMATCH, FALSE, {alarmHandler_log}}
+
+ };
+
+ if(count > ALRM_MAX) return;
+
+ memset(alarms, 0, count * sizeof(AlarmEntry));
+ memcpy(alarms, alarmTemplate, sizeof(alarmTemplate));
+
+ for(int i = 0; i < count; i++) {
+ alarms[i].userData = userData;
+ }
+
+}
+
+/*
+ * we are passing a generic pointer because eventually the alarm will have a "source" field,
+ * that being PTP, NTP or any other subsystem, which may have separate routines to handle this
+ */
+void
+configureAlarms(AlarmEntry *alarms, int count, void * userData)
+{
+
+ PtpClock *ptpClock = (PtpClock*) userData;
+
+ for(int i = 0; i < count; i++) {
+ alarms[i].minAge = ptpClock->rtOpts->alarmMinAge;
+ alarms[i].enabled = ptpClock->rtOpts->alarmsEnabled;
+#ifdef PTPD_SNMP
+ if(ptpClock->rtOpts->snmpEnabled && ptpClock->rtOpts->snmpTrapsEnabled) {
+ DBG("SNMP alarm handler attached for %s\n", alarms[i].name);
+ alarms[i].handlers[1] = alarmHandler_snmp;
+ } else {
+ alarms[i].handlers[1] = NULL;
+ }
+#endif
+ }
+
+}
+
+void
+enableAlarms(AlarmEntry* alarms, int count, Boolean enabled)
+{
+ for(int i = 0; i < count; i++) {
+ alarms[i].enabled = enabled;
+ }
+
+}
+
+void
+setAlarmCondition(AlarmEntry *alarm, Boolean condition, PtpClock *ptpClock)
+{
+ if(!alarm->enabled) {
+ return;
+ }
+
+ Boolean change = condition ^ alarm->condition;
+
+ /* if there is no change, exit */
+ if(!change) {
+ return;
+ }
+
+ /* condition has cleared but event has not yet been processed - don't touch it */
+ if(!condition && alarm->unhandled) {
+ return;
+ }
+
+ /* capture event data and time if condition is met */
+
+ capturePtpEventData(&alarm->eventData, ptpClock, ptpClock->rtOpts);
+
+ if(condition) {
+ getTime(&alarm->timeSet);
+ } else {
+ getTime(&alarm->timeCleared);
+ }
+
+ DBG("Alarm %s condition set to %s\n", alarm->name, condition ? "TRUE" : "FALSE");
+
+ alarm->age = 0;
+ alarm->condition = condition;
+ alarm->unhandled = TRUE;
+
+}
+
+void
+capturePtpEventData(PtpEventData *eventData, PtpClock *ptpClock, RunTimeOpts *rtOpts)
+{
+
+ eventData->defaultDS = ptpClock->defaultDS;
+ eventData->currentDS = ptpClock->currentDS;
+ eventData->timePropertiesDS = ptpClock->timePropertiesDS;
+ eventData->portDS = ptpClock->portDS;
+ eventData->parentDS = ptpClock->parentDS;
+
+ if(ptpClock->bestMaster != NULL) {
+ eventData->bestMaster = *ptpClock->bestMaster;
+ } else {
+ memset(&eventData->bestMaster, 0, sizeof(ForeignMasterRecord));
+ }
+
+ eventData->ofmAlarmThreshold = rtOpts->ofmAlarmThreshold;
+}
+
+/*
+ * this is our asynchronous event/alarm processor / state machine. ALARM_TIMEOUT_PERIOD protects us from alarms flapping,
+ * and ensures that an alarm will last at least n seconds: notification is processed only when the alarm is set for the
+ * first time (from UNSET state), and is not cancelled until the timeout passes (in the meantime, alarm condition can be
+ * triggered again and timer is reset, so it can flap at will), and cancel notification will only be sent when the condition
+ * has cleared and timeout has passed.
+ */
+void
+updateAlarms(AlarmEntry *alarms, int count)
+{
+
+ DBG("updateAlarms()\n");
+
+ AlarmEntry *alarm;
+ AlarmState lastState;
+
+ for(int i = 0; i < count; i++) {
+
+ alarm = &alarms[i];
+
+ if(!alarm->enabled) {
+ continue;
+ }
+
+ lastState = alarm->state;
+ /* this is a one-off event */
+ if(alarm->eventOnly) {
+ if(alarm->condition) {
+ dispatchEvent(alarm);
+ }
+ } else {
+ /* this is an alarm */
+ if(!alarm->condition) {
+ /* condition is false, alarm is cleared and aged out: unset */
+ if(alarm->state == ALARM_CLEARED && alarm->age >= alarm->minAge ) {
+ alarm->state = ALARM_UNSET;
+ /* inform, run handlers */
+ dispatchAlarm(alarm);
+ clearTime(&alarm->timeSet);
+ clearTime(&alarm->timeCleared);
+ /* condition is false and alarm was set - clear and wait for age out */
+ } else if (alarm->state == ALARM_SET) {
+ alarm->state = ALARM_CLEARED;
+ }
+ /* condition is true and age is 0: condition just changed */
+ } else if (alarm->age == 0) {
+ alarm->state = ALARM_SET;
+ /* react only if alarm was set from unset (don't inform multiple times until it fully clears */
+ if(lastState == ALARM_UNSET) {
+ /* inform, run handlers */
+ dispatchAlarm(alarm);
+ }
+ }
+ alarm->age+=ALARM_UPDATE_INTERVAL;
+ }
+ alarm->unhandled = FALSE;
+ }
+
+}
+
+void
+displayAlarms(AlarmEntry *alarms, int count)
+{
+
+ INFO("----------------------------------------------\n");
+ INFO(" Alarm status: \n");
+ INFO("----------------------------------------------\n");
+ INFO("ALARM NAME\t| STATE | DESCRIPTION\n");
+ INFO("----------------------------------------------\n");
+ for(int i=0; i < count; i++) {
+ if(alarms[i].eventOnly) continue;
+ INFO("%-15s\t| %-8s | %s\n", alarms[i].name,
+ alarmStateToString(alarms[i].state),
+ alarms[i].description);
+ }
+ INFO("----------------------------------------------\n");
+
+}
+
+/* populate @output with a one-line alarm summary, consisting of multiple:
+ * ALCD[x]
+ * where:
+ * - ALCD is the short name(code)
+ * - x is "!" when alarm is set
+ * - x is "." when alarm is cleared
+ * - when alarm is gone (unset) its code is not listed
+ *
+ * Returning the number of bytes written. When output is null, only the number of bytes needed is returned
+ * (e.g. to allow us to allocate a buffer)
+ */
+int
+getAlarmSummary(char * output, int size, AlarmEntry *alarms, int count)
+{
+
+ int len = 0;
+ int i = 0;
+
+ char item[9];
+
+ AlarmEntry *alarm;
+
+ if(output != NULL) {
+ memset(output, 0, size);
+ }
+
+ for(i=0; i < count; i++) {
+ memset(item, 0, 8);
+ alarm = &alarms[i];
+
+ if(alarm->state == ALARM_UNSET) {
+ continue;
+ }
+
+ strncpy(item, alarm->shortName, 4);
+ snprintf(item + strlen(item), 5, "[%s] ", alarm->state == ALARM_SET ? "!" : ".");
+
+ if(output != NULL) {
+ strncat(output, item, size - len);
+ }
+
+ len += strlen(item) + 1;
+
+ }
+
+ return len + 1;
+
+}
diff --git a/rtemsbsd/ptpd/src/dep/alarms.h b/rtemsbsd/ptpd/src/dep/alarms.h
new file mode 100644
index 00000000..0a1d3f18
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/alarms.h
@@ -0,0 +1,100 @@
+#ifndef PTPDALARMS_H_
+#define PTPDALARMS_H_
+
+/*-
+ * Copyright (c) 2015 Wojciech Owczarek,
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file alarms.h
+ * @authors Wojciech Owczarek
+ * @date Wed Dec 9 19:13:10 2015
+ * Data type and function definitions related to
+ * handling raising and clearing alarms.
+ */
+
+#include "../datatypes.h"
+
+#define DOMAIN_MISMATCH_MIN 10 /* trigger domain mismatch alarm after at least 10 mismatches */
+#define ALARM_UPDATE_INTERVAL 1 /* how often we process alarms */
+#define ALARM_TIMEOUT_PERIOD 30 /* minimal alarm age to clear */
+#define ALARM_HANDLERS_MAX 3 /* max number of alarm handlers */
+#define ALARM_MESSAGE_LENGTH 100/* length of an alarm-specific message string */
+
+/* explicitly numbered because these are referenced in the MIB as textual convention */
+typedef enum {
+ ALRM_PORT_STATE = 0, /*x done*/
+ ALRM_OFM_THRESHOLD = 1, /*x done */
+ ALRM_OFM_SECONDS = 2, /*x done*/
+ ALRM_CLOCK_STEP = 3, /*x done*/
+ ALRM_NO_SYNC = 4, /*x done*/
+ ALRM_NO_DELAY = 5, /*x done*/
+ ALRM_MASTER_CHANGE = 6, /*x done*/
+ ALRM_NETWORK_FLT = 7, /*x done*/
+ ALRM_FAST_ADJ = 8, /*+/- currently only at maxppb */
+ ALRM_TIMEPROP_CHANGE = 9, /*x done*/
+ ALRM_DOMAIN_MISMATCH = 10, /*+/- currently only when all packets come from an incorrect domain */
+ ALRM_MAX
+} AlarmType;
+
+/* explicitly numbered because these are referenced in the MIB as textual convention */
+typedef enum {
+ ALARM_UNSET = 0, /* idle */
+ ALARM_SET = 1, /* condition has been triggerd */
+ ALARM_CLEARED = 2 /* condition has cleared */
+} AlarmState;
+
+struct _alarmEntry {
+ char shortName[5]; /* short code i.e. OFS, DLY, SYN, FLT etc. */
+ char name[31]; /* full name i.e. OFFSET_THRESHOLD, NO_DELAY, NO_SYNC etc. */
+ char description[101]; /* text description */
+ Boolean enabled; /* is the alarm operational ? */
+ uint8_t id; /* alarm ID */
+ Boolean eventOnly; /* this is only an event - don't manage state, just dispatch/inform when condition is met */
+ void (*handlers[ALARM_HANDLERS_MAX])(struct _alarmEntry *); /* alarm handlers */
+ void * userData; /* user data pointer */
+ Boolean unhandled; /* this event is pending pick-up - prevets from clearing condition until it's handled */
+ uint32_t age; /* age of alarm in current state (seconds) */
+ uint32_t minAge; /* minimum age of alarm (time between set and clear notification - condition can go away earlier */
+ AlarmState state; /* state of the alarm */
+ Boolean condition; /* is the alarm condition met? (so we can check conditions and set alarms separately */
+ TimeInternal timeSet; /* time when set */
+ TimeInternal timeCleared; /* time when cleared */
+ Boolean internalOnly; /* do not display in status file / indicate that the alarm is internal only */
+ PtpEventData eventData; /* the event data union - so we can capture any data we need without the need to capture a whole PtpClock */
+};
+
+typedef struct _alarmEntry AlarmEntry;
+
+void initAlarms(AlarmEntry* alarms, int count, void* userData); /* fill an array with initial alarm data */
+void configureAlarms(AlarmEntry* alarms, int count, void* userData); /* fill an array with initial alarm data */
+void enableAlarms(AlarmEntry* alarms, int count, Boolean enabled); /* enable/disable all alarms */
+void updateAlarms(AlarmEntry *alarms, int count); /* dispatch alarms: age, set, clear alarms etc. */
+void displayAlarms(AlarmEntry *alarms, int count); /* display a formatted alarm summary table */
+int getAlarmSummary(char * output, int size, AlarmEntry *alarms, int count); /* produce a one-line alarm summary string */
+void handleAlarm(AlarmEntry *alarms, void *userData);
+
+#endif /*PTPDALARMS_H_*/
diff --git a/rtemsbsd/ptpd/src/dep/configdefaults.c b/rtemsbsd/ptpd/src/dep/configdefaults.c
new file mode 100644
index 00000000..16509b00
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/configdefaults.c
@@ -0,0 +1,675 @@
+/* Copyright (c) 2013-2015 Wojciech Owczarek,
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file configtemplates.c
+ * @date Mon Oct 12 03:25:32 2015
+ *
+ * @brief Definitions of configuration templates
+ *
+ */
+
+#include "../ptpd.h"
+
+#define CONFIG_ISTRUE(key) \
+ (iniparser_getboolean(dict,key,FALSE)==TRUE)
+
+#define CONFIG_ISSET(key) \
+ (strcmp(iniparser_getstring(dict, key, ""),"") != 0)
+
+#define IS_QUIET()\
+ ( CONFIG_ISTRUE("%quiet%:%quiet%") )
+
+/*
+ * static definition of configuration templates - maximum 100 settings per template,
+ * as defined in configtemplates.h. Template for a template below.
+ */
+
+/* =============== configuration templates begin ===============*/
+
+static const ConfigTemplate configTemplates[] = {
+
+ { "g8265-e2e-master", "ITU-T G.8265.1 (Telecom profile) unicast master", {
+ {"ptpengine:preset", "masteronly"},
+ {"ptpengine:ip_mode", "unicast"},
+ {"ptpengine:unicast_negotiation", "y"},
+ {"ptpengine:domain", "4"},
+ {"ptpengine:disable_bmca", "y"},
+ {"ptpengine:delay_mechanism", "E2E"},
+ {"ptpengine:log_sync_interval", "-6"},
+ {"ptpengine:log_delayreq_interval", "-6"},
+ {"ptpengine:announce_receipt_timeout", "3"},
+ {"ptpengine:priority1", "128"},
+ {"ptpengine:priority2", "128"},
+ {NULL}}
+ },
+
+ { "g8265-e2e-slave", "ITU-T G.8265.1 (Telecom profile) unicast slave", {
+ {"ptpengine:preset", "slaveonly"},
+ {"ptpengine:ip_mode", "unicast"},
+ {"ptpengine:unicast_negotiation", "y"},
+ {"ptpengine:domain", "4"},
+ {"ptpengine:delay_mechanism", "E2E"},
+ {"ptpengine:announce_receipt_timeout", "3"},
+ {NULL}}
+ },
+
+ { "layer2-p2p-master", "Layer 2 master using Peer to Peer", {
+ {"ptpengine:transport","ethernet"},
+ {"ptpengine:delay_mechanism","P2P"},
+ {"ptpengine:preset","masteronly"},
+// {"",""},
+ {NULL}}
+ },
+
+ { "layer2-p2p-slave", "Layer 2 slave running End to End", {
+ {"ptpengine:transport","ethernet"},
+ {"ptpengine:delay_mechanism","E2E"},
+ {"ptpengine:preset","slaveonly"},
+// {"",""},
+ {NULL}}
+ },
+
+ { "valid-utc-properties", "Basic property set to announce valid UTC, UTC offset and leap seconds", {
+ {"ptpengine:ptp_timescale","PTP"},
+ {"ptpengine:time_traceable","y"},
+ {"ptpengine:frequency_traceable","y"},
+ {"clock:leap_seconds_file", DATADIR"/"PACKAGE_NAME"/leap-seconds.list"},
+ /*
+ * UTC offset value and UTC offset valid flag
+ * will be announced if leap file is parsed and up to date
+ */
+ {NULL}}
+ },
+
+ { "full-logging", "Enable logging for all facilities (statistics, status, log file)", {
+ {"global:log_status", "y"},
+ {"global:status_file", "/var/run/ptpd2.status"},
+ {"global:statistics_log_interval", "1"},
+ {"global:lock_file", "/var/run/ptpd2.pid"},
+ {"global:log_statistics", "y"},
+ {"global:statistics_file", "/var/log/ptpd2.statistics"},
+// {"global:statistics_file_truncate", "n"},
+ {"global:log_file", "/var/log/ptpd2.log"},
+ {"global:statistics_timestamp_format", "both"},
+ {NULL}}
+ },
+
+ { "full-logging-instance", "Logging for all facilities using 'instance' variable which the user should provide", {
+ {"global:log_status", "y"},
+ {"global:status_file", "@rundir@/ptpd2. at instance@.status"},
+ {"global:statistics_log_interval", "1"},
+ {"global:lock_file", "@rundir@/ptpd2. at instance@.pid"},
+ {"global:log_statistics", "y"},
+ {"global:statistics_file", "@logdir@/ptpd2. at instance@.statistics"},
+// {"global:statistics_file_truncate", "n"},
+ {"global:log_file", "@logdir@/ptpd2. at instance@.log"},
+ {"global:statistics_timestamp_format", "both"},
+ {NULL}}
+ },
+
+ {NULL}
+};
+
+/* =============== configuration templates end ===============*/
+
+/* template of a template looks like this... */
+
+/*
+ { "template-name", "template description", {
+ {"section:key","value"},
+ {"",""},
+ {NULL}}
+ },
+
+ { "", "", {
+ {"",""},
+ {"",""},
+ {NULL}}
+ },
+
+
+*/
+
+/* Load all rtOpts defaults */
+void
+loadDefaultSettings( RunTimeOpts* rtOpts )
+{
+
+ /* Wipe the memory first to avoid unconsistent behaviour - no need to set Boolean to FALSE, int to 0 etc. */
+ memset(rtOpts, 0, sizeof(RunTimeOpts));
+
+ rtOpts->logAnnounceInterval = DEFAULT_ANNOUNCE_INTERVAL;
+ rtOpts->logSyncInterval = DEFAULT_SYNC_INTERVAL;
+ rtOpts->logMinPdelayReqInterval = DEFAULT_PDELAYREQ_INTERVAL;
+ rtOpts->clockQuality.clockAccuracy = DEFAULT_CLOCK_ACCURACY;
+ rtOpts->clockQuality.clockClass = DEFAULT_CLOCK_CLASS;
+ rtOpts->clockQuality.offsetScaledLogVariance = DEFAULT_CLOCK_VARIANCE;
+ rtOpts->priority1 = DEFAULT_PRIORITY1;
+ rtOpts->priority2 = DEFAULT_PRIORITY2;
+ rtOpts->domainNumber = DEFAULT_DOMAIN_NUMBER;
+ rtOpts->portNumber = NUMBER_PORTS;
+
+ rtOpts->anyDomain = FALSE;
+
+ rtOpts->transport = UDP_IPV4;
+
+ /* timePropertiesDS */
+ rtOpts->timeProperties.currentUtcOffsetValid = DEFAULT_UTC_VALID;
+ rtOpts->timeProperties.currentUtcOffset = DEFAULT_UTC_OFFSET;
+ rtOpts->timeProperties.timeSource = INTERNAL_OSCILLATOR;
+ rtOpts->timeProperties.timeTraceable = FALSE;
+ rtOpts->timeProperties.frequencyTraceable = FALSE;
+ rtOpts->timeProperties.ptpTimescale = TRUE;
+
+ rtOpts->ipMode = IPMODE_MULTICAST;
+ rtOpts->dot1AS = FALSE;
+
+ rtOpts->disableUdpChecksums = TRUE;
+
+ rtOpts->unicastNegotiation = FALSE;
+ rtOpts->unicastNegotiationListening = FALSE;
+ rtOpts->disableBMCA = FALSE;
+ rtOpts->unicastGrantDuration = 300;
+ rtOpts->unicastAcceptAny = FALSE;
+ rtOpts->unicastPortMask = 0;
+
+ rtOpts->noAdjust = NO_ADJUST; // false
+ rtOpts->logStatistics = TRUE;
+ rtOpts->statisticsTimestamp = TIMESTAMP_DATETIME;
+
+ rtOpts->periodicUpdates = FALSE; /* periodically log a status update */
+
+ /* Deep display of all packets seen by the daemon */
+ rtOpts->displayPackets = FALSE;
+
+ rtOpts->s = DEFAULT_DELAY_S;
+ rtOpts->inboundLatency.nanoseconds = DEFAULT_INBOUND_LATENCY;
+ rtOpts->outboundLatency.nanoseconds = DEFAULT_OUTBOUND_LATENCY;
+ rtOpts->max_foreign_records = DEFAULT_MAX_FOREIGN_RECORDS;
+ rtOpts->nonDaemon = FALSE;
+
+ /*
+ * defaults for new options
+ */
+ rtOpts->ignore_delayreq_interval_master = FALSE;
+ rtOpts->do_IGMP_refresh = TRUE;
+ rtOpts->useSysLog = FALSE;
+ rtOpts->announceReceiptTimeout = DEFAULT_ANNOUNCE_RECEIPT_TIMEOUT;
+#ifdef RUNTIME_DEBUG
+ rtOpts->debug_level = LOG_INFO; /* by default debug messages as disabled, but INFO messages and below are printed */
+#endif
+ rtOpts->ttl = 64;
+ rtOpts->delayMechanism = DEFAULT_DELAY_MECHANISM;
+ rtOpts->noResetClock = DEFAULT_NO_RESET_CLOCK;
+ rtOpts->portDisabled = FALSE;
+ rtOpts->stepOnce = FALSE;
+ rtOpts->stepForce = FALSE;
+#ifdef HAVE_LINUX_RTC_H
+ rtOpts->setRtc = FALSE;
+#endif /* HAVE_LINUX_RTC_H */
+
+ rtOpts->clearCounters = FALSE;
+ rtOpts->statisticsLogInterval = 0;
+
+ rtOpts->initial_delayreq = DEFAULT_DELAYREQ_INTERVAL;
+ rtOpts->logMinDelayReqInterval = DEFAULT_DELAYREQ_INTERVAL;
+ rtOpts->autoDelayReqInterval = TRUE;
+ rtOpts->masterRefreshInterval = 60;
+
+ /* maximum values for unicast negotiation */
+ rtOpts->logMaxPdelayReqInterval = 5;
+ rtOpts->logMaxDelayReqInterval = 5;
+ rtOpts->logMaxSyncInterval = 5;
+ rtOpts->logMaxAnnounceInterval = 5;
+
+ rtOpts->drift_recovery_method = DRIFT_KERNEL;
+ strncpy(rtOpts->lockDirectory, DEFAULT_LOCKDIR, PATH_MAX);
+ strncpy(rtOpts->driftFile, DEFAULT_DRIFTFILE, PATH_MAX);
+/* strncpy(rtOpts->lockFile, DEFAULT_LOCKFILE, PATH_MAX); */
+ rtOpts->autoLockFile = FALSE;
+ rtOpts->snmpEnabled = FALSE;
+ rtOpts->snmpTrapsEnabled = FALSE;
+ rtOpts->alarmsEnabled = FALSE;
+ rtOpts->alarmInitialDelay = 0;
+ rtOpts->alarmMinAge = 30;
+ /* This will only be used if the "none" preset is configured */
+#ifndef PTPD_SLAVE_ONLY
+ rtOpts->slaveOnly = FALSE;
+#else
+ rtOpts->slaveOnly = TRUE;
+#endif /* PTPD_SLAVE_ONLY */
+ /* Otherwise default to slave only via the preset */
+ rtOpts->selectedPreset = PTP_PRESET_SLAVEONLY;
+ rtOpts->pidAsClockId = FALSE;
+
+ strncpy(rtOpts->portDescription,"ptpd", sizeof(rtOpts->portDescription));
+
+ /* highest possible */
+ rtOpts->logLevel = LOG_ALL;
+
+ /* ADJ_FREQ_MAX by default */
+ rtOpts->servoMaxPpb = ADJ_FREQ_MAX / 1000;
+ /* kP and kI are scaled to 10000 and are gains now - values same as originally */
+ rtOpts->servoKP = 0.1;
+ rtOpts->servoKI = 0.001;
+
+ rtOpts->servoDtMethod = DT_CONSTANT;
+ /* when measuring dT, use a maximum of 5 sync intervals (would correspond to avg 20% discard rate) */
+ rtOpts->servoMaxdT = 5.0;
+
+ /* disabled by default */
+ rtOpts->announceTimeoutGracePeriod = 0;
+
+ /* currentUtcOffsetValid compatibility flags */
+ rtOpts->alwaysRespectUtcOffset = TRUE;
+ rtOpts->preferUtcValid = FALSE;
+ rtOpts->requireUtcValid = FALSE;
+
+ /* Try 46 for expedited forwarding */
+ rtOpts->dscpValue = 0;
+
+#if (defined(linux) && defined(HAVE_SCHED_H)) || defined(HAVE_SYS_CPUSET_H) || defined (__QNXNTO__)
+ rtOpts-> cpuNumber = -1;
+#endif /* (linux && HAVE_SCHED_H) || HAVE_SYS_CPUSET_H*/
+
+#ifdef PTPD_STATISTICS
+
+ rtOpts->oFilterMSConfig.enabled = FALSE;
+ rtOpts->oFilterMSConfig.discard = TRUE;
+ rtOpts->oFilterMSConfig.autoTune = TRUE;
+ rtOpts->oFilterMSConfig.stepDelay = FALSE;
+ rtOpts->oFilterMSConfig.alwaysFilter = FALSE;
+ rtOpts->oFilterMSConfig.stepThreshold = 1000000;
+ rtOpts->oFilterMSConfig.stepLevel = 500000;
+ rtOpts->oFilterMSConfig.capacity = 20;
+ rtOpts->oFilterMSConfig.threshold = 1.0;
+ rtOpts->oFilterMSConfig.weight = 1;
+ rtOpts->oFilterMSConfig.minPercent = 20;
+ rtOpts->oFilterMSConfig.maxPercent = 95;
+ rtOpts->oFilterMSConfig.thresholdStep = 0.1;
+ rtOpts->oFilterMSConfig.minThreshold = 0.1;
+ rtOpts->oFilterMSConfig.maxThreshold = 5.0;
+ rtOpts->oFilterMSConfig.delayCredit = 200;
+ rtOpts->oFilterMSConfig.creditIncrement = 10;
+ rtOpts->oFilterMSConfig.maxDelay = 1500;
+
+ rtOpts->oFilterSMConfig.enabled = FALSE;
+ rtOpts->oFilterSMConfig.discard = TRUE;
+ rtOpts->oFilterSMConfig.autoTune = TRUE;
+ rtOpts->oFilterSMConfig.stepDelay = FALSE;
+ rtOpts->oFilterSMConfig.alwaysFilter = FALSE;
+ rtOpts->oFilterSMConfig.stepThreshold = 1000000;
+ rtOpts->oFilterSMConfig.stepLevel = 500000;
+ rtOpts->oFilterSMConfig.capacity = 20;
+ rtOpts->oFilterSMConfig.threshold = 1.0;
+ rtOpts->oFilterSMConfig.weight = 1;
+ rtOpts->oFilterSMConfig.minPercent = 20;
+ rtOpts->oFilterSMConfig.maxPercent = 95;
+ rtOpts->oFilterSMConfig.thresholdStep = 0.1;
+ rtOpts->oFilterSMConfig.minThreshold = 0.1;
+ rtOpts->oFilterSMConfig.maxThreshold = 5.0;
+ rtOpts->oFilterSMConfig.delayCredit = 200;
+ rtOpts->oFilterSMConfig.creditIncrement = 10;
+ rtOpts->oFilterSMConfig.maxDelay = 1500;
+
+ rtOpts->filterMSOpts.enabled = FALSE;
+ rtOpts->filterMSOpts.filterType = FILTER_MIN;
+ rtOpts->filterMSOpts.windowSize = 4;
+ rtOpts->filterMSOpts.windowType = WINDOW_SLIDING;
+
+ rtOpts->filterSMOpts.enabled = FALSE;
+ rtOpts->filterSMOpts.filterType = FILTER_MIN;
+ rtOpts->filterSMOpts.windowSize = 4;
+ rtOpts->filterSMOpts.windowType = WINDOW_SLIDING;
+
+ /* How often refresh statistics (seconds) */
+ rtOpts->statsUpdateInterval = 30;
+ /* Servo stability detection settings follow */
+ rtOpts->servoStabilityDetection = FALSE;
+ /* Stability threshold (ppb) - observed drift std dev value considered stable */
+ rtOpts->servoStabilityThreshold = 10;
+ /* How many consecutive statsUpdateInterval periods of observed drift std dev within threshold means stable servo */
+ rtOpts->servoStabilityPeriod = 1;
+ /* How many minutes without servo stabilisation means servo has not stabilised */
+ rtOpts->servoStabilityTimeout = 10;
+ /* How long to wait for one-way delay prefiltering */
+ rtOpts->calibrationDelay = 0;
+ /* if set to TRUE and maxDelay is defined, only check against threshold if servo is stable */
+ rtOpts->maxDelayStableOnly = FALSE;
+ /* if set to non-zero, reset slave if more than this amount of consecutive delay measurements was above maxDelay */
+ rtOpts->maxDelayMaxRejected = 0;
+#endif
+
+ /* status file options */
+ rtOpts->statusFileUpdateInterval = 1;
+
+ rtOpts->ofmAlarmThreshold = 0;
+
+ /* panic mode options */
+ rtOpts->enablePanicMode = FALSE;
+ rtOpts->panicModeDuration = 2;
+ rtOpts->panicModeExitThreshold = 0;
+
+ /* full network reset after 5 times in listening */
+ rtOpts->maxListen = 5;
+
+ rtOpts->panicModeReleaseClock = FALSE;
+ rtOpts->ntpOptions.enableEngine = FALSE;
+ rtOpts->ntpOptions.enableControl = FALSE;
+ rtOpts->ntpOptions.enableFailover = FALSE;
+ rtOpts->ntpOptions.failoverTimeout = 120;
+ rtOpts->ntpOptions.checkInterval = 15;
+ rtOpts->ntpOptions.keyId = 0;
+ strncpy(rtOpts->ntpOptions.hostAddress,"localhost",MAXHOSTNAMELEN); /* not configurable, but could be */
+ rtOpts->preferNTP = FALSE;
+
+ rtOpts->leapSecondPausePeriod = 5;
+ /* by default, announce the leap second 12 hours before the event:
+ * Clause 9.4 paragraph 5 */
+ rtOpts->leapSecondNoticePeriod = 43200;
+ rtOpts->leapSecondHandling = LEAP_ACCEPT;
+ rtOpts->leapSecondSmearPeriod = 86400;
+
+/* timing domain */
+ rtOpts->idleTimeout = 120; /* idle timeout */
+ rtOpts->electionDelay = 15; /* anti-flapping delay */
+
+/* Log file settings */
+
+ rtOpts->statisticsLog.logID = "statistics";
+ rtOpts->statisticsLog.openMode = "a+";
+ rtOpts->statisticsLog.logFP = NULL;
+ rtOpts->statisticsLog.truncateOnReopen = FALSE;
+ rtOpts->statisticsLog.unlinkOnClose = FALSE;
+ rtOpts->statisticsLog.maxSize = 0;
+
+ rtOpts->recordLog.logID = "record";
+ rtOpts->recordLog.openMode = "a+";
+ rtOpts->recordLog.logFP = NULL;
+ rtOpts->recordLog.truncateOnReopen = FALSE;
+ rtOpts->recordLog.unlinkOnClose = FALSE;
+ rtOpts->recordLog.maxSize = 0;
+
+ rtOpts->eventLog.logID = "log";
+ rtOpts->eventLog.openMode = "a+";
+ rtOpts->eventLog.logFP = NULL;
+ rtOpts->eventLog.truncateOnReopen = FALSE;
+ rtOpts->eventLog.unlinkOnClose = FALSE;
+ rtOpts->eventLog.maxSize = 0;
+
+ rtOpts->statusLog.logID = "status";
+ rtOpts->statusLog.openMode = "w";
+ strncpy(rtOpts->statusLog.logPath, DEFAULT_STATUSFILE, PATH_MAX);
+ rtOpts->statusLog.logFP = NULL;
+ rtOpts->statusLog.truncateOnReopen = FALSE;
+ rtOpts->statusLog.unlinkOnClose = TRUE;
+
+/* Management message support settings */
+ rtOpts->managementEnabled = TRUE;
+ rtOpts->managementSetEnable = FALSE;
+
+/* IP ACL settings */
+
+ rtOpts->timingAclEnabled = FALSE;
+ rtOpts->managementAclEnabled = FALSE;
+ rtOpts->timingAclOrder = ACL_DENY_PERMIT;
+ rtOpts->managementAclOrder = ACL_DENY_PERMIT;
+
+ // by default we don't check Sync message sequence continuity
+ rtOpts->syncSequenceChecking = FALSE;
+ rtOpts->clockUpdateTimeout = 0;
+
+}
+
+/* The PtpEnginePreset structure for reference:
+
+typedef struct {
+
+ char* presetName;
+ Boolean slaveOnly;
+ Boolean noAdjust;
+ UInteger8_option clockClass;
+
+} PtpEnginePreset;
+*/
+
+PtpEnginePreset
+getPtpPreset(int presetNumber, RunTimeOpts* rtOpts)
+{
+
+ PtpEnginePreset ret;
+
+ memset(&ret,0,sizeof(ret));
+
+ switch(presetNumber) {
+
+ case PTP_PRESET_SLAVEONLY:
+ ret.presetName="slaveonly";
+ ret.slaveOnly = TRUE;
+ ret.noAdjust = FALSE;
+ ret.clockClass.minValue = SLAVE_ONLY_CLOCK_CLASS;
+ ret.clockClass.maxValue = SLAVE_ONLY_CLOCK_CLASS;
+ ret.clockClass.defaultValue = SLAVE_ONLY_CLOCK_CLASS;
+ break;
+ case PTP_PRESET_MASTERSLAVE:
+ ret.presetName = "masterslave";
+ ret.slaveOnly = FALSE;
+ ret.noAdjust = FALSE;
+ ret.clockClass.minValue = 128;
+ ret.clockClass.maxValue = 254;
+ ret.clockClass.defaultValue = DEFAULT_CLOCK_CLASS;
+ break;
+ case PTP_PRESET_MASTERONLY:
+ ret.presetName = "masteronly";
+ ret.slaveOnly = FALSE;
+ ret.noAdjust = TRUE;
+ ret.clockClass.minValue = 0;
+ ret.clockClass.maxValue = 127;
+ ret.clockClass.defaultValue = DEFAULT_CLOCK_CLASS__APPLICATION_SPECIFIC_TIME_SOURCE;
+ break;
+ default:
+ ret.presetName = "none";
+ ret.slaveOnly = rtOpts->slaveOnly;
+ ret.noAdjust = rtOpts->noAdjust;
+ ret.clockClass.minValue = 0;
+ ret.clockClass.maxValue = 255;
+ ret.clockClass.defaultValue = rtOpts->clockQuality.clockClass;
+ }
+
+ return ret;
+}
+
+/* Apply template search from dictionary src to dictionary dst */
+static int
+applyTemplateFromDictionary(dictionary *dict, dictionary *src, char *search, int overwrite) {
+
+ char *key, *value, *pos;
+ int i = 0;
+ int found;
+
+ /* -1 on not found, 0+ on actual applies */
+ found = -1;
+
+ for(i=0; i<src->n; i++) {
+ key = src->key[i];
+ value = src->val[i];
+ pos = strstr(key, ":");
+ if(value != NULL && pos != NULL) {
+ *pos = '\0';
+ pos++;
+ if(!strcmp(search, key) && (strstr(pos,":") != NULL)) {
+ if(found < 0) found = 0;
+ if( overwrite || !CONFIG_ISSET(pos)) {
+ DBG("template %s setting %s to %s\n", search, pos, value);
+ dictionary_set(dict, pos, value);
+ found++;
+ }
+ }
+ /* reverse the damage - no need for strdup() */
+ pos--;
+ *pos = ':';
+ }
+ }
+
+ return found;
+
+}
+
+static void loadFileList(dictionary *dict, char *list) {
+
+ char* stash;
+ char* text_;
+ char* text__;
+ char *filename;
+
+ if(dict == NULL) return;
+ if(!strlen(list)) return;
+
+ text_=strdup(list);
+
+ for(text__=text_;; text__=NULL) {
+ filename=strtok_r(text__,", ;\t",&stash);
+ if(filename==NULL) break;
+ if(!iniparser_merge_file(dict, filename,1)) {
+ ERROR("Could not load template file %s\n", filename);
+ } else {
+ INFO("Loaded template file %s\n",filename);
+ }
+ }
+
+ if(text_ != NULL) {
+ free(text_);
+ }
+
+}
+
+/* split list to tokens, look for each template and apply it if found */
+int
+applyConfigTemplates(dictionary *target, char *templateNames, char *files) {
+
+ ConfigTemplate *template = NULL;
+ TemplateOption *option = NULL;
+ char *templateName = NULL;
+ int iFound = -1;
+ int fFound = -1;
+
+ dictionary *fileDict;
+ dictionary *dict;
+
+ char* stash;
+ char* text_;
+ char* text__;
+
+ if(target == NULL) {
+ return 0;
+ }
+
+ if(!strlen(templateNames)) return 0;
+
+ fileDict = dictionary_new(0);
+ dict = dictionary_new(0);
+
+ loadFileList(fileDict, DEFAULT_TEMPLATE_FILE);
+ loadFileList(fileDict, files);
+
+ text_=strdup(templateNames);
+
+ for(text__=text_;; text__=NULL) {
+
+ iFound = -1;
+ fFound = -1;
+
+ /* apply from built-in templates */
+ templateName=strtok_r(text__,", ;\t",&stash);
+ if(templateName==NULL) break;
+
+ template = (ConfigTemplate*)configTemplates;
+ while(template->name != NULL) {
+ if(!strcmp(template->name, templateName)) {
+ if(iFound < 0) iFound = 0;
+ DBG("Loading built-in config template %s\n", template->name);
+ option = (TemplateOption*)template->options;
+ while(option->name != NULL) {
+ dictionary_set(dict, option->name, option->value);
+ DBG("----> %s = %s",option->name, option->value);
+ option++;
+ iFound++;
+ }
+ break;
+ }
+ template++;
+ }
+
+ /* apply from previously loaded files */
+ fFound = applyTemplateFromDictionary(dict, fileDict, templateName, 1);
+
+ if (!IS_QUIET()) {
+ if((iFound < 0) && (fFound < 0)) {
+ WARNING("Configuration template \"%s\" not found\n", templateName);
+ } else {
+ if(iFound < 0) iFound = 0;
+ if(fFound < 0) fFound = 0;
+ NOTICE("Applied configuration template \"%s\" (%d matches)\n", templateName,
+ iFound + fFound);
+ }
+ }
+
+ }
+
+ if(text_ != NULL) {
+ free(text_);
+ }
+
+ /* preserve existing settings */
+ dictionary_merge(dict, target, 0 , !IS_QUIET(), NULL);
+ dictionary_del(&fileDict);
+ dictionary_del(&dict);
+ return 0;
+
+}
+
+/* dump the list of templates provided */
+void
+dumpConfigTemplates() {
+
+ ConfigTemplate *template = NULL;
+ TemplateOption *option = NULL;
+ printf("\n");
+ template = (ConfigTemplate*)configTemplates;
+ while(template->name != NULL) {
+ printf(" Template: %s\n", template->name);
+ printf("Description: %s\n\n", template->description);
+ option = (TemplateOption*)template->options;
+ while(option->name != NULL) {
+ printf("\t\t%s=\"%s\"\n", option->name, option->value);
+ option++;
+ }
+ template++;
+ printf("\n-\n\n");
+ }
+
+}
diff --git a/rtemsbsd/ptpd/src/dep/configdefaults.h b/rtemsbsd/ptpd/src/dep/configdefaults.h
new file mode 100644
index 00000000..4bca18ae
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/configdefaults.h
@@ -0,0 +1,52 @@
+/**
+ * @file configdefaults.h
+ *
+ * @brief definitions related to config templates and defaults
+ *
+ *
+ */
+
+#ifndef PTPD_CONFIGDEFAULTS_H_
+#define PTPD_CONFIGDEFAULTS_H_
+
+#include <stddef.h>
+#include "iniparser/iniparser.h"
+
+#define DEFAULT_TEMPLATE_FILE DATADIR"/"PACKAGE_NAME"/templates.conf"
+
+typedef struct {
+ char * name;
+ char * value;
+} TemplateOption;
+
+typedef struct {
+ char * name;
+ char * description;
+ TemplateOption options[100];
+} ConfigTemplate;
+
+/* Structure defining a PTP engine preset */
+typedef struct {
+
+ char* presetName;
+ Boolean slaveOnly;
+ Boolean noAdjust;
+ UInteger8_option clockClass;
+
+} PtpEnginePreset;
+
+/* Preset definitions */
+enum {
+ PTP_PRESET_NONE,
+ PTP_PRESET_SLAVEONLY,
+ PTP_PRESET_MASTERSLAVE,
+ PTP_PRESET_MASTERONLY,
+ PTP_PRESET_MAX
+};
+
+void loadDefaultSettings( RunTimeOpts* rtOpts );
+int applyConfigTemplates(dictionary *dict, char *templateNames, char *files);
+PtpEnginePreset getPtpPreset(int presetNumber, RunTimeOpts* rtOpts);
+void dumpConfigTemplates();
+
+#endif /* PTPD_CONFIGDEFAULTS_H_ */
diff --git a/rtemsbsd/ptpd/src/dep/constants_dep.h b/rtemsbsd/ptpd/src/dep/constants_dep.h
new file mode 100644
index 00000000..ad0f39f6
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/constants_dep.h
@@ -0,0 +1,251 @@
+
+/* constants_dep.h */
+
+#ifndef CONSTANTS_DEP_H
+#define CONSTANTS_DEP_H
+
+/**
+*\file
+* \brief Plateform-dependent constants definition
+*
+* This header defines all includes and constants which are plateform-dependent
+*
+* ptpdv2 is only implemented for linux, NetBSD and FreeBSD
+ */
+
+/* platform dependent */
+
+#if !defined(linux) && !defined(__NetBSD__) && !defined(__FreeBSD__) && \
+ !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__sun) && !defined(__QNXNTO__)
+#error PTPD hasn't been ported to this OS - should be possible \
+if it's POSIX compatible, if you succeed, report it to ptpd-devel at sourceforge.net
+#endif
+
+#ifdef linux
+#include<netinet/in.h>
+#include<net/if.h>
+#include<net/if_arp.h>
+#include <ifaddrs.h>
+#define IFACE_NAME_LENGTH IF_NAMESIZE
+#define NET_ADDRESS_LENGTH INET_ADDRSTRLEN
+
+#define IFCONF_LENGTH 10
+
+#define octet ether_addr_octet
+#endif /* linux */
+
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__sun) || defined(__QNXNTO__)
+# include <sys/types.h>
+# include <sys/socket.h>
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif /* HAVE_SYS_SOCKIO_H */
+# include <netinet/in.h>
+# include <net/if.h>
+# include <net/if_dl.h>
+# include <net/if_types.h>
+#ifdef HAVE_NET_IF_ETHER_H
+# include <net/if_ether.h>
+#endif
+#ifdef HAVE_SYS_UIO_H
+# include <sys/uio.h>
+#endif
+#ifdef HAVE_NET_ETHERNET_H
+# include <net/ethernet.h>
+#endif
+#include <ifaddrs.h>
+# define IFACE_NAME_LENGTH IF_NAMESIZE
+# define NET_ADDRESS_LENGTH INET_ADDRSTRLEN
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif /* HAVE_SYS_PARAM_H */
+
+#ifdef __QNXNTO__
+#include <sys/neutrino.h>
+#include <sys/syspage.h>
+#define BSD_INTERFACE_FUNCTIONS
+#endif /* __QNXNTO __ */
+
+
+#if !defined(ETHER_ADDR_LEN) && defined(ETHERADDRL)
+# define ETHER_ADDR_LEN ETHERADDRL
+#endif /* ETHER_ADDR_LEN && ETHERADDRL */
+
+#ifndef ETHER_HDR_LEN
+# define ETHER_HDR_LEN sizeof (struct ether_header)
+#endif /* ETHER_ADDR_LEN && ETHERADDRL */
+
+
+# define IFCONF_LENGTH 10
+
+# define adjtimex ntp_adjtime
+
+
+#endif
+
+#ifdef HAVE_MACHINE_ENDIAN_H
+# include <machine/endian.h>
+#endif /* HAVE_MACHINE_ENDIAN_H */
+
+#ifdef HAVE_ENDIAN_H
+# include <endian.h>
+#endif /* HAVE_ENDIAN_H */
+
+#ifdef HAVE_SYS_ISA_DEFS_H
+# include <sys/isa_defs.h>
+#endif /* HAVE_SYS_ISA_DEFS_H */
+
+# if BYTE_ORDER == LITTLE_ENDIAN || defined(_LITTLE_ENDIAN) || (defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN)
+# define PTPD_LSBF
+# elif BYTE_ORDER == BIG_ENDIAN || defined(_BIG_ENDIAN) || (defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN)
+# define PTPD_MSBF
+# endif
+
+#define CLOCK_IDENTITY_LENGTH 8
+#define ADJ_FREQ_MAX 500000
+
+/* UDP/IPv4 dependent */
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK 0x7f000001UL
+#endif
+
+#define SUBDOMAIN_ADDRESS_LENGTH 4
+#define PORT_ADDRESS_LENGTH 2
+#define PTP_UUID_LENGTH 6
+#define CLOCK_IDENTITY_LENGTH 8
+#define FLAG_FIELD_LENGTH 2
+
+#define PACKET_SIZE 300
+#define PACKET_BEGIN_UDP (ETHER_HDR_LEN + sizeof(struct ip) + \
+ sizeof(struct udphdr))
+#define PACKET_BEGIN_ETHER (ETHER_HDR_LEN)
+
+#define PTP_EVENT_PORT 319
+#define PTP_GENERAL_PORT 320
+
+#define DEFAULT_PTP_DOMAIN_ADDRESS "224.0.1.129"
+#define PEER_PTP_DOMAIN_ADDRESS "224.0.0.107"
+
+/* 802.3 Support */
+
+#define PTP_ETHER_DST "01:1b:19:00:00:00"
+#define PTP_ETHER_TYPE 0x88f7
+#define PTP_ETHER_PEER "01:80:c2:00:00:0E"
+
+#ifdef PTPD_UNICAST_MAX
+#define UNICAST_MAX_DESTINATIONS PTPD_UNICAST_MAX
+#else
+#define UNICAST_MAX_DESTINATIONS 16
+#endif /* PTPD_UNICAST_MAX */
+
+/* dummy clock driver designation in preparation for generic clock driver API */
+#define DEFAULT_CLOCKDRIVER "kernelclock"
+/* default lock file location and mode */
+#define DEFAULT_LOCKMODE F_WRLCK
+#define DEFAULT_LOCKDIR "/var/run"
+#define DEFAULT_LOCKFILE_NAME PTPD_PROGNAME".lock"
+//define DEFAULT_LOCKFILE_PATH DEFAULT_LOCKDIR"/"DEFAULT_LOCKFILE_NAME
+#define DEFAULT_FILE_PERMS (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
+
+/* default drift file location */
+#define DEFAULT_DRIFTFILE "/etc/"PTPD_PROGNAME"_"DEFAULT_CLOCKDRIVER".drift"
+
+/* default status file location */
+#define DEFAULT_STATUSFILE DEFAULT_LOCKDIR"/"PTPD_PROGNAME".status"
+
+/* Highest log level (default) catches all */
+#define LOG_ALL LOG_DEBUGV
+
+/* Difference between Unix time / UTC and NTP time */
+#define NTP_EPOCH 2208988800ULL
+
+/* wait a maximum of 10 ms for a late TX timestamp */
+#define LATE_TXTIMESTAMP_US 10000
+
+/* drift recovery metod for use with -F */
+enum {
+ DRIFT_RESET = 0,
+ DRIFT_KERNEL,
+ DRIFT_FILE
+};
+/* IP transmission mode */
+enum {
+ IPMODE_MULTICAST = 0,
+ IPMODE_UNICAST,
+ IPMODE_HYBRID,
+#if 0
+ IPMODE_UNICAST_SIGNALING
+#endif
+};
+
+/* log timestamp mode */
+enum {
+ TIMESTAMP_DATETIME,
+ TIMESTAMP_UNIX,
+ TIMESTAMP_BOTH
+};
+
+/* servo dT calculation mode */
+enum {
+ DT_NONE,
+ DT_CONSTANT,
+ DT_MEASURED
+};
+
+/* StatFilter op type */
+enum {
+ FILTER_NONE,
+ FILTER_MEAN,
+ FILTER_MIN,
+ FILTER_MAX,
+ FILTER_ABSMIN,
+ FILTER_ABSMAX,
+ FILTER_MEDIAN,
+ FILTER_MAXVALUE
+};
+
+/* StatFilter window type */
+enum {
+ WINDOW_INTERVAL,
+ WINDOW_SLIDING,
+// WINDOW_OVERLAPPING
+};
+
+/* Leap second handling */
+enum {
+ LEAP_ACCEPT,
+ LEAP_IGNORE,
+ LEAP_STEP,
+ LEAP_SMEAR
+};
+
+/* Alarm codes */
+enum {
+ ALARM_PORTSTATE,
+ ALARM_OFFSET_THRESHOLD,
+ ALARM_CLOCK_STEP,
+ ALARM_OFFSET_1SEC
+};
+
+#define MM_STARTING_BOUNDARY_HOPS 0x7fff
+
+/* others */
+
+/* bigger screen size constants */
+#define SCREEN_BUFSZ 228
+#define SCREEN_MAXSZ 180
+
+/* default size for string buffers */
+#define BUF_SIZE 1000
+
+#define NANOSECONDS_MAX 999999999
+
+// limit operator messages to once every X seconds
+#define OPERATOR_MESSAGES_INTERVAL 300.0
+
+#define MAX_SEQ_ERRORS 50
+
+#define MAXTIMESTR 32
+
+#endif /*CONSTANTS_DEP_H_*/
diff --git a/rtemsbsd/ptpd/src/dep/daemonconfig.c b/rtemsbsd/ptpd/src/dep/daemonconfig.c
new file mode 100644
index 00000000..5b55895f
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/daemonconfig.c
@@ -0,0 +1,3192 @@
+/*-
+ * Copyright (c) 2013-2015 Wojciech Owczarek,
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file daemonconfig.c
+ * @date Sun May 27 00:45:32 2013
+ *
+ * @brief Code to handle configuration file and default settings
+ *
+ * Functions in this file deal with config file parsing, reloading,
+ * loading default parameters, parsing command-line options, printing
+ * help output, etc.
+ *
+ */
+
+#include "../ptpd.h"
+
+/*-
+ * Helper macros - this is effectively the API for using the new config file interface.
+ * The macros below cover all operations required in parsing the configuration.
+ * This allows the parseConfig() function to be very straightforward and easy to expand
+ * - it pretty much only uses macros at this stage. The use of macros reduces the
+ * complexity of the parseConfig fuction. A lot of the macros are designed to work
+ * within parseConfig - they assume the existence of the "dictionary*" dict variable.
+ */
+
+static void printComment(const char* helptext);
+
+static int configSettingChanged(dictionary *oldConfig, dictionary *newConfig, const char *key);
+
+static void warnRestart(const char *key, int flags);
+
+static int configMapBoolean(int opCode, void *opArg, dictionary* dict,
+ dictionary *target, const char * key, int restartFlags, Boolean *var, Boolean def, const char* helptext);
+
+static int configMapString(int opCode, void *opArg, dictionary *dict,
+ dictionary *target, const char *key, int restartFlags, char *var, int size, char *def, const char* helptext);
+
+static int checkRangeInt(dictionary *dict, const char *key, int rangeFlags, int minBound, int maxBound);
+
+static int configMapInt(int opCode, void *opArg, dictionary *dict,
+ dictionary *target, const char *key, int restartFlags, int intType, void *var, int def,
+ const char *helptext, int rangeFlags, int minBound, int maxBound);
+
+static int checkRangeDouble(dictionary *dict, const char *key, int rangeFlags, double minBound, double maxBound);
+
+static int configMapDouble(int opCode, void *opArg, dictionary *dict,
+ dictionary *target, const char *key, int restartFlags, double *var, double def,
+ const char *helptext, int rangeFlags, double minBound, double maxBound);
+
+static int configMapSelectValue(int opCode, void *opArg, dictionary *dict,
+ dictionary *target, const char* key, int restartFlags, uint8_t *var, int def, const char *helptext, ...);
+
+static void parseUserVariables(dictionary *dict, dictionary *target);
+
+static void findUnknownSettings(int opCode, dictionary* source, dictionary* dict);
+
+
+/* Basic helper macros */
+
+#define STRING_EMPTY(string)\
+ (!strcmp(string,""))
+
+#define CONFIG_ISSET(key) \
+ (strcmp(iniparser_getstring(dict, key, ""),"") != 0)
+
+#define CONFIG_ISPRESENT(key) \
+ (iniparser_find_entry(dict, key) != 0)
+
+#define CONFIG_ISTRUE(key) \
+ (iniparser_getboolean(dict,key,FALSE)==TRUE)
+
+#define DICT_ISTRUE(dict,key) \
+ (iniparser_getboolean(dict,key,FALSE)==TRUE)
+
+/* Macros handling required settings, triggers, conflicts and dependencies */
+
+#define CONFIG_KEY_REQUIRED(key) \
+ if( !(opCode & CFGOP_PARSE_QUIET) && !(opCode & CFGOP_HELP_FULL) && !(opCode & CFGOP_HELP_SINGLE) && !CONFIG_ISSET(key) )\
+ { \
+ ERROR("Configuration error: option \"%s\" is required\n", key); \
+ parseResult = FALSE;\
+ }
+
+#define CONFIG_KEY_DEPENDENCY(key1, key2) \
+ if( CONFIG_ISSET(key1) && \
+ !CONFIG_ISSET(key2)) \
+ { \
+ if(!(opCode & CFGOP_PARSE_QUIET))\
+ ERROR("Configuration error: option \"%s\" requires option \"%s\"\n", key1, key2); \
+ parseResult = FALSE;\
+ }
+
+#define CONFIG_KEY_CONFLICT(key1, key2) \
+ if( CONFIG_ISSET(key1) && \
+ CONFIG_ISSET(key2)) \
+ { \
+ if(!(opCode & CFGOP_PARSE_QUIET))\
+ ERROR("Configuration error: option \"%s\" cannot be used with option \"%s\"\n", key1, key2); \
+ parseResult = FALSE;\
+ }
+
+#define CONFIG_KEY_CONDITIONAL_WARNING_NOTSET(condition,dep,messageText) \
+ if ( (condition) && \
+ !CONFIG_ISSET(dep) ) \
+ { \
+ if(!(opCode & CFGOP_PARSE_QUIET))\
+ WARNING("Warning: %s\n", messageText); \
+ }
+
+#define CONFIG_KEY_CONDITIONAL_WARNING_ISSET(condition,dep,messageText) \
+ if ( (condition) && \
+ CONFIG_ISSET(dep) ) \
+ { \
+ if(!(opCode & CFGOP_PARSE_QUIET))\
+ WARNING("Warning: %s\n", messageText); \
+ }
+
+#define CONFIG_KEY_CONDITIONAL_DEPENDENCY(key,condition,stringval,dep) \
+ if ( (condition) && \
+ !CONFIG_ISSET(dep) ) \
+ { \
+ if(!(opCode & CFGOP_PARSE_QUIET))\
+ ERROR("Configuration error: option \"%s=%s\" requires option \"%s\"\n", key, stringval, dep); \
+ parseResult = FALSE;\
+ }
+
+#define CONFIG_KEY_CONDITIONAL_CONFLICT(key,condition,stringval,dep) \
+ if ( (condition) && \
+ CONFIG_ISSET(dep) ) \
+ { \
+ if(!(opCode & CFGOP_PARSE_QUIET))\
+ ERROR("Configuration error: option \"%s=%s\" cannot be used with option \"%s\"\n", key, stringval, dep); \
+ parseResult = FALSE;\
+ }
+
+#define CONFIG_KEY_VALUE_FORBIDDEN(key,condition,stringval,message) \
+ if ( (condition) && \
+ CONFIG_ISSET(key) ) \
+ { \
+ if(!(opCode & CFGOP_PARSE_QUIET))\
+ ERROR("Configuration error: option \"%s=%s\" cannot be used: \n%s", key, stringval, message); \
+ parseResult = FALSE;\
+ }
+
+
+#define CONFIG_KEY_TRIGGER(key,variable,value, otherwise) \
+ if (CONFIG_ISSET(key) ) \
+ { \
+ variable = value;\
+ } else { \
+ variable = otherwise;\
+ }
+
+#define CONFIG_KEY_CONDITIONAL_TRIGGER(condition,variable,value,otherwise) \
+ if ((condition)) \
+ { \
+ variable = value; \
+ } else { \
+ variable = otherwise; \
+ }
+
+#define CONFIG_KEY_CONDITIONAL_ASSERTION(key,condition,warningtext) \
+ if ( (condition) && \
+ CONFIG_ISSET(key) ) \
+ { \
+ if(!(opCode & CFGOP_PARSE_QUIET))\
+ ERROR("%s\n", warningtext); \
+ parseResult = FALSE;\
+ }
+
+#define CONFIG_CONDITIONAL_ASSERTION(condition,warningtext) \
+ if ( (condition) ) \
+ { \
+ if(!(opCode & CFGOP_PARSE_QUIET))\
+ ERROR("%s\n", warningtext); \
+ parseResult = FALSE;\
+ }
+
+/* Macro printing a warning for a deprecated command-line option */
+#define WARN_DEPRECATED_COMMENT( old,new,long,key,comment )\
+printf("Note: The use of '-%c' option is deprecated %s- consider using '-%c' (--%s) or the %s setting\n",\
+ old, comment, new, long, key);
+
+#define WARN_DEPRECATED(old,new,long,key)\
+ WARN_DEPRECATED_COMMENT( old,new,long,key,"")
+
+#define CONFIG_KEY_ALIAS(src,dest) \
+ { \
+ if(!STRING_EMPTY(dictionary_get(dict,src,""))) {\
+ dictionary_set(dict,dest,dictionary_get(dict,src,""));\
+ dictionary_unset(dict,src);\
+ }\
+ }
+
+/* Output a potentially multi-line string, prefixed with ;s */
+static void
+printComment(const char* helptext)
+{
+
+ int i, len;
+ len = strlen(helptext);
+
+ if(len > 0)
+ printf("\n; ");
+
+ for (i = 0; i < len; i++) {
+ printf("%c",helptext[i]);
+ if(helptext[i]=='\n') {
+ printf("; ");
+ while(i < len) {
+ if( helptext[++i]!=' ' && helptext[i]!='\t') {
+ i--;
+ break;
+ }
+ }
+ }
+ }
+ printf("\n");
+
+}
+
+static int
+configSettingChanged(dictionary *oldConfig, dictionary *newConfig, const char *key)
+{
+
+ return(strcmp(
+ dictionary_get(newConfig, key,""),
+ dictionary_get(oldConfig, key,"")
+ ) != 0 );
+
+}
+
+/* warn about restart required if needed */
+static void
+warnRestart(const char *key, int flags)
+{
+ if(flags & PTPD_RESTART_DAEMON) {
+ NOTIFY("Change of %s setting requires "PTPD_PROGNAME" restart\n",key);
+ } else {
+ DBG("Setting %s changed, restart of subsystem %d required\n",key,flags);
+ }
+}
+
+static int
+configMapBoolean(int opCode, void *opArg, dictionary* dict, dictionary *target,
+ const char * key, int restartFlags, Boolean *var, Boolean def, const char* helptext)
+{
+
+ if(opCode & CFGOP_RESTART_FLAGS) {
+ if(CONFIG_ISSET(key)) {
+ *(int*)opArg |= restartFlags;
+ if(opCode & CFGOP_RESTART_FLAGS && !(opCode & CFGOP_PARSE_QUIET)) {
+ warnRestart(key, restartFlags);
+ }
+ }
+ return 1;
+ } else if(opCode & CFGOP_HELP_FULL || opCode & CFGOP_HELP_SINGLE) {
+
+ char *helpKey = (char*)opArg;
+
+ if((opCode & CFGOP_HELP_SINGLE)){
+ if (strcmp(key, helpKey)) {
+ return 1;
+ } else {
+ /* this way we tell the caller that we have already found the setting we were looking for */
+ helpKey[0] = '\0';
+ }
+ }
+
+ printf("setting: %s (--%s)\n", key, key);
+ printf(" type: BOOLEAN (value must start with t/T/y/Y/1/f/F/n/N/0)\n");
+ printf(" usage: %s\n", helptext);
+ printf("default: %s\n", def ? "Y" : "N");
+ printf("\n");
+ return 1;
+ } else {
+ if (!CONFIG_ISPRESENT(key)) {
+ *var = def;
+ dictionary_set(target,key,(*var)?"Y":"N");
+ if(strcmp(helptext, "") && opCode & CFGOP_PRINT_DEFAULT) {
+ printComment(helptext);
+ printf("%s = %s\n", key,(*var)?"Y":"N");
+ }
+ return 1;
+ } else if(!CONFIG_ISSET(key) || iniparser_getboolean(dict,key,-1) == -1) {
+ if(!(opCode & CFGOP_PARSE_QUIET)) {
+ ERROR("Configuration error: option \"%s='%s'\" has unknown boolean value: must start with 0/1/t/T/f/F/y/Y/n/N\n",key,iniparser_getstring(dict,key,""));
+ }
+ dictionary_set(target,key,""); /* suppress the "unknown entry" warning for malformed boolean values */ \
+ return 0;
+ } else {
+ *var=iniparser_getboolean(dict,key,def);
+ dictionary_set(target,key,(*var)?"Y":"N");
+ if(strcmp(helptext, "") && opCode & CFGOP_PRINT_DEFAULT) {
+ printComment(helptext);\
+ printf("%s = %s\n", key,(*var)?"Y":"N");
+ }
+ return 1;
+ }
+ }
+}
+
+static int
+configMapString(int opCode, void *opArg, dictionary *dict, dictionary *target,
+ const char *key, int restartFlags, char *var, int size, char *def, const char* helptext)
+{
+ if(opCode & CFGOP_RESTART_FLAGS) {
+ if(CONFIG_ISSET(key)) {
+ *(int*)opArg |= restartFlags;
+ if(opCode & CFGOP_RESTART_FLAGS && !(opCode & CFGOP_PARSE_QUIET)) {
+ warnRestart(key, restartFlags);
+ }
+ }
+ return 1;
+ } else if(opCode & CFGOP_HELP_FULL || opCode & CFGOP_HELP_SINGLE) {
+
+ char *helpKey = (char*)opArg;
+
+ if((opCode & CFGOP_HELP_SINGLE)){
+ if (strcmp(key, helpKey)) {
+ return 1;
+ } else {
+ /* this way we tell the caller that we have already found the setting we were looking for */
+ helpKey[0] = '\0';
+ }
+ }
+
+ printf("setting: %s (--%s)\n", key, key);
+ printf(" type: STRING\n");
+ printf(" usage: %s\n", helptext);
+ printf("default: %s\n", (strcmp(def, "") == 0) ? "[none]" : def);
+ printf("\n");\
+ return 1;
+ } else {
+ char *tmpstring = iniparser_getstring(dict,key,def);
+ /* do not overwrite the same pointer with the same pointer */
+ if (var!=tmpstring) {
+ strncpy(var, tmpstring, size);
+ var[size-1] = '\0';
+ }
+ dictionary_set(target, key, tmpstring);
+ if(strcmp(helptext,"") && opCode & CFGOP_PRINT_DEFAULT) {
+ printComment(helptext);
+ printf("%s = %s\n", key,tmpstring);
+ }
+ return 1;
+ }
+}
+
+static int
+checkRangeInt(dictionary *dict, const char *key, int rangeFlags, int minBound, int maxBound)
+{
+ int tmpdouble = iniparser_getint(dict,key,minBound);
+
+ int ret = 1;
+
+ switch(rangeFlags) {
+ case RANGECHECK_NONE:
+ return ret;
+ case RANGECHECK_RANGE:
+ ret = !(tmpdouble < minBound || tmpdouble > maxBound);
+ break;
+ case RANGECHECK_MIN:
+ ret = !(tmpdouble < minBound);
+ break;
+ case RANGECHECK_MAX:
+ ret = !(tmpdouble > maxBound);
+ break;
+ default:
+ return 0;
+ }
+
+ return ret;
+}
+
+
+static int
+configMapInt(int opCode, void *opArg, dictionary *dict, dictionary *target, const char *key, int restartFlags, int intType,
+ void *var, int def, const char *helptext, int rangeFlags,
+ int minBound, int maxBound)
+{
+ int ret = 0;
+
+ if(opCode & CFGOP_RESTART_FLAGS) {
+ if(CONFIG_ISSET(key)) {
+ *(int*)opArg |= restartFlags;
+ if(opCode & CFGOP_RESTART_FLAGS && !(opCode & CFGOP_PARSE_QUIET)) {
+ warnRestart(key, restartFlags);
+ }
+ }
+ return 1;
+ } else if(opCode & CFGOP_HELP_FULL || opCode & CFGOP_HELP_SINGLE) {
+
+ char *helpKey = (char*)opArg;
+
+ if((opCode & CFGOP_HELP_SINGLE)){
+ if (strcmp(key, helpKey)) {
+ return 1;
+ } else {
+ /* this way we tell the caller that we have already found the setting we were looking for */
+ helpKey[0] = '\0';
+ }
+ }
+
+ switch(rangeFlags) {
+ case RANGECHECK_NONE:
+ printf("setting: %s (--%s)\n", key, key);
+ printf(" type: INT\n");\
+ printf(" usage: %s\n", helptext);
+ printf("default: %d\n", def);
+ printf("\n");
+ return 1;
+ case RANGECHECK_RANGE:
+ printf("setting: %s (--%s)\n", key, key);
+ printf(" type: INT ");
+ if( minBound != maxBound )
+ printf("(min: %d, max: %d)\n", minBound, maxBound);
+ else
+ printf("(only value of %d allowed)", minBound);
+ printf("\n");
+ printf(" usage: %s\n", helptext);
+ printf("default: %d\n", def);
+ printf("\n");
+ return 1;
+ case RANGECHECK_MIN:
+ printf("setting: %s (--%s)\n", key, key);
+ printf(" type: INT (min: %d)\n", minBound);
+ printf(" usage: %s\n", helptext);
+ printf("default: %d\n", def);
+ printf("\n");
+ return 1;
+ case RANGECHECK_MAX:
+ printf("setting: %s (--%s)\n", key, key);
+ printf(" type: FLOAT (max: %d)\n", maxBound);
+ printf(" usage: %s\n", helptext);
+ printf("default: %d\n", def);
+ printf("\n");
+ return 1;
+ default:
+ return 0;
+ }
+ } else {
+ char buf[50];
+ int userVar = iniparser_getint(dict,key,def);
+
+ switch(intType) {
+ case INTTYPE_INT:
+ *(int*)var = userVar;
+ break;
+ case INTTYPE_I8:
+ *(int8_t*)var = (int8_t)userVar;
+ break;
+ case INTTYPE_U8:
+ *(uint8_t*)var = (uint8_t)userVar;
+ break;
+ case INTTYPE_I16:
+ *(int16_t*)var = (int16_t)userVar;
+ break;
+ case INTTYPE_U16:
+ *(uint16_t*)var = (uint16_t)userVar;
+ break;
+ case INTTYPE_I32:
+ *(int32_t*)var = (int32_t)userVar;
+ break;
+ case INTTYPE_U32:
+ *(uint32_t*)var = (uint32_t)userVar;
+ break;
+ default:
+ break;
+ }
+
+ memset(buf, 0, 50);
+ snprintf(buf, 50, "%d", userVar);
+ dictionary_set(target,key,buf);
+ if(strcmp(helptext, "") && opCode & CFGOP_PRINT_DEFAULT) {
+ printComment(helptext);
+ printf("%s = %s\n", key,buf);
+ }
+ ret = checkRangeInt(dict, key, rangeFlags, minBound, maxBound);
+
+ if(!ret && !(opCode & CFGOP_PARSE_QUIET)) {
+ switch(rangeFlags) {
+ case RANGECHECK_RANGE:
+ ERROR("Configuration error: option \"%s=%s\" not within allowed range: %d..%d\n", key, buf, minBound, maxBound);
+ break;
+ case RANGECHECK_MIN:
+ ERROR("Configuration error: option \"%s=%s\" below allowed minimum: %d\n", key, buf, minBound);
+ break;
+ case RANGECHECK_MAX:
+ ERROR("Configuration error: option \"%s=%s\" above allowed maximum: %d\n", key, buf, maxBound);
+ break;
+ default:
+ return ret;
+ }
+ }
+
+ return ret;
+ }
+}
+
+static int
+checkRangeDouble(dictionary *dict, const char *key, int rangeFlags, double minBound, double maxBound)
+{
+ double tmpdouble = iniparser_getdouble(dict,key,minBound);
+
+ int ret = 1;
+
+ switch(rangeFlags) {
+ case RANGECHECK_NONE:
+ return ret;
+ case RANGECHECK_RANGE:
+ ret = !(tmpdouble < minBound || tmpdouble > maxBound);
+ break;
+ case RANGECHECK_MIN:
+ ret = !(tmpdouble < minBound);
+ break;
+ case RANGECHECK_MAX:
+ ret = !(tmpdouble > maxBound);
+ break;
+ default:
+ return 0;
+ }
+
+ return ret;
+}
+
+static int
+configMapDouble(int opCode, void *opArg, dictionary *dict, dictionary *target, const char *key, int restartFlags,
+ double *var, double def, const char *helptext, int rangeFlags,
+ double minBound, double maxBound)
+{
+
+ int ret = 0;
+ if(opCode & CFGOP_RESTART_FLAGS) {
+ if(CONFIG_ISSET(key)) {
+ *(int*)opArg |= restartFlags;
+ if(opCode & CFGOP_RESTART_FLAGS && !(opCode & CFGOP_PARSE_QUIET)) {
+ warnRestart(key, restartFlags);
+ }
+ }
+ return 1;
+ } else if(opCode & CFGOP_HELP_FULL || opCode & CFGOP_HELP_SINGLE) {
+
+ char *helpKey = (char*)opArg;
+
+ if((opCode & CFGOP_HELP_SINGLE)){
+ if (strcmp(key, helpKey)) {
+ return 1;
+ } else {
+ /* this way we tell the caller that we have already found the setting we were looking for */
+ helpKey[0] = '\0';
+ }
+ }
+
+ switch(rangeFlags) {
+ case RANGECHECK_NONE:
+ printf("setting: %s (--%s)\n", key, key);
+ printf(" type: FLOAT\n");\
+ printf(" usage: %s\n", helptext);
+ printf("default: %f\n", def);
+ printf("\n");
+ return 1;
+ case RANGECHECK_RANGE:
+ printf("setting: %s (--%s)\n", key, key);
+ printf(" type: FLOAT ");
+ if( minBound != maxBound )
+ printf("(min: %f, max: %f)\n", minBound, maxBound);
+ else
+ printf("(only value of %f allowed)", minBound);
+ printf("\n");
+ printf(" usage: %s\n", helptext);
+ printf("default: %f\n", def);
+ printf("\n");
+ return 1;
+ case RANGECHECK_MIN:
+ printf("setting: %s (--%s)\n", key, key);
+ printf(" type: FLOAT(min: %f)\n", minBound);
+ printf(" usage: %s\n", helptext);
+ printf("default: %f\n", def);
+ printf("\n");
+ return 1;
+ case RANGECHECK_MAX:
+ printf("setting: %s (--%s)\n", key, key);
+ printf(" type: FLOAT(max: %f)\n", maxBound);
+ printf(" usage: %s\n", helptext);
+ printf("default: %f\n", def);
+ printf("\n");
+ return 1;
+ default:
+ return 0;
+ }
+ } else {
+ char buf[50];
+ *var = iniparser_getdouble(dict,key,def);
+ memset(buf, 0, 50);
+ snprintf(buf, 50, "%f", *var);
+ dictionary_set(target,key,buf);
+ if(strcmp(helptext, "") && opCode & CFGOP_PRINT_DEFAULT) {
+ printComment(helptext);
+ printf("%s = %s\n", key,buf);
+ }
+
+ ret = checkRangeDouble(dict, key, rangeFlags, minBound, maxBound);
+
+ if(!ret && !(opCode & CFGOP_PARSE_QUIET)) {
+ switch(rangeFlags) {
+ case RANGECHECK_RANGE:
+ ERROR("Configuration error: option \"%s=%f\" not within allowed range: %f..%f\n", key, *var, minBound, maxBound);
+ break;
+ case RANGECHECK_MIN:
+ ERROR("Configuration error: option \"%s=%f\" below allowed minimum: %f\n", key, *var, minBound);
+ break;
+ case RANGECHECK_MAX:
+ ERROR("Configuration error: option \"%s=%f\" above allowed maximum: %f\n", key, *var, maxBound);
+ break;
+ default:
+ return ret;
+ }
+ }
+
+ return ret;
+ }
+}
+
+static int
+configMapSelectValue(int opCode, void *opArg, dictionary *dict, dictionary *target,
+const char* key, int restartFlags, uint8_t *var, int def, const char *helptext, ...)
+{
+
+ int ret;
+ int i;
+ /* fixed maximum to avoid while(1) */
+ int maxCount = 100;
+ char *name = NULL;
+ char *keyValue;
+ char *defValue = NULL;
+ int value;
+ va_list ap;
+ int selectedValue = -1;
+
+ char sbuf[SCREEN_BUFSZ];
+ int len = 0;
+
+ memset(sbuf, 0, sizeof(sbuf));
+
+ keyValue = iniparser_getstring(dict, key, "");
+
+ va_start(ap, helptext);
+
+ for(i=0; i < maxCount; i++) {
+
+ name = (char*)va_arg(ap, char*);
+ if(name == NULL) break;
+
+ value = (int)va_arg(ap, int);
+
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, "%s ", name);
+
+ if(value == def) {
+ defValue = strdup(name);
+ }
+
+ if(!strcmp(name, keyValue)) {
+ selectedValue = value;
+ }
+
+ }
+
+ if(!strcmp(keyValue, "")) {
+ selectedValue = def;
+ keyValue = iniparser_getstring(dict, key, defValue);
+ }
+
+ va_end(ap);
+ if(opCode & CFGOP_RESTART_FLAGS) {
+ if(CONFIG_ISSET(key)) {
+ *(int*)opArg |= restartFlags;
+ if(opCode & CFGOP_RESTART_FLAGS && !(opCode & CFGOP_PARSE_QUIET)) {
+ warnRestart(key, restartFlags);
+ }
+ }
+ return 1;
+ } else if(opCode & CFGOP_HELP_FULL || opCode & CFGOP_HELP_SINGLE) {
+
+ char *helpKey = (char*)opArg;
+
+ if((opCode & CFGOP_HELP_SINGLE)){
+ if (strcmp(key, helpKey)) {
+ return 1;
+ } else {
+ /* this way we tell the caller that we have already found the setting we were looking for */
+ helpKey[0] = '\0';
+ }
+ }
+
+ printf("setting: %s (--%s)\n", key, key);
+ printf(" type: SELECT\n");
+ printf(" usage: %s\n", helptext);
+ printf("options: %s\n", sbuf);
+ printf("default: %s\n", defValue);
+ printf("\n");
+ ret = 1;
+ goto result;
+ } else {
+ dictionary_set(target, key, keyValue);
+ if(selectedValue < 0) {
+ if(!(opCode & CFGOP_PARSE_QUIET)) {
+ ERROR("Configuration error: option \"%s\" has unknown value: %s - allowed values: %s\n", key, keyValue, sbuf);
+ }
+ ret = 0;
+ } else {
+ *var = (uint8_t)selectedValue;
+ if(strcmp(helptext, "") && opCode & CFGOP_PRINT_DEFAULT) {
+ printComment(helptext);
+ printf("; Options: %s\n", sbuf);
+ printf("%s = %s\n", key,keyValue);
+ }
+ ret = 1;
+ }
+ }
+result:
+
+ if(defValue != NULL) {
+ free(defValue);
+ }
+
+ return ret;
+
+}
+
+void setConfig(dictionary *dict, const char* key, const char *value)
+{
+
+ if(dict == NULL) {
+ return;
+ }
+
+ dictionary_set(dict, key, value);
+
+}
+
+static void
+parseUserVariables(dictionary *dict, dictionary *target)
+{
+
+ int i = 0;
+ char *search, *replace, *key;
+ char varname[100];
+
+ memset(varname, 0, sizeof(varname));
+
+ if(dict == NULL) return;
+
+
+ for(i = 0; i < dict->n; i++) {
+ key = dict->key[i];
+ /* key starts with "variables" */
+ if(strstr(key,"variables:") == key) {
+ /* this cannot fail if we are here */
+ search = strstr(key,":");
+ search++;
+ replace = dictionary_get(dict, key, "");
+ snprintf(varname, sizeof(varname), "@%s@", search);
+ DBGV("replacing %s with %s in config\n", varname, replace);
+ dictionary_replace(dict, varname, replace);
+ dictionary_set(target, key, replace);
+ }
+
+ }
+
+}
+
+/**
+ * Iterate through dictionary dict (template) and check for keys in source
+ * that are not present in the template - display only.
+ */
+static void
+findUnknownSettings(int opCode, dictionary* source, dictionary* dict)
+{
+
+ int i = 0;
+
+ if( source == NULL || dict == NULL) return;
+
+ for(i = 0; i < source->n; i++) {
+ /* skip if the key is null or is a section */
+ if(source->key[i] == NULL || strstr(source->key[i],":") == NULL)
+ continue;
+ if ( !iniparser_find_entry(dict, source->key[i]) && !(opCode & CFGOP_PARSE_QUIET) )
+ WARNING("Unknown configuration entry: %s - setting will be ignored\n", source->key[i]);
+ }
+}
+
+/*
+ * IT HAPPENS HERE
+ *
+ * Map all options from @dict dictionary to corresponding @rtopts fields,
+ * using existing @rtopts fields as defaults. Return a dictionary free
+ * of unknown options, with explicitly set defaults.
+ * NOTE: when adding options, also map them in checkSubsystemRestart to
+ * ensure correct config reload behaviour.
+ */
+dictionary*
+parseConfig ( int opCode, void *opArg, dictionary* dict, RunTimeOpts *rtOpts )
+{
+
+/*-
+ * This function assumes that rtOpts has got all the defaults loaded,
+ * hence the default values for all options are taken from rtOpts.
+ * Therefore loadDefaultSettings should normally be used before parseConfig
+ */
+
+
+
+ /*-
+ * WARNING: for ease of use, a limited number of keys is set
+ * via getopt in loadCommanLineOptions(). When renaming settings, make sure
+ * you check it for inconsistencies. If we decide to
+ * drop all short options in favour of section:key only,
+ * this warning can be removed.
+ */
+ /**
+ * Prepare the target dictionary. Every mapped option will be set in
+ * the target dictionary, including the defaults which are typically
+ * not present in the original dictionary. As a result, the target
+ * will contain all the mapped options with explicitly set defaults
+ * if they were not present in the original dictionary. In the end
+ * of this function we return this pointer. The resulting dictionary
+ * is complete and free of any unknown options. In the end, warning
+ * is issued for unknown options. On any errors, NULL is returned
+ */
+
+ dictionary* target = dictionary_new(0);
+
+ Boolean parseResult = TRUE;
+
+ PtpEnginePreset ptpPreset;
+
+ if(!(opCode & CFGOP_PARSE_QUIET)) {
+ INFO("Checking configuration\n");
+ }
+
+ /*
+ * apply the configuration template(s) as statically defined in configdefaults.c
+ * and in template files. The list of templates and files is comma, space or tab separated.
+ * names. Any templates are applied before anything else: so any settings after this can override
+ */
+ if (CONFIG_ISPRESENT("global:config_templates")) {
+ applyConfigTemplates(
+ dict,
+ dictionary_get(dict, "global:config_templates", ""),
+ dictionary_get(dict, "global:template_files", "")
+ );
+
+ /* also set the template names in the target dictionary */
+ dictionary_set(target, "global:config_templates", dictionary_get(dict, "global:config_templates", ""));
+ dictionary_set(target, "global:template_files", dictionary_get(dict, "global:template_files", ""));
+ }
+
+ /* set automatic variables */
+
+ /* @pid@ */
+ tmpsnprintf(tmpStr, 20, "%d", (uint32_t)getpid());
+ dictionary_set(dict, "variables:pid", tmpStr);
+
+ /* @hostname@ */
+ char hostName[MAXHOSTNAMELEN+1];
+ memset(hostName, 0, MAXHOSTNAMELEN + 1);
+ gethostname(hostName, MAXHOSTNAMELEN);
+ dictionary_set(dict, "variables:hostname", hostName);
+
+ parseUserVariables(dict, target);
+
+ dictionary_unset(dict, "variables:pid");
+ dictionary_unset(target, "variables:pid");
+
+ dictionary_unset(dict, "variables:hostname");
+ dictionary_unset(target, "variables:hostname");
+
+
+/* ============= BEGIN CONFIG MAPPINGS, TRIGGERS AND DEPENDENCIES =========== */
+
+/* ===== ptpengine section ===== */
+
+ CONFIG_KEY_REQUIRED("ptpengine:interface");
+
+ parseResult &= configMapString(opCode, opArg, dict, target, "ptpengine:interface",
+ PTPD_RESTART_NETWORK, rtOpts->primaryIfaceName, sizeof(rtOpts->primaryIfaceName), rtOpts->primaryIfaceName,
+ "Network interface to use - eth0, igb0 etc. (required).");
+
+ parseResult &= configMapString(opCode, opArg, dict, target, "ptpengine:backup_interface",
+ PTPD_RESTART_NETWORK, rtOpts->backupIfaceName, sizeof(rtOpts->backupIfaceName), rtOpts->backupIfaceName,
+ "Backup network interface to use - eth0, igb0 etc. When no GM available, \n"
+ " slave will keep alternating between primary and secondary until a GM is found.\n");
+
+ CONFIG_KEY_TRIGGER("ptpengine:backup_interface", rtOpts->backupIfaceEnabled,TRUE,FALSE);
+
+ /* Preset option names have to be mapped to defined presets - no free strings here */
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "ptpengine:preset",
+ PTPD_RESTART_PROTOCOL, &rtOpts->selectedPreset, rtOpts->selectedPreset,
+ "PTP engine preset:\n"
+ " none = Defaults, no clock class restrictions\n"
+ " masteronly = Master, passive when not best master (clock class 0..127)\n"
+ " masterslave = Full IEEE 1588 implementation:\n"
+ " Master, slave when not best master\n"
+ " (clock class 128..254)\n"
+ " slaveonly = Slave only (clock class 255 only)\n",
+#ifndef PTPD_SLAVE_ONLY
+ (getPtpPreset(PTP_PRESET_NONE, rtOpts)).presetName, PTP_PRESET_NONE,
+ (getPtpPreset(PTP_PRESET_MASTERONLY, rtOpts)).presetName, PTP_PRESET_MASTERONLY,
+ (getPtpPreset(PTP_PRESET_MASTERSLAVE, rtOpts)).presetName, PTP_PRESET_MASTERSLAVE,
+#endif /* PTPD_SLAVE_ONLY */
+ (getPtpPreset(PTP_PRESET_SLAVEONLY, rtOpts)).presetName, PTP_PRESET_SLAVEONLY, NULL
+ );
+
+ CONFIG_KEY_CONDITIONAL_CONFLICT("ptpengine:preset",
+ rtOpts->selectedPreset == PTP_PRESET_MASTERSLAVE,
+ "masterslave",
+ "ptpengine:unicast_negotiation");
+
+ ptpPreset = getPtpPreset(rtOpts->selectedPreset, rtOpts);
+
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "ptpengine:transport",
+ PTPD_RESTART_NETWORK, &rtOpts->transport, rtOpts->transport,
+ "Transport type for PTP packets. Ethernet transport requires libpcap support.",
+ "ipv4", UDP_IPV4,
+#if 0
+ "ipv6", UDP_IPV6,
+#endif
+ "ethernet", IEEE_802_3, NULL
+ );
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:dot1as", PTPD_UPDATE_DATASETS, &rtOpts->dot1AS, rtOpts->dot1AS,
+ "Enable TransportSpecific field compatibility with 802.1AS / AVB (requires Ethernet transport)");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:disabled", PTPD_RESTART_PROTOCOL, &rtOpts->portDisabled, rtOpts->portDisabled,
+ "Disable PTP port. Causes the PTP state machine to stay in PTP_DISABLED state indefinitely,\n"
+ " until it is re-enabled via configuration change or ENABLE_PORT management message.");
+
+
+ CONFIG_KEY_CONDITIONAL_WARNING_ISSET((rtOpts->transport != IEEE_802_3) && rtOpts->dot1AS,
+ "ptpengine:dot1as",
+ "802.1AS compatibility can only be used with the Ethernet transport\n");
+
+ CONFIG_KEY_CONDITIONAL_TRIGGER(rtOpts->transport != IEEE_802_3, rtOpts->dot1AS,FALSE, rtOpts->dot1AS);
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "ptpengine:ip_mode",
+ PTPD_RESTART_NETWORK, &rtOpts->ipMode, rtOpts->ipMode,
+ "IP transmission mode (requires IP transport) - hybrid mode uses\n"
+ " multicast for sync and announce, and unicast for delay request and\n"
+ " response; unicast mode uses unicast for all transmission.\n"
+ " When unicast mode is selected, destination IP(s) may need to be configured\n"
+ " (ptpengine:unicast_destinations).",
+ "multicast", IPMODE_MULTICAST,
+ "unicast", IPMODE_UNICAST,
+ "hybrid", IPMODE_HYBRID, NULL
+ );
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:unicast_negotiation",
+ PTPD_RESTART_PROTOCOL, &rtOpts->unicastNegotiation, rtOpts->unicastNegotiation,
+ "Enable unicast negotiation support using signaling messages\n");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:unicast_any_master",
+ PTPD_RESTART_NONE, &rtOpts->unicastAcceptAny, rtOpts->unicastAcceptAny,
+ "When using unicast negotiation (slave), accept PTP messages from any master.\n"
+ " By default, only messages from acceptable masters (ptpengine:unicast_destinations)\n"
+ " are accepted, and only if transmission was granted by the master\n");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:unicast_port_mask",
+ PTPD_RESTART_PROTOCOL, INTTYPE_U16, &rtOpts->unicastPortMask, rtOpts->unicastPortMask,
+ "PTP port number wildcard mask applied onto port identities when running\n"
+ " unicast negotiation: allows multiple port identities to be accepted as one.\n"
+ " This option can be used as a workaround where a node sends signaling messages and\n"
+ " timing messages with different port identities", RANGECHECK_RANGE, 0,65535);
+
+ CONFIG_KEY_CONDITIONAL_WARNING_ISSET((rtOpts->transport == IEEE_802_3) && rtOpts->unicastNegotiation,
+ "ptpengine:unicast_negotiation",
+ "Unicast negotiation cannot be used with Ethernet transport\n");
+
+ CONFIG_KEY_CONDITIONAL_WARNING_ISSET((rtOpts->ipMode != IPMODE_UNICAST) && rtOpts->unicastNegotiation,
+ "ptpengine:unicast_negotiation",
+ "Unicast negotiation can only be used with unicast transmission\n");
+
+ /* disable unicast negotiation unless running unicast */
+ CONFIG_KEY_CONDITIONAL_TRIGGER(rtOpts->transport == IEEE_802_3, rtOpts->unicastNegotiation,FALSE, rtOpts->unicastNegotiation);
+ CONFIG_KEY_CONDITIONAL_TRIGGER(rtOpts->ipMode != IPMODE_UNICAST, rtOpts->unicastNegotiation,FALSE, rtOpts->unicastNegotiation);
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:disable_bmca",
+ PTPD_RESTART_PROTOCOL, &rtOpts->disableBMCA, rtOpts->disableBMCA,
+ "Disable Best Master Clock Algorithm for unicast masters:\n"
+ " Only effective for masteronly preset - all Announce messages\n"
+ " will be ignored and clock will transition directly into MASTER state.\n");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:unicast_negotiation_listening",
+ PTPD_RESTART_NONE, &rtOpts->unicastNegotiationListening, rtOpts->unicastNegotiationListening,
+ "When unicast negotiation enabled on a master clock, \n"
+ " reply to transmission requests also in LISTENING state.");
+
+#if defined(PTPD_PCAP) && defined(__sun) && !defined(PTPD_EXPERIMENTAL)
+ if(CONFIG_ISTRUE("ptpengine:use_libpcap"))
+ INFO("Libpcap support is currently marked broken/experimental on Solaris platforms.\n"
+ "To test it, please build with --enable-experimental-options\n");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:use_libpcap",
+ PTPD_RESTART_NETWORK, &rtOpts->pcap,FALSE,
+ "Use libpcap for sending and receiving traffic (automatically enabled\n"
+ " in Ethernet mode).");
+
+ /* cannot set ethernet transport without libpcap */
+ CONFIG_KEY_VALUE_FORBIDDEN("ptpengine:transport",
+ rtOpts->transport == IEEE_802_3,
+ "ethernet",
+ "Libpcap support is currently marked broken/experimental on Solaris platforms.\n"
+ "To test it and use the Ethernet transport, please build with --enable-experimental-options\n");
+#elif defined(PTPD_PCAP)
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:use_libpcap",
+ PTPD_RESTART_NETWORK, &rtOpts->pcap, rtOpts->pcap,
+ "Use libpcap for sending and receiving traffic (automatically enabled\n"
+ " in Ethernet mode).");
+
+ /* in ethernet mode, activate pcap and overwrite previous setting */
+ CONFIG_KEY_CONDITIONAL_TRIGGER(rtOpts->transport==IEEE_802_3, rtOpts->pcap,TRUE, rtOpts->pcap);
+#else
+ if(CONFIG_ISTRUE("ptpengine:use_libpcap"))
+ INFO("Libpcap support disabled or not available. Please install libpcap,\n"
+ "build without --disable-pcap, or try building with ---with-pcap-config\n"
+ " to use ptpengine:use_libpcap.\n");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:use_libpcap",
+ PTPD_RESTART_NETWORK, &rtOpts->pcap,FALSE,
+ "Use libpcap for sending and receiving traffic (automatically enabled\n"
+ " in Ethernet mode).");
+
+ /* cannot set ethernet transport without libpcap */
+ CONFIG_KEY_VALUE_FORBIDDEN("ptpengine:transport",
+ rtOpts->transport == IEEE_802_3,
+ "ethernet",
+ "Libpcap support disabled or not available. Please install libpcap,\n"
+ "build without --disable-pcap, or try building with ---with-pcap-config\n"
+ "to use Ethernet transport. "PTPD_PROGNAME" was built with no libpcap support.\n");
+
+#endif /* PTPD_PCAP */
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:disable_udp_checksums",
+ PTPD_RESTART_NETWORK, &rtOpts->disableUdpChecksums, rtOpts->disableUdpChecksums,
+ "Disable UDP checksum validation on UDP sockets (Linux only).\n"
+ " Workaround for situations where a node (like Transparent Clock).\n"
+ " does not rewrite checksums\n");
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "ptpengine:delay_mechanism",
+ PTPD_RESTART_PROTOCOL, &rtOpts->delayMechanism, rtOpts->delayMechanism,
+ "Delay detection mode used - use DELAY_DISABLED for syntonisation only\n"
+ " (no full synchronisation).",
+ delayMechToString(E2E), E2E,
+ delayMechToString(P2P), P2P,
+ delayMechToString(DELAY_DISABLED), DELAY_DISABLED, NULL
+ );
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:domain",
+ PTPD_RESTART_PROTOCOL, INTTYPE_U8, &rtOpts->domainNumber, rtOpts->domainNumber,
+ "PTP domain number.", RANGECHECK_RANGE, 0,127);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:port_number", PTPD_UPDATE_DATASETS, INTTYPE_U16, &rtOpts->portNumber, rtOpts->portNumber,
+ "PTP port number (part of PTP Port Identity - not UDP port).\n"
+ " For ordinary clocks (single port), the default should be used, \n"
+ " but when running multiple instances to simulate a boundary clock, \n"
+ " The port number can be changed.",RANGECHECK_RANGE,1,65534);
+
+ parseResult &= configMapString(opCode, opArg, dict, target, "ptpengine:port_description",
+ PTPD_UPDATE_DATASETS, rtOpts->portDescription, sizeof(rtOpts->portDescription), rtOpts->portDescription,
+ "Port description (returned in the userDescription field of PORT_DESCRIPTION management message and USER_DESCRIPTION"
+ " management message) - maximum 64 characters");
+
+ parseResult &= configMapString(opCode, opArg, dict, target, "variables:product_description",
+ PTPD_UPDATE_DATASETS, rtOpts->productDescription, sizeof(rtOpts->productDescription), rtOpts->productDescription,"");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:any_domain",
+ PTPD_RESTART_PROTOCOL, &rtOpts->anyDomain, rtOpts->anyDomain,
+ "Usability extension: if enabled, a slave-only clock will accept\n"
+ " masters from any domain, while preferring the configured domain,\n"
+ " and preferring lower domain number.\n"
+ " NOTE: this behaviour is not part of the standard.");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:slave_only",
+ PTPD_RESTART_NONE, &rtOpts->slaveOnly, ptpPreset.slaveOnly,
+ "Slave only mode (sets clock class to 255, overriding value from preset).");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:inbound_latency",
+ PTPD_RESTART_NONE, INTTYPE_I32, &rtOpts->inboundLatency.nanoseconds, rtOpts->inboundLatency.nanoseconds,
+ "Specify latency correction (nanoseconds) for incoming packets.", RANGECHECK_NONE, 0,0);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:outbound_latency",
+ PTPD_RESTART_NONE, INTTYPE_I32, &rtOpts->outboundLatency.nanoseconds, rtOpts->outboundLatency.nanoseconds,
+ "Specify latency correction (nanoseconds) for outgoing packets.", RANGECHECK_NONE,0,0);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:offset_shift",
+ PTPD_RESTART_NONE, INTTYPE_I32, &rtOpts->ofmShift.nanoseconds, rtOpts->ofmShift.nanoseconds,
+ "Apply an arbitrary shift (nanoseconds) to offset from master when\n"
+ " in slave state. Value can be positive or negative - useful for\n"
+ " correcting for antenna latencies, delay assymetry\n"
+ " and IP stack latencies. This will not be visible in the offset \n"
+ " from master value - only in the resulting clock correction.", RANGECHECK_NONE, 0,0);
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:always_respect_utc_offset",
+ PTPD_RESTART_NONE, &rtOpts->alwaysRespectUtcOffset, rtOpts->alwaysRespectUtcOffset,
+ "Compatibility option: In slave state, always respect UTC offset\n"
+ " announced by best master, even if the the\n"
+ " currrentUtcOffsetValid flag is announced FALSE.\n"
+ " NOTE: this behaviour is not part of the standard.");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:prefer_utc_offset_valid",
+ PTPD_RESTART_NONE, &rtOpts->preferUtcValid, rtOpts->preferUtcValid,
+ "Compatibility extension to BMC algorithm: when enabled,\n"
+ " BMC for both master and save clocks will prefer masters\n"
+ " nannouncing currrentUtcOffsetValid as TRUE.\n"
+ " NOTE: this behaviour is not part of the standard.");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:require_utc_offset_valid",
+ PTPD_RESTART_NONE, &rtOpts->requireUtcValid, rtOpts->requireUtcValid,
+ "Compatibility option: when enabled, ptpd2 will ignore\n"
+ " Announce messages from masters announcing currentUtcOffsetValid\n"
+ " as FALSE.\n"
+ " NOTE: this behaviour is not part of the standard.");
+
+ /* from 30 seconds to 7 days */
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:unicast_grant_duration",
+ PTPD_RESTART_PROTOCOL, INTTYPE_U32, &rtOpts->unicastGrantDuration, rtOpts->unicastGrantDuration,
+ "Time (seconds) unicast messages are requested for by slaves\n"
+ " when using unicast negotiation, and maximum time unicast message\n"
+ " transmission is granted to slaves by masters\n", RANGECHECK_RANGE, 30, 604800);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:log_announce_interval", PTPD_UPDATE_DATASETS, INTTYPE_I8, &rtOpts->logAnnounceInterval, rtOpts->logAnnounceInterval,
+ "PTP announce message interval in master state. When using unicast negotiation, for\n"
+ " slaves this is the minimum interval requested, and for masters\n"
+ " this is the only interval granted.\n"
+#ifdef PTPD_EXPERIMENTAL
+ " "LOG2_HELP,RANGECHECK_RANGE,-30,30);
+#else
+ " "LOG2_HELP,RANGECHECK_RANGE,-4,7);
+#endif
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:log_announce_interval_max", PTPD_UPDATE_DATASETS, INTTYPE_I8, &rtOpts->logMaxAnnounceInterval, rtOpts->logMaxAnnounceInterval,
+ "Maximum Announce message interval requested by slaves "
+ "when using unicast negotiation,\n"
+#ifdef PTPD_EXPERIMENTAL
+ " "LOG2_HELP,RANGECHECK_RANGE,-30,30);
+#else
+ " "LOG2_HELP,RANGECHECK_RANGE,-1,7);
+#endif
+
+ CONFIG_CONDITIONAL_ASSERTION(rtOpts->logAnnounceInterval >= rtOpts->logMaxAnnounceInterval,
+ "ptpengine:log_announce_interval value must be lower than ptpengine:log_announce_interval_max\n");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:announce_receipt_timeout", PTPD_UPDATE_DATASETS, INTTYPE_I8, &rtOpts->announceReceiptTimeout, rtOpts->announceReceiptTimeout,
+ "PTP announce receipt timeout announced in master state.",RANGECHECK_RANGE,2,255);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:announce_receipt_grace_period",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->announceTimeoutGracePeriod, rtOpts->announceTimeoutGracePeriod,
+ "PTP announce receipt timeout grace period in slave state:\n"
+ " when announce receipt timeout occurs, disqualify current best GM,\n"
+ " then wait n times announce receipt timeout before resetting.\n"
+ " Allows for a seamless GM failover when standby GMs are slow\n"
+ " to react. When set to 0, this option is not used.", RANGECHECK_RANGE,
+ 0,20);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:log_sync_interval", PTPD_UPDATE_DATASETS, INTTYPE_I8, &rtOpts->logSyncInterval, rtOpts->logSyncInterval,
+ "PTP sync message interval in master state. When using unicast negotiation, for\n"
+ " slaves this is the minimum interval requested, and for masters\n"
+ " this is the only interval granted.\n"
+#ifdef PTPD_EXPERIMENTAL
+ " "LOG2_HELP,RANGECHECK_RANGE,-30,30);
+#else
+ " "LOG2_HELP,RANGECHECK_RANGE,-7,7);
+#endif
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:log_sync_interval_max", PTPD_UPDATE_DATASETS, INTTYPE_I8, &rtOpts->logMaxSyncInterval, rtOpts->logMaxSyncInterval,
+ "Maximum Sync message interval requested by slaves "
+ "when using unicast negotiation,\n"
+#ifdef PTPD_EXPERIMENTAL
+ " "LOG2_HELP,RANGECHECK_RANGE,-30,30);
+#else
+ " "LOG2_HELP,RANGECHECK_RANGE,-1,7);
+#endif
+
+ CONFIG_CONDITIONAL_ASSERTION(rtOpts->logSyncInterval >= rtOpts->logMaxSyncInterval,
+ "ptpengine:log_sync_interval value must be lower than ptpengine:log_sync_interval_max\n");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:log_delayreq_override", PTPD_UPDATE_DATASETS, &rtOpts->ignore_delayreq_interval_master,
+ rtOpts->ignore_delayreq_interval_master,
+ "Override the Delay Request interval announced by best master.");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:log_delayreq_auto", PTPD_UPDATE_DATASETS, &rtOpts->autoDelayReqInterval,
+ rtOpts->autoDelayReqInterval,
+ "Automatically override the Delay Request interval\n"
+ " if the announced value is 127 (0X7F), such as in\n"
+ " unicast messages (unless using unicast negotiation)");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:log_delayreq_interval_initial",
+ PTPD_RESTART_NONE, INTTYPE_I8, &rtOpts->initial_delayreq, rtOpts->initial_delayreq,
+ "Delay request interval used before receiving first delay response\n"
+#ifdef PTPD_EXPERIMENTAL
+ " "LOG2_HELP,RANGECHECK_RANGE,-30,30);
+#else
+ " "LOG2_HELP,RANGECHECK_RANGE,-7,7);
+#endif
+
+ /* take the delayreq_interval from config, otherwise use the initial setting as default */
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:log_delayreq_interval", PTPD_UPDATE_DATASETS, INTTYPE_I8, &rtOpts->logMinDelayReqInterval, rtOpts->initial_delayreq,
+ "Minimum delay request interval announced when in master state,\n"
+ " in slave state overrides the master interval,\n"
+ " required in hybrid mode. When using unicast negotiation, for\n"
+ " slaves this is the minimum interval requested, and for masters\n"
+ " this is the minimum interval granted.\n"
+#ifdef PTPD_EXPERIMENTAL
+ " "LOG2_HELP,RANGECHECK_RANGE,-30,30);
+#else
+ " "LOG2_HELP,RANGECHECK_RANGE,-7,7);
+#endif
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:log_delayreq_interval_max", PTPD_UPDATE_DATASETS, INTTYPE_I8, &rtOpts->logMaxDelayReqInterval, rtOpts->logMaxDelayReqInterval,
+ "Maximum Delay Response interval requested by slaves "
+ "when using unicast negotiation,\n"
+#ifdef PTPD_EXPERIMENTAL
+ " "LOG2_HELP,RANGECHECK_RANGE,-30,30);
+#else
+ " "LOG2_HELP,RANGECHECK_RANGE,-1,7);
+#endif
+
+ CONFIG_CONDITIONAL_ASSERTION(rtOpts->logMinDelayReqInterval >= rtOpts->logMaxDelayReqInterval,
+ "ptpengine:log_delayreq_interval value must be lower than ptpengine:log_delayreq_interval_max\n");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:log_peer_delayreq_interval",
+ PTPD_RESTART_NONE, INTTYPE_I8, &rtOpts->logMinPdelayReqInterval, rtOpts->logMinPdelayReqInterval,
+ "Minimum peer delay request message interval in peer to peer delay mode.\n"
+ " When using unicast negotiation, this is the minimum interval requested, \n"
+ " and the only interval granted.\n"
+#ifdef PTPD_EXPERIMENTAL
+ " "LOG2_HELP,RANGECHECK_RANGE,-30,30);
+#else
+ " "LOG2_HELP,RANGECHECK_RANGE,-7,7);
+#endif
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:log_peer_delayreq_interval_max",
+ PTPD_RESTART_NONE, INTTYPE_I8, &rtOpts->logMaxPdelayReqInterval, rtOpts->logMaxPdelayReqInterval,
+ "Maximum Peer Delay Response interval requested by slaves "
+ "when using unicast negotiation,\n"
+#ifdef PTPD_EXPERIMENTAL
+ " "LOG2_HELP,RANGECHECK_RANGE,-30,30);
+#else
+ " "LOG2_HELP,RANGECHECK_RANGE,-1,7);
+#endif
+
+ CONFIG_CONDITIONAL_ASSERTION(rtOpts->logMinPdelayReqInterval >= rtOpts->logMaxPdelayReqInterval,
+ "ptpengine:log_peer_delayreq_interval value must be lower than ptpengine:log_peer_delayreq_interval_max\n");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:foreignrecord_capacity",
+ PTPD_RESTART_DAEMON, INTTYPE_I16, &rtOpts->max_foreign_records, rtOpts->max_foreign_records,
+ "Foreign master record size (Maximum number of foreign masters).",RANGECHECK_RANGE,5,10);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:ptp_allan_variance", PTPD_UPDATE_DATASETS, INTTYPE_U16, &rtOpts->clockQuality.offsetScaledLogVariance, rtOpts->clockQuality.offsetScaledLogVariance,
+ "Specify Allan variance announced in master state.",RANGECHECK_RANGE,0,65535);
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "ptpengine:ptp_clock_accuracy", PTPD_UPDATE_DATASETS, &rtOpts->clockQuality.clockAccuracy, rtOpts->clockQuality.clockAccuracy,
+ "Clock accuracy range announced in master state.",
+ accToString(ACC_25NS), ACC_25NS,
+ accToString(ACC_100NS), ACC_100NS,
+ accToString(ACC_250NS), ACC_250NS,
+ accToString(ACC_1US), ACC_1US,
+ accToString(ACC_2_5US), ACC_2_5US,
+ accToString(ACC_10US), ACC_10US,
+ accToString(ACC_25US), ACC_25US,
+ accToString(ACC_100US), ACC_100US,
+ accToString(ACC_250US), ACC_250US,
+ accToString(ACC_1MS), ACC_1MS,
+ accToString(ACC_2_5MS), ACC_2_5MS,
+ accToString(ACC_10MS), ACC_10MS,
+ accToString(ACC_25MS), ACC_25MS,
+ accToString(ACC_100MS), ACC_100MS,
+ accToString(ACC_250MS), ACC_250MS,
+ accToString(ACC_1S), ACC_1S,
+ accToString(ACC_10S), ACC_10S,
+ accToString(ACC_10SPLUS), ACC_10SPLUS,
+ accToString(ACC_UNKNOWN), ACC_UNKNOWN, NULL
+ );
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:utc_offset", PTPD_UPDATE_DATASETS, INTTYPE_I16, &rtOpts->timeProperties.currentUtcOffset, rtOpts->timeProperties.currentUtcOffset,
+ "Underlying time source UTC offset announced in master state.", RANGECHECK_NONE,0,0);
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:utc_offset_valid", PTPD_UPDATE_DATASETS, &rtOpts->timeProperties.currentUtcOffsetValid,
+ rtOpts->timeProperties.currentUtcOffsetValid,
+ "Underlying time source UTC offset validity announced in master state.");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:time_traceable", PTPD_UPDATE_DATASETS, &rtOpts->timeProperties.timeTraceable,
+ rtOpts->timeProperties.timeTraceable,
+ "Underlying time source time traceability announced in master state.");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:frequency_traceable", PTPD_UPDATE_DATASETS, &rtOpts->timeProperties.frequencyTraceable,
+ rtOpts->timeProperties.frequencyTraceable,
+ "Underlying time source frequency traceability announced in master state.");
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "ptpengine:ptp_timescale", PTPD_UPDATE_DATASETS, (uint8_t*)&rtOpts->timeProperties.ptpTimescale, rtOpts->timeProperties.ptpTimescale,
+ "Time scale announced in master state (with ARB, UTC properties\n"
+ " are ignored by slaves). When clock class is set to 13 (application\n"
+ " specific), this value is ignored and ARB is used.",
+ "PTP", TRUE,
+ "ARB", FALSE, NULL
+ );
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "ptpengine:ptp_timesource", PTPD_UPDATE_DATASETS, &rtOpts->timeProperties.timeSource, rtOpts->timeProperties.timeSource,
+ "Time source announced in master state.",
+ "ATOMIC_CLOCK", ATOMIC_CLOCK,
+ "GPS", GPS,
+ "TERRESTRIAL_RADIO", TERRESTRIAL_RADIO,
+ "PTP", PTP,
+ "NTP", NTP,
+ "HAND_SET", HAND_SET,
+ "OTHER", OTHER,
+ "INTERNAL_OSCILLATOR", INTERNAL_OSCILLATOR, NULL
+ );
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:clock_class", PTPD_UPDATE_DATASETS, INTTYPE_U8, &rtOpts->clockQuality.clockClass,ptpPreset.clockClass.defaultValue,
+ "Clock class - announced in master state. Always 255 for slave-only.\n"
+ " Minimum, maximum and default values are controlled by presets.\n"
+ " If set to 13 (application specific time source), announced \n"
+ " time scale is always set to ARB. This setting controls the\n"
+ " states a PTP port can be in. If below 128, port will only\n"
+ " be in MASTER or PASSIVE states (master only). If above 127,\n"
+ " port will be in MASTER or SLAVE states.", RANGECHECK_RANGE,
+ ptpPreset.clockClass.minValue,ptpPreset.clockClass.maxValue);
+
+ /* ClockClass = 13 triggers ARB */
+ CONFIG_KEY_CONDITIONAL_TRIGGER(rtOpts->clockQuality.clockClass==DEFAULT_CLOCK_CLASS__APPLICATION_SPECIFIC_TIME_SOURCE,
+ rtOpts->timeProperties.ptpTimescale,FALSE, rtOpts->timeProperties.ptpTimescale);
+
+ /* ClockClass = 14 triggers ARB */
+ CONFIG_KEY_CONDITIONAL_TRIGGER(rtOpts->clockQuality.clockClass==14,
+ rtOpts->timeProperties.ptpTimescale,FALSE, rtOpts->timeProperties.ptpTimescale);
+
+ /* ClockClass = 6 triggers PTP*/
+ CONFIG_KEY_CONDITIONAL_TRIGGER(rtOpts->clockQuality.clockClass==6,
+ rtOpts->timeProperties.ptpTimescale,TRUE, rtOpts->timeProperties.ptpTimescale);
+
+ /* ClockClass = 7 triggers PTP*/
+ CONFIG_KEY_CONDITIONAL_TRIGGER(rtOpts->clockQuality.clockClass==7,
+ rtOpts->timeProperties.ptpTimescale,TRUE, rtOpts->timeProperties.ptpTimescale);
+
+ /* ClockClass = 255 triggers slaveOnly */
+ CONFIG_KEY_CONDITIONAL_TRIGGER(rtOpts->clockQuality.clockClass==SLAVE_ONLY_CLOCK_CLASS, rtOpts->slaveOnly,TRUE,FALSE);
+ /* ...and vice versa */
+ CONFIG_KEY_CONDITIONAL_TRIGGER(rtOpts->slaveOnly==TRUE, rtOpts->clockQuality.clockClass,SLAVE_ONLY_CLOCK_CLASS, rtOpts->clockQuality.clockClass);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:priority1", PTPD_UPDATE_DATASETS, INTTYPE_U8, &rtOpts->priority1, rtOpts->priority1,
+ "Priority 1 announced in master state,used for Best Master\n"
+ " Clock selection.",RANGECHECK_RANGE,0,248);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:priority2", PTPD_UPDATE_DATASETS, INTTYPE_U8, &rtOpts->priority2, rtOpts->priority2,
+ "Priority 2 announced in master state, used for Best Master\n"
+ " Clock selection.",RANGECHECK_RANGE,0,248);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:max_listen",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->maxListen, rtOpts->maxListen,
+ "Number of consecutive resets to LISTENING before full network reset\n",RANGECHECK_MIN,1,0);
+
+ /*
+ * TODO: in unicast and hybrid mode, automativally override master delayreq interval with a default,
+ * rather than require setting it manually.
+ */
+
+ /* hybrid mode -> should specify delayreq interval: override set in the bottom of this function */
+ CONFIG_KEY_CONDITIONAL_WARNING_NOTSET(rtOpts->ipMode == IPMODE_HYBRID,
+ "ptpengine:log_delayreq_interval",
+ "It is recommended to manually set the delay request interval (ptpengine:log_delayreq_interval) to required value in hybrid mode"
+ );
+
+ /* unicast mode -> should specify delayreq interval if we can become a slave */
+ CONFIG_KEY_CONDITIONAL_WARNING_NOTSET(rtOpts->ipMode == IPMODE_UNICAST &&
+ rtOpts->clockQuality.clockClass > 127,
+ "ptpengine:log_delayreq_interval",
+ "It is recommended to manually set the delay request interval (ptpengine:log_delayreq_interval) to required value in unicast mode"
+ );
+
+ CONFIG_KEY_ALIAS("ptpengine:unicast_address","ptpengine:unicast_destinations");
+
+ /* unicast signaling slave -> must specify unicast destination(s) */
+ CONFIG_KEY_CONDITIONAL_DEPENDENCY("ptpengine:ip_mode",
+ rtOpts->clockQuality.clockClass > 127 &&
+ rtOpts->ipMode == IPMODE_UNICAST &&
+ rtOpts->unicastNegotiation,
+ "unicast",
+ "ptpengine:unicast_destinations");
+
+ /* unicast master without signaling - must specify unicast destinations */
+ CONFIG_KEY_CONDITIONAL_DEPENDENCY("ptpengine:ip_mode",
+ rtOpts->clockQuality.clockClass <= 127 &&
+ rtOpts->ipMode == IPMODE_UNICAST &&
+ !rtOpts->unicastNegotiation,
+ "unicast",
+ "ptpengine:unicast_destinations");
+
+ CONFIG_KEY_TRIGGER("ptpengine:unicast_destinations", rtOpts->unicastDestinationsSet,TRUE, rtOpts->unicastDestinationsSet);
+
+ parseResult &= configMapString(opCode, opArg, dict, target, "ptpengine:unicast_destinations",
+ PTPD_RESTART_NETWORK, rtOpts->unicastDestinations, sizeof(rtOpts->unicastDestinations), rtOpts->unicastDestinations,
+ "Specify unicast slave addresses for unicast master operation, or unicast\n"
+ " master addresses for slave operation. Format is similar to an ACL: comma,\n"
+ " tab or space-separated IPv4 unicast addresses, one or more. For a slave,\n"
+ " when unicast negotiation is used, setting this is mandatory.");
+
+ parseResult &= configMapString(opCode, opArg, dict, target, "ptpengine:unicast_domains",
+ PTPD_RESTART_NETWORK, rtOpts->unicastDomains, sizeof(rtOpts->unicastDomains), rtOpts->unicastDomains,
+ "Specify PTP domain number for each configured unicast destination (ptpengine:unicast_destinations).\n"
+ " This is only used by slave-only clocks using unicast destinations to allow for each master\n"
+ " to be in a separate domain, such as with Telecom Profile. The number of entries should match the number\n"
+ " of unicast destinations, otherwise unconfigured domains or domains set to 0 are set to domain configured in\n"
+ " ptpengine:domain. The format is a comma, tab or space-separated list of 8-bit unsigned integers (0 .. 255)");
+
+ parseResult &= configMapString(opCode, opArg, dict, target, "ptpengine:unicast_local_preference",
+ PTPD_RESTART_NETWORK, rtOpts->unicastLocalPreference, sizeof(rtOpts->unicastLocalPreference), rtOpts->unicastLocalPreference,
+ "Specify a local preference for each configured unicast destination (ptpengine:unicast_destinations).\n"
+ " This is only used by slave-only clocks using unicast destinations to allow for each master's\n"
+ " BMC selection to be influenced by the slave, such as with Telecom Profile. The number of entries should match the number\n"
+ " of unicast destinations, otherwise unconfigured preference is set to 0 (highest).\n"
+ " The format is a comma, tab or space-separated list of 8-bit unsigned integers (0 .. 255)");
+
+ /* unicast P2P - must specify unicast peer destination */
+ CONFIG_KEY_CONDITIONAL_DEPENDENCY("ptpengine:delay_mechanism",
+ rtOpts->delayMechanism == P2P &&
+ rtOpts->ipMode == IPMODE_UNICAST,
+ "P2P",
+ "ptpengine:unicast_peer_destination");
+
+ CONFIG_KEY_TRIGGER("ptpengine:unicast_peer_destination", rtOpts->unicastPeerDestinationSet,TRUE, rtOpts->unicastPeerDestinationSet);
+
+ parseResult &= configMapString(opCode, opArg, dict, target, "ptpengine:unicast_peer_destination",
+ PTPD_RESTART_NETWORK, rtOpts->unicastPeerDestination, sizeof(rtOpts->unicastPeerDestination), rtOpts->unicastPeerDestination,
+ "Specify peer unicast adress for P2P unicast. Mandatory when\n"
+ " running unicast mode and P2P delay mode.");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:management_enable",
+ PTPD_RESTART_NONE, &rtOpts->managementEnabled, rtOpts->managementEnabled,
+ "Enable handling of PTP management messages.");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:management_set_enable",
+ PTPD_RESTART_NONE, &rtOpts->managementSetEnable, rtOpts->managementSetEnable,
+ "Accept SET and COMMAND management messages.");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:igmp_refresh",
+ PTPD_RESTART_NONE, &rtOpts->do_IGMP_refresh, rtOpts->do_IGMP_refresh,
+ "Send explicit IGMP joins between engine resets and periodically\n"
+ " in master state.");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:master_igmp_refresh_interval",
+ PTPD_RESTART_PROTOCOL, INTTYPE_I8, &rtOpts->masterRefreshInterval, rtOpts->masterRefreshInterval,
+ "Periodic IGMP join interval (seconds) in master state when running\n"
+ " IPv4 multicast: when set below 10 or when ptpengine:igmp_refresh\n"
+ " is disabled, this setting has no effect.",RANGECHECK_RANGE,0,255);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:multicast_ttl",
+ PTPD_RESTART_NETWORK, INTTYPE_INT, &rtOpts->ttl, rtOpts->ttl,
+ "Multicast time to live for multicast PTP packets (ignored and set to 1\n"
+ " for peer to peer messages).",RANGECHECK_RANGE,1,64);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:ip_dscp",
+ PTPD_RESTART_NETWORK, INTTYPE_INT, &rtOpts->dscpValue, rtOpts->dscpValue,
+ "DiffServ CodepPoint for packet prioritisation (decimal). When set to zero, \n"
+ " this option is not used. Use 46 for Expedited Forwarding (0x2e).",RANGECHECK_RANGE,0,63);
+
+#ifdef PTPD_STATISTICS
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:sync_stat_filter_enable",
+ PTPD_RESTART_FILTERS, &rtOpts->filterMSOpts.enabled, rtOpts->filterMSOpts.enabled,
+ "Enable statistical filter for Sync messages.");
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "ptpengine:sync_stat_filter_type",
+ PTPD_RESTART_FILTERS, &rtOpts->filterMSOpts.filterType, rtOpts->filterMSOpts.filterType,
+ "Type of filter used for Sync message filtering",
+ "none", FILTER_NONE,
+ "mean", FILTER_MEAN,
+ "min", FILTER_MIN,
+ "max", FILTER_MAX,
+ "absmin", FILTER_ABSMIN,
+ "absmax", FILTER_ABSMAX,
+ "median", FILTER_MEDIAN, NULL);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:sync_stat_filter_window",
+ PTPD_RESTART_FILTERS, INTTYPE_INT, &rtOpts->filterMSOpts.windowSize, rtOpts->filterMSOpts.windowSize,
+ "Number of samples used for the Sync statistical filter",RANGECHECK_RANGE,3,128);
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "ptpengine:sync_stat_filter_window_type",
+ PTPD_RESTART_FILTERS, &rtOpts->filterMSOpts.windowType, rtOpts->filterMSOpts.windowType,
+ "Sample window type used for Sync message statistical filter. Delay Response outlier filter action.\n"
+ " Sliding window is continuous, interval passes every n-th sample only.",
+ "sliding", WINDOW_SLIDING,
+ "interval", WINDOW_INTERVAL, NULL);
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:delay_stat_filter_enable",
+ PTPD_RESTART_FILTERS, &rtOpts->filterSMOpts.enabled, rtOpts->filterSMOpts.enabled,
+ "Enable statistical filter for Delay messages.");
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "ptpengine:delay_stat_filter_type",
+ PTPD_RESTART_FILTERS, &rtOpts->filterSMOpts.filterType, rtOpts->filterSMOpts.filterType,
+ "Type of filter used for Delay message statistical filter",
+ "none", FILTER_NONE,
+ "mean", FILTER_MEAN,
+ "min", FILTER_MIN,
+ "max", FILTER_MAX,
+ "absmin", FILTER_ABSMIN,
+ "absmax", FILTER_ABSMAX,
+ "median", FILTER_MEDIAN, NULL);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:delay_stat_filter_window",
+ PTPD_RESTART_FILTERS, INTTYPE_INT, &rtOpts->filterSMOpts.windowSize, rtOpts->filterSMOpts.windowSize,
+ "Number of samples used for the Delay statistical filter",RANGECHECK_RANGE,3,128);
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "ptpengine:delay_stat_filter_window_type",
+ PTPD_RESTART_FILTERS, &rtOpts->filterSMOpts.windowType, rtOpts->filterSMOpts.windowType,
+ "Sample window type used for Delay message statistical filter\n"
+ " Sliding window is continuous, interval passes every n-th sample only",
+ "sliding", WINDOW_SLIDING,
+ "interval", WINDOW_INTERVAL, NULL);
+
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:delay_outlier_filter_enable",
+ PTPD_RESTART_FILTERS, &rtOpts->oFilterSMConfig.enabled, rtOpts->oFilterSMConfig.enabled,
+ "Enable outlier filter for the Delay Response component in slave state");
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "ptpengine:delay_outlier_filter_action",
+ PTPD_RESTART_NONE, (uint8_t*)&rtOpts->oFilterSMConfig.discard, rtOpts->oFilterSMConfig.discard,
+ "Delay Response outlier filter action. If set to 'filter', outliers are\n"
+ " replaced with moving average.",
+ "discard", TRUE,
+ "filter", FALSE, NULL);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:delay_outlier_filter_capacity",
+ PTPD_RESTART_FILTERS, INTTYPE_INT, &rtOpts->oFilterSMConfig.capacity, rtOpts->oFilterSMConfig.capacity,
+ "Number of samples in the Delay Response outlier filter buffer",RANGECHECK_RANGE,10,STATCONTAINER_MAX_SAMPLES);
+
+ parseResult &= configMapDouble(opCode, opArg, dict, target, "ptpengine:delay_outlier_filter_threshold",
+ PTPD_RESTART_NONE, &rtOpts->oFilterSMConfig.threshold, rtOpts->oFilterSMConfig.threshold,
+ "Delay Response outlier filter threshold (: multiplier for Peirce's maximum\n"
+ " standard deviation. When set below 1.0, filter is tighter, when set above\n"
+ " 1.0, filter is looser than standard Peirce's test.\n"
+ " When autotune enabled, this is the starting threshold.", RANGECHECK_RANGE, 0.001, 1000.0);
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:delay_outlier_filter_always_filter",
+ PTPD_RESTART_NONE, &rtOpts->oFilterSMConfig.alwaysFilter, rtOpts->oFilterSMConfig.alwaysFilter,
+ "Always run the Delay Response outlier filter, even if clock is being slewed at maximum rate");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:delay_outlier_filter_autotune_enable",
+ PTPD_RESTART_FILTERS, &rtOpts->oFilterSMConfig.autoTune, rtOpts->oFilterSMConfig.autoTune,
+ "Enable automatic threshold control for Delay Response outlier filter.");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:delay_outlier_filter_autotune_minpercent",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->oFilterSMConfig.minPercent, rtOpts->oFilterSMConfig.minPercent,
+ "Delay Response outlier filter autotune low watermark - minimum percentage\n"
+ " of discarded samples in the update period before filter is tightened\n"
+ " by the autotune step value.",RANGECHECK_RANGE,0,99);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:delay_outlier_filter_autotune_maxpercent",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->oFilterSMConfig.maxPercent, rtOpts->oFilterSMConfig.maxPercent,
+ "Delay Response outlier filter autotune high watermark - maximum percentage\n"
+ " of discarded samples in the update period before filter is loosened\n"
+ " by the autotune step value.",RANGECHECK_RANGE,1,100);
+
+ parseResult &= configMapDouble(opCode, opArg, dict, target, "ptpengine:delay_outlier_autotune_step",
+ PTPD_RESTART_NONE, &rtOpts->oFilterSMConfig.thresholdStep, rtOpts->oFilterSMConfig.thresholdStep,
+ "The value the Delay Response outlier filter threshold is increased\n"
+ " or decreased by when auto-tuning.", RANGECHECK_RANGE, 0.01,10.0);
+
+ parseResult &= configMapDouble(opCode, opArg, dict, target, "ptpengine:delay_outlier_filter_autotune_minthreshold",
+ PTPD_RESTART_NONE, &rtOpts->oFilterSMConfig.minThreshold, rtOpts->oFilterSMConfig.minThreshold,
+ "Minimum Delay Response filter threshold value used when auto-tuning", RANGECHECK_RANGE, 0.01,10.0);
+
+ parseResult &= configMapDouble(opCode, opArg, dict, target, "ptpengine:delay_outlier_filter_autotune_maxthreshold",
+ PTPD_RESTART_NONE, &rtOpts->oFilterSMConfig.maxThreshold, rtOpts->oFilterSMConfig.maxThreshold,
+ "Maximum Delay Response filter threshold value used when auto-tuning", RANGECHECK_RANGE, 0.01,10.0);
+
+ CONFIG_CONDITIONAL_ASSERTION(rtOpts->oFilterSMConfig.maxPercent <= rtOpts->oFilterSMConfig.minPercent,
+ "ptpengine:delay_outlier_filter_autotune_maxpercent value has to be greater "
+ "than ptpengine:delay_outlier_filter_autotune_minpercent\n");
+
+ CONFIG_CONDITIONAL_ASSERTION(rtOpts->oFilterSMConfig.maxThreshold <= rtOpts->oFilterSMConfig.minThreshold,
+ "ptpengine:delay_outlier_filter_autotune_maxthreshold value has to be greater "
+ "than ptpengine:delay_outlier_filter_autotune_minthreshold\n");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:delay_outlier_filter_stepdetect_enable",
+ PTPD_RESTART_FILTERS, &rtOpts->oFilterSMConfig.stepDelay, rtOpts->oFilterSMConfig.stepDelay,
+ "Enable Delay filter step detection (delaySM) to block when certain level exceeded");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:delay_outlier_filter_stepdetect_threshold",
+ PTPD_RESTART_NONE, INTTYPE_I32, &rtOpts->oFilterSMConfig.stepThreshold, rtOpts->oFilterSMConfig.stepThreshold,
+ "Delay Response step detection threshold. Step detection is performed\n"
+ " only when delaySM is below this threshold (nanoseconds)", RANGECHECK_RANGE, 50000, NANOSECONDS_MAX);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:delay_outlier_filter_stepdetect_level",
+ PTPD_RESTART_NONE, INTTYPE_I32, &rtOpts->oFilterSMConfig.stepLevel, rtOpts->oFilterSMConfig.stepLevel,
+ "Delay Response step level. When step detection enabled and operational,\n"
+ " delaySM above this level (nanosecond) is considered a clock step and updates are paused", RANGECHECK_RANGE,50000, NANOSECONDS_MAX);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:delay_outlier_filter_stepdetect_credit",
+ PTPD_RESTART_FILTERS, INTTYPE_I32, &rtOpts->oFilterSMConfig.delayCredit, rtOpts->oFilterSMConfig.delayCredit,
+ "Initial credit (number of samples) the Delay step detection filter can block for\n"
+ " When credit is exhausted, filter stops blocking. Credit is gradually restored",RANGECHECK_RANGE,50,1000);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:delay_outlier_filter_stepdetect_credit_increment",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->oFilterSMConfig.creditIncrement, rtOpts->oFilterSMConfig.creditIncrement,
+ "Amount of credit for the Delay step detection filter restored every full sample window",RANGECHECK_RANGE,1,100);
+
+ parseResult &= configMapDouble(opCode, opArg, dict, target, "ptpengine:delay_outlier_weight",
+ PTPD_RESTART_NONE, &rtOpts->oFilterSMConfig.weight, rtOpts->oFilterSMConfig.weight,
+ "Delay Response outlier weight: if an outlier is detected, determines\n"
+ " the amount of its deviation from mean that is used to build the standard\n"
+ " deviation statistics and influence further outlier detection.\n"
+ " When set to 1.0, the outlier is used as is.", RANGECHECK_RANGE, 0.01, 2.0);
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:sync_outlier_filter_enable",
+ PTPD_RESTART_FILTERS, &rtOpts->oFilterMSConfig.enabled, rtOpts->oFilterMSConfig.enabled,
+ "Enable outlier filter for the Sync component in slave state.");
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "ptpengine:sync_outlier_filter_action",
+ PTPD_RESTART_NONE, (uint8_t*)&rtOpts->oFilterMSConfig.discard, rtOpts->oFilterMSConfig.discard,
+ "Sync outlier filter action. If set to 'filter', outliers are replaced\n"
+ " with moving average.",
+ "discard", TRUE,
+ "filter", FALSE, NULL);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:sync_outlier_filter_capacity",
+ PTPD_RESTART_FILTERS, INTTYPE_INT, &rtOpts->oFilterMSConfig.capacity, rtOpts->oFilterMSConfig.capacity,
+ "Number of samples in the Sync outlier filter buffer.",RANGECHECK_RANGE,10,STATCONTAINER_MAX_SAMPLES);
+
+ parseResult &= configMapDouble(opCode, opArg, dict, target, "ptpengine:sync_outlier_filter_threshold",
+ PTPD_RESTART_NONE, &rtOpts->oFilterMSConfig.threshold, rtOpts->oFilterMSConfig.threshold,
+ "Sync outlier filter threshold: multiplier for the Peirce's maximum standard\n"
+ " deviation. When set below 1.0, filter is tighter, when set above 1.0,\n"
+ " filter is looser than standard Peirce's test.", RANGECHECK_RANGE, 0.001, 1000.0);
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:sync_outlier_filter_always_filter",
+ PTPD_RESTART_NONE, &rtOpts->oFilterMSConfig.alwaysFilter, rtOpts->oFilterMSConfig.alwaysFilter,
+ "Always run the Sync outlier filter, even if clock is being slewed at maximum rate");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:sync_outlier_filter_autotune_enable",
+ PTPD_RESTART_FILTERS, &rtOpts->oFilterMSConfig.autoTune, rtOpts->oFilterMSConfig.autoTune,
+ "Enable automatic threshold control for Sync outlier filter.");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:sync_outlier_filter_autotune_minpercent",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->oFilterMSConfig.minPercent, rtOpts->oFilterMSConfig.minPercent,
+ "Sync outlier filter autotune low watermark - minimum percentage\n"
+ " of discarded samples in the update period before filter is tightened\n"
+ " by the autotune step value.",RANGECHECK_RANGE,0,99);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:sync_outlier_filter_autotune_maxpercent",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->oFilterMSConfig.maxPercent, rtOpts->oFilterMSConfig.maxPercent,
+ "Sync outlier filter autotune high watermark - maximum percentage\n"
+ " of discarded samples in the update period before filter is loosened\n"
+ " by the autotune step value.",RANGECHECK_RANGE,1,100);
+
+ parseResult &= configMapDouble(opCode, opArg, dict, target, "ptpengine:sync_outlier_autotune_step",
+ PTPD_RESTART_NONE, &rtOpts->oFilterMSConfig.thresholdStep, rtOpts->oFilterMSConfig.thresholdStep,
+ "Value the Sync outlier filter threshold is increased\n"
+ " or decreased by when auto-tuning.", RANGECHECK_RANGE, 0.01,10.0);
+
+ parseResult &= configMapDouble(opCode, opArg, dict, target, "ptpengine:sync_outlier_filter_autotune_minthreshold",
+ PTPD_RESTART_NONE, &rtOpts->oFilterMSConfig.minThreshold, rtOpts->oFilterMSConfig.minThreshold,
+ "Minimum Sync outlier filter threshold value used when auto-tuning", RANGECHECK_RANGE, 0.01,10.0);
+
+ parseResult &= configMapDouble(opCode, opArg, dict, target, "ptpengine:sync_outlier_filter_autotune_maxthreshold",
+ PTPD_RESTART_NONE, &rtOpts->oFilterMSConfig.maxThreshold, rtOpts->oFilterMSConfig.maxThreshold,
+ "Maximum Sync outlier filter threshold value used when auto-tuning", RANGECHECK_RANGE, 0.01,10.0);
+
+ CONFIG_CONDITIONAL_ASSERTION(rtOpts->oFilterMSConfig.maxPercent <= rtOpts->oFilterMSConfig.minPercent,
+ "ptpengine:sync_outlier_filter_autotune_maxpercent value has to be greater "
+ "than ptpengine:sync_outlier_filter_autotune_minpercent\n");
+
+ CONFIG_CONDITIONAL_ASSERTION(rtOpts->oFilterMSConfig.maxThreshold <= rtOpts->oFilterMSConfig.minThreshold,
+ "ptpengine:sync_outlier_filter_autotune_maxthreshold value has to be greater "
+ "than ptpengine:sync_outlier_filter_autotune_minthreshold\n");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:sync_outlier_filter_stepdetect_enable",
+ PTPD_RESTART_FILTERS, &rtOpts->oFilterMSConfig.stepDelay, rtOpts->oFilterMSConfig.stepDelay,
+ "Enable Sync filter step detection (delayMS) to block when certain level exceeded.");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:sync_outlier_filter_stepdetect_threshold",
+ PTPD_RESTART_NONE, INTTYPE_I32, &rtOpts->oFilterMSConfig.stepThreshold, rtOpts->oFilterMSConfig.stepThreshold,
+ "Sync step detection threshold. Step detection is performed\n"
+ " only when delayMS is below this threshold (nanoseconds)", RANGECHECK_RANGE,100000, NANOSECONDS_MAX);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:sync_outlier_filter_stepdetect_level",
+ PTPD_RESTART_NONE, INTTYPE_I32, &rtOpts->oFilterMSConfig.stepLevel, rtOpts->oFilterMSConfig.stepLevel,
+ "Sync step level. When step detection enabled and operational,\n"
+ " delayMS above this level (nanosecond) is considered a clock step and updates are paused", RANGECHECK_RANGE,100000, NANOSECONDS_MAX);
+
+ CONFIG_CONDITIONAL_ASSERTION(rtOpts->oFilterMSConfig.stepThreshold <= (rtOpts->oFilterMSConfig.stepLevel + 100000),
+ "ptpengine:sync_outlier_filter_stepdetect_threshold has to be at least "
+ "100 us (100000) greater than ptpengine:sync_outlier_filter_stepdetect_level\n");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:sync_outlier_filter_stepdetect_credit",
+ PTPD_RESTART_FILTERS, INTTYPE_INT, &rtOpts->oFilterMSConfig.delayCredit, rtOpts->oFilterMSConfig.delayCredit,
+ "Initial credit (number of samples) the Sync step detection filter can block for.\n"
+ " When credit is exhausted, filter stops blocking. Credit is gradually restored",RANGECHECK_RANGE,50,1000);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:sync_outlier_filter_stepdetect_credit_increment",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->oFilterMSConfig.creditIncrement, rtOpts->oFilterMSConfig.creditIncrement,
+ "Amount of credit for the Sync step detection filter restored every full sample window",RANGECHECK_RANGE,1,100);
+
+ parseResult &= configMapDouble(opCode, opArg, dict, target, "ptpengine:sync_outlier_weight",
+ PTPD_RESTART_NONE, &rtOpts->oFilterMSConfig.weight, rtOpts->oFilterMSConfig.weight,
+ "Sync outlier weight: if an outlier is detected, this value determines the\n"
+ " amount of its deviation from mean that is used to build the standard \n"
+ " deviation statistics and influence further outlier detection.\n"
+ " When set to 1.0, the outlier is used as is.", RANGECHECK_RANGE, 0.01, 2.0);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:calibration_delay",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->calibrationDelay, rtOpts->calibrationDelay,
+ "Delay between moving to slave state and enabling clock updates (seconds).\n"
+ " This allows one-way delay to stabilise before starting clock updates.\n"
+ " Activated when going into slave state and during slave's GM failover.\n"
+ " 0 - not used.",RANGECHECK_RANGE,0,300);
+
+#endif /* PTPD_STATISTICS */
+
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:idle_timeout",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->idleTimeout, rtOpts->idleTimeout,
+ "PTP idle timeout: if PTPd is in SLAVE state and there have been no clock\n"
+ " updates for this amout of time, PTPd releases clock control.\n", RANGECHECK_RANGE,10,3600);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:offset_alarm_threshold", PTPD_UPDATE_DATASETS, INTTYPE_U32, &rtOpts->ofmAlarmThreshold, rtOpts->ofmAlarmThreshold,
+ "PTP slave offset from master threshold (nanoseconds - absolute value)\n"
+ " When offset exceeds this value, an alarm is raised (also SNMP trap if configured).\n"
+ " 0 = disabled.", RANGECHECK_NONE,0,NANOSECONDS_MAX);
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:panic_mode",
+ PTPD_RESTART_NONE, &rtOpts->enablePanicMode, rtOpts->enablePanicMode,
+ "Enable panic mode: when offset from master is above 1 second, stop updating\n"
+ " the clock for a period of time and then step the clock if offset remains\n"
+ " above 1 second.");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:panic_mode_duration",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->panicModeDuration, rtOpts->panicModeDuration,
+ "Duration (minutes) of the panic mode period (no clock updates) when offset\n"
+ " above 1 second detected.",RANGECHECK_RANGE,1,60);
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:panic_mode_release_clock",
+ PTPD_RESTART_NONE, &rtOpts->panicModeReleaseClock, rtOpts->panicModeReleaseClock,
+ "When entering panic mode, release clock control while panic mode lasts\n"
+ " if ntpengine:* configured, this will fail over to NTP,\n"
+ " if not set, PTP will hold clock control during panic mode.");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:panic_mode_exit_threshold",
+ PTPD_RESTART_NONE, INTTYPE_U32, &rtOpts->panicModeExitThreshold, rtOpts->panicModeExitThreshold,
+ "Do not exit panic mode until offset drops below this value (nanoseconds).\n"
+ " 0 = not used.",RANGECHECK_RANGE,0,NANOSECONDS_MAX);
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:pid_as_clock_identity",
+ PTPD_RESTART_PROTOCOL, &rtOpts->pidAsClockId, rtOpts->pidAsClockId,
+ "Use PTPd's process ID as the middle part of the PTP clock ID - useful for running multiple instances.");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:ntp_failover",
+ PTPD_RESTART_NONE, &rtOpts->ntpOptions.enableFailover, rtOpts->ntpOptions.enableFailover,
+ "Fail over to NTP when PTP time sync not available - requires\n"
+ " ntpengine:enabled, but does not require the rest of NTP configuration:\n"
+ " will warn instead of failing over if cannot control ntpd.");
+
+ CONFIG_KEY_DEPENDENCY("ptpengine:ntp_failover", "ntpengine:enabled");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:ntp_failover_timeout",
+ PTPD_RESTART_NTPCONFIG, INTTYPE_INT, &rtOpts->ntpOptions.failoverTimeout,
+ rtOpts->ntpOptions.failoverTimeout,
+ "NTP failover timeout in seconds: time between PTP slave going into\n"
+ " LISTENING state, and releasing clock control. 0 = fail over immediately.", RANGECHECK_RANGE,0, 1800);
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:prefer_ntp",
+ PTPD_RESTART_NTPCONFIG, &rtOpts->preferNTP, rtOpts->preferNTP,
+ "Prefer NTP time synchronisation. Only use PTP when NTP not available,\n"
+ " could be used when NTP runs with a local GPS receiver or another reference");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:panic_mode_ntp",
+ PTPD_RESTART_NONE, &rtOpts->panicModeReleaseClock, rtOpts->panicModeReleaseClock,
+ "Legacy option from 2.3.0: same as ptpengine:panic_mode_release_clock");
+
+ CONFIG_KEY_DEPENDENCY("ptpengine:panic_mode_ntp", "ntpengine:enabled");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:sigusr2_clears_counters",
+ PTPD_RESTART_NONE, &rtOpts->clearCounters, rtOpts->clearCounters,
+ "Clear counters after dumping all counter values on SIGUSR2.");
+
+ /* Defining the ACLs enables ACL matching */
+ CONFIG_KEY_TRIGGER("ptpengine:timing_acl_permit", rtOpts->timingAclEnabled,TRUE, rtOpts->timingAclEnabled);
+ CONFIG_KEY_TRIGGER("ptpengine:timing_acl_deny", rtOpts->timingAclEnabled,TRUE, rtOpts->timingAclEnabled);
+ CONFIG_KEY_TRIGGER("ptpengine:management_acl_permit", rtOpts->managementAclEnabled,TRUE, rtOpts->managementAclEnabled);
+ CONFIG_KEY_TRIGGER("ptpengine:management_acl_deny", rtOpts->managementAclEnabled,TRUE, rtOpts->managementAclEnabled);
+
+ parseResult &= configMapString(opCode, opArg, dict, target, "ptpengine:timing_acl_permit",
+ PTPD_RESTART_ACLS, rtOpts->timingAclPermitText, sizeof(rtOpts->timingAclPermitText), rtOpts->timingAclPermitText,
+ "Permit access control list for timing packets. Format is a series of \n"
+ " comma, space or tab separated network prefixes: IPv4 addresses or full CIDR notation a.b.c.d/x,\n"
+ " where a.b.c.d is the subnet and x is the decimal mask, or a.b.c.d/v.x.y.z where a.b.c.d is the\n"
+ " subnet and v.x.y.z is the 4-octet mask. The match is performed on the source IP address of the\n"
+ " incoming messages. IP access lists are only supported when using the IP transport.");
+
+ parseResult &= configMapString(opCode, opArg, dict, target, "ptpengine:timing_acl_deny",
+ PTPD_RESTART_ACLS, rtOpts->timingAclDenyText, sizeof(rtOpts->timingAclDenyText), rtOpts->timingAclDenyText,
+ "Deny access control list for timing packets. Format is a series of \n"
+ " comma, space or tab separated network prefixes: IPv4 addresses or full CIDR notation a.b.c.d/x,\n"
+ " where a.b.c.d is the subnet and x is the decimal mask, or a.b.c.d/v.x.y.z where a.b.c.d is the\n"
+ " subnet and v.x.y.z is the 4-octet mask. The match is performed on the source IP address of the\n"
+ " incoming messages. IP access lists are only supported when using the IP transport.");
+
+ parseResult &= configMapString(opCode, opArg, dict, target, "ptpengine:management_acl_permit",
+ PTPD_RESTART_ACLS, rtOpts->managementAclPermitText, sizeof(rtOpts->managementAclPermitText), rtOpts->managementAclPermitText,
+ "Permit access control list for management messages. Format is a series of \n"
+ " comma, space or tab separated network prefixes: IPv4 addresses or full CIDR notation a.b.c.d/x,\n"
+ " where a.b.c.d is the subnet and x is the decimal mask, or a.b.c.d/v.x.y.z where a.b.c.d is the\n"
+ " subnet and v.x.y.z is the 4-octet mask. The match is performed on the source IP address of the\n"
+ " incoming messages. IP access lists are only supported when using the IP transport.");
+
+ parseResult &= configMapString(opCode, opArg, dict, target, "ptpengine:management_acl_deny",
+ PTPD_RESTART_ACLS, rtOpts->managementAclDenyText, sizeof(rtOpts->managementAclDenyText), rtOpts->managementAclDenyText,
+ "Deny access control list for management messages. Format is a series of \n"
+ " comma, space or tab separated network prefixes: IPv4 addresses or full CIDR notation a.b.c.d/x,\n"
+ " where a.b.c.d is the subnet and x is the decimal mask, or a.b.c.d/v.x.y.z where a.b.c.d is the\n"
+ " subnet and v.x.y.z is the 4-octet mask. The match is performed on the source IP address of the\n"
+ " incoming messages. IP access lists are only supported when using the IP transport.");
+
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "ptpengine:timing_acl_order",
+ PTPD_RESTART_ACLS, &rtOpts->timingAclOrder, rtOpts->timingAclOrder,
+ "Order in which permit and deny access lists are evaluated for timing\n"
+ " packets, the evaluation process is the same as for Apache httpd.",
+ "permit-deny", ACL_PERMIT_DENY,
+ "deny-permit", ACL_DENY_PERMIT, NULL
+ );
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "ptpengine:management_acl_order",
+ PTPD_RESTART_ACLS, &rtOpts->managementAclOrder, rtOpts->managementAclOrder,
+ "Order in which permit and deny access lists are evaluated for management\n"
+ " messages, the evaluation process is the same as for Apache httpd.",
+ "permit-deny", ACL_PERMIT_DENY,
+ "deny-permit", ACL_DENY_PERMIT, NULL
+ );
+
+
+ /* Ethernet mode disables ACL processing*/
+ CONFIG_KEY_CONDITIONAL_TRIGGER(rtOpts->transport == IEEE_802_3, rtOpts->timingAclEnabled,FALSE, rtOpts->timingAclEnabled);
+ CONFIG_KEY_CONDITIONAL_TRIGGER(rtOpts->transport == IEEE_802_3, rtOpts->managementAclEnabled,FALSE, rtOpts->managementAclEnabled);
+
+
+
+/* ===== clock section ===== */
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "clock:no_adjust",
+ PTPD_RESTART_NONE, &rtOpts->noAdjust,ptpPreset.noAdjust,
+ "Do not adjust the clock");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "clock:no_reset",
+ PTPD_RESTART_NONE, &rtOpts->noResetClock, rtOpts->noResetClock,
+ "Do not step the clock - only slew");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "clock:step_startup_force",
+ PTPD_RESTART_NONE, &rtOpts->stepForce, rtOpts->stepForce,
+ "Force clock step on first sync after startup regardless of offset and clock:no_reset");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "clock:step_startup",
+ PTPD_RESTART_NONE, &rtOpts->stepOnce, rtOpts->stepOnce,
+ "Step clock on startup if offset >= 1 second, ignoring\n"
+ " panic mode and clock:no_reset");
+
+
+#ifdef HAVE_LINUX_RTC_H
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "clock:set_rtc_on_step",
+ PTPD_RESTART_NONE, &rtOpts->setRtc, rtOpts->setRtc,
+ "Attempt setting the RTC when stepping clock (Linux only - FreeBSD does \n"
+ " this for us. WARNING: this will always set the RTC to OS clock time,\n"
+ " regardless of time zones, so this assumes that RTC runs in UTC or \n"
+ " at least in the same timescale as PTP. true at least on most \n"
+ " single-boot x86 Linux systems.");
+#endif /* HAVE_LINUX_RTC_H */
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "clock:drift_handling",
+ PTPD_RESTART_NONE, &rtOpts->drift_recovery_method, rtOpts->drift_recovery_method,
+ "Observed drift handling method between servo restarts:\n"
+ " reset: set to zero (not recommended)\n"
+ " preserve: use kernel value,\n"
+ " file: load/save to drift file on startup/shutdown, use kernel\n"
+ " value inbetween. To specify drift file, use the clock:drift_file setting."
+ ,
+ "reset", DRIFT_RESET,
+ "preserve", DRIFT_KERNEL,
+ "file", DRIFT_FILE, NULL
+ );
+
+ parseResult &= configMapString(opCode, opArg, dict, target, "clock:drift_file",
+ PTPD_RESTART_NONE, rtOpts->driftFile, sizeof(rtOpts->driftFile), rtOpts->driftFile,
+ "Specify drift file");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "clock:leap_second_pause_period",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->leapSecondPausePeriod,
+ rtOpts->leapSecondPausePeriod,
+ "Time (seconds) before and after midnight that clock updates should pe suspended for\n"
+ " during a leap second event. The total duration of the pause is twice\n"
+ " the configured duration",RANGECHECK_RANGE,5,600);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "clock:leap_second_notice_period",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->leapSecondNoticePeriod,
+ rtOpts->leapSecondNoticePeriod,
+ "Time (seconds) before midnight that PTPd starts announcing the leap second\n"
+ " if it's running as master",RANGECHECK_RANGE,3600,86400);
+
+ parseResult &= configMapString(opCode, opArg, dict, target, "clock:leap_seconds_file",
+ PTPD_RESTART_NONE, rtOpts->leapFile, sizeof(rtOpts->leapFile), rtOpts->leapFile,
+ "Specify leap second file location - up to date version can be downloaded from \n"
+ " http://www.ietf.org/timezones/data/leap-seconds.list");
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "clock:leap_second_handling",
+ PTPD_RESTART_NONE, &rtOpts->leapSecondHandling, rtOpts->leapSecondHandling,
+ "Behaviour during a leap second event:\n"
+ " accept: inform the OS kernel of the event\n"
+ " ignore: do nothing - ends up with a 1-second offset which is then slewed\n"
+ " step: similar to ignore, but steps the clock immediately after the leap second event\n"
+ " smear: do not inform kernel, gradually introduce the leap second before the event\n"
+ " by modifying clock offset (see clock:leap_second_smear_period)",
+ "accept", LEAP_ACCEPT,
+ "ignore", LEAP_IGNORE,
+ "step", LEAP_STEP,
+ "smear", LEAP_SMEAR, NULL
+ );
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "clock:leap_second_smear_period",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->leapSecondSmearPeriod,
+ rtOpts->leapSecondSmearPeriod,
+ "Time period (Seconds) over which the leap second is introduced before the event.\n"
+ " Example: when set to 86400 (24 hours), an extra 11.5 microseconds is added every second"
+ ,RANGECHECK_RANGE,3600,86400);
+
+
+#ifdef HAVE_STRUCT_TIMEX_TICK
+ /* This really is clock specific - different clocks may allow different ranges */
+ parseResult &= configMapInt(opCode, opArg, dict, target, "clock:max_offset_ppm",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->servoMaxPpb, rtOpts->servoMaxPpb,
+ "Maximum absolute frequency shift which can be applied to the clock servo\n"
+ " when slewing the clock. Expressed in parts per million (1 ppm = shift of\n"
+ " 1 us per second. Values above 512 will use the tick duration correction\n"
+ " to allow even faster slewing. Default maximum is 512 without using tick.", RANGECHECK_RANGE,
+ ADJ_FREQ_MAX/1000,ADJ_FREQ_MAX/500);
+#endif /* HAVE_STRUCT_TIMEX_TICK */
+
+ /*
+ * TimeProperties DS - in future when clock driver API is implemented,
+ * a slave PTP engine should inform a clock about this, and then that
+ * clock should pass this information to any master PTP engines, unless
+ * we override this. here. For now we just supply this to RtOpts.
+ */
+
+
+/* ===== servo section ===== */
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "servo:delayfilter_stiffness",
+ PTPD_RESTART_NONE, INTTYPE_I16, &rtOpts->s, rtOpts->s,
+ "One-way delay filter stiffness.", RANGECHECK_NONE,0,0);
+
+ parseResult &= configMapDouble(opCode, opArg, dict, target, "servo:kp",
+ PTPD_RESTART_NONE, &rtOpts->servoKP, rtOpts->servoKP,
+ "Clock servo PI controller proportional component gain (kP).", RANGECHECK_MIN, 0.000001, 0);
+ parseResult &= configMapDouble(opCode, opArg, dict, target, "servo:ki",
+ PTPD_RESTART_NONE, &rtOpts->servoKI, rtOpts->servoKI,
+ "Clock servo PI controller integral component gain (kI).", RANGECHECK_MIN, 0.000001,0);
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "servo:dt_method",
+ PTPD_RESTART_NONE, &rtOpts->servoDtMethod, rtOpts->servoDtMethod,
+ "How servo update interval (delta t) is calculated:\n"
+ " none: servo not corrected for update interval (dt always 1),\n"
+ " constant: constant value (target servo update rate - sync interval for PTP,\n"
+ " measured: servo measures how often it's updated and uses this interval.",
+ "none", DT_NONE,
+ "constant", DT_CONSTANT,
+ "measured", DT_MEASURED, NULL
+ );
+
+ parseResult &= configMapDouble(opCode, opArg, dict, target, "servo:dt_max",
+ PTPD_RESTART_NONE, &rtOpts->servoMaxdT, rtOpts->servoMaxdT,
+ "Maximum servo update interval (delta t) when using measured servo update interval\n"
+ " (servo:dt_method = measured), specified as sync interval multiplier.", RANGECHECK_RANGE, 1.5,100.0);
+
+#ifdef PTPD_STATISTICS
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "servo:stability_detection",
+ PTPD_RESTART_NONE, &rtOpts->servoStabilityDetection,
+ rtOpts->servoStabilityDetection,
+ "Enable clock synchronisation servo stability detection\n"
+ " (based on standard deviation of the observed drift value)\n"
+ " - drift will be saved to drift file / cached when considered stable,\n"
+ " also clock stability status will be logged.");
+
+ parseResult &= configMapDouble(opCode, opArg, dict, target, "servo:stability_threshold",
+ PTPD_RESTART_NONE, &rtOpts->servoStabilityThreshold,
+ rtOpts->servoStabilityThreshold,
+ "Specify the observed drift standard deviation threshold in parts per\n"
+ " billion (ppb) - if stanard deviation is within the threshold, servo\n"
+ " is considered stable.", RANGECHECK_RANGE, 1.0,10000.0);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "servo:stability_period",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->servoStabilityPeriod,
+ rtOpts->servoStabilityPeriod,
+ "Specify for how many statistics update intervals the observed drift\n"
+ " standard deviation has to stay within threshold to be considered stable.", RANGECHECK_RANGE,
+ 1,100);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "servo:stability_timeout",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->servoStabilityTimeout,
+ rtOpts->servoStabilityTimeout,
+ "Specify after how many minutes without stabilisation servo is considered\n"
+ " unstable. Assists with logging servo stability information and\n"
+ " allows to preserve observed drift if servo cannot stabilise.\n", RANGECHECK_RANGE,
+ 1,60);
+
+#endif
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "servo:max_delay",
+ PTPD_RESTART_NONE, INTTYPE_I32, &rtOpts->maxDelay, rtOpts->maxDelay,
+ "Do accept master to slave delay (delayMS - from Sync message) or slave to master delay\n"
+ " (delaySM - from Delay messages) if greater than this value (nanoseconds). 0 = not used.", RANGECHECK_RANGE,
+ 0,NANOSECONDS_MAX);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "servo:max_delay_max_rejected",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->maxDelayMaxRejected, rtOpts->maxDelayMaxRejected,
+ "Maximum number of consecutive delay measurements exceeding maxDelay threshold,\n"
+ " before slave is reset.", RANGECHECK_MIN,0,0);
+
+#ifdef PTPD_STATISTICS
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "servo:max_delay_stable_only",
+ PTPD_RESTART_NONE, &rtOpts->maxDelayStableOnly, rtOpts->maxDelayStableOnly,
+ "If servo:max_delay is set, perform the check only if clock servo has stabilised.\n");
+#endif
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ptpengine:sync_sequence_checking",
+ PTPD_RESTART_NONE, &rtOpts->syncSequenceChecking, rtOpts->syncSequenceChecking,
+ "When enabled, Sync messages will only be accepted if sequence ID is increasing."
+ " This is limited to 50 dropped messages.\n");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ptpengine:clock_update_timeout",
+ PTPD_RESTART_PROTOCOL, INTTYPE_INT, &rtOpts->clockUpdateTimeout, rtOpts->clockUpdateTimeout,
+ "If set to non-zero, timeout in seconds, after which the slave resets if no clock updates made. \n", RANGECHECK_RANGE,
+ 0, 3600);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "servo:max_offset",
+ PTPD_RESTART_NONE, INTTYPE_I32, &rtOpts->maxOffset, rtOpts->maxOffset,
+ "Do not reset the clock if offset from master is greater\n"
+ " than this value (nanoseconds). 0 = not used.", RANGECHECK_RANGE,
+ 0,NANOSECONDS_MAX);
+
+/* ===== global section ===== */
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "global:enable_alarms",
+ PTPD_RESTART_ALARMS, &rtOpts->alarmsEnabled, rtOpts->alarmsEnabled,
+ "Enable support for alarm and event notifications.\n");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "global:alarm_timeout",
+ PTPD_RESTART_ALARMS, INTTYPE_INT, &rtOpts->alarmMinAge, rtOpts->alarmMinAge,
+ "Mininmum alarm age (seconds) - minimal time between alarm set and clear notifications.\n"
+ " The condition can clear while alarm lasts, but notification (log or SNMP) will only \n"
+ " be triggered after the timeout. This option prevents from alarms flapping.", RANGECHECK_RANGE, 0, 3600);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "global:alarm_initial_delay",
+ PTPD_RESTART_DAEMON, INTTYPE_INT, &rtOpts->alarmInitialDelay, rtOpts->alarmInitialDelay,
+ "Delay the start of alarm processing (seconds) after ptpd startup. This option \n"
+ " allows to avoid unnecessary alarms before PTPd starts synchronising.\n",
+ RANGECHECK_RANGE, 0, 3600);
+
+#ifdef PTPD_SNMP
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "global:enable_snmp",
+ PTPD_RESTART_DAEMON, &rtOpts->snmpEnabled, rtOpts->snmpEnabled,
+ "Enable SNMP agent (if compiled with PTPD_SNMP).");
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "global:enable_snmp_traps",
+ PTPD_RESTART_ALARMS, &rtOpts->snmpTrapsEnabled, rtOpts->snmpTrapsEnabled,
+ "Enable sending SNMP traps (only if global:enable_alarms set and global:enable_snmp set).\n");
+#else
+ if(!(opCode & CFGOP_PARSE_QUIET) && CONFIG_ISTRUE("global:enable_snmp"))
+ INFO("SNMP support not enabled. Please compile with PTPD_SNMP to use global:enable_snmp\n");
+ if(!(opCode & CFGOP_PARSE_QUIET) && CONFIG_ISTRUE("global:enable_snmp_traps"))
+ INFO("SNMP support not enabled. Please compile with PTPD_SNMP to use global:enable_snmp_traps\n");
+#endif /* PTPD_SNMP */
+
+
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "global:use_syslog",
+ PTPD_RESTART_LOGGING, &rtOpts->useSysLog, rtOpts->useSysLog,
+ "Send log messages to syslog. Disabling this\n"
+ " sends all messages to stdout (or speficied log file).");
+
+ parseResult &= configMapString(opCode, opArg, dict, target, "global:lock_file",
+ PTPD_RESTART_DAEMON, rtOpts->lockFile, sizeof(rtOpts->lockFile), rtOpts->lockFile,
+ "Lock file location");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "global:auto_lockfile",
+ PTPD_RESTART_DAEMON, &rtOpts->autoLockFile, rtOpts->autoLockFile,
+ " Use mode specific and interface specific lock file\n"
+ " (overrides global:lock_file).");
+
+ parseResult &= configMapString(opCode, opArg, dict, target, "global:lock_directory",
+ PTPD_RESTART_DAEMON, rtOpts->lockDirectory, sizeof(rtOpts->lockDirectory), rtOpts->lockDirectory,
+ "Lock file directory: used with automatic mode-specific lock files,\n"
+ " also used when no lock file is specified. When lock file\n"
+ " is specified, it's expected to be an absolute path.");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "global:ignore_lock",
+ PTPD_RESTART_DAEMON, &rtOpts->ignore_daemon_lock, rtOpts->ignore_daemon_lock,
+ "Skip lock file checking and locking.");
+
+ /* if quality file specified, enable quality recording */
+ CONFIG_KEY_TRIGGER("global:quality_file", rtOpts->recordLog.logEnabled,TRUE,FALSE);
+ parseResult &= configMapString(opCode, opArg, dict, target, "global:quality_file",
+ PTPD_RESTART_LOGGING, rtOpts->recordLog.logPath, sizeof(rtOpts->recordLog.logPath), rtOpts->recordLog.logPath,
+ "File used to record data about sync packets. Enables recording when set.");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "global:quality_file_max_size",
+ PTPD_RESTART_LOGGING, INTTYPE_U32, &rtOpts->recordLog.maxSize, rtOpts->recordLog.maxSize,
+ "Maximum sync packet record file size (in kB) - file will be truncated\n"
+ " if size exceeds the limit. 0 - no limit.", RANGECHECK_MIN,0,0);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "global:quality_file_max_files",
+ PTPD_RESTART_LOGGING, INTTYPE_INT, &rtOpts->recordLog.maxFiles, rtOpts->recordLog.maxFiles,
+ "Enable log rotation of the sync packet record file up to n files.\n"
+ " 0 - do not rotate.\n", RANGECHECK_RANGE,0, 100);
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "global:quality_file_truncate",
+ PTPD_RESTART_LOGGING, &rtOpts->recordLog.truncateOnReopen, rtOpts->recordLog.truncateOnReopen,
+ "Truncate the sync packet record file every time it is (re) opened:\n"
+ " startup and SIGHUP.");
+
+ /* if status file specified, enable status logging*/
+ CONFIG_KEY_TRIGGER("global:status_file", rtOpts->statusLog.logEnabled,TRUE,FALSE);
+ parseResult &= configMapString(opCode, opArg, dict, target, "global:status_file",
+ PTPD_RESTART_LOGGING, rtOpts->statusLog.logPath, sizeof(rtOpts->statusLog.logPath), rtOpts->statusLog.logPath,
+ "File used to log "PTPD_PROGNAME" status information.");
+ /* status file can be disabled even if specified */
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "global:log_status",
+ PTPD_RESTART_NONE, &rtOpts->statusLog.logEnabled, rtOpts->statusLog.logEnabled,
+ "Enable / disable writing status information to file.");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "global:status_update_interval",
+ PTPD_RESTART_LOGGING, INTTYPE_INT, &rtOpts->statusFileUpdateInterval, rtOpts->statusFileUpdateInterval,
+ "Status file update interval in seconds.", RANGECHECK_RANGE,
+ 1,30);
+
+#ifdef RUNTIME_DEBUG
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "global:debug_level",
+ PTPD_RESTART_NONE, (uint8_t*)&rtOpts->debug_level, rtOpts->debug_level,
+ "Specify debug level (if compiled with RUNTIME_DEBUG).",
+ "LOG_INFO", LOG_INFO,
+ "LOG_DEBUG", LOG_DEBUG,
+ "LOG_DEBUG1", LOG_DEBUG1,
+ "LOG_DEBUG2", LOG_DEBUG2,
+ "LOG_DEBUG3", LOG_DEBUG3,
+ "LOG_DEBUGV", LOG_DEBUGV, NULL
+ );
+#else
+ if (!(opCode & CFGOP_PARSE_QUIET) && CONFIG_ISSET("global:debug_level"))
+ INFO("Runtime debug not enabled. Please compile with RUNTIME_DEBUG to use global:debug_level.\n");
+#endif /* RUNTIME_DEBUG */
+
+ /*
+ * Log files are reopened and / or re-created on SIGHUP anyway,
+ */
+
+ /* if log file specified, enable file logging - otherwise disable */
+ CONFIG_KEY_TRIGGER("global:log_file", rtOpts->eventLog.logEnabled,TRUE,FALSE);
+ parseResult &= configMapString(opCode, opArg, dict, target, "global:log_file",
+ PTPD_RESTART_LOGGING, rtOpts->eventLog.logPath, sizeof(rtOpts->eventLog.logPath), rtOpts->eventLog.logPath,
+ "Specify log file path (event log). Setting this enables logging to file.");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "global:log_file_max_size",
+ PTPD_RESTART_LOGGING, INTTYPE_U32, &rtOpts->eventLog.maxSize, rtOpts->eventLog.maxSize,
+ "Maximum log file size (in kB) - log file will be truncated if size exceeds\n"
+ " the limit. 0 - no limit.", RANGECHECK_MIN,0,0);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "global:log_file_max_files",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->eventLog.maxFiles, rtOpts->eventLog.maxFiles,
+ "Enable log rotation of the sync packet record file up to n files.\n"
+ " 0 - do not rotate.\n", RANGECHECK_RANGE,0, 100);
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "global:log_file_truncate",
+ PTPD_RESTART_LOGGING, &rtOpts->eventLog.truncateOnReopen, rtOpts->eventLog.truncateOnReopen,
+ "Truncate the log file every time it is (re) opened: startup and SIGHUP.");
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "global:log_level",
+ PTPD_RESTART_NONE, &rtOpts->logLevel, rtOpts->logLevel,
+ "Specify log level (only messages at this priority or higer will be logged).\n"
+ " The minimal level is LOG_ERR. LOG_ALL enables debug output if compiled with\n"
+ " RUNTIME_DEBUG.",
+ "LOG_ERR", LOG_ERR,
+ "LOG_WARNING", LOG_WARNING,
+ "LOG_NOTICE", LOG_NOTICE,
+ "LOG_INFO", LOG_INFO,
+ "LOG_ALL", LOG_ALL, NULL
+ );
+
+ /* if statistics file specified, enable statistics logging - otherwise disable - log_statistics also controlled further below*/
+ CONFIG_KEY_TRIGGER("global:statistics_file", rtOpts->statisticsLog.logEnabled,TRUE,FALSE);
+ CONFIG_KEY_TRIGGER("global:statistics_file", rtOpts->logStatistics,TRUE,FALSE);
+ parseResult &= configMapString(opCode, opArg, dict, target, "global:statistics_file",
+ PTPD_RESTART_LOGGING, rtOpts->statisticsLog.logPath, sizeof(rtOpts->statisticsLog.logPath), rtOpts->statisticsLog.logPath,
+ "Specify statistics log file path. Setting this enables logging of \n"
+ " statistics, but can be overriden with global:log_statistics.");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "global:statistics_log_interval",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->statisticsLogInterval, rtOpts->statisticsLogInterval,
+ "Log timing statistics every n seconds for Sync and Delay messages\n"
+ " (0 - log all).",RANGECHECK_MIN,0,0);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "global:statistics_file_max_size",
+ PTPD_RESTART_LOGGING, INTTYPE_U32, &rtOpts->statisticsLog.maxSize, rtOpts->statisticsLog.maxSize,
+ "Maximum statistics log file size (in kB) - log file will be truncated\n"
+ " if size exceeds the limit. 0 - no limit.",RANGECHECK_MIN,0,0);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "global:statistics_file_max_files",
+ PTPD_RESTART_LOGGING, INTTYPE_INT, &rtOpts->statisticsLog.maxFiles, rtOpts->statisticsLog.maxFiles,
+ "Enable log rotation of the statistics file up to n files. 0 - do not rotate.", RANGECHECK_RANGE,0, 100);
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "global:statistics_file_truncate",
+ PTPD_RESTART_LOGGING, &rtOpts->statisticsLog.truncateOnReopen, rtOpts->statisticsLog.truncateOnReopen,
+ "Truncate the statistics file every time it is (re) opened: startup and SIGHUP.");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "global:dump_packets",
+ PTPD_RESTART_NONE, &rtOpts->displayPackets, rtOpts->displayPackets,
+ "Dump the contents of every PTP packet");
+
+ /* this also checks if the verbose_foreground flag is set correctly */
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "global:verbose_foreground",
+ PTPD_RESTART_DAEMON, &rtOpts->nonDaemon, rtOpts->nonDaemon,
+ "Run in foreground with statistics and all messages logged to stdout.\n"
+ " Overrides log file and statistics file settings and disables syslog.\n");
+
+ if(CONFIG_ISTRUE("global:verbose_foreground")) {
+ rtOpts->useSysLog = FALSE;
+ rtOpts->logStatistics = TRUE;
+ rtOpts->statisticsLogInterval = 0;
+ rtOpts->eventLog.logEnabled = FALSE;
+ rtOpts->statisticsLog.logEnabled = FALSE;
+ }
+
+ /*
+ * this HAS to be executed after the verbose_foreground mapping because of the same
+ * default field used for both. verbose_foreground triggers nonDaemon which is OK,
+ * but we don't want foreground=y to switch on verbose_foreground if not set.
+ */
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "global:foreground",
+ PTPD_RESTART_DAEMON, &rtOpts->nonDaemon, rtOpts->nonDaemon,
+ "Run in foreground - ignored when global:verbose_foreground is set");
+
+ if(CONFIG_ISTRUE("global:verbose_foreground")) {
+ rtOpts->nonDaemon = TRUE;
+ }
+
+ /* If this is processed after verbose_foreground, we can still control logStatistics */
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "global:log_statistics",
+ PTPD_RESTART_NONE, &rtOpts->logStatistics, rtOpts->logStatistics,
+ "Log timing statistics for every PTP packet received\n");
+
+ parseResult &= configMapSelectValue(opCode, opArg, dict, target, "global:statistics_timestamp_format",
+ PTPD_RESTART_NONE, &rtOpts->statisticsTimestamp, rtOpts->statisticsTimestamp,
+ "Timestamp format used when logging timing statistics\n"
+ " (when global:log_statistics is enabled):\n"
+ " datetime - formatttted date and time: YYYY-MM-DD hh:mm:ss.uuuuuu\n"
+ " unix - Unix timestamp with nanoseconds: s.ns\n"
+ " both - Formatted date and time, followed by unix timestamp\n"
+ " (adds one extra field to the log)\n",
+ "datetime", TIMESTAMP_DATETIME,
+ "unix", TIMESTAMP_UNIX,
+ "both", TIMESTAMP_BOTH, NULL
+ );
+
+ /* If statistics file is enabled but logStatistics isn't, disable logging to file */
+ CONFIG_KEY_CONDITIONAL_TRIGGER(rtOpts->statisticsLog.logEnabled && !rtOpts->logStatistics,
+ rtOpts->statisticsLog.logEnabled, FALSE, rtOpts->statisticsLog.logEnabled);
+
+#if (defined(linux) && defined(HAVE_SCHED_H)) || defined(HAVE_SYS_CPUSET_H) || defined (__QNXNTO__)
+ parseResult &= configMapInt(opCode, opArg, dict, target, "global:cpuaffinity_cpucore", PTPD_CHANGE_CPUAFFINITY, INTTYPE_INT, &rtOpts->cpuNumber, rtOpts->cpuNumber,
+ "Bind "PTPD_PROGNAME" process to a selected CPU core number.\n"
+ " 0 = first CPU core, etc. -1 = do not bind to a single core.", RANGECHECK_RANGE,
+ -1,255);
+#endif /* (linux && HAVE_SCHED_H) || HAVE_SYS_CPUSET_H */
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "global:statistics_update_interval",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->statsUpdateInterval,
+ rtOpts->statsUpdateInterval,
+ "Clock synchronisation statistics update interval in seconds\n", RANGECHECK_RANGE,1, 60);
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "global:periodic_updates",
+ PTPD_RESTART_LOGGING, &rtOpts->periodicUpdates, rtOpts->periodicUpdates,
+ "Log a status update every time statistics are updated (global:statistics_update_interval).\n"
+ " The updates are logged even when ptpd is configured without statistics support");
+
+#ifdef PTPD_STATISTICS
+
+ CONFIG_CONDITIONAL_ASSERTION( rtOpts->servoStabilityDetection && (
+ (rtOpts->statsUpdateInterval * rtOpts->servoStabilityPeriod) / 60 >=
+ rtOpts->servoStabilityTimeout),
+ "The configured servo stabilisation timeout has to be longer than\n"
+ " servo stabilisation period");
+
+#endif /* PTPD_STATISTICS */
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "global:timingdomain_election_delay",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->electionDelay, rtOpts->electionDelay,
+ " Delay (seconds) before releasing a time service (NTP or PTP)"
+ " and electing a new one to control a clock. 0 = elect immediately\n", RANGECHECK_RANGE,0, 3600);
+
+
+/* ===== ntpengine section ===== */
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ntpengine:enabled",
+ PTPD_RESTART_NTPENGINE, &rtOpts->ntpOptions.enableEngine, rtOpts->ntpOptions.enableEngine,
+ "Enable NTPd integration");
+
+ parseResult &= configMapBoolean(opCode, opArg, dict, target, "ntpengine:control_enabled",
+ PTPD_RESTART_NONE, &rtOpts->ntpOptions.enableControl, rtOpts->ntpOptions.enableControl,
+ "Enable control over local NTPd daemon");
+
+ CONFIG_KEY_DEPENDENCY("ntpengine:control_enabled", "ntpengine:enabled");
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ntpengine:check_interval",
+ PTPD_RESTART_NTPCONFIG, INTTYPE_INT, &rtOpts->ntpOptions.checkInterval,
+ rtOpts->ntpOptions.checkInterval,
+ "NTP control check interval in seconds\n", RANGECHECK_RANGE, 5, 600);
+
+ parseResult &= configMapInt(opCode, opArg, dict, target, "ntpengine:key_id",
+ PTPD_RESTART_NONE, INTTYPE_INT, &rtOpts->ntpOptions.keyId, rtOpts->ntpOptions.keyId,
+ "NTP key number - must be configured as a trusted control key in ntp.conf,\n"
+ " and be non-zero for the ntpengine:control_enabled setting to take effect.\n", RANGECHECK_RANGE,0, 65535);
+
+ parseResult &= configMapString(opCode, opArg, dict, target, "ntpengine:key",
+ PTPD_RESTART_NONE, rtOpts->ntpOptions.key, sizeof(rtOpts->ntpOptions.key), rtOpts->ntpOptions.key,
+ "NTP key (plain text, max. 20 characters) - must match the key configured in\n"
+ " ntpd's keys file, and must be non-zero for the ntpengine:control_enabled\n"
+ " setting to take effect.\n");
+
+ CONFIG_KEY_DEPENDENCY("ntpengine:control:enabled", "ntpengine:key_id");
+ CONFIG_KEY_DEPENDENCY("ntpengine:control:enabled", "ntpengine:key");
+
+
+/* ============== END CONFIG MAPPINGS, TRIGGERS AND DEPENDENCIES =========== */
+
+/* ==== Any additional logic should go here ===== */
+
+ rtOpts->ifaceName = rtOpts->primaryIfaceName;
+
+ /* Check timing packet ACLs */
+ if(rtOpts->timingAclEnabled) {
+
+ int pResult, dResult;
+
+ if((pResult = maskParser(rtOpts->timingAclPermitText, NULL)) == -1)
+ ERROR("Error while parsing timing permit access list: \"%s\"\n",
+ rtOpts->timingAclPermitText);
+ if((dResult = maskParser(rtOpts->timingAclDenyText, NULL)) == -1)
+ ERROR("Error while parsing timing deny access list: \"%s\"\n",
+ rtOpts->timingAclDenyText);
+
+ /* -1 = ACL format error*/
+ if(pResult == -1 || dResult == -1) {
+ parseResult = FALSE;
+ rtOpts->timingAclEnabled = FALSE;
+ }
+ /* 0 = no entries - we simply don't match */
+ if(pResult == 0 && dResult == 0) {
+ rtOpts->timingAclEnabled = FALSE;
+ }
+ }
+
+
+ /* Check management message ACLs */
+ if(rtOpts->managementAclEnabled) {
+
+ int pResult, dResult;
+
+ if((pResult = maskParser(rtOpts->managementAclPermitText, NULL)) == -1)
+ ERROR("Error while parsing management permit access list: \"%s\"\n",
+ rtOpts->managementAclPermitText);
+ if((dResult = maskParser(rtOpts->managementAclDenyText, NULL)) == -1)
+ ERROR("Error while parsing management deny access list: \"%s\"\n",
+ rtOpts->managementAclDenyText);
+
+ /* -1 = ACL format error*/
+ if(pResult == -1 || dResult == -1) {
+ parseResult = FALSE;
+ rtOpts->managementAclEnabled = FALSE;
+ }
+ /* 0 = no entries - we simply don't match */
+ if(pResult == 0 && dResult == 0) {
+ rtOpts->managementAclEnabled = FALSE;
+ }
+ }
+
+ /* Scale the maxPPM to PPB */
+ rtOpts->servoMaxPpb *= 1000;
+
+ /* Shift DSCP to accept the 6-bit value */
+ rtOpts->dscpValue = rtOpts->dscpValue << 2;
+
+ /*
+ * We're in hybrid mode and we haven't specified the delay request interval:
+ * use override with a default value
+ */
+ if((rtOpts->ipMode == IPMODE_HYBRID) &&
+ !CONFIG_ISSET("ptpengine:log_delayreq_interval"))
+ rtOpts->ignore_delayreq_interval_master=TRUE;
+
+ /*
+ * We're in unicast slave-capable mode and we haven't specified the delay request interval:
+ * use override with a default value
+ */
+ if((rtOpts->ipMode == IPMODE_UNICAST &&
+ rtOpts->clockQuality.clockClass > 127) &&
+ !CONFIG_ISSET("ptpengine:log_delayreq_interval"))
+ rtOpts->ignore_delayreq_interval_master=TRUE;
+
+ /*
+ * construct the lock file name based on operation mode:
+ * if clock class is <128 (master only), use "master" and interface name
+ * if clock class is >127 (can be slave), use clock driver and interface name
+ */
+ if(rtOpts->autoLockFile) {
+
+ memset(rtOpts->lockFile, 0, PATH_MAX);
+ snprintf(rtOpts->lockFile, PATH_MAX,
+ "%s/"PTPD_PROGNAME"_%s_%s.lock",
+ rtOpts->lockDirectory,
+ (rtOpts->clockQuality.clockClass<128 && !rtOpts->slaveOnly) ? "master" : DEFAULT_CLOCKDRIVER,
+ rtOpts->ifaceName);
+ DBG("Automatic lock file name is: %s\n", rtOpts->lockFile);
+ /*
+ * Otherwise use default lock file name, with the specified lock directory
+ * which will be set do default from constants_dep.h if not configured
+ */
+ } else {
+ if(!CONFIG_ISSET("global:lock_file"))
+ snprintf(rtOpts->lockFile, PATH_MAX,
+ "%s/%s", rtOpts->lockDirectory, DEFAULT_LOCKFILE_NAME);
+ }
+
+/* ==== END additional logic */
+
+ if(opCode & CFGOP_PARSE) {
+
+ findUnknownSettings(opCode, dict, target);
+
+ if (parseResult)
+ INFO("Configuration OK\n");
+ else
+ ERROR("There are errors in the configuration - see previous messages\n");
+ }
+
+ if ((opCode & CFGOP_PARSE || opCode & CFGOP_PARSE_QUIET) && parseResult) {
+ return target;
+ }
+
+ dictionary_del(&target);
+ return NULL;
+
+
+}
+
+/**
+ * Wrapper around iniparser_load, which loads the ini file
+ * contents into an existing dictionary - iniparser_load
+ * creates a new dictionary
+ */
+Boolean
+loadConfigFile(dictionary **target, RunTimeOpts *rtOpts)
+{
+
+ dictionary *dict;
+
+ if ( (dict = iniparser_load(rtOpts->configFile)) == NULL) {
+ ERROR("Could not load configuration file: %s\n", rtOpts->configFile);
+ return FALSE;
+ }
+
+ dictionary_merge( dict, *target, 0, 0, NULL);
+ dictionary_del(&dict);
+
+ return TRUE;
+}
+
+
+/* -
+ * this function scans argv looking for what looks like correctly formatted
+ * long options in the form "--section:key=value", "-section:key=value",
+ * "--section:key value" and "-section:key value".
+ * it then clears them from argv so that getopt can deal with the rest.
+ * This is in a way better than getopt_long() because we don't need to
+ * pre-define all possible options. Unknown or malformed ones are ignored.
+ */
+void
+loadCommandLineKeys(dictionary* dict, int argc,char** argv)
+{
+
+ int i;
+ char key[PATH_MAX],val[PATH_MAX];
+
+ for ( i=0; i<argc; i++) {
+
+ if( strlen(argv[i]) > 3 &&
+ index(argv[i],':') != NULL ) {
+ /* check if the option is passed as sec:key=value */
+ if (sscanf(argv[i],"--%[-_a-zA-Z0-9:]=%s",key,val)==2 ||
+ sscanf(argv[i],"-%[-_a-zA-Z0-9:]=%s",key,val)==2) {
+ /* wipe the used argv entry so that getopt doesn't get confused */
+ argv[i]="";
+ } else
+ /*
+ * there are no optional arguments for key:sec options - if there's no =,
+ * the next argument is the value.
+ */
+ if (sscanf(argv[i],"--%[-_a-zA-Z0-9:]",key)==1 ||
+ sscanf(argv[i],"-%[-_a-zA-Z0-9:]",key)==1 ) {
+ argv[i]="";
+ memset(val, 0, PATH_MAX);
+ if (i+1 < argc) {
+ if( (argv[i+1][0]!='-') &&
+ ( (strlen(argv[i+i]) > 1) && argv[i+1][1] !='-' )) {
+ strncpy(val,argv[i+1],PATH_MAX);
+ argv[i+1]="";
+ }
+
+ }
+
+ }
+ /*
+ * there is no argument --sec:key=val
+ */
+ else {
+ val[0] = '\0';
+ key[0] = '\0';
+ }
+
+ if(strlen(key) > 0) {
+ DBGV("setting loaded from command line: %s = %s\n",key,val);
+ dictionary_set(dict, key, val);
+ }
+
+ }
+
+ }
+}
+
+/**
+ * Create a dummy rtOpts with defaults, create a dummy dictionary,
+ * Set the "secret" key in the dictionary, causing parseConfig
+ * to switch from parse mode to print default mode
+ */
+void
+printDefaultConfig()
+{
+
+ RunTimeOpts rtOpts;
+ dictionary *dict;
+
+ loadDefaultSettings(&rtOpts);
+ dict = dictionary_new(0);
+
+ printf( "; ========================================\n");
+ printf( "; "USER_DESCRIPTION" version "USER_VERSION" default configuration\n");
+ printf( "; ========================================\n\n");
+ printf( "; NOTE: the following settings are affected by ptpengine:preset selection:\n"
+ "; ptpengine:slave_only\n"
+ "; clock:no_adjust\n"
+ "; ptpengine:clock_class - allowed range and default value\n"
+ "; To see all preset settings, run "PTPD_PROGNAME" -H (--long-help)\n");
+
+ /* NULL will always be returned in this mode */
+ parseConfig(CFGOP_PRINT_DEFAULT | CFGOP_PARSE_QUIET, NULL, dict, &rtOpts);
+ dictionary_del(&dict);
+
+ printf("\n; ========= newline required in the end ==========\n\n");
+
+}
+
+
+
+/**
+ * Create a dummy rtOpts with defaults, create a dummy dictionary,
+ * Set the "secret" key in the dictionary, causing parseConfig
+ * to switch from parse mode to help mode.
+ */
+void
+printConfigHelp()
+{
+
+ RunTimeOpts rtOpts;
+ dictionary *dict;
+
+ loadDefaultSettings(&rtOpts);
+ dict = dictionary_new(0);
+
+ printf("\n============== Full list of "PTPD_PROGNAME" settings ========\n\n");
+
+ /* NULL will always be returned in this mode */
+ parseConfig(CFGOP_HELP_FULL | CFGOP_PARSE_QUIET, NULL, dict, &rtOpts);
+
+ dictionary_del(&dict);
+
+}
+
+/**
+ * Create a dummy rtOpts with defaults, create a dummy dictionary,
+ * Set the "secret" key in the dictionary, causing parseConfig
+ * to switch from parse mode to help mode, for a selected key only.
+ */
+void
+printSettingHelp(char* key)
+{
+
+ RunTimeOpts rtOpts;
+ dictionary *dict;
+ char* origKey = strdup(key);
+
+ loadDefaultSettings(&rtOpts);
+ dict = dictionary_new(0);
+
+
+ printf("\n");
+ /* NULL will always be returned in this mode */
+ parseConfig(CFGOP_HELP_SINGLE | CFGOP_PARSE_QUIET, key, dict, &rtOpts);
+
+ /* if the setting has been found (and help printed), the first byte will be cleared */
+ if(key[0] != '\0') {
+ printf("Unknown setting: %s\n\n", origKey);
+ }
+ printf("Use -H or --long-help to show help for all settings.\n\n");
+
+ dictionary_del(&dict);
+ free(origKey);
+}
+
+/**
+ * Handle standard options with getopt_long. Returns FALSE if ptpd should not continue.
+ * If a required setting, such as interface name, or a setting
+ * requiring a range check is to be set via getopts_long,
+ * the respective currentConfig dictionary entry should be set,
+ * instead of just setting the rtOpts field.
+ */
+Boolean loadCommandLineOptions(RunTimeOpts* rtOpts, dictionary* dict, int argc, char** argv, Integer16* ret) {
+
+ int c;
+#ifdef HAVE_GETOPT_LONG
+ int opt_index = 0;
+#endif /* HAVE_GETOPT_LONG */
+ *ret = 0;
+
+ /* there's NOTHING wrong with this */
+ if(argc==1) {
+ *ret = 1;
+ goto short_help;
+ }
+
+#ifdef HAVE_GETOPT_LONG
+ /**
+ * A minimal set of long and short CLI options,
+ * for basic operations only. Maintained compatibility
+ * with previous versions. Any other settings
+ * should be given in the config file or using
+ * --section:key options
+ **/
+ static struct option long_options[] = {
+ {"config-file", required_argument, 0, 'c'},
+ {"check-config", optional_argument, 0, 'k'},
+ {"interface", required_argument, 0, 'i'},
+ {"domain", required_argument, 0, 'd'},
+ {"slaveonly", no_argument, 0, 's'},
+ {"masterslave", no_argument, 0, 'm'},
+ {"masteronly", no_argument, 0, 'M'},
+ {"hybrid", no_argument, 0, 'y'},
+ {"e2e", optional_argument, 0, 'E'},
+ {"p2p", optional_argument, 0, 'P'},
+ {"noadjust", optional_argument, 0, 'n'},
+ {"ignore-lock", optional_argument, 0, 'L'},
+ {"auto-lock", optional_argument, 0, 'A'},
+ {"lockfile", optional_argument, 0, 'l'},
+ {"print-lockfile", optional_argument, 0, 'p'},
+ {"lock-directory", required_argument, 0, 'R'},
+ {"log-file", required_argument, 0, 'f'},
+ {"statistics-file", required_argument, 0, 'S'},
+ {"delay-interval", required_argument, 0, 'r'},
+ {"delay-override", no_argument, 0, 'a'},
+ {"debug", no_argument, 0, 'D'},
+ {"version", optional_argument, 0, 'v'},
+ {"foreground", no_argument, 0, 'C'},
+ {"verbose", no_argument, 0, 'V'},
+ {"help", optional_argument, 0, 'h'},
+ {"long-help", no_argument, 0, 'H'},
+ {"explain", required_argument, 0, 'e'},
+ {"default-config", optional_argument, 0, 'O'},
+ {"templates", required_argument, 0, 't'},
+ {"show-templates", no_argument, 0, 'T'},
+ {"unicast", optional_argument, 0, 'U'},
+ {"unicast-negotiation", optional_argument, 0, 'g'},
+ {"unicast-destinations", required_argument, 0, 'u'},
+ {0, 0 , 0, 0}
+ };
+
+ while ((c = getopt_long(argc, argv, "?c:kb:i:d:sgmGMWyUu:nf:S:r:DvCVHTt:he:Y:tOLEPAaR:l:p", long_options, &opt_index)) != -1) {
+#else
+ while ((c = getopt(argc, argv, "?c:kb:i:d:sgmGMWyUu:nf:S:r:DvCVHTt:he:Y:tOLEPAaR:l:p")) != -1) {
+#endif
+ switch(c) {
+/* non-config options first */
+
+ /* getopt error or an actual ? */
+ case '?':
+ printf("Run "PTPD_PROGNAME" with -h to see available command-line options.\n");
+ printf("Run "PTPD_PROGNAME" with -H or --long-help to show detailed help for all settings.\n\n");
+ *ret = 1;
+ return FALSE;
+ case 'h':
+short_help:
+ printf("\n");
+ printShortHelp();
+ return FALSE;
+ case 'H':
+ printLongHelp();
+ return FALSE;
+ case 'e':
+ if(strlen(optarg) > 0)
+ printSettingHelp(optarg);
+ return FALSE;
+ case 'O':
+ printDefaultConfig();
+ return FALSE;
+ case 'T':
+ dumpConfigTemplates();
+ return FALSE;
+/* regular ptpd options */
+
+ /* config file path */
+ case 'c':
+ strncpy(rtOpts->configFile, optarg, PATH_MAX);
+ break;
+ /* check configuration and exit */
+ case 'k':
+ rtOpts->checkConfigOnly = TRUE;
+ break;
+ /* interface */
+ case 'b':
+ WARN_DEPRECATED('b', 'i', "interface", "ptpengine:interface");
+ case 'i':
+ /* if we got a number here, we've been given the domain number */
+ if( (c=='i') && strlen(optarg) > 0 && isdigit((unsigned char)optarg[0]) ) {
+ WARN_DEPRECATED_COMMENT('i', 'd', "domain", "ptpengine:domain",
+ "for specifying domain number ");
+ dictionary_set(dict,"ptpengine:domain", optarg);
+ } else
+ dictionary_set(dict,"ptpengine:interface", optarg);
+ break;
+ /* domain */
+ case 'd':
+ dictionary_set(dict,"ptpengine:domain", optarg);
+ break;
+ case 's':
+ dictionary_set(dict,"ptpengine:preset", "slaveonly");
+ break;
+ /* master/slave */
+ case 'W':
+ WARN_DEPRECATED('W', 'm', "masterslave", "ptpengine:preset=masterslave");
+ case 'm':
+ dictionary_set(dict,"ptpengine:preset", "masterslave");
+ break;
+ /* master only */
+ case 'G':
+ WARN_DEPRECATED('G', 'M', "masteronly", "ptpengine:preset=masteronly");
+ case 'M':
+ dictionary_set(dict,"ptpengine:preset", "masteronly");
+ break;
+ case 'y':
+ dictionary_set(dict,"ptpengine:ip_mode", "hybrid");
+ break;
+ /* unicast */
+ case 'U':
+ dictionary_set(dict,"ptpengine:ip_mode", "unicast");
+ break;
+ case 'g':
+ dictionary_set(dict,"ptpengine:unicast_negotiation", "y");
+ break;
+ case 'u':
+ dictionary_set(dict,"ptpengine:ip_mode", "unicast");
+ dictionary_set(dict,"ptpengine:unicast_destinations", optarg);
+ break;
+ case 'n':
+ dictionary_set(dict,"clock:no_adjust", "Y");
+ break;
+ /* log file */
+ case 'f':
+ dictionary_set(dict,"global:log_file", optarg);
+ break;
+ /* statistics file */
+ case 'S':
+ dictionary_set(dict,"global:statistics_file", optarg);
+ break;
+ case 't':
+ dictionary_set(dict,"global:config_templates", optarg);
+ /* Override delay request interval from master */
+ case 'a':
+ dictionary_set(dict,"ptpengine:log_delayreq_override", "Y");
+ break;
+ /* Delay request interval - needed for hybrid mode */
+ case 'Y':
+ WARN_DEPRECATED('Y', 'r', "delay-interval", "ptpengine:log_delayreq_interval");
+ case 'r':
+ dictionary_set(dict,"ptpengine:log_delayreq_interval", optarg);
+ break;
+ case 'D':
+#ifdef RUNTIME_DEBUG
+ (rtOpts->debug_level)++;
+ if(rtOpts->debug_level > LOG_DEBUGV ){
+ rtOpts->debug_level = LOG_DEBUGV;
+ }
+#else
+ printf("Runtime debug not enabled. Please compile with RUNTIME_DEBUG\n");
+#endif
+ break;
+ /* print version string */
+ case 'v':
+ printf(PTPD_PROGNAME" version "USER_VERSION
+#ifdef CODE_REVISION
+ CODE_REVISION
+ " built on "BUILD_DATE
+#endif
+ "\n");
+
+ return FALSE;
+ /* run in foreground */
+ case 'C':
+ rtOpts->nonDaemon=1;
+ dictionary_set(dict,"global:foreground", "Y");
+ break;
+ /* verbose mode */
+ case 'V':
+ rtOpts->nonDaemon=1;
+ dictionary_set(dict,"global:foreground", "Y");
+ dictionary_set(dict,"global:verbose_foreground", "Y");
+ break;
+ /* Skip locking */
+ case 'L':
+ dictionary_set(dict,"global:ignore_lock", "Y");
+ break;
+ /* End to end delay detection mode */
+ case 'E':
+ dictionary_set(dict,"ptpengine:delay_mechanism", "E2E");
+ break;
+ /* Peer to peer delay detection mode */
+ case 'P':
+ dictionary_set(dict,"ptpengine:delay_mechanism", "P2P");
+ break;
+ /* Auto-lock */
+ case 'A':
+ dictionary_set(dict,"global:auto_lock", "Y");
+ break;
+ /* Print lock file only */
+ case 'p':
+ rtOpts->printLockFile = TRUE;
+ break;
+ /* Lock file */
+ case 'l':
+ dictionary_set(dict,"global:lock_file", optarg);
+ break;
+ /* Lock directory */
+ case 'R':
+ dictionary_set(dict,"global:lock_directory", optarg);
+ break;
+ default:
+ break;
+
+ }
+ }
+
+return TRUE;
+
+}
+
+/* Display informatin about the built-in presets */
+void
+printPresetHelp()
+{
+
+ int i = 0;
+ PtpEnginePreset preset;
+ RunTimeOpts defaultOpts;
+
+ loadDefaultSettings(&defaultOpts);
+
+ printf("\n==================AVAILABLE PTP ENGINE PRESETS===============\n\n");
+
+ for(i = PTP_PRESET_NONE; i < PTP_PRESET_MAX; i++) {
+
+ preset = getPtpPreset(i, &defaultOpts);
+ printf(" preset name: %s\n", preset.presetName);
+ printf(" slave only: %s\n", preset.slaveOnly ? "TRUE" : "FALSE");
+ printf("allow clock control: %s\n", preset.noAdjust ? "FALSE" : "TRUE");
+ printf("default clock class: %d%s\n", preset.clockClass.defaultValue,
+ preset.clockClass.minValue == preset.clockClass.maxValue ?
+ " (only allowed value)" : "");
+ if (preset.clockClass.minValue != preset.clockClass.maxValue) {
+
+ printf(" clock class range: %d..%d\n",
+ preset.clockClass.minValue,
+ preset.clockClass.maxValue);
+ }
+ printf("\n");
+
+ }
+
+ printf("\n=============================================================\n");
+
+}
+
+/* print "short" help - standard parameters only */
+void
+printShortHelp()
+{
+ printf(
+ USER_DESCRIPTION" "USER_VERSION"\n"
+ "\n"
+ "usage: "PTPD_PROGNAME" <options> < --section:key=value...>\n"
+ "\n"
+ "WARNING: Any command-line options take priority over options from config file!\n"
+#ifndef HAVE_GETOPT_LONG
+ "WARNING: *** long options below (--some-option) are not supported on this system! \n"
+ "NOTE: *** config file style options (--section:key=value) are still supported\n"
+#endif
+ "\n"
+ "Basic options: \n"
+ "\n"
+ "-c --config-file [path] Configuration file\n"
+ "-k --check-config Check configuration and exit\n"
+ "-v --version Print version string\n"
+ "-h --help Show this help screen\n"
+ "-H --long-help Show detailed help for all settings and behaviours\n"
+ "-e --explain [section:key] Show help for a single setting\n"
+ "-O --default-config Output default configuration (usable as config file)\n"
+ "-L --ignore-lock Skip lock file checks and locking\n"
+ "-A --auto-lock Use preset / port mode specific lock files\n"
+ "-l --lockfile [path] Specify path to lock file\n"
+ "-p --print-lockfile Print path to lock file and exit (useful for init scripts)\n"
+ "-R --lock-directory [path] Directory to store lock files\n"
+ "-f --log-file [path] global:log_file=[path] Log file\n"
+ "-S --statistics-file [path] global:statistics_file=[path] Statistics file\n"
+ "-T --show-templates display available configuration templates\n"
+ "-t --templates [name],[name],[...]\n"
+ " apply configuration template(s) - see man(5) ptpd2.conf\n"
+ "\n"
+ "Basic PTP protocol and daemon configuration options: \n"
+ "\n"
+ "Command-line option Config key(s) Description\n"
+ "------------------------------------------------------------------------------------\n"
+
+ "-i --interface [dev] ptpengine:interface=<dev> Interface to use (required)\n"
+ "-d --domain [n] ptpengine:domain=<n> PTP domain number\n"
+ "-s --slaveonly ptpengine:preset=slaveonly Slave only mode\n"
+ "-m --masterslave ptpengine:preset=masterslave Master, slave when not best GM\n"
+ "-M --masteronly ptpengine:preset=masteronly Master, passive when not best GM\n"
+ "-y --hybrid ptpengine:ip_mode=hybrid Hybrid mode (multicast for sync\n"
+ " and announce, unicast for delay\n"
+ " request and response)\n"
+ "-U --unicast ptpengine:ip_mode=unicast Unicast mode\n"
+ "-g --unicast-negotiation ptpengine:unicast_negotiation=y Enable unicast negotiation (signaling)\n"
+ "-u --unicast-destinations ptpengine:ip_mode=unicast Unicast destination list\n"
+ " [ip/host, ...] ptpengine:unicast_destinations=<ip/host, ...>\n\n"
+ "-E --e2e ptpengine:delay_mechanism=E2E End to end delay detection\n"
+ "-P --p2p ptpengine:delay_mechanism=P2P Peer to peer delay detection\n"
+ "\n"
+ "-a --delay-override ptpengine:log_delayreq_override Override delay request interval\n"
+ " announced by master\n"
+ "-r --delay-interval [n] ptpengine:log_delayreq_interval=<n> Delay request interval\n"
+ " (log 2)\n"
+ "\n"
+ "-n --noadjust clock:no_adjust Do not adjust the clock\n"
+ "-D<DD...> --debug global:debug_level=<level> Debug level\n"
+ "-C --foreground global:foreground=<Y/N> Don't run in background\n"
+ "-V --verbose global:verbose_foreground=<Y/N> Run in foreground, log all\n"
+ " messages to standard output\n"
+ "\n"
+ "------------------------------------------------------------------------------------\n"
+ "\n"
+ PTPD_PROGNAME" accepts a configuration file (.ini style) where settings are either\n"
+ "grouped into sections:\n\n"
+ "[section]\n"
+ "; comment\n"
+ "key = value\n\n"
+ "or are specified as:\n\n"
+ "section:key = value\n\n"
+ "All settings can also be supplied as command-line parameters (after other options):\n"
+ "\n"
+ "--section:key=<value> --section:key <value> -section:key=<value> -section:key <value>\n"
+ "\n"
+ "To see the full help for all configurable options, run: "PTPD_PROGNAME" with -H or --long-help\n"
+ "To see help for a single setting only, run: "PTPD_PROGNAME" with -e (--explain) [section:key]\n"
+ "To see all default settings with descriptions, run: "PTPD_PROGNAME" with -O (--default-config)\n"
+ "\n"
+ "------------------------------------------------------------------------------------\n"
+ "\n"
+ USER_DESCRIPTION" compatibility options for migration from versions below 2.3.0:\n"
+ "\n"
+ "-b [dev] Network interface to use\n"
+ "-i [n] PTP domain number\n"
+ "-G 'Master mode with NTP' (ptpengine:preset=masteronly)\n"
+ "-W 'Master mode without NTP' (ptpengine:preset=masterslave) \n"
+ "-Y [n] Delay request interval (log 2)\n"
+ "-t Do not adjust the clock\n"
+ "\n"
+ "Note 1: the above parameters are deprecated and their use will issue a warning.\n"
+ "Note 2: -U and -g options have been re-purposed in 2.3.1.\n"
+ "\n\n"
+ );
+}
+
+/* Print the full help */
+void
+printLongHelp()
+{
+
+ printShortHelp();
+
+ printConfigHelp();
+
+ printPresetHelp();
+
+ printf("\n"
+ " Configuration templates available (see man(5) ptpd2.conf):\n"
+ "\n usage:\n"
+ " -t [name],[name],...\n"
+ " --templates [name],[name],...\n"
+ " --global:config_templates=[name],[name],...\n\n");
+
+ dumpConfigTemplates();
+
+ printf("========================\n");
+
+
+ printf("\n"
+ "Possible internal states:\n"
+ " init: INITIALIZING\n"
+ " flt: FAULTY\n"
+ " lstn_init: LISTENING (first time)\n"
+ " lstn_reset: LISTENING (non first time)\n"
+ " pass: PASSIVE Master (not best in BMC, not announcing)\n"
+ " uncl: UNCALIBRATED\n"
+ " slv: SLAVE\n"
+ " pmst: PRE Master\n"
+ " mst: ACTIVE Master\n"
+ " dsbl: DISABLED\n"
+ " ?: UNKNOWN state\n"
+ "\n"
+
+ "Handled signals:\n"
+ " SIGHUP Reload configuration file and close / re-open log files\n"
+ " SIGUSR1 Manually step clock to current OFM value\n"
+ " (overides clock:no_reset, but honors clock:no_adjust)\n"
+ " SIGUSR2 Dump all PTP protocol counters to current log target\n"
+ " (and clear if ptpengine:sigusr2_clears_counters set)\n"
+ "\n"
+ " SIGINT|SIGTERM Close open files, remove lock file and exit cleanly\n"
+ " SIGKILL Force an unclean exit\n"
+ "\n"
+ "BMC Algorithm defaults:\n"
+ " Software: P1(128) > Class(13|248) > Accuracy(\"unk\"/0xFE) > Variance(65536) > P2(128)\n"
+ );
+
+}
+
+
+/*
+ * Iterate through every key in newConfig and compare to oldConfig,
+ * return TRUE if both equal, otherwise FALSE;
+ */
+Boolean
+compareConfig(dictionary* newConfig, dictionary* oldConfig)
+{
+
+ int i = 0;
+
+ if( newConfig == NULL || oldConfig == NULL) return -1;
+
+ for(i = 0; i < newConfig->n; i++) {
+
+ if(newConfig->key[i] == NULL)
+ continue;
+
+ if(strcmp(newConfig->val[i], dictionary_get(oldConfig, newConfig->key[i],"")) != 0 ) {
+ DBG("Setting %s changed from '%s' to '%s'\n", newConfig->key[i],dictionary_get(oldConfig, newConfig->key[i],""),
+ newConfig->val[i]);
+ return FALSE;
+ }
+ }
+
+return TRUE;
+
+}
+
+/* Compare two configurations and set flags to mark components requiring restart */
+int checkSubsystemRestart(dictionary* newConfig, dictionary* oldConfig, RunTimeOpts *rtOpts)
+{
+
+ int restartFlags = 0;
+ dictionary *tmpDict = dictionary_new(0);
+
+ char *a, *b, *key;
+
+ int i;
+
+ /* both should be post-parse so will have all settings and thus same number of keys */
+ for(i = 0; i < newConfig->n; i++) {
+
+ key = newConfig->key[i];
+
+ if(key == NULL) {
+ continue;
+ }
+
+ a = dictionary_get(oldConfig, key, "");
+ b = dictionary_get(newConfig, key, "");
+
+ if(strcmp(a,b)) {
+ DBG("+ setting %s changed from %s to %s\n", key, a, b);
+ dictionary_set(tmpDict, key, "x");
+ }
+
+ }
+
+ /* run parser in restart check mode */
+ parseConfig(CFGOP_RESTART_FLAGS | CFGOP_PARSE_QUIET, &restartFlags, tmpDict, rtOpts);
+
+ dictionary_del(&tmpDict);
+
+ /* ========= Any additional logic goes here =========== */
+
+ /* Set of possible PTP port states has changed */
+ if(configSettingChanged(oldConfig, newConfig, "ptpengine:slave_only") ||
+ configSettingChanged(oldConfig, newConfig, "ptpengine:clock_class")) {
+
+ int clockClass_old = iniparser_getint(oldConfig,"ptpengine:ptp_clockclass",0);
+ int clockClass_new = iniparser_getint(newConfig,"ptpengine:ptp_clockclass",0);
+
+ /*
+ * We're changing from a mode where slave state is possible
+ * to a master only mode, or vice versa
+ */
+ if ((clockClass_old < 128 && clockClass_new > 127) ||
+ (clockClass_old > 127 && clockClass_new < 128)) {
+
+ /* If we are in auto lockfile mode, trigger a check if other locks match */
+ if( !configSettingChanged(oldConfig, newConfig, "global:auto_lockfile") &&
+ DICT_ISTRUE(oldConfig, "global:auto_lockfile")) {
+ restartFlags|=PTPD_CHECK_LOCKS;
+ }
+ /* We can potentially be running in a different mode now, restart protocol */
+ restartFlags|=PTPD_RESTART_PROTOCOL;
+ }
+ }
+
+ /* Flag for config options that can be applied on the fly - only to trigger a log message */
+ if(restartFlags == 0)
+ restartFlags = PTPD_RESTART_NONE;
+
+ return restartFlags;
+
+}
diff --git a/rtemsbsd/ptpd/src/dep/daemonconfig.h b/rtemsbsd/ptpd/src/dep/daemonconfig.h
new file mode 100644
index 00000000..9ae2e66a
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/daemonconfig.h
@@ -0,0 +1,89 @@
+/**
+ * @file daemonconfig.h
+ *
+ * @brief definitions related to config file handling.
+ *
+ *
+ */
+
+#ifndef PTPD_DAEMONCONFIG_H_
+#define PTPD_DAEMONCONFIG_H_
+
+#include "iniparser/iniparser.h"
+#include "configdefaults.h"
+
+/* Config reload - component restart status flags */
+
+/* No restart required - can continue with new config */
+#define PTPD_RESTART_NONE 1 << 0
+/* PTP port datasetw can be updated without restarting FSM */
+#define PTPD_UPDATE_DATASETS 1 << 1
+/* PTP FSM port re-initialisation required (PTP_INITIALIZING) */
+#define PTPD_RESTART_PROTOCOL 1 << 2
+/* Network config changed: PTP_INITIALIZING handles this so far */
+#define PTPD_RESTART_NETWORK 1 << 3
+/* Logging config changes: log files need closed / reopened */
+#define PTPD_RESTART_LOGGING 1 << 4
+/* Configuration changes need checking lock files */
+#define PTPD_CHECK_LOCKS 1 << 5
+/* CPU core has changed */
+#define PTPD_CHANGE_CPUAFFINITY 1 << 6
+/* Configuration changes require daemon restart */
+#define PTPD_RESTART_DAEMON 1 << 7
+
+#ifdef PTPD_STATISTICS
+/* Configuration changes require filter restart */
+#define PTPD_RESTART_FILTERS 1 << 8
+#endif
+
+#define PTPD_RESTART_ACLS 1 << 9
+#define PTPD_RESTART_NTPENGINE 1 << 10
+#define PTPD_RESTART_NTPCONFIG 1 << 11
+#define PTPD_RESTART_ALARMS 1 << 12
+
+#define LOG2_HELP "(expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)"
+#define MAX_LINE_SIZE 1024
+
+/* int/double range check flags */
+enum {
+ RANGECHECK_NONE,
+ RANGECHECK_RANGE,
+ RANGECHECK_MIN,
+ RANGECHECK_MAX
+};
+
+/* int type to cast parsed config data to */
+enum {
+ INTTYPE_INT,
+ INTTYPE_I8,
+ INTTYPE_U8,
+ INTTYPE_I16,
+ INTTYPE_U16,
+ INTTYPE_I32,
+ INTTYPE_U32
+};
+
+/* config parser operations */
+
+#define CFGOP_PARSE 1<<0 /* parse all config options, return success/failure */
+#define CFGOP_PARSE_QUIET 1<<1 /* parse config but display no warnings */
+#define CFGOP_PRINT_DEFAULT 1<<2 /* print default settings only */
+#define CFGOP_HELP_FULL 1<<3 /* print help for all settings */
+#define CFGOP_HELP_SINGLE 1<<4 /* print help for one entry */
+#define CFGOP_RESTART_FLAGS 1<<5 /* return subsystems affected by config changes */
+
+Boolean loadConfigFile (dictionary**, RunTimeOpts*);
+void loadCommandLineKeys(dictionary*, int, char**);
+Boolean loadCommandLineOptions(RunTimeOpts*, dictionary*, int, char** , Integer16*);
+dictionary* parseConfig (int, void*, dictionary*, RunTimeOpts*);
+int reloadConfig ( RunTimeOpts*, PtpClock* );
+Boolean compareConfig(dictionary* source, dictionary* target);
+int checkSubsystemRestart(dictionary* newConfig, dictionary* oldConfig, RunTimeOpts *rtOpts);
+void printConfigHelp();
+void printDefaultConfig();
+void printShortHelp();
+void printLongHelp();
+void printSettingHelp(char*);
+void setConfig(dictionary *dict, const char* key, const char *value);
+
+#endif /*PTPD_DAEMONCONFIG_H_*/
diff --git a/rtemsbsd/ptpd/src/dep/datatypes_dep.h b/rtemsbsd/ptpd/src/dep/datatypes_dep.h
new file mode 100644
index 00000000..77a645c4
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/datatypes_dep.h
@@ -0,0 +1,179 @@
+#ifndef DATATYPES_DEP_H_
+#define DATATYPES_DEP_H_
+
+#include "../ptp_primitives.h"
+
+/**
+*\file
+* \brief Implementation specific datatype
+
+ */
+
+/**
+* \brief Struct used to average the offset from master
+*
+* The FIR filtering of the offset from master input is a simple, two-sample average
+ */
+typedef struct {
+ Integer32 nsec_prev, y;
+} offset_from_master_filter;
+
+/**
+* \brief Struct used to average the one way delay
+*
+* It is a variable cutoff/delay low-pass, infinite impulse response (IIR) filter.
+*
+* The one-way delay filter has the difference equation: s*y[n] - (s-1)*y[n-1] = x[n]/2 + x[n-1]/2, where increasing the stiffness (s) lowers the cutoff and increases the delay.
+ */
+typedef struct {
+ Integer32 nsec_prev, y;
+ Integer32 s_exp;
+} one_way_delay_filter;
+
+/**
+* \brief Struct containing interface information and capabilities
+ */
+typedef struct {
+ struct sockaddr afAddress;
+ unsigned char hwAddress[14];
+ Boolean hasHwAddress;
+ Boolean hasAfAddress;
+ int addressFamily;
+ unsigned int flags;
+ int ifIndex;
+} InterfaceInfo;
+
+/**
+* \brief Struct describing network transport data
+ */
+typedef struct {
+ Integer32 eventSock, generalSock;
+ Integer32 multicastAddr, peerMulticastAddr;
+
+ /* Interface address and capability descriptor */
+ InterfaceInfo interfaceInfo;
+
+ /* used by IGMP refresh */
+ struct in_addr interfaceAddr;
+ /* Typically MAC address - outer 6 octers of ClockIdendity */
+ Octet interfaceID[ETHER_ADDR_LEN];
+ /* source address of last received packet - used for unicast replies to Delay Requests */
+ Integer32 lastSourceAddr;
+ /* destination address of last received packet - used for unicast FollowUp for multiple slaves*/
+ Integer32 lastDestAddr;
+
+ uint64_t sentPackets;
+ uint64_t receivedPackets;
+
+ uint64_t sentPacketsTotal;
+ uint64_t receivedPacketsTotal;
+
+#ifdef PTPD_PCAP
+ pcap_t *pcapEvent;
+ pcap_t *pcapGeneral;
+ Integer32 pcapEventSock;
+ Integer32 pcapGeneralSock;
+#endif
+ Integer32 headerOffset;
+
+ /* used for tracking the last TTL set */
+ int ttlGeneral;
+ int ttlEvent;
+ Boolean joinedPeer;
+ Boolean joinedGeneral;
+ struct ether_addr etherDest;
+ struct ether_addr peerEtherDest;
+ Boolean txTimestampFailure;
+
+ Ipv4AccessList* timingAcl;
+ Ipv4AccessList* managementAcl;
+
+} NetPath;
+
+typedef struct {
+
+ char* logID;
+ char* openMode;
+ char logPath[PATH_MAX+1];
+ FILE* logFP;
+
+ Boolean logEnabled;
+ Boolean truncateOnReopen;
+ Boolean unlinkOnClose;
+
+ uint32_t lastHash;
+ UInteger32 maxSize;
+ UInteger32 fileSize;
+ int maxFiles;
+
+} LogFileHandler;
+
+
+typedef struct{
+
+ UInteger8 minValue;
+ UInteger8 maxValue;
+ UInteger8 defaultValue;
+
+} UInteger8_option;
+
+typedef struct{
+
+ Integer32 minValue;
+ Integer32 maxValue;
+ Integer32 defaultValue;
+
+} Integer32_option;
+
+typedef struct{
+
+ UInteger32 minValue;
+ UInteger32 maxValue;
+ UInteger32 defaultValue;
+
+} UInteger32_option;
+
+typedef struct{
+
+ Integer16 minValue;
+ Integer16 maxValue;
+ Integer16 defaultValue;
+
+} Integer16_option;
+
+typedef struct{
+
+ UInteger16 minValue;
+ UInteger16 maxValue;
+ UInteger16 defaultValue;
+
+} UInteger16_option;
+
+typedef union { uint32_t *uintval; int32_t *intval; double *doubleval; Boolean *boolval; char *strval; } ConfigPointer;
+typedef union { uint32_t uintval; int32_t intval; double doubleval; Boolean boolval; char *strval; } ConfigSetting;
+
+typedef struct ConfigOption ConfigOption;
+
+struct ConfigOption {
+ char *key;
+ enum { CO_STRING, CO_INT, CO_UINT, CO_DOUBLE, CO_BOOL, CO_SELECT } type;
+ enum { CO_MIN, CO_MAX, CO_RANGE, CO_STRLEN } restriction;
+ ConfigPointer target;
+ ConfigPointer defvalue;
+ ConfigSetting constraint1;
+ ConfigSetting constraint2;
+ int restartFlags;
+ ConfigOption *next;
+};
+
+typedef struct {
+ int currentOffset;
+ int nextOffset;
+ int leapType;
+ Integer32 startTime;
+ Integer32 endTime;
+ Boolean valid;
+ Boolean offsetValid;
+} LeapSecondInfo;
+
+#endif /*DATATYPES_DEP_H_*/
diff --git a/rtemsbsd/ptpd/src/dep/eventtimer.c b/rtemsbsd/ptpd/src/dep/eventtimer.c
new file mode 100644
index 00000000..de98338f
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/eventtimer.c
@@ -0,0 +1,133 @@
+/*-
+ * Copyright (c) 2015 Wojciech Owczarek,
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file timer.c
+ * @date Wed Oct 1 00:41:26 2014
+ *
+ * @brief Common code for the EventTimer object
+ *
+ * Creation and deletion plus maintaining a linked list of
+ * all created instances.
+ */
+
+#include "../ptpd.h"
+
+/* linked list - so that we can control all registered objects centrally */
+static EventTimer *_first = NULL;
+static EventTimer *_last = NULL;
+
+EventTimer
+*createEventTimer(const char* id)
+{
+
+ EventTimer *timer;
+
+ if ( !(timer = calloc (1, sizeof(EventTimer))) ) {
+ return NULL;
+ }
+
+
+ setupEventTimer(timer);
+
+ strncpy(timer->id, id, EVENTTIMER_MAX_DESC);
+
+ /* maintain the linked list */
+
+ if(_first == NULL) {
+ _first = timer;
+ }
+
+ if(_last != NULL) {
+ timer->_prev = _last;
+ timer->_prev->_next = timer;
+ }
+
+ _last = timer;
+
+ timer->_first = _first;
+
+ DBGV("created itimer eventtimer %s\n", timer->id);
+
+ return timer;
+}
+
+void
+freeEventTimer
+(EventTimer **timer)
+{
+ if(timer == NULL) {
+ return;
+ }
+
+ EventTimer *ptimer = *timer;
+
+ if(ptimer == NULL) {
+ return;
+ }
+
+ ptimer->shutdown(ptimer);
+
+ /* maintain the linked list */
+
+ if(ptimer->_prev != NULL) {
+
+ if(ptimer == _last) {
+ _last = ptimer->_prev;
+ }
+
+ if(ptimer->_next != NULL) {
+ ptimer->_prev->_next = ptimer->_next;
+ } else {
+ ptimer->_prev->_next = NULL;
+ }
+ /* last one */
+ } else if (ptimer->_next == NULL) {
+ _first = NULL;
+ }
+
+ if(ptimer->_next != NULL) {
+
+ if(ptimer == _first) {
+ _first = ptimer->_next;
+ }
+
+ if(ptimer->_prev != NULL) {
+ ptimer->_next->_prev = ptimer->_prev;
+ } else {
+ ptimer->_next->_prev = NULL;
+ }
+
+ }
+
+ if(*timer != NULL) {
+ free(*timer);
+ }
+
+ *timer = NULL;
+
+}
diff --git a/rtemsbsd/ptpd/src/dep/eventtimer.h b/rtemsbsd/ptpd/src/dep/eventtimer.h
new file mode 100644
index 00000000..9d41156d
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/eventtimer.h
@@ -0,0 +1,77 @@
+#ifndef EVENTTIMER_H_
+#define EVENTTIMER_H_
+
+#include "../ptpd.h"
+
+/*-
+ * Copyright (c) 2015 Wojciech Owczarek,
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define EVENTTIMER_MAX_DESC 20
+#define EVENTTIMER_MIN_INTERVAL_US 250 /* 4000/sec */
+
+typedef struct EventTimer EventTimer;
+
+struct EventTimer {
+
+ /* data */
+ char id[EVENTTIMER_MAX_DESC + 1];
+ Boolean expired;
+ Boolean running;
+
+ /* "methods" */
+ void (*start) (EventTimer* timer, double interval);
+ void (*stop) (EventTimer* timer);
+ void (*reset) (EventTimer* timer);
+ void (*shutdown) (EventTimer* timer);
+ Boolean (*isExpired) (EventTimer* timer);
+ Boolean (*isRunning) (EventTimer* timer);
+
+ /* implementation data */
+#ifdef PTPD_PTIMERS
+ timer_t timerId;
+#else
+ int32_t itimerInterval;
+ int32_t itimerLeft;
+#endif /* PTPD_PTIMERS */
+
+ /* linked list */
+ EventTimer *_first;
+ EventTimer *_next;
+ EventTimer *_prev;
+
+};
+
+EventTimer *createEventTimer(const char *id);
+void freeEventTimer(EventTimer **timer);
+void setupEventTimer(EventTimer *timer);
+
+void startEventTimers();
+void shutdownEventTimers();
+
+
+#endif /* EVENTTIMER_H_ */
+
diff --git a/rtemsbsd/ptpd/src/dep/eventtimer_itimer.c b/rtemsbsd/ptpd/src/dep/eventtimer_itimer.c
new file mode 100644
index 00000000..2fa0c3ac
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/eventtimer_itimer.c
@@ -0,0 +1,239 @@
+/*-
+ * Copyright (c) 2012-2015 Wojciech Owczarek,
+ * Copyright (c) 2011-2012 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen,
+ * Inaqui Delgado,
+ * Rick Ratzel,
+ * National Instruments.
+ * Copyright (c) 2009-2010 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen
+ *
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file eventtimer_itimer.c
+ * @date Wed Oct 1 00:41:26 2014
+ *
+ * @brief EventTimer implementation using interval timers
+ *
+ * The original interval timer timer implementation.
+ */
+
+#include "../ptpd.h"
+
+#define US_TIMER_INTERVAL (31250)
+
+static volatile unsigned int elapsed;
+
+static void eventTimerStart_itimer(EventTimer *timer, double interval);
+static void eventTimerStop_itimer(EventTimer *timer);
+static void eventTimerReset_itimer(EventTimer *timer);
+static void eventTimerShutdown_itimer(EventTimer *timer);
+static Boolean eventTimerIsRunning_itimer(EventTimer *timer);
+static Boolean eventTimerIsExpired_itimer(EventTimer *timer);
+
+static void itimerUpdate(EventTimer *et);
+static void timerSignalHandler(int sig);
+
+void
+setupEventTimer(EventTimer *timer)
+{
+
+ if(timer == NULL) {
+ return;
+ }
+
+ memset(timer, 0, sizeof(EventTimer));
+
+ timer->start = eventTimerStart_itimer;
+ timer->stop = eventTimerStop_itimer;
+ timer->reset = eventTimerReset_itimer;
+ timer->shutdown = eventTimerShutdown_itimer;
+ timer->isExpired = eventTimerIsExpired_itimer;
+ timer->isRunning = eventTimerIsRunning_itimer;
+}
+
+static void
+eventTimerStart_itimer(EventTimer *timer, double interval)
+{
+
+ timer->expired = FALSE;
+ timer->running = TRUE;
+
+ /*
+ * US_TIMER_INTERVAL defines the minimum interval between sigalarms.
+ * timerStart has a float parameter for the interval, which is casted to integer.
+ * very small amounts are forced to expire ASAP by setting the interval to 1
+ */
+ timer->itimerLeft = (interval * 1E6) / US_TIMER_INTERVAL;
+ if(timer->itimerLeft == 0){
+ /*
+ * the interval is too small, raise it to 1 to make sure it expires ASAP
+ */
+ timer->itimerLeft = 1;
+ }
+
+ timer->itimerInterval = timer->itimerLeft;
+
+ DBG2("timerStart: Set timer %s to %f New interval: %d; new left: %d\n", timer->id, interval, timer->itimerLeft , timer->itimerInterval);
+}
+
+static void
+eventTimerStop_itimer(EventTimer *timer)
+{
+
+ timer->itimerInterval = 0;
+ timer->running = FALSE;
+ DBG2("timerStop: Stopping timer %s\n", timer->id);
+
+}
+
+static void
+itimerUpdate(EventTimer *et)
+{
+
+ EventTimer *timer = NULL;
+
+ if (elapsed <= 0)
+ return;
+
+ /*
+ * if time actually passed, then decrease every timer left
+ * the one(s) that went to zero or negative are:
+ * a) rearmed at the original time (ignoring the time that may have passed ahead)
+ * b) have their expiration latched until timerExpired() is called
+ */
+
+ for(timer = et->_first; timer != NULL; timer = timer->_next) {
+ if ( (timer->itimerInterval > 0) && ((timer->itimerLeft -= elapsed) <= 0)) {
+ timer->itimerLeft = timer->itimerInterval;
+ timer->expired = TRUE;
+ DBG("TimerUpdate: Timer %s has now expired. Re-armed with interval %d\n", timer->id, timer->itimerInterval);
+ }
+ }
+
+ elapsed = 0;
+
+}
+
+
+
+static void
+eventTimerReset_itimer(EventTimer *timer)
+{
+}
+
+static void
+eventTimerShutdown_itimer(EventTimer *timer)
+{
+}
+
+
+static Boolean
+eventTimerIsRunning_itimer(EventTimer *timer)
+{
+
+ itimerUpdate(timer);
+
+ DBG2("timerIsRunning: Timer %s %s running\n", timer->id,
+ timer->running ? "is" : "is not");
+
+ return timer->running;
+}
+
+static Boolean
+eventTimerIsExpired_itimer(EventTimer *timer)
+{
+
+ Boolean ret;
+
+ itimerUpdate(timer);
+
+ ret = timer->expired;
+
+ DBG2("timerIsExpired: Timer %s %s expired\n", timer->id,
+ timer->expired ? "is" : "is not");
+
+ if(ret) {
+ timer->expired = FALSE;
+ }
+
+ return ret;
+
+}
+
+void
+startEventTimers(void)
+{
+ struct itimerval itimer;
+
+ DBG("initTimer\n");
+
+#ifdef __sun
+ sigset(SIGALRM, SIG_IGN);
+#else
+ signal(SIGALRM, SIG_IGN);
+#endif /* __sun */
+
+ elapsed = 0;
+ itimer.it_value.tv_sec = itimer.it_interval.tv_sec = 0;
+ itimer.it_value.tv_usec = itimer.it_interval.tv_usec =
+ US_TIMER_INTERVAL;
+
+#ifdef __sun
+ sigset(SIGALRM, timerSignalHandler);
+#else
+ signal(SIGALRM, timerSignalHandler);
+#endif /* __sun */
+ setitimer(ITIMER_REAL, &itimer, 0);
+}
+
+void
+shutdownEventTimers(void)
+{
+
+#ifdef __sun
+ sigset(SIGALRM, SIG_IGN);
+#else
+ signal(SIGALRM, SIG_IGN);
+#endif /* __sun */
+}
+
+static void
+timerSignalHandler(int sig)
+{
+ elapsed++;
+ /* be sure to NOT call DBG in asynchronous handlers! */
+}
diff --git a/rtemsbsd/ptpd/src/dep/eventtimer_posix.c b/rtemsbsd/ptpd/src/dep/eventtimer_posix.c
new file mode 100644
index 00000000..16164fd7
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/eventtimer_posix.c
@@ -0,0 +1,239 @@
+/*-
+ * Copyright (c) 2015 Wojciech Owczarek,
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file eventtimer_posix.c
+ * @date Wed Oct 1 00:41:26 2014
+ *
+ * @brief EventTimer implementation using POSIX timers
+ *
+ * The less signal intensive timer implementation,
+ * allowing for high(er) message intervals.
+ */
+
+#include "../ptpd.h"
+
+#define TIMER_SIGNAL SIGALRM
+
+/* timers should be based on CLOCK_MONOTONIC,
+ * but if it's not implemented, use CLOCK_REALTIME
+ */
+
+#ifdef _POSIX_MONOTONIC_CLOCK
+#define CLK_TYPE CLOCK_MONOTONIC
+#else
+#define CLK_TYPE CLOCK_REALTIME
+#endif /* CLOCK_MONOTONIC */
+
+static void eventTimerStart_posix(EventTimer *timer, double interval);
+static void eventTimerStop_posix(EventTimer *timer);
+static void eventTimerReset_posix(EventTimer *timer);
+static void eventTimerShutdown_posix(EventTimer *timer);
+static Boolean eventTimerIsRunning_posix(EventTimer *timer);
+static Boolean eventTimerIsExpired_posix(EventTimer *timer);
+static void timerSignalHandler(int sig, siginfo_t *info, void *usercontext);
+
+void
+setupEventTimer(EventTimer *timer)
+{
+
+ struct sigevent sev;
+
+ if(timer == NULL) {
+ return;
+ }
+
+ memset(&sev, 0, sizeof(sev));
+ memset(timer, 0, sizeof(EventTimer));
+
+ timer->start = eventTimerStart_posix;
+ timer->stop = eventTimerStop_posix;
+ timer->reset = eventTimerReset_posix;
+ timer->shutdown = eventTimerShutdown_posix;
+ timer->isExpired = eventTimerIsExpired_posix;
+ timer->isRunning = eventTimerIsRunning_posix;
+
+ sev.sigev_notify = SIGEV_SIGNAL;
+ sev.sigev_signo = TIMER_SIGNAL;
+ sev.sigev_value.sival_ptr = timer;
+
+ if(timer_create(CLK_TYPE, &sev, &timer->timerId) == -1) {
+ PERROR("Could not create posix timer %s", timer->id);
+ } else {
+ DBGV("Created posix timer %s ",timer->id);
+ }
+}
+
+static void
+eventTimerStart_posix(EventTimer *timer, double interval)
+{
+
+ struct timespec ts;
+ struct itimerspec its;
+
+ memset(&its, 0, sizeof(its));
+
+ ts.tv_sec = interval;
+ ts.tv_nsec = (interval - ts.tv_sec) * 1E9;
+
+ if(!ts.tv_sec && ts.tv_nsec < EVENTTIMER_MIN_INTERVAL_US * 1000) {
+ ts.tv_nsec = EVENTTIMER_MIN_INTERVAL_US * 1000;
+ }
+
+ DBGV("Timer %s start requested at %d.%4d sec interval\n", timer->id, ts.tv_sec, ts.tv_nsec);
+
+ its.it_interval = ts;
+ its.it_value = ts;
+
+ if (timer_settime(timer->timerId, 0, &its, NULL) < 0) {
+ PERROR("could not arm posix timer %s", timer->id);
+ return;
+ }
+
+ DBG2("timerStart: Set timer %s to %f\n", timer->id, interval);
+
+ timer->expired = FALSE;
+ timer->running = TRUE;
+
+}
+
+static void
+eventTimerStop_posix(EventTimer *timer)
+{
+
+ struct itimerspec its;
+
+ DBGV("Timer %s stop requested\n", timer->id);
+
+ memset(&its, 0, sizeof(its));
+
+ if (timer_settime(timer->timerId, 0, &its, NULL) < 0) {
+ PERROR("could not stop posix timer %s", timer->id);
+ return;
+ }
+
+ timer->running = FALSE;
+
+ DBG2("timerStop: stopped timer %s\n", timer->id);
+
+}
+
+static void
+eventTimerReset_posix(EventTimer *timer)
+{
+}
+
+static void
+eventTimerShutdown_posix(EventTimer *timer)
+{
+ if(timer_delete(timer->timerId) == -1) {
+ PERROR("Could not delete timer %s!", timer->id);
+ }
+
+}
+
+static Boolean
+eventTimerIsRunning_posix(EventTimer *timer)
+{
+
+ DBG2("timerIsRunning: Timer %s %s running\n", timer->id,
+ timer->running ? "is" : "is not");
+
+ return timer->running;
+}
+
+static Boolean
+eventTimerIsExpired_posix(EventTimer *timer)
+{
+
+ Boolean ret;
+
+ ret = timer->expired;
+
+ DBG2("timerIsExpired: Timer %s %s expired\n", timer->id,
+ timer->expired ? "is" : "is not");
+
+ /* the five monkeys experiment */
+ if(ret) {
+ timer->expired = FALSE;
+ }
+
+ return ret;
+
+}
+
+void
+startEventTimers(void)
+{
+ struct sigaction sa;
+
+ DBG("initTimer\n");
+
+#ifdef __sun
+ sigset(SIGALRM, SIG_IGN);
+#else
+ signal(SIGALRM, SIG_IGN);
+#endif /* __sun */
+
+ sa.sa_flags = SA_SIGINFO;
+ sa.sa_sigaction = timerSignalHandler;
+ sigemptyset(&sa.sa_mask);
+ if(sigaction(TIMER_SIGNAL, &sa, NULL) == -1) {
+ PERROR("Could not initialise timer handler");
+ }
+
+}
+
+void
+shutdownEventTimers(void)
+{
+
+#ifdef __sun
+ sigset(SIGALRM, SIG_IGN);
+#else
+ signal(SIGALRM, SIG_IGN);
+#endif /* __sun */
+}
+
+/* this only ever gets called when a timer expires */
+static void
+timerSignalHandler(int sig, siginfo_t *info, void *usercontext)
+{
+
+ /* retrieve the user data structure */
+ EventTimer * timer = (EventTimer*)info->si_value.sival_ptr;
+
+ /* Ignore if the signal wasn't sent by a timer */
+ if(info->si_code != SI_TIMER)
+ return;
+
+ /* Hopkirk (deceased) */
+ if(timer != NULL) {
+ timer->expired = TRUE;
+ }
+
+}
diff --git a/rtemsbsd/ptpd/src/dep/iniparser/AUTHORS b/rtemsbsd/ptpd/src/dep/iniparser/AUTHORS
new file mode 100644
index 00000000..f847f923
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/iniparser/AUTHORS
@@ -0,0 +1,7 @@
+Author: Nicolas Devillard <ndevilla at free.fr>
+
+This tiny library has received countless contributions and I have
+not kept track of all the people who contributed. Let them be thanked
+for their ideas, code, suggestions, corrections, enhancements!
+
+Further enhancements by Wojciech Owczarek for the PTPd project
diff --git a/rtemsbsd/ptpd/src/dep/iniparser/LICENSE b/rtemsbsd/ptpd/src/dep/iniparser/LICENSE
new file mode 100644
index 00000000..5a3a80ba
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/iniparser/LICENSE
@@ -0,0 +1,21 @@
+Copyright (c) 2000-2011 by Nicolas Devillard.
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
diff --git a/rtemsbsd/ptpd/src/dep/iniparser/README b/rtemsbsd/ptpd/src/dep/iniparser/README
new file mode 100644
index 00000000..bc697876
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/iniparser/README
@@ -0,0 +1,12 @@
+
+Welcome to iniParser -- version 3.1
+released 08 Apr 2012
+
+This modules offers parsing of ini files from the C level.
+See a complete documentation in HTML format, from this directory
+open the file html/index.html with any HTML-capable browser.
+
+Enjoy!
+
+N.Devillard
+Sun Apr 8 16:38:09 CEST 2012
diff --git a/rtemsbsd/ptpd/src/dep/iniparser/dictionary.c b/rtemsbsd/ptpd/src/dep/iniparser/dictionary.c
new file mode 100644
index 00000000..ffe35791
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/iniparser/dictionary.c
@@ -0,0 +1,520 @@
+/*-------------------------------------------------------------------------*/
+/**
+ @file dictionary.c
+ @author N. Devillard
+ @brief Implements a dictionary for string variables.
+
+ This module implements a simple dictionary object, i.e. a list
+ of string/string associations. This object is useful to store e.g.
+ informations retrieved from a configuration file (ini files).
+*/
+/*--------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+ Includes
+ ---------------------------------------------------------------------------*/
+#include "dictionary.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/** Maximum value size for integers and doubles. */
+#define MAXVALSZ 1024
+
+/** Minimal allocated number of entries in a dictionary */
+#define DICTMINSZ 64
+
+/** Invalid key token */
+#define DICT_INVALID_KEY ((char*)-1)
+
+/*---------------------------------------------------------------------------
+ Private functions
+ ---------------------------------------------------------------------------*/
+
+/* Doubles the allocated size associated to a pointer */
+/* 'size' is the current allocated size. */
+static void * mem_double(void * ptr, int size)
+{
+ void * newptr ;
+
+ newptr = calloc(2*size, 1);
+ if (newptr==NULL) {
+ return NULL ;
+ }
+ memcpy(newptr, ptr, size);
+ free(ptr);
+ return newptr ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Duplicate a string
+ @param s String to duplicate
+ @return Pointer to a newly allocated string, to be freed with free()
+
+ This is a replacement for strdup(). This implementation is provided
+ for systems that do not have it.
+ */
+/*--------------------------------------------------------------------------*/
+static char * xstrdup(const char * s)
+{
+ char * t ;
+ if (!s)
+ return NULL ;
+ t = (char*)calloc(strlen(s)+1,sizeof(char)) ;
+ if (t) {
+ strncpy(t,s,strlen(s));
+ }
+ return t ;
+}
+
+/*---------------------------------------------------------------------------
+ Function codes
+ ---------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Compute the hash key for a string.
+ @param key Character string to use for key.
+ @return 1 unsigned int on at least 32 bits.
+
+ This hash function has been taken from an Article in Dr Dobbs Journal.
+ This is normally a collision-free function, distributing keys evenly.
+ The key is stored anyway in the struct so that collision can be avoided
+ by comparing the key itself in last resort.
+ */
+/*--------------------------------------------------------------------------*/
+unsigned dictionary_hash(const char * key)
+{
+ int len ;
+ unsigned hash ;
+ int i ;
+
+ len = strlen(key);
+ for (hash=0, i=0 ; i<len ; i++) {
+ hash += (unsigned)key[i] ;
+ hash += (hash<<10);
+ hash ^= (hash>>6) ;
+ }
+ hash += (hash <<3);
+ hash ^= (hash >>11);
+ hash += (hash <<15);
+ return hash ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Create a new dictionary object.
+ @param size Optional initial size of the dictionary.
+ @return 1 newly allocated dictionary objet.
+
+ This function allocates a new dictionary object of given size and returns
+ it. If you do not know in advance (roughly) the number of entries in the
+ dictionary, give size=0.
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * dictionary_new(int size)
+{
+ dictionary * d ;
+
+ /* If no size was specified, allocate space for DICTMINSZ */
+ if (size<DICTMINSZ) size=DICTMINSZ ;
+
+ if (!(d = (dictionary *)calloc(1, sizeof(dictionary)))) {
+ return NULL;
+ }
+ d->size = size ;
+ d->val = (char **)calloc(size, sizeof(char*));
+ d->key = (char **)calloc(size, sizeof(char*));
+ d->hash = (unsigned int *)calloc(size, sizeof(unsigned));
+ return d ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a dictionary object
+ @param d dictionary object to deallocate.
+ @return void
+
+ Deallocate a dictionary object and all memory associated to it.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_del(dictionary ** d)
+{
+ int i ;
+
+ if (*d==NULL) return ;
+ for (i=0 ; i<(*d)->size ; i++) {
+ if ((*d)->key[i]!=NULL)
+ free((*d)->key[i]);
+ if ((*d)->val[i]!=NULL)
+ free((*d)->val[i]);
+ }
+ free((*d)->val);
+ free((*d)->key);
+ free((*d)->hash);
+ free(*d);
+ *d = NULL;
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get a value from a dictionary.
+ @param d dictionary object to search.
+ @param key Key to look for in the dictionary.
+ @param def Default value to return if key not found.
+ @return 1 pointer to internally allocated character string.
+
+ This function locates a key in a dictionary and returns a pointer to its
+ value, or the passed 'def' pointer if no such key can be found in
+ dictionary. The returned character pointer points to data internal to the
+ dictionary object, you should not try to free it or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * dictionary_get(dictionary * d, const char * key, char * def)
+{
+ unsigned hash ;
+ int i ;
+
+ hash = dictionary_hash(key);
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ /* Compare hash */
+ if (hash==d->hash[i]) {
+ /* Compare string, to avoid hash collisions */
+ if (!strcmp(key, d->key[i])) {
+ return d->val[i] ;
+ }
+ }
+ }
+ return def ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set a value in a dictionary.
+ @param d dictionary object to modify.
+ @param key Key to modify or add.
+ @param val Value to add.
+ @return int 0 if Ok, anything else otherwise
+
+ If the given key is found in the dictionary, the associated value is
+ replaced by the provided one. If the key cannot be found in the
+ dictionary, it is added to it.
+
+ It is Ok to provide a NULL value for val, but NULL values for the dictionary
+ or the key are considered as errors: the function will return immediately
+ in such a case.
+
+ Notice that if you dictionary_set a variable to NULL, a call to
+ dictionary_get will return a NULL value: the variable will be found, and
+ its value (NULL) is returned. In other words, setting the variable
+ content to NULL is equivalent to deleting the variable from the
+ dictionary. It is not possible (in this implementation) to have a key in
+ the dictionary without value.
+
+ This function returns non-zero in case of failure.
+ */
+/*--------------------------------------------------------------------------*/
+int dictionary_set(dictionary * d, const char * key, const char * val)
+{
+ int i ;
+ unsigned hash ;
+
+ if (d==NULL || key==NULL) return -1 ;
+
+ /* Compute hash for this key */
+ hash = dictionary_hash(key) ;
+ /* Find if value is already in dictionary */
+ if (d->n>0) {
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ if (hash==d->hash[i]) { /* Same hash value */
+ if (!strcmp(key, d->key[i])) { /* Same key */
+ /* Found a value: modify and return */
+ if (d->val[i]!=NULL)
+ free(d->val[i]);
+ d->val[i] = val ? xstrdup(val) : NULL ;
+ /* Value has been modified: return */
+ return 0 ;
+ }
+ }
+ }
+ }
+ /* Add a new value */
+ /* See if dictionary needs to grow */
+ if (d->n==d->size) {
+
+ /* Reached maximum size: reallocate dictionary */
+ d->val = (char **)mem_double(d->val, d->size * sizeof(char*)) ;
+ d->key = (char **)mem_double(d->key, d->size * sizeof(char*)) ;
+ d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ;
+ if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) {
+ /* Cannot grow dictionary */
+ return -1 ;
+ }
+ /* Double size */
+ d->size *= 2 ;
+ }
+
+ /* Insert key in the first empty slot. Start at d->n and wrap at
+ d->size. Because d->n < d->size this will necessarily
+ terminate. */
+ for (i=d->n ; d->key[i] ; ) {
+ if(++i == d->size) i = 0;
+ }
+ /* Copy key */
+ d->key[i] = xstrdup(key);
+ d->val[i] = val ? xstrdup(val) : NULL ;
+ d->hash[i] = hash;
+ d->n ++ ;
+
+ return 0 ;
+
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a key in a dictionary
+ @param d dictionary object to modify.
+ @param key Key to remove.
+ @return void
+
+ This function deletes a key in a dictionary. Nothing is done if the
+ key cannot be found.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_unset(dictionary * d, const char * key)
+{
+ unsigned hash ;
+ int i ;
+
+ if (key == NULL) {
+ return;
+ }
+
+ hash = dictionary_hash(key);
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ /* Compare hash */
+ if (hash==d->hash[i]) {
+ /* Compare string, to avoid hash collisions */
+ if (!strcmp(key, d->key[i])) {
+ /* Found key */
+ break ;
+ }
+ }
+ }
+ if (i>=d->size)
+ /* Key not found */
+ return ;
+
+ free(d->key[i]);
+ d->key[i] = NULL ;
+ if (d->val[i]!=NULL) {
+ free(d->val[i]);
+ d->val[i] = NULL ;
+ }
+ d->hash[i] = 0 ;
+ d->n -- ;
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Replace string in all values of a dictionary
+ @param d dictionary object to modify.
+ @param search string to be replaced
+ @param replace string to replace with
+ @return void
+
+ This function globally replaces all occurrences of one string in the ditcionary
+ with another in key values.
+
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_replace(dictionary * d, const char * search, const char * replace)
+{
+
+ int bufsize = MAXVALSZ;
+ char out[bufsize+1];
+ char *in = NULL;
+ char *val = NULL;
+ char *found = NULL;
+ int i = 0, j = 0;
+ int maxcount = 0;
+ char *pos = NULL;
+ char *data = NULL;
+
+ if (search==NULL || replace==NULL ) return;
+
+ for(i = 0; i < d->n; i++) {
+
+ memset(out, 0, bufsize+1);
+ /* skip if the key is null or is a section */
+ if(d->key[i] == NULL || strstr(d->key[i],":") == NULL)
+ continue;
+
+ val = dictionary_get(d, d->key[i], "");
+ data = strdup(val);
+ in = data;
+ found=strstr(in, search);
+
+ if(found != NULL) {
+ do {
+ pos=found;
+
+ for (j=0; j < strlen(search); j++) {
+ *pos='\0';
+ pos++;
+ }
+ maxcount = bufsize - strlen(out);
+ maxcount = (maxcount < 0) ? 0 : maxcount;
+ strncat(out,in,maxcount);
+
+ maxcount = bufsize - strlen(out);
+ maxcount = (maxcount < 0) ? 0 : maxcount;
+ strncat(out,replace,maxcount);
+
+ in = pos;
+
+ found=strstr(in, search);
+
+ } while (found != NULL);
+
+ if(*pos != 0) {
+ maxcount = bufsize - strlen(out);
+ maxcount = (maxcount < 0) ? 0 : maxcount;
+ strncat(out,pos,maxcount);
+ }
+
+ dictionary_set(d, d->key[i], out);
+/*
+ printf("Replaced token \"%s\" with \"%s\": \"%s\" -> \"%s\"\n",
+ search, replace, val, out);
+*/
+ }
+ free(data);
+
+ }
+
+
+}
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump
+ @param f Opened file pointer.
+ @return void
+
+ Dumps a dictionary onto an opened file pointer. Key pairs are printed out
+ as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
+ output file pointers.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_dump(dictionary * d, FILE * out)
+{
+ int i ;
+
+ if (d==NULL || out==NULL) return ;
+ if (d->n<1) {
+ fprintf(out, "empty dictionary\n");
+ return ;
+ }
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]) {
+ fprintf(out, "%20s\t[%s]\n",
+ d->key[i],
+ d->val[i] ? d->val[i] : "UNDEF");
+ }
+ }
+ return ;
+}
+
+int dictionary_merge(dictionary* source, dictionary* dest, int overwrite, int warn, const char* warnStr)
+{
+
+ int i = 0;
+ int clobber = 1;
+
+ if( source == NULL || dest == NULL) return -1;
+
+ if(warnStr == NULL) warnStr = "";
+ for(i = 0; i < source->n; i++) {
+ clobber = 1;
+ if(source->key[i] == NULL)
+ continue;
+ /* do not overwrite with an empty key */
+ if((source->val[i] == NULL) || (strlen(source->val[i])==0))
+ continue;
+ /* no need to warn for settings whose value will not change */
+ if((strcmp(dictionary_get(dest,source->key[i],""),"") != 0) &&
+ (strcmp(dictionary_get(dest,source->key[i],""),source->val[i]) != 0)) {
+ if(overwrite && warn) {
+ WARNING("Warning: %s=\"%s\" : value \"%s\" takes priority %s\n",
+ source->key[i], dictionary_get(dest,source->key[i],""),
+ source->val[i], warnStr);
+ }
+ if(!overwrite) {
+ clobber = 0;
+ }
+ }
+ if (clobber) {
+ if(dictionary_set( dest, source->key[i], source->val[i]) != 0) {
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Test code */
+#ifdef TESTDIC
+#define NVALS 20000
+int main(int argc, char *argv[])
+{
+ dictionary * d ;
+ char * val ;
+ int i ;
+ char cval[90] ;
+
+ /* Allocate dictionary */
+ printf("allocating...\n");
+ d = dictionary_new(0);
+
+ /* Set values in dictionary */
+ printf("setting %d values...\n", NVALS);
+ for (i=0 ; i<NVALS ; i++) {
+ sprintf(cval, "%04d", i);
+ dictionary_set(d, cval, "salut");
+ }
+ printf("getting %d values...\n", NVALS);
+ for (i=0 ; i<NVALS ; i++) {
+ sprintf(cval, "%04d", i);
+ val = dictionary_get(d, cval, DICT_INVALID_KEY);
+ if (val==DICT_INVALID_KEY) {
+ printf("cannot get value for key [%s]\n", cval);
+ }
+ }
+ printf("unsetting %d values...\n", NVALS);
+ for (i=0 ; i<NVALS ; i++) {
+ sprintf(cval, "%04d", i);
+ dictionary_unset(d, cval);
+ }
+ if (d->n != 0) {
+ printf("error deleting values\n");
+ }
+ printf("deallocating...\n");
+ dictionary_del(&d);
+ return 0 ;
+}
+#endif
+/* vim: set ts=4 et sw=4 tw=75 */
diff --git a/rtemsbsd/ptpd/src/dep/iniparser/dictionary.h b/rtemsbsd/ptpd/src/dep/iniparser/dictionary.h
new file mode 100644
index 00000000..cf1027e7
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/iniparser/dictionary.h
@@ -0,0 +1,189 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+ @file dictionary.h
+ @author N. Devillard
+ @brief Implements a dictionary for string variables.
+
+ This module implements a simple dictionary object, i.e. a list
+ of string/string associations. This object is useful to store e.g.
+ informations retrieved from a configuration file (ini files).
+*/
+/*--------------------------------------------------------------------------*/
+
+#ifndef _DICTIONARY_H_
+#define _DICTIONARY_H_
+
+/*---------------------------------------------------------------------------
+ Includes
+ ---------------------------------------------------------------------------*/
+
+#include <syslog.h>
+
+void logMessage(int priority, const char *format, ...);
+
+#ifndef WARNING
+#define WARNING(x, ...) logMessage(LOG_WARNING, x, ##__VA_ARGS__)
+#endif /* WARNING */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*---------------------------------------------------------------------------
+ New types
+ ---------------------------------------------------------------------------*/
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dictionary object
+
+ This object contains a list of string/string associations. Each
+ association is identified by a unique string key. Looking up values
+ in the dictionary is speeded up by the use of a (hopefully collision-free)
+ hash function.
+ */
+/*-------------------------------------------------------------------------*/
+typedef struct _dictionary_ {
+ int n ; /** Number of entries in dictionary */
+ int size ; /** Storage size */
+ char ** val ; /** List of string values */
+ char ** key ; /** List of string keys */
+ unsigned * hash ; /** List of hash values for keys */
+} dictionary ;
+
+
+/*---------------------------------------------------------------------------
+ Function prototypes
+ ---------------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Compute the hash key for a string.
+ @param key Character string to use for key.
+ @return 1 unsigned int on at least 32 bits.
+
+ This hash function has been taken from an Article in Dr Dobbs Journal.
+ This is normally a collision-free function, distributing keys evenly.
+ The key is stored anyway in the struct so that collision can be avoided
+ by comparing the key itself in last resort.
+ */
+/*--------------------------------------------------------------------------*/
+unsigned dictionary_hash(const char * key);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Create a new dictionary object.
+ @param size Optional initial size of the dictionary.
+ @return 1 newly allocated dictionary objet.
+
+ This function allocates a new dictionary object of given size and returns
+ it. If you do not know in advance (roughly) the number of entries in the
+ dictionary, give size=0.
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * dictionary_new(int size);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a dictionary object
+ @param d dictionary object to deallocate.
+ @return void
+
+ Deallocate a dictionary object and all memory associated to it.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_del(dictionary ** vd);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get a value from a dictionary.
+ @param d dictionary object to search.
+ @param key Key to look for in the dictionary.
+ @param def Default value to return if key not found.
+ @return 1 pointer to internally allocated character string.
+
+ This function locates a key in a dictionary and returns a pointer to its
+ value, or the passed 'def' pointer if no such key can be found in
+ dictionary. The returned character pointer points to data internal to the
+ dictionary object, you should not try to free it or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * dictionary_get(dictionary * d, const char * key, char * def);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set a value in a dictionary.
+ @param d dictionary object to modify.
+ @param key Key to modify or add.
+ @param val Value to add.
+ @return int 0 if Ok, anything else otherwise
+
+ If the given key is found in the dictionary, the associated value is
+ replaced by the provided one. If the key cannot be found in the
+ dictionary, it is added to it.
+
+ It is Ok to provide a NULL value for val, but NULL values for the dictionary
+ or the key are considered as errors: the function will return immediately
+ in such a case.
+
+ Notice that if you dictionary_set a variable to NULL, a call to
+ dictionary_get will return a NULL value: the variable will be found, and
+ its value (NULL) is returned. In other words, setting the variable
+ content to NULL is equivalent to deleting the variable from the
+ dictionary. It is not possible (in this implementation) to have a key in
+ the dictionary without value.
+
+ This function returns non-zero in case of failure.
+ */
+/*--------------------------------------------------------------------------*/
+int dictionary_set(dictionary * vd, const char * key, const char * val);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete a key in a dictionary
+ @param d dictionary object to modify.
+ @param key Key to remove.
+ @return void
+
+ This function deletes a key in a dictionary. Nothing is done if the
+ key cannot be found.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_unset(dictionary * d, const char * key);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Replace string in all values of a dictionary
+ @param d dictionary object to modify.
+ @param search string to be replaced
+ @param replace string to replace with
+ @return void
+
+ This function globally replaces all occurrences of one string in the ditcionary
+ with another in key values.
+
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_replace(dictionary * d, const char * search, const char * replace);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump
+ @param f Opened file pointer.
+ @return void
+
+ Dumps a dictionary onto an opened file pointer. Key pairs are printed out
+ as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
+ output file pointers.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_dump(dictionary * d, FILE * out);
+
+int dictionary_merge(dictionary * source, dictionary * dest, int overwrite, int warn, const char* warnStr);
+
+#endif
diff --git a/rtemsbsd/ptpd/src/dep/iniparser/iniparser.c b/rtemsbsd/ptpd/src/dep/iniparser/iniparser.c
new file mode 100644
index 00000000..04443e5b
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/iniparser/iniparser.c
@@ -0,0 +1,771 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+ @file iniparser.c
+ @author N. Devillard
+ @brief Parser for ini files.
+*/
+/*--------------------------------------------------------------------------*/
+/*---------------------------- Includes ------------------------------------*/
+#include <ctype.h>
+#include "iniparser.h"
+
+/*---------------------------- Defines -------------------------------------*/
+#define ASCIILINESZ (1024)
+#define INI_INVALID_KEY ((char*)-1)
+
+/*---------------------------------------------------------------------------
+ Private to this module
+ ---------------------------------------------------------------------------*/
+/**
+ * This enum stores the status for each parsed line (internal use only).
+ */
+typedef enum _line_status_ {
+ LINE_UNPROCESSED,
+ LINE_ERROR,
+ LINE_EMPTY,
+ LINE_COMMENT,
+ LINE_SECTION,
+ LINE_VALUE
+} line_status ;
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Convert a string to lowercase.
+ @param s String to convert.
+ @return ptr to statically allocated string.
+
+ This function returns a pointer to a statically allocated string
+ containing a lowercased version of the input string. Do not free
+ or modify the returned string! Since the returned string is statically
+ allocated, it will be modified at each function call (not re-entrant).
+ */
+/*--------------------------------------------------------------------------*/
+static char * strlwc(const char * s)
+{
+ static char l[ASCIILINESZ+1];
+ int i ;
+
+ if (s==NULL) return NULL ;
+ memset(l, 0, ASCIILINESZ+1);
+ i=0 ;
+ while (s[i] && i<ASCIILINESZ) {
+ l[i] = (char)tolower((int)s[i]);
+ i++ ;
+ }
+ l[ASCIILINESZ]=(char)0;
+ return l ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Remove blanks at the beginning and the end of a string.
+ @param s String to parse.
+ @return ptr to statically allocated string.
+
+ This function returns a pointer to a statically allocated string,
+ which is identical to the input string, except that all blank
+ characters at the end and the beg. of the string have been removed.
+ Do not free or modify the returned string! Since the returned string
+ is statically allocated, it will be modified at each function call
+ (not re-entrant).
+ */
+/*--------------------------------------------------------------------------*/
+static char * strstrip(const char * s)
+{
+ static char l[ASCIILINESZ+1];
+ char * last ;
+
+ if (s==NULL) return NULL ;
+
+ while (isspace((int)*s) && *s) s++;
+ memset(l, 0, ASCIILINESZ+1);
+ strncpy(l, s, ASCIILINESZ);
+ last = l + strlen(l);
+ while (last > l) {
+ if (!isspace((int)*(last-1)))
+ break ;
+ last -- ;
+ }
+ *last = (char)0;
+ return (char*)l ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get number of sections in a dictionary
+ @param d Dictionary to examine
+ @return int Number of sections found in dictionary
+
+ This function returns the number of sections found in a dictionary.
+ The test to recognize sections is done on the string stored in the
+ dictionary: a section name is given as "section" whereas a key is
+ stored as "section:key", thus the test looks for entries that do not
+ contain a colon.
+
+ This clearly fails in the case a section name contains a colon, but
+ this should simply be avoided.
+
+ This function returns -1 in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getnsec(dictionary * d)
+{
+ int i ;
+ int nsec ;
+
+ if (d==NULL) return -1 ;
+ nsec=0 ;
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ if (strchr(d->key[i], ':')==NULL) {
+ nsec ++ ;
+ }
+ }
+ return nsec ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get name for section n in a dictionary.
+ @param d Dictionary to examine
+ @param n Section number (from 0 to nsec-1).
+ @return Pointer to char string
+
+ This function locates the n-th section in a dictionary and returns
+ its name as a pointer to a string statically allocated inside the
+ dictionary. Do not free or modify the returned string!
+
+ This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getsecname(dictionary * d, int n)
+{
+ int i ;
+ int foundsec ;
+
+ if (d==NULL || n<0) return NULL ;
+ foundsec=0 ;
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ if (strchr(d->key[i], ':')==NULL) {
+ foundsec++ ;
+ if (foundsec>n)
+ break ;
+ }
+ }
+ if (foundsec<=n) {
+ return NULL ;
+ }
+ return d->key[i] ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump.
+ @param f Opened file pointer to dump to.
+ @return void
+
+ This function prints out the contents of a dictionary, one element by
+ line, onto the provided file pointer. It is OK to specify @c stderr
+ or @c stdout as output files. This function is meant for debugging
+ purposes mostly.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump(dictionary * d, FILE * f)
+{
+ int i ;
+
+ if (d==NULL || f==NULL) return ;
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ if (d->val[i]!=NULL) {
+ fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
+ } else {
+ fprintf(f, "[%s]=UNDEF\n", d->key[i]);
+ }
+ }
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Save a dictionary to a loadable ini file
+ @param d Dictionary to dump
+ @param f Opened file pointer to dump to
+ @return void
+
+ This function dumps a given dictionary into a loadable ini file.
+ It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump_ini(dictionary * d, FILE * f)
+{
+ int i ;
+ int nsec ;
+ char * secname ;
+
+ if (d==NULL || f==NULL) return ;
+
+ nsec = iniparser_getnsec(d);
+ if (nsec<1) {
+ /* No section in file: dump all keys as they are */
+ for (i=0 ; i<d->size ; i++) {
+ if (d->key[i]==NULL)
+ continue ;
+ fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
+ }
+ return ;
+ }
+ for (i=0 ; i<nsec ; i++) {
+ secname = iniparser_getsecname(d, i) ;
+ iniparser_dumpsection_ini(d, secname, f) ;
+ }
+ fprintf(f, "\n");
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Save a dictionary section to a loadable ini file
+ @param d Dictionary to dump
+ @param s Section name of dictionary to dump
+ @param f Opened file pointer to dump to
+ @return void
+
+ This function dumps a given section of a given dictionary into a loadable ini
+ file. It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f)
+{
+ int j ;
+ char keym[ASCIILINESZ+1];
+ int seclen ;
+
+ if (d==NULL || f==NULL) return ;
+ if (! iniparser_find_entry(d, s)) return ;
+
+ seclen = (int)strlen(s);
+ fprintf(f, "\n[%s]\n", s);
+ snprintf(keym, ASCIILINESZ, "%s:", s);
+ for (j=0 ; j<d->size ; j++) {
+ if (d->key[j]==NULL)
+ continue ;
+ if (!strncmp(d->key[j], keym, seclen+1)) {
+ fprintf(f,
+ "%-30s = %s\n",
+ d->key[j]+seclen+1,
+ d->val[j] ? d->val[j] : "");
+ }
+ }
+ fprintf(f, "\n");
+ return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the number of keys in a section of a dictionary.
+ @param d Dictionary to examine
+ @param s Section name of dictionary to examine
+ @return Number of keys in section
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getsecnkeys(dictionary * d, char * s)
+{
+ int seclen, nkeys ;
+ char keym[ASCIILINESZ+1];
+ int j ;
+
+ nkeys = 0;
+
+ if (d==NULL) return nkeys;
+ if (! iniparser_find_entry(d, s)) return nkeys;
+
+ seclen = (int)strlen(s);
+ snprintf(keym, ASCIILINESZ, "%s:", s);
+
+ for (j=0 ; j<d->size ; j++) {
+ if (d->key[j]==NULL)
+ continue ;
+ if (!strncmp(d->key[j], keym, seclen+1))
+ nkeys++;
+ }
+
+ return nkeys;
+
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the number of keys in a section of a dictionary.
+ @param d Dictionary to examine
+ @param s Section name of dictionary to examine
+ @return pointer to statically allocated character strings
+
+ This function queries a dictionary and finds all keys in a given section.
+ Each pointer in the returned char pointer-to-pointer is pointing to
+ a string allocated in the dictionary; do not free or modify them.
+
+ This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char ** iniparser_getseckeys(dictionary * d, char * s)
+{
+
+ char **keys;
+
+ int i, j ;
+ char keym[ASCIILINESZ+1];
+ int seclen, nkeys ;
+
+ keys = NULL;
+
+ if (d==NULL) return keys;
+ if (! iniparser_find_entry(d, s)) return keys;
+
+ nkeys = iniparser_getsecnkeys(d, s);
+
+ keys = (char**) malloc(nkeys*sizeof(char*));
+
+ seclen = (int)strlen(s);
+ snprintf(keym, ASCIILINESZ, "%s:", s);
+
+ i = 0;
+
+ for (j=0 ; j<d->size ; j++) {
+ if (d->key[j]==NULL)
+ continue ;
+ if (!strncmp(d->key[j], keym, seclen+1)) {
+ keys[i] = d->key[j];
+ i++;
+ }
+ }
+
+ return keys;
+
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param def Default value to return if key not found.
+ @return pointer to statically allocated character string
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the pointer passed as 'def' is returned.
+ The returned char pointer is pointing to a string allocated in
+ the dictionary, do not free or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getstring(dictionary * d, const char * key, char * def)
+{
+ char * lc_key ;
+ char * sval ;
+
+ if (d==NULL || key==NULL)
+ return def ;
+
+ lc_key = strlwc(key);
+ sval = dictionary_get(d, lc_key, def);
+ return sval ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to an int
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ Supported values for integers include the usual C notation
+ so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+ are supported. Examples:
+
+ "42" -> 42
+ "042" -> 34 (octal -> decimal)
+ "0x42" -> 66 (hexa -> decimal)
+
+ Warning: the conversion may overflow in various ways. Conversion is
+ totally outsourced to strtol(), see the associated man page for overflow
+ handling.
+
+ Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getint(dictionary * d, const char * key, int notfound)
+{
+ char * str ;
+
+ str = iniparser_getstring(d, key, INI_INVALID_KEY);
+ if (str==INI_INVALID_KEY) return notfound ;
+ return (int)strtol(str, NULL, 0);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a double
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return double
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+ */
+/*--------------------------------------------------------------------------*/
+double iniparser_getdouble(dictionary * d, const char * key, double notfound)
+{
+ char * str ;
+
+ str = iniparser_getstring(d, key, INI_INVALID_KEY);
+ if (str==INI_INVALID_KEY) return notfound ;
+ return atof(str);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a boolean
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ A true boolean is found if one of the following is matched:
+
+ - A string starting with 'y'
+ - A string starting with 'Y'
+ - A string starting with 't'
+ - A string starting with 'T'
+ - A string starting with '1'
+
+ A false boolean is found if one of the following is matched:
+
+ - A string starting with 'n'
+ - A string starting with 'N'
+ - A string starting with 'f'
+ - A string starting with 'F'
+ - A string starting with '0'
+
+ The notfound value returned if no boolean is identified, does not
+ necessarily have to be 0 or 1.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getboolean(dictionary * d, const char * key, int notfound)
+{
+ char * c ;
+ int ret ;
+
+ c = iniparser_getstring(d, key, INI_INVALID_KEY);
+ if (c==INI_INVALID_KEY) return notfound ;
+ if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
+ ret = 1 ;
+ } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
+ ret = 0 ;
+ } else {
+ ret = notfound ;
+ }
+ return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Finds out if a given entry exists in a dictionary
+ @param ini Dictionary to search
+ @param entry Name of the entry to look for
+ @return integer 1 if entry exists, 0 otherwise
+
+ Finds out if a given entry exists in the dictionary. Since sections
+ are stored as keys with NULL associated values, this is the only way
+ of querying for the presence of sections in a dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_find_entry(
+ dictionary * ini,
+ const char * entry
+)
+{
+ int found=0 ;
+ if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) {
+ found = 1 ;
+ }
+ return found ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set an entry in a dictionary.
+ @param ini Dictionary to modify.
+ @param entry Entry to modify (entry name)
+ @param val New value to associate to the entry.
+ @return int 0 if Ok, -1 otherwise.
+
+ If the given entry can be found in the dictionary, it is modified to
+ contain the provided value. If it cannot be found, -1 is returned.
+ It is Ok to set val to NULL.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_set(dictionary * ini, const char * entry, const char * val)
+{
+ char section[ASCIILINESZ+1];
+ if(sscanf(entry,":%[^:]:",section)>0)
+ dictionary_set(ini,section,NULL);
+ if(sscanf(entry,"%[^:]:",section)>0)
+ dictionary_set(ini,section,NULL);
+ return dictionary_set(ini, strlwc(entry), val) ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete an entry in a dictionary
+ @param ini Dictionary to modify
+ @param entry Entry to delete (entry name)
+ @return void
+
+ If the given entry can be found, it is deleted from the dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_unset(dictionary * ini, const char * entry)
+{
+ dictionary_unset(ini, strlwc(entry));
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Load a single line from an INI file
+ @param input_line Input line, may be concatenated multi-line input
+ @param section Output space to store section
+ @param key Output space to store key
+ @param value Output space to store value
+ @return line_status value
+ */
+/*--------------------------------------------------------------------------*/
+static line_status iniparser_line(
+ const char * input_line,
+ char * section,
+ char * key,
+ char * value)
+{
+ line_status sta ;
+ char line[ASCIILINESZ+1];
+ int len ;
+ memset(line, 0, ASCIILINESZ + 1);
+ strncpy(line, strstrip(input_line), ASCIILINESZ);
+ len = (int)strlen(line);
+
+ sta = LINE_UNPROCESSED ;
+ if (len<1) {
+ /* Empty line */
+ sta = LINE_EMPTY ;
+ } else if (line[0]=='#' || line[0]==';') {
+ /* Comment line */
+ sta = LINE_COMMENT ;
+ } else if (line[0]=='[' && line[len-1]==']') {
+ /* Section name */
+ sscanf(line, "[%[^]]", section);
+ strncpy(section, strstrip(section), ASCIILINESZ);
+ strncpy(section, strlwc(section), ASCIILINESZ);
+ sta = LINE_SECTION ;
+ } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
+ || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2
+ || sscanf (line, "%[^=] = %[^;#]", key, value) == 2) {
+ /* Usual key=value, with or without comments */
+ strncpy(key, strstrip(key), ASCIILINESZ);
+ strncpy(key, strlwc(key), ASCIILINESZ);
+ strncpy(value, strstrip(value), ASCIILINESZ);
+ /*
+ * sscanf cannot handle '' or "" as empty values
+ * this is done here
+ */
+ if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
+ value[0]=0 ;
+ }
+ sta = LINE_VALUE ;
+ } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
+ || sscanf(line, "%[^=] %[=]", key, value) == 2) {
+ /*
+ * Special cases:
+ * key=
+ * key=;
+ * key=#
+ */
+ strncpy(key, strstrip(key), ASCIILINESZ);
+ strncpy(key, strlwc(key), ASCIILINESZ);
+ value[0]=0 ;
+ sta = LINE_VALUE ;
+ } else {
+ /* Generate syntax error */
+ sta = LINE_ERROR ;
+ }
+ return sta ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Parse an ini file and return an allocated dictionary object
+ @param ininame Name of the ini file to read.
+ @return Pointer to newly allocated dictionary
+
+ This is the parser for ini files. This function is called, providing
+ the name of the file to be read. It returns a dictionary object that
+ should not be accessed directly, but through accessor functions
+ instead.
+
+ The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load(const char * ininame)
+{
+ FILE * in ;
+
+ char line [ASCIILINESZ+1] ;
+ char section [ASCIILINESZ+1] ;
+ char key [ASCIILINESZ+1] ;
+ char tmp [ASCIILINESZ+1] ;
+ char val [ASCIILINESZ+1] ;
+
+ int last=0 ;
+ int len ;
+ int lineno=0 ;
+ int errs=0;
+
+ dictionary * dict ;
+
+ if ((in=fopen(ininame, "r"))==NULL) {
+ fprintf(stderr, "iniparser: cannot open %s\n", ininame);
+ return NULL ;
+ }
+
+ dict = dictionary_new(0) ;
+ if (!dict) {
+ fclose(in);
+ return NULL ;
+ }
+
+ memset(line, 0, ASCIILINESZ);
+ memset(section, 0, ASCIILINESZ);
+ memset(key, 0, ASCIILINESZ);
+ memset(val, 0, ASCIILINESZ);
+ memset(tmp, 0, ASCIILINESZ);
+ last=0 ;
+
+ while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
+ lineno++ ;
+ len = (int)strlen(line)-1;
+ if (len==0)
+ continue;
+ /* Safety check against buffer overflows */
+ if (line[len]!='\n') {
+ fprintf(stderr,
+ "iniparser: input line too long or no newline in the end in %s (%d)\n",
+ ininame,
+ lineno);
+ dictionary_del(&dict);
+ fclose(in);
+ return NULL ;
+ }
+ /* Get rid of \n and spaces at end of line */
+ while ((len>=0) &&
+ ((line[len]=='\n') || (isspace((unsigned char)line[len])))) {
+ line[len]=0 ;
+ len-- ;
+ }
+ /* Detect multi-line */
+ if (line[len]=='\\') {
+ /* Multi-line value */
+ last=len ;
+ continue ;
+ } else {
+ last=0 ;
+ }
+ switch (iniparser_line(line, section, key, val)) {
+ case LINE_EMPTY:
+ case LINE_COMMENT:
+ break ;
+
+ case LINE_SECTION:
+ errs = dictionary_set(dict, section, NULL);
+ break ;
+
+ case LINE_VALUE:
+ snprintf(tmp, ASCIILINESZ, "%s%s%s", section, strlen(section)==0?"":":", key);
+ errs = dictionary_set(dict, tmp, val) ;
+ break ;
+
+ case LINE_ERROR:
+ fprintf(stderr, "iniparser: syntax error in %s (%d):\n",
+ ininame,
+ lineno);
+ fprintf(stderr, "-> %s\n", line);
+ errs++ ;
+ break;
+
+ default:
+ break ;
+ }
+ memset(line, 0, ASCIILINESZ);
+ last=0;
+ if (errs<0) {
+ fprintf(stderr, "iniparser: memory allocation failure\n");
+ break ;
+ }
+ }
+ if (errs) {
+ dictionary_del(&dict);
+ dict = NULL ;
+ }
+ fclose(in);
+ return dict ;
+}
+
+
+int iniparser_merge_file(dictionary *dict, const char *filename, int overwrite) {
+
+ dictionary *src;
+
+ if ((src = iniparser_load(filename)) == NULL) {
+ return 0;
+ }
+
+ dictionary_merge(src, dict, overwrite, 0, NULL);
+
+ dictionary_del(&src);
+
+ return 1;
+
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Free all memory associated to an ini dictionary
+ @param d Dictionary to free
+ @return void
+
+ Free all memory associated to an ini dictionary.
+ It is mandatory to call this function before the dictionary object
+ gets out of the current context.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_freedict(dictionary ** d)
+{
+ dictionary_del(d);
+}
+
+/* vim: set ts=4 et sw=4 tw=75 */
diff --git a/rtemsbsd/ptpd/src/dep/iniparser/iniparser.h b/rtemsbsd/ptpd/src/dep/iniparser/iniparser.h
new file mode 100644
index 00000000..a247ac60
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/iniparser/iniparser.h
@@ -0,0 +1,311 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+ @file iniparser.h
+ @author N. Devillard
+ @brief Parser for ini files.
+*/
+/*--------------------------------------------------------------------------*/
+
+#ifndef _INIPARSER_H_
+#define _INIPARSER_H_
+
+/*---------------------------------------------------------------------------
+ Includes
+ ---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * The following #include is necessary on many Unixes but not Linux.
+ * It is not needed for Windows platforms.
+ * Uncomment it if needed.
+ */
+/* #include <unistd.h> */
+
+#include "dictionary.h"
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get number of sections in a dictionary
+ @param d Dictionary to examine
+ @return int Number of sections found in dictionary
+
+ This function returns the number of sections found in a dictionary.
+ The test to recognize sections is done on the string stored in the
+ dictionary: a section name is given as "section" whereas a key is
+ stored as "section:key", thus the test looks for entries that do not
+ contain a colon.
+
+ This clearly fails in the case a section name contains a colon, but
+ this should simply be avoided.
+
+ This function returns -1 in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+
+int iniparser_getnsec(dictionary * d);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get name for section n in a dictionary.
+ @param d Dictionary to examine
+ @param n Section number (from 0 to nsec-1).
+ @return Pointer to char string
+
+ This function locates the n-th section in a dictionary and returns
+ its name as a pointer to a string statically allocated inside the
+ dictionary. Do not free or modify the returned string!
+
+ This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+
+char * iniparser_getsecname(dictionary * d, int n);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Save a dictionary to a loadable ini file
+ @param d Dictionary to dump
+ @param f Opened file pointer to dump to
+ @return void
+
+ This function dumps a given dictionary into a loadable ini file.
+ It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+
+void iniparser_dump_ini(dictionary * d, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Save a dictionary section to a loadable ini file
+ @param d Dictionary to dump
+ @param s Section name of dictionary to dump
+ @param f Opened file pointer to dump to
+ @return void
+
+ This function dumps a given section of a given dictionary into a loadable ini
+ file. It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+
+void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Dump a dictionary to an opened file pointer.
+ @param d Dictionary to dump.
+ @param f Opened file pointer to dump to.
+ @return void
+
+ This function prints out the contents of a dictionary, one element by
+ line, onto the provided file pointer. It is OK to specify @c stderr
+ or @c stdout as output files. This function is meant for debugging
+ purposes mostly.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump(dictionary * d, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the number of keys in a section of a dictionary.
+ @param d Dictionary to examine
+ @param s Section name of dictionary to examine
+ @return Number of keys in section
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getsecnkeys(dictionary * d, char * s);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the number of keys in a section of a dictionary.
+ @param d Dictionary to examine
+ @param s Section name of dictionary to examine
+ @return pointer to statically allocated character strings
+
+ This function queries a dictionary and finds all keys in a given section.
+ Each pointer in the returned char pointer-to-pointer is pointing to
+ a string allocated in the dictionary; do not free or modify them.
+
+ This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+char ** iniparser_getseckeys(dictionary * d, char * s);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param def Default value to return if key not found.
+ @return pointer to statically allocated character string
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the pointer passed as 'def' is returned.
+ The returned char pointer is pointing to a string allocated in
+ the dictionary, do not free or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+char * iniparser_getstring(dictionary * d, const char * key, char * def);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to an int
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ Supported values for integers include the usual C notation
+ so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+ are supported. Examples:
+
+ - "42" -> 42
+ - "042" -> 34 (octal -> decimal)
+ - "0x42" -> 66 (hexa -> decimal)
+
+ Warning: the conversion may overflow in various ways. Conversion is
+ totally outsourced to strtol(), see the associated man page for overflow
+ handling.
+
+ Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getint(dictionary * d, const char * key, int notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a double
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return double
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+ */
+/*--------------------------------------------------------------------------*/
+double iniparser_getdouble(dictionary * d, const char * key, double notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Get the string associated to a key, convert to a boolean
+ @param d Dictionary to search
+ @param key Key string to look for
+ @param notfound Value to return in case of error
+ @return integer
+
+ This function queries a dictionary for a key. A key as read from an
+ ini file is given as "section:key". If the key cannot be found,
+ the notfound value is returned.
+
+ A true boolean is found if one of the following is matched:
+
+ - A string starting with 'y'
+ - A string starting with 'Y'
+ - A string starting with 't'
+ - A string starting with 'T'
+ - A string starting with '1'
+
+ A false boolean is found if one of the following is matched:
+
+ - A string starting with 'n'
+ - A string starting with 'N'
+ - A string starting with 'f'
+ - A string starting with 'F'
+ - A string starting with '0'
+
+ The notfound value returned if no boolean is identified, does not
+ necessarily have to be 0 or 1.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getboolean(dictionary * d, const char * key, int notfound);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Set an entry in a dictionary.
+ @param ini Dictionary to modify.
+ @param entry Entry to modify (entry name)
+ @param val New value to associate to the entry.
+ @return int 0 if Ok, -1 otherwise.
+
+ If the given entry can be found in the dictionary, it is modified to
+ contain the provided value. If it cannot be found, -1 is returned.
+ It is Ok to set val to NULL.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_set(dictionary * ini, const char * entry, const char * val);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Delete an entry in a dictionary
+ @param ini Dictionary to modify
+ @param entry Entry to delete (entry name)
+ @return void
+
+ If the given entry can be found, it is deleted from the dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_unset(dictionary * ini, const char * entry);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Finds out if a given entry exists in a dictionary
+ @param ini Dictionary to search
+ @param entry Name of the entry to look for
+ @return integer 1 if entry exists, 0 otherwise
+
+ Finds out if a given entry exists in the dictionary. Since sections
+ are stored as keys with NULL associated values, this is the only way
+ of querying for the presence of sections in a dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_find_entry(dictionary * ini, const char * entry) ;
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Parse an ini file and return an allocated dictionary object
+ @param ininame Name of the ini file to read.
+ @return Pointer to newly allocated dictionary
+
+ This is the parser for ini files. This function is called, providing
+ the name of the file to be read. It returns a dictionary object that
+ should not be accessed directly, but through accessor functions
+ instead.
+
+ The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load(const char * ininame);
+
+int iniparser_merge_file(dictionary *dict, const char * filename, int overwrite);
+
+dictionary * iniparser_load_list(const char* list);
+
+/*-------------------------------------------------------------------------*/
+/**
+ @brief Free all memory associated to an ini dictionary
+ @param d Dictionary to free
+ @return void
+
+ Free all memory associated to an ini dictionary.
+ It is mandatory to call this function before the dictionary object
+ gets out of the current context.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_freedict(dictionary ** d);
+
+#endif
diff --git a/rtemsbsd/ptpd/src/dep/ipv4_acl.c b/rtemsbsd/ptpd/src/dep/ipv4_acl.c
new file mode 100644
index 00000000..5b2bba3d
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/ipv4_acl.c
@@ -0,0 +1,486 @@
+/*-
+ * Copyright (c) 2013-2014 Wojciech Owczarek,
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file ipv4_acl.c
+ * @date Sun Oct 13 21:02:23 2013
+ *
+ * @brief Code to handle IPv4 access control lists
+ *
+ * Functions in this file parse, create and match IPv4 ACLs.
+ *
+ */
+
+#include "../ptpd.h"
+#include "string.h"
+
+/**
+ * strdup + free are used across code using strtok_r, so as to
+ * protect the original string, as strtok* modify it.
+ */
+
+/* count tokens in string delimited by delim */
+static int countTokens(const char* text, const char* delim) {
+
+ int count=0;
+ char* stash = NULL;
+ char* text_;
+ char* text__;
+
+ if(text==NULL || delim==NULL)
+ return 0;
+
+ text_=strdup(text);
+
+ for(text__=text_,count=0;strtok_r(text__,delim, &stash) != NULL; text__=NULL) {
+ count++;
+ }
+ free(text_);
+ return count;
+
+}
+
+/* Parse a dotted-decimal string into an uint8_t array - return -1 on error */
+static int ipToArray(const char* text, uint8_t dest[], int maxOctets, Boolean isMask)
+{
+
+ char* text_;
+ char* text__;
+ char* subtoken;
+ char* stash = NULL;
+ char* endptr;
+ long octet;
+
+ int count = 0;
+ int result = 0;
+
+ memset(&dest[0], 0, maxOctets * sizeof(uint8_t));
+
+ text_=strdup(text);
+
+ for(text__=text_;;text__=NULL) {
+
+ if(count > maxOctets) {
+ result = -1;
+ goto end;
+ }
+
+ subtoken = strtok_r(text__,".",&stash);
+
+ if(subtoken == NULL)
+ goto end;
+
+ errno = 0;
+ octet=strtol(subtoken,&endptr,10);
+ if(errno!=0 || !IN_RANGE(octet,0,255)) {
+ result = -1;
+ goto end;
+
+ }
+
+ dest[count++] = (uint8_t)octet;
+
+ /* If we're parsing a mask and an octet is less than 0xFF, whole rest is zeros */
+ if(isMask && octet < 255)
+ goto end;
+ }
+
+ end:
+
+ free(text_);
+ return result;
+
+}
+
+/* Parse a single net mask into an AclEntry */
+static int parseAclEntry(const char* line, AclEntry* acl) {
+
+ int result = 1;
+ char* stash = NULL;
+ char* text_;
+ char* token;
+ char* endptr;
+ long octet = 0;
+
+ uint8_t net_octets[4];
+ uint8_t mask_octets[4];
+
+ if(line == NULL || acl == NULL)
+ return -1;
+
+ if(countTokens(line,"/") == 0)
+ return -1;
+
+ text_=strdup(line);
+
+ token=strtok_r(text_,"/",&stash);
+
+ if((countTokens(token,".") > 4) ||
+ (countTokens(token,".") < 1) ||
+ (ipToArray(token,net_octets,4,0) < 0)) {
+ result=-1;
+ goto end;
+ }
+
+ acl->network = (net_octets[0] << 24) | (net_octets[1] << 16) | ( net_octets[2] << 8) | net_octets[3];
+
+ token=strtok_r(NULL,"/",&stash);
+
+ if(token == NULL) {
+ acl->netmask=32;
+ acl->bitmask=~0;
+ } else if(countTokens(token,".") == 1) {
+ errno = 0;
+ octet = strtol(token,&endptr,10);
+ if(errno != 0 || !IN_RANGE(octet,0,32)) {
+ result = -1;
+ goto end;
+ }
+
+ if(octet == 0)
+ acl->bitmask = 0;
+ else
+ acl->bitmask = ~0 << (32 - octet);
+ acl->netmask = (uint16_t)octet;
+
+ } else if((countTokens(token,".") > 4) ||
+ (ipToArray(token,mask_octets,4,1) < 0)) {
+ result=-1;
+ goto end;
+ } else {
+
+ acl->bitmask = (mask_octets[0] << 24) | (mask_octets[1] << 16) | ( mask_octets[2] << 8) | mask_octets[3];
+ uint32_t tmp = acl->bitmask;
+ int count = 0;
+ for(tmp = acl->bitmask;tmp<<count;count++);
+ acl->netmask=count;
+ }
+
+ end:
+ free(text_);
+
+ acl->network &= acl->bitmask;
+
+ return result;
+
+}
+
+
+/* qsort() compare function for sorting ACL entries */
+static int
+cmpAclEntry(const void *p1, const void *p2)
+{
+
+ const AclEntry *left = p1;
+ const AclEntry *right = p2;
+
+ if(left->network > right->network)
+ return 1;
+ if(left->network < right->network)
+ return -1;
+ return 0;
+
+}
+
+/* Parse an ACL string into an aclEntry table and return number of entries. output can be NULL */
+int
+maskParser(const char* input, AclEntry* output)
+{
+
+ char* token;
+ char* stash;
+ int found = 0;
+ char* text_;
+ char* text__;
+ AclEntry tmp;
+
+ tmp.hitCount=0;
+
+ if(strlen(input)==0) return 0;
+
+ text_=strdup(input);
+
+ for(text__=text_;;text__=NULL) {
+
+ token=strtok_r(text__,", ;\t",&stash);
+ if(token==NULL) break;
+
+ if(parseAclEntry(token,&tmp)<1) {
+
+ found = -1;
+ break;
+
+ }
+
+ if(output != NULL)
+ output[found]=tmp;
+
+ found++;
+
+
+ }
+
+ if(found && (output != NULL))
+ qsort(output, found, sizeof(AclEntry), cmpAclEntry);
+
+ /* We got input but found nothing - error */
+ if (!found)
+ found = -1;
+
+ free(text_);
+ return found;
+
+}
+
+/* Create a maskTable from a text ACL */
+static MaskTable*
+createMaskTable(const char* input)
+{
+ MaskTable* ret;
+ int masksFound = maskParser(input, NULL);
+ if(masksFound>=0) {
+ ret=(MaskTable*)calloc(1,sizeof(MaskTable));
+ ret->entries = (AclEntry*)calloc(masksFound, sizeof(AclEntry));
+ ret->numEntries = maskParser(input,ret->entries);
+ return ret;
+ } else {
+ ERROR("Error while parsing access list: \"%s\"\n", input);
+ return NULL;
+ }
+}
+
+/* Print the contents of a single mask table */
+static void
+dumpMaskTable(MaskTable* table)
+{
+ int i;
+ uint32_t network;
+ if(table == NULL)
+ return;
+ INFO("number of entries: %d\n",table->numEntries);
+ if(table->entries != NULL) {
+ for(i = 0; i < table->numEntries; i++) {
+ AclEntry this = table->entries[i];
+#ifdef PTPD_MSBF
+ network = this.network;
+#else
+ network = htonl(this.network);
+#endif
+ INFO("%d.%d.%d.%d/%d\t(0x%.8x/0x%.8x), matches: %d\n",
+ *((uint8_t*)&network), *((uint8_t*)&network+1),
+ *((uint8_t*)&network+2), *((uint8_t*)&network+3), this.netmask,
+ this.network, this.bitmask, this.hitCount);
+ }
+ }
+
+}
+
+/* Free a MaskTable structure */
+static void freeMaskTable(MaskTable** table)
+{
+ if(*table == NULL)
+ return;
+
+ if((*table)->entries != NULL) {
+ free((*table)->entries);
+ (*table)->entries = NULL;
+ }
+ free(*table);
+ *table = NULL;
+}
+
+/* Destroy an Ipv4AccessList structure */
+void
+freeIpv4AccessList(Ipv4AccessList** acl)
+{
+ if(*acl == NULL)
+ return;
+
+ freeMaskTable(&(*acl)->permitTable);
+ freeMaskTable(&(*acl)->denyTable);
+
+ free(*acl);
+ *acl = NULL;
+}
+
+/* Structure initialisation for Ipv4AccessList */
+Ipv4AccessList*
+createIpv4AccessList(const char* permitList, const char* denyList, int processingOrder)
+{
+ Ipv4AccessList* ret;
+ ret = (Ipv4AccessList*)calloc(1,sizeof(Ipv4AccessList));
+ ret->permitTable = createMaskTable(permitList);
+ ret->denyTable = createMaskTable(denyList);
+ if(ret->permitTable == NULL || ret->denyTable == NULL) {
+ freeIpv4AccessList(&ret);
+ return NULL;
+ }
+ ret->processingOrder = processingOrder;
+ return ret;
+}
+
+
+/* Match an IP address against a MaskTable */
+static int
+matchAddress(const uint32_t addr, MaskTable* table)
+{
+
+ int i;
+ if(table == NULL || table->entries == NULL || table->numEntries==0)
+ return -1;
+ for(i = 0; i < table->numEntries; i++) {
+ DBGV("addr: %08x, addr & mask: %08x, network: %08x\n",addr, table->entries[i].bitmask & addr, table->entries[i].network);
+ if((table->entries[i].bitmask & addr) == table->entries[i].network) {
+ table->entries[i].hitCount++;
+ return 1;
+ }
+ }
+
+ return 0;
+
+}
+
+/* Test an IP address against an ACL */
+int
+matchIpv4AccessList(Ipv4AccessList* acl, const uint32_t addr)
+{
+
+ int ret;
+ int matchPermit = 0;
+ int matchDeny = 0;
+
+ /* Non-functional ACL permits everything */
+ if(acl == NULL) {
+ ret = 1;
+ goto end;
+ }
+
+ if(acl->permitTable != NULL)
+ matchPermit = matchAddress(addr,acl->permitTable) > 0;
+
+ if(acl->denyTable != NULL)
+ matchDeny = matchAddress(addr,acl->denyTable) > 0;
+
+ switch(acl->processingOrder) {
+ case ACL_PERMIT_DENY:
+ if(!matchPermit) {
+ ret = 0;
+ break;
+ }
+ if(matchDeny) {
+ ret = 0;
+ break;
+ }
+ ret = 1;
+ break;
+ default:
+ case ACL_DENY_PERMIT:
+ if (matchDeny && !matchPermit) {
+ ret = 0;
+ break;
+ }
+ else {
+ ret = 1;
+ break;
+ }
+ }
+
+ end:
+
+ if(ret)
+ acl->passedCounter++;
+ else
+ acl->droppedCounter++;
+
+ return ret;
+
+}
+
+/* Dump the contents and hit counters of an ACL */
+void dumpIpv4AccessList(Ipv4AccessList* acl)
+{
+
+ INFO("\n\n");
+ if(acl == NULL) {
+ INFO("(uninitialised ACL)\n");
+ return;
+ }
+ switch(acl->processingOrder) {
+ case ACL_DENY_PERMIT:
+ INFO("ACL order: deny,permit\n");
+ INFO("Passed packets: %d, dropped packets: %d\n",
+ acl->passedCounter, acl->droppedCounter);
+ INFO("--------\n");
+ INFO("Deny list:\n");
+ dumpMaskTable(acl->denyTable);
+ INFO("--------\n");
+ INFO("Permit list:\n");
+ dumpMaskTable(acl->permitTable);
+ break;
+ case ACL_PERMIT_DENY:
+ default:
+ INFO("ACL order: permit,deny\n");
+ INFO("Passed packets: %d, dropped packets: %d\n",
+ acl->passedCounter, acl->droppedCounter);
+ INFO("--------\n");
+ INFO("Permit list:\n");
+ dumpMaskTable(acl->permitTable);
+ INFO("--------\n");
+ INFO("Deny list:\n");
+ dumpMaskTable(acl->denyTable);
+ break;
+ }
+ INFO("\n\n");
+}
+
+/* Clear counters in a MaskTable */
+static void
+clearMaskTableCounters(MaskTable* table)
+{
+
+ int i, count;
+ if(table==NULL || table->numEntries==0)
+ return;
+ count = table->numEntries;
+
+ for(i=0; i<count; i++) {
+ table->entries[i].hitCount = 0;
+ }
+
+}
+
+/* Clear ACL counter */
+void clearIpv4AccessListCounters(Ipv4AccessList* acl) {
+
+ if(acl == NULL)
+ return;
+ acl->passedCounter=0;
+ acl->droppedCounter=0;
+ clearMaskTableCounters(acl->permitTable);
+ clearMaskTableCounters(acl->denyTable);
+
+}
diff --git a/rtemsbsd/ptpd/src/dep/ipv4_acl.h b/rtemsbsd/ptpd/src/dep/ipv4_acl.h
new file mode 100644
index 00000000..65f7cd1d
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/ipv4_acl.h
@@ -0,0 +1,52 @@
+/**
+ * @file ipv4_acl.h
+ *
+ * @brief definitions related to IPv4 access control list handling
+ *
+ */
+
+#ifndef PTPD_IPV4_ACL_H_
+#define PTPD_IPV4_ACL_H_
+
+#define IN_RANGE(num, min,max) \
+ (num >= min && num <= max)
+
+enum {
+ ACL_PERMIT_DENY,
+ ACL_DENY_PERMIT
+};
+
+typedef struct {
+ uint32_t network;
+ uint32_t bitmask;
+ uint16_t netmask;
+ uint32_t hitCount;
+} AclEntry;
+
+typedef struct {
+ int numEntries;
+ AclEntry* entries;
+} MaskTable;
+
+typedef struct {
+ MaskTable* permitTable;
+ MaskTable* denyTable;
+ int processingOrder;
+ uint32_t passedCounter;
+ uint32_t droppedCounter;
+} Ipv4AccessList;
+
+/* Parse string into AclEntry array */
+int maskParser(const char* input, AclEntry* output);
+/* Destroy an Ipv4AccessList structure */
+void freeIpv4AccessList(Ipv4AccessList** acl);
+/* Initialise an Ipv4AccessList structure */
+Ipv4AccessList* createIpv4AccessList(const char* permitList, const char* denyList, int processingOrder);
+/* Match on an IP address */
+int matchIpv4AccessList(Ipv4AccessList* acl, const uint32_t addr);
+/* Display the contents and hit counters of an access list */
+void dumpIpv4AccessList(Ipv4AccessList* acl);
+/* Clear counters */
+void clearIpv4AccessListCounters(Ipv4AccessList* acl);
+
+#endif /* PTPD_IPV4_ACL_H_ */
diff --git a/rtemsbsd/ptpd/src/dep/msg.c b/rtemsbsd/ptpd/src/dep/msg.c
new file mode 100644
index 00000000..346fb821
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/msg.c
@@ -0,0 +1,2757 @@
+/*-
+ * Copyright (c) 2012-2015 Wojciech Owczarek,
+ * Copyright (c) 2011-2012 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen,
+ * Inaqui Delgado,
+ * Rick Ratzel,
+ * National Instruments.
+ * Copyright (c) 2009-2010 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen
+ *
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file msg.c
+ * @author George Neville-Neil <gnn at neville-neil.com>
+ * @date Tue Jul 20 16:17:05 2010
+ *
+ * @brief Functions to pack and unpack messages.
+ *
+ * See spec annex d
+ */
+
+#include "../ptpd.h"
+
+extern RunTimeOpts rtOpts;
+
+#define PACK_SIMPLE( type ) \
+void pack##type( void* from, void* to ) \
+{ \
+ *(type *)to = *(type *)from; \
+} \
+void unpack##type( void* from, void* to, PtpClock *ptpClock ) \
+{ \
+ pack##type( from, to ); \
+}
+
+#define PACK_ENDIAN( type, size ) \
+void pack##type( void* from, void* to ) \
+{ \
+ *(type *)to = flip##size( *(type *)from ); \
+} \
+void unpack##type( void* from, void* to, PtpClock *ptpClock ) \
+{ \
+ pack##type( from, to ); \
+}
+
+#define PACK_LOWER_AND_UPPER( type ) \
+void pack##type##Lower( void* from, void* to ) \
+{ \
+ *(char *)to = *(char *)to & 0xF0; \
+ *(char *)to = *(char *)to | *(type *)from; \
+} \
+\
+void pack##type##Upper( void* from, void* to ) \
+{ \
+ *(char *)to = *(char *)to & 0x0F; \
+ *(char *)to = *(char *)to | (*(type *)from << 4); \
+} \
+\
+void unpack##type##Lower( void* from, void* to, PtpClock *ptpClock ) \
+{ \
+ *(type *)to = *(char *)from & 0x0F; \
+} \
+\
+void unpack##type##Upper( void* from, void* to, PtpClock *ptpClock ) \
+{ \
+ *(type *)to = (*(char *)from >> 4) & 0x0F; \
+}
+
+PACK_SIMPLE( Boolean )
+PACK_SIMPLE( UInteger8 )
+PACK_SIMPLE( Octet )
+PACK_SIMPLE( Enumeration8 )
+PACK_SIMPLE( Integer8 )
+
+PACK_ENDIAN( Enumeration16, 16 )
+PACK_ENDIAN( Integer16, 16 )
+PACK_ENDIAN( UInteger16, 16 )
+PACK_ENDIAN( Integer32, 32 )
+PACK_ENDIAN( UInteger32, 32 )
+
+PACK_LOWER_AND_UPPER( Enumeration4 )
+PACK_LOWER_AND_UPPER( UInteger4 )
+PACK_LOWER_AND_UPPER( Nibble )
+
+/* The free function is intentionally empty. However, this simplifies
+ * the procedure to deallocate complex data types
+ */
+#define FREE( type ) \
+void free##type( void* x) \
+{}
+
+FREE ( Boolean )
+FREE ( UInteger8 )
+FREE ( Octet )
+FREE ( Enumeration8 )
+FREE ( Integer8 )
+FREE ( Enumeration16 )
+FREE ( Integer16 )
+FREE ( UInteger16 )
+FREE ( Integer32 )
+FREE ( UInteger32 )
+FREE ( Enumeration4 )
+FREE ( UInteger4 )
+FREE ( Nibble )
+
+static inline int bufGuard(int max, long base, int len, long beginning, int size);
+
+/*
+ * check if data we want to read is within the allocated buffer,
+ * and if it is within the message length given.
+ */
+static inline int
+bufGuard(int max, long base, int len, long beginning, int size)
+{
+#ifdef RUNTIME_DEBUG
+ int ok = ((beginning - base) < len) && ((beginning + size - base) < len) &&
+ ((beginning - base) < max) && ((beginning + size - base) < max);
+ printf("bufGuard: beginning %ld end %ld: maxlen: %d size %d (%ld %ld %d): %s\n",
+ beginning - base, beginning + size - base, len, size, base,
+ beginning, size, ok ? "OK" : "!");
+#endif
+ return(
+ ((beginning - base) < len) && ((beginning + size - base) < len) &&
+ ((beginning - base) < max) && ((beginning + size - base) < max)
+ );
+}
+
+void
+unpackUInteger48( void *buf, void *i, PtpClock *ptpClock)
+{
+
+ unpackUInteger16(buf, &((UInteger48*)i)->msb, ptpClock);
+ unpackUInteger32(buf + 2, &((UInteger48*)i)->lsb, ptpClock);
+}
+
+void
+packUInteger48( void *i, void *buf)
+{
+ packUInteger16(&((UInteger48*)i)->msb, buf);
+ packUInteger32(&((UInteger48*)i)->lsb, buf + 2);
+}
+
+void
+unpackInteger64( void *buf, void *i, PtpClock *ptpClock)
+{
+ unpackInteger32(buf, &((Integer64*)i)->msb, ptpClock);
+ unpackUInteger32(buf + 4, &((Integer64*)i)->lsb, ptpClock);
+}
+
+void
+packInteger64( void* i, void *buf )
+{
+ packInteger32(&((Integer64*)i)->msb, buf);
+ packUInteger32(&((Integer64*)i)->lsb, buf + 4);
+}
+
+/* NOTE: the unpack functions for management messages can probably be refactored into a macro */
+int
+unpackMMSlaveOnly( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMSlaveOnly));
+ MMSlaveOnly* data = (MMSlaveOnly*)m->tlv->dataField;
+ /* see src/def/README for a note on this X-macro */
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/slaveOnly.def"
+
+ #ifdef PTPD_DBG
+ mMSlaveOnly_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+/* NOTE: the pack functions for management messsages can probably be refactored into a macro */
+UInteger16
+packMMSlaveOnly( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMSlaveOnly* data = (MMSlaveOnly*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/slaveOnly.def"
+
+ /* return length */
+ return offset;
+}
+
+int
+unpackMMClockDescription( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMClockDescription));
+ MMClockDescription* data = (MMClockDescription*)m->tlv->dataField;
+ memset(data, 0, sizeof(MMClockDescription));
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/clockDescription.def"
+
+ #ifdef PTPD_DBG
+ mMClockDescription_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMClockDescription( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ Octet pad = 0;
+ MMClockDescription* data = (MMClockDescription*)m->tlv->dataField;
+ data->reserved = 0;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset); \
+ offset = offset + size;
+ #include "../def/managementTLV/clockDescription.def"
+
+ /* is the TLV length odd? TLV must be even according to Spec 5.3.8 */
+ if(offset % 2) {
+ /* add pad of 1 according to Table 41 to make TLV length even */
+ packOctet(&pad, buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset);
+ offset = offset + 1;
+ }
+
+ /* return length */
+ return offset;
+}
+
+void
+freeMMClockDescription( MMClockDescription* data)
+{
+ #define OPERATE( name, size, type ) \
+ free##type( &data->name);
+ #include "../def/managementTLV/clockDescription.def"
+}
+
+int
+unpackMMUserDescription( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMUserDescription));
+ MMUserDescription* data = (MMUserDescription*)m->tlv->dataField;
+ memset(data, 0, sizeof(MMUserDescription));
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/userDescription.def"
+
+ #ifdef PTPD_DBG
+ /* mMUserDescription_display(data, ptpClock); */
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMUserDescription( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ Octet pad = 0;
+ MMUserDescription* data = (MMUserDescription*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset); \
+ offset = offset + size;
+ #include "../def/managementTLV/userDescription.def"
+
+ /* is the TLV length odd? TLV must be even according to Spec 5.3.8 */
+ if(offset % 2) {
+ /* add pad of 1 according to Table 41 to make TLV length even */
+ packOctet(&pad, buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset);
+ offset = offset + 1;
+ }
+
+ /* return length */
+ return offset;
+}
+
+void
+freeMMUserDescription( MMUserDescription* data)
+{
+ #define OPERATE( name, size, type ) \
+ free##type( &data->name);
+ #include "../def/managementTLV/userDescription.def"
+}
+
+int unpackMMInitialize( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMInitialize));
+ MMInitialize* data = (MMInitialize*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/initialize.def"
+
+ #ifdef PTPD_DBG
+ mMInitialize_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMInitialize( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMInitialize* data = (MMInitialize*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/initialize.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMDefaultDataSet( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMDefaultDataSet));
+ MMDefaultDataSet* data = (MMDefaultDataSet*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/defaultDataSet.def"
+
+ #ifdef PTPD_DBG
+ mMDefaultDataSet_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMDefaultDataSet( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMDefaultDataSet* data = (MMDefaultDataSet*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/defaultDataSet.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMCurrentDataSet( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMCurrentDataSet));
+ MMCurrentDataSet* data = (MMCurrentDataSet*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/currentDataSet.def"
+
+ #ifdef PTPD_DBG
+ mMCurrentDataSet_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMCurrentDataSet( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMCurrentDataSet* data = (MMCurrentDataSet*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/currentDataSet.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMParentDataSet( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMParentDataSet));
+ MMParentDataSet* data = (MMParentDataSet*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/parentDataSet.def"
+
+ #ifdef PTPD_DBG
+ mMParentDataSet_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMParentDataSet( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMParentDataSet* data = (MMParentDataSet*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/parentDataSet.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMTimePropertiesDataSet( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMTimePropertiesDataSet));
+ MMTimePropertiesDataSet* data = (MMTimePropertiesDataSet*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/timePropertiesDataSet.def"
+
+ #ifdef PTPD_DBG
+ mMTimePropertiesDataSet_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMTimePropertiesDataSet( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMTimePropertiesDataSet* data = (MMTimePropertiesDataSet*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/timePropertiesDataSet.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMPortDataSet( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMPortDataSet));
+ MMPortDataSet* data = (MMPortDataSet*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/portDataSet.def"
+
+ #ifdef PTPD_DBG
+ mMPortDataSet_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMPortDataSet( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMPortDataSet* data = (MMPortDataSet*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/portDataSet.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMPriority1( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMPriority1));
+ MMPriority1* data = (MMPriority1*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/priority1.def"
+
+ #ifdef PTPD_DBG
+ mMPriority1_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMPriority1( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMPriority1* data = (MMPriority1*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/priority1.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMPriority2( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMPriority2));
+ MMPriority2* data = (MMPriority2*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/priority2.def"
+
+ #ifdef PTPD_DBG
+ mMPriority2_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMPriority2( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMPriority2* data = (MMPriority2*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/priority2.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMDomain( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMDomain));
+ MMDomain* data = (MMDomain*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/domain.def"
+
+ #ifdef PTPD_DBG
+ mMDomain_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMDomain( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMDomain* data = (MMDomain*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/domain.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMLogAnnounceInterval( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMLogAnnounceInterval));
+ MMLogAnnounceInterval* data = (MMLogAnnounceInterval*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/logAnnounceInterval.def"
+
+ #ifdef PTPD_DBG
+ mMLogAnnounceInterval_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMLogAnnounceInterval( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMLogAnnounceInterval* data = (MMLogAnnounceInterval*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/logAnnounceInterval.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMAnnounceReceiptTimeout( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField,sizeof(MMAnnounceReceiptTimeout));
+ MMAnnounceReceiptTimeout* data = (MMAnnounceReceiptTimeout*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/announceReceiptTimeout.def"
+
+ #ifdef PTPD_DBG
+ mMAnnounceReceiptTimeout_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMAnnounceReceiptTimeout( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMAnnounceReceiptTimeout* data = (MMAnnounceReceiptTimeout*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/announceReceiptTimeout.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMLogSyncInterval( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMLogSyncInterval));
+ MMLogSyncInterval* data = (MMLogSyncInterval*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/logSyncInterval.def"
+
+ #ifdef PTPD_DBG
+ mMLogSyncInterval_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMLogSyncInterval( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMLogSyncInterval* data = (MMLogSyncInterval*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/logSyncInterval.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMVersionNumber( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMVersionNumber));
+ MMVersionNumber* data = (MMVersionNumber*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/versionNumber.def"
+
+ #ifdef PTPD_DBG
+ mMVersionNumber_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMVersionNumber( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMVersionNumber* data = (MMVersionNumber*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/versionNumber.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMTime( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMTime));
+ MMTime* data = (MMTime*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/time.def"
+
+ #ifdef PTPD_DBG
+ mMTime_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMTime( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMTime* data = (MMTime*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/time.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMClockAccuracy( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMClockAccuracy));
+ MMClockAccuracy* data = (MMClockAccuracy*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/clockAccuracy.def"
+
+ #ifdef PTPD_DBG
+ mMClockAccuracy_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMClockAccuracy( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMClockAccuracy* data = (MMClockAccuracy*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/clockAccuracy.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMUtcProperties( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMUtcProperties));
+ MMUtcProperties* data = (MMUtcProperties*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/utcProperties.def"
+
+ #ifdef PTPD_DBG
+ mMUtcProperties_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMUtcProperties( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMUtcProperties* data = (MMUtcProperties*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/utcProperties.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMTraceabilityProperties( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMTraceabilityProperties));
+ MMTraceabilityProperties* data = (MMTraceabilityProperties*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/traceabilityProperties.def"
+
+ #ifdef PTPD_DBG
+ mMTraceabilityProperties_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMTraceabilityProperties( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMTraceabilityProperties* data = (MMTraceabilityProperties*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/traceabilityProperties.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMTimescaleProperties( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMTimescaleProperties));
+ MMTimescaleProperties* data = (MMTimescaleProperties*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/timescaleProperties.def"
+ data->ptp = (data->ptp & 0x08) >> 3;
+ #ifdef PTPD_DBG
+ mMTimescaleProperties_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMTimescaleProperties( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMTimescaleProperties* data = (MMTimescaleProperties*)m->tlv->dataField;
+ data->ptp = (data->ptp << 3) & 0x08;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/timescaleProperties.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMUnicastNegotiationEnable( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMUnicastNegotiationEnable));
+ MMUnicastNegotiationEnable* data = (MMUnicastNegotiationEnable*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/unicastNegotiationEnable.def"
+
+ #ifdef PTPD_DBG
+ mMUnicastNegotiationEnable_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMUnicastNegotiationEnable( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMUnicastNegotiationEnable* data = (MMUnicastNegotiationEnable*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/unicastNegotiationEnable.def"
+
+ /* return length*/
+ return offset;
+}
+
+
+int unpackMMDelayMechanism( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMDelayMechanism));
+ MMDelayMechanism* data = (MMDelayMechanism*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/delayMechanism.def"
+
+ #ifdef PTPD_DBG
+ mMDelayMechanism_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMDelayMechanism( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMDelayMechanism* data = (MMDelayMechanism*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/delayMechanism.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMLogMinPdelayReqInterval( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMLogMinPdelayReqInterval));
+ MMLogMinPdelayReqInterval* data = (MMLogMinPdelayReqInterval*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/logMinPdelayReqInterval.def"
+
+ #ifdef PTPD_DBG
+ mMLogMinPdelayReqInterval_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+UInteger16
+packMMLogMinPdelayReqInterval( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ MMLogMinPdelayReqInterval* data = (MMLogMinPdelayReqInterval*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/logMinPdelayReqInterval.def"
+
+ /* return length*/
+ return offset;
+}
+
+int unpackMMErrorStatus( Octet *buf, int baseOffset, MsgManagement* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->dataField, sizeof(MMErrorStatus));
+ MMErrorStatus* data = (MMErrorStatus*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ if(!bufGuard(PACKET_SIZE, (long)buf, m->header.messageLength, (long)(buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset), size)) return 0;\
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + TLV_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/errorStatus.def"
+
+ #ifdef PTPD_DBG
+ mMErrorStatus_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+ return 1;
+
+}
+
+void
+freeMMErrorStatus( MMErrorStatus* data)
+{
+ #define OPERATE( name, size, type ) \
+ free##type( &data->name);
+ #include "../def/managementTLV/errorStatus.def"
+}
+
+UInteger16
+packMMErrorStatus( MsgManagement* m, Octet *buf)
+{
+ int offset = 0;
+ Octet pad = 0;
+ MMErrorStatus* data = (MMErrorStatus*)m->tlv->dataField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/errorStatus.def"
+
+ /* is the TLV length odd? TLV must be even according to Spec 5.3.8 */
+ if(offset % 2) {
+ /* add pad of 1 according to Table 41 to make TLV length even */
+ packOctet(&pad, buf + MANAGEMENT_LENGTH + TLV_LENGTH + offset);
+ offset = offset + 1;
+ }
+
+ /* return length*/
+ return offset;
+}
+
+void
+unpackSMRequestUnicastTransmission( Octet *buf, MsgSignaling* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->valueField, sizeof(SMRequestUnicastTransmission));
+ SMRequestUnicastTransmission* data = (SMRequestUnicastTransmission*)m->tlv->valueField;
+ /* see src/def/README for a note on this X-macro */
+ #define OPERATE( name, size, type ) \
+ unpack##type( buf + SIGNALING_LENGTH + TL_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/signalingTLV/requestUnicastTransmission.def"
+
+ #ifdef PTPD_DBG
+ sMRequestUnicastTransmission_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+
+}
+
+UInteger16
+packSMRequestUnicastTransmission( MsgSignaling* m, Octet *buf)
+{
+ int offset = 0;
+ SMRequestUnicastTransmission* data = (SMRequestUnicastTransmission*)m->tlv->valueField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + SIGNALING_LENGTH + TL_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/signalingTLV/requestUnicastTransmission.def"
+
+ /* return length */
+ return offset;
+}
+
+void
+unpackSMGrantUnicastTransmission( Octet *buf, MsgSignaling* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->valueField, sizeof(SMGrantUnicastTransmission));
+ SMGrantUnicastTransmission* data = (SMGrantUnicastTransmission*)m->tlv->valueField;
+
+ /* see src/def/README for a note on this X-macro */
+ #define OPERATE( name, size, type ) \
+ unpack##type( buf + SIGNALING_LENGTH + TL_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/signalingTLV/requestUnicastTransmission.def"
+
+ #ifdef PTPD_DBG
+ sMGrantUnicastTransmission_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+}
+
+UInteger16
+packSMGrantUnicastTransmission( MsgSignaling* m, Octet *buf)
+{
+ int offset = 0;
+ SMGrantUnicastTransmission* data = (SMGrantUnicastTransmission*)m->tlv->valueField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + SIGNALING_LENGTH + TL_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/signalingTLV/grantUnicastTransmission.def"
+
+ /* return length */
+ return offset;
+}
+
+void
+unpackSMCancelUnicastTransmission( Octet *buf, MsgSignaling* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->valueField, sizeof(SMCancelUnicastTransmission));
+ SMCancelUnicastTransmission* data = (SMCancelUnicastTransmission*)m->tlv->valueField;
+ /* see src/def/README for a note on this X-macro */
+ #define OPERATE( name, size, type ) \
+ unpack##type( buf + SIGNALING_LENGTH + TL_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/signalingTLV/cancelUnicastTransmission.def"
+
+ #ifdef PTPD_DBG
+ sMCancelUnicastTransmission_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+}
+
+UInteger16
+packSMCancelUnicastTransmission( MsgSignaling* m, Octet *buf)
+{
+ int offset = 0;
+ SMCancelUnicastTransmission* data = (SMCancelUnicastTransmission*)m->tlv->valueField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + SIGNALING_LENGTH + TL_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/signalingTLV/cancelUnicastTransmission.def"
+
+ /* return length */
+ return offset;
+}
+
+void
+unpackSMAcknowledgeCancelUnicastTransmission( Octet *buf, MsgSignaling* m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv->valueField, sizeof(SMAcknowledgeCancelUnicastTransmission));
+ SMAcknowledgeCancelUnicastTransmission* data = (SMAcknowledgeCancelUnicastTransmission*)m->tlv->valueField;
+ /* see src/def/README for a note on this X-macro */
+ #define OPERATE( name, size, type ) \
+ unpack##type( buf + SIGNALING_LENGTH + TL_LENGTH + offset,\
+ &data->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/signalingTLV/acknowledgeCancelUnicastTransmission.def"
+
+ #ifdef PTPD_DBG
+ sMAcknowledgeCancelUnicastTransmission_display(data, ptpClock);
+ #endif /* PTPD_DBG */
+
+
+}
+
+UInteger16
+packSMAcknowledgeCancelUnicastTransmission( MsgSignaling* m, Octet *buf)
+{
+ int offset = 0;
+ SMAcknowledgeCancelUnicastTransmission* data = (SMAcknowledgeCancelUnicastTransmission*)m->tlv->valueField;
+ #define OPERATE( name, size, type ) \
+ pack##type( &data->name,\
+ buf + SIGNALING_LENGTH + TL_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/signalingTLV/acknowledgeCancelUnicastTransmission.def"
+
+ /* return length */
+ return offset;
+}
+
+void
+unpackClockIdentity( Octet *buf, ClockIdentity *c, PtpClock *ptpClock)
+{
+ int i;
+ for(i = 0; i < CLOCK_IDENTITY_LENGTH; i++) {
+ unpackOctet((buf+i),&((*c)[i]), ptpClock);
+ }
+}
+
+void packClockIdentity( ClockIdentity *c, Octet *buf)
+{
+ int i;
+ for(i = 0; i < CLOCK_IDENTITY_LENGTH; i++) {
+ packOctet(&((*c)[i]),(buf+i));
+ }
+}
+
+void
+freeClockIdentity( ClockIdentity *c) {
+ /* nothing to free */
+}
+
+void
+unpackClockQuality( Octet *buf, ClockQuality *c, PtpClock *ptpClock)
+{
+ int offset = 0;
+ ClockQuality* data = c;
+ #define OPERATE( name, size, type) \
+ unpack##type (buf + offset, &data->name, ptpClock); \
+ offset = offset + size;
+ #include "../def/derivedData/clockQuality.def"
+}
+
+void
+packClockQuality( ClockQuality *c, Octet *buf)
+{
+ int offset = 0;
+ ClockQuality *data = c;
+ #define OPERATE( name, size, type) \
+ pack##type (&data->name, buf + offset); \
+ offset = offset + size;
+ #include "../def/derivedData/clockQuality.def"
+}
+
+void
+freeClockQuality( ClockQuality *c)
+{
+ /* nothing to free */
+}
+
+void
+unpackTimeInterval( Octet *buf, TimeInterval *t, PtpClock *ptpClock)
+{
+ int offset = 0;
+ TimeInterval* data = t;
+ #define OPERATE( name, size, type) \
+ unpack##type (buf + offset, &data->name, ptpClock); \
+ offset = offset + size;
+ #include "../def/derivedData/timeInterval.def"
+}
+
+void
+packTimeInterval( TimeInterval *t, Octet *buf)
+{
+ int offset = 0;
+ TimeInterval *data = t;
+ #define OPERATE( name, size, type) \
+ pack##type (&data->name, buf + offset); \
+ offset = offset + size;
+ #include "../def/derivedData/timeInterval.def"
+}
+
+void
+freeTimeInterval( TimeInterval *t)
+{
+ /* nothing to free */
+}
+
+void
+unpackTimestamp( Octet *buf, Timestamp *t, PtpClock *ptpClock)
+{
+ int offset = 0;
+ Timestamp* data = t;
+ #define OPERATE( name, size, type) \
+ unpack##type (buf + offset, &data->name, ptpClock); \
+ offset = offset + size;
+ #include "../def/derivedData/timestamp.def"
+}
+
+void
+packTimestamp( Timestamp *t, Octet *buf)
+{
+ int offset = 0;
+ Timestamp *data = t;
+ #define OPERATE( name, size, type) \
+ pack##type (&data->name, buf + offset); \
+ offset = offset + size;
+ #include "../def/derivedData/timestamp.def"
+}
+
+void
+freeTimestamp( Timestamp *t)
+{
+ /* nothing to free */
+}
+
+
+void
+unpackPortIdentity( Octet *buf, PortIdentity *p, PtpClock *ptpClock)
+{
+ int offset = 0;
+ PortIdentity* data = p;
+ #define OPERATE( name, size, type) \
+ unpack##type (buf + offset, &data->name, ptpClock); \
+ offset = offset + size;
+ #include "../def/derivedData/portIdentity.def"
+}
+
+void
+packPortIdentity( PortIdentity *p, Octet *buf)
+{
+ int offset = 0;
+ PortIdentity *data = p;
+ #define OPERATE( name, size, type) \
+ pack##type (&data->name, buf + offset); \
+ offset = offset + size;
+ #include "../def/derivedData/portIdentity.def"
+}
+
+void
+freePortIdentity( PortIdentity *p)
+{
+ /* nothing to free */
+}
+
+void
+unpackPortAddress( Octet *buf, PortAddress *p, PtpClock *ptpClock)
+{
+ unpackEnumeration16( buf, &p->networkProtocol, ptpClock);
+ unpackUInteger16( buf+2, &p->addressLength, ptpClock);
+ if(p->addressLength) {
+ XMALLOC(p->addressField, p->addressLength);
+ memcpy( p->addressField, buf+4, p->addressLength);
+ } else {
+ p->addressField = NULL;
+ }
+}
+
+void
+packPortAddress(PortAddress *p, Octet *buf)
+{
+ packEnumeration16(&p->networkProtocol, buf);
+ packUInteger16(&p->addressLength, buf+2);
+ if(p->addressLength) {
+ memcpy( buf+4, p->addressField, p->addressLength);
+ }
+}
+
+void
+freePortAddress(PortAddress *p)
+{
+ if(p->addressField) {
+ free(p->addressField);
+ p->addressField = NULL;
+ }
+}
+
+void
+unpackPTPText( Octet *buf, PTPText *s, PtpClock *ptpClock)
+{
+ unpackUInteger8( buf, &s->lengthField, ptpClock);
+ if(s->lengthField) {
+ XMALLOC(s->textField, s->lengthField);
+ memcpy( s->textField, buf+1, s->lengthField);
+ } else {
+ s->textField = NULL;
+ }
+}
+
+void
+packPTPText(PTPText *s, Octet *buf)
+{
+ packUInteger8(&s->lengthField, buf);
+ if(s->lengthField) {
+ memcpy( buf+1, s->textField, s->lengthField);
+ }
+}
+
+void
+freePTPText(PTPText *s)
+{
+ if(s->textField) {
+ free(s->textField);
+ s->textField = NULL;
+ }
+}
+
+void
+unpackPhysicalAddress( Octet *buf, PhysicalAddress *p, PtpClock *ptpClock)
+{
+ unpackUInteger16( buf, &p->addressLength, ptpClock);
+ if(p->addressLength) {
+ XMALLOC(p->addressField, p->addressLength);
+ memcpy( p->addressField, buf+2, p->addressLength);
+ } else {
+ p->addressField = NULL;
+ }
+}
+
+void
+packPhysicalAddress(PhysicalAddress *p, Octet *buf)
+{
+ packUInteger16(&p->addressLength, buf);
+ if(p->addressLength) {
+ memcpy( buf+2, p->addressField, p->addressLength);
+ }
+}
+
+void
+freePhysicalAddress(PhysicalAddress *p)
+{
+ if(p->addressField) {
+ free(p->addressField);
+ p->addressField = NULL;
+ }
+}
+
+void
+copyClockIdentity( ClockIdentity dest, ClockIdentity src)
+{
+ memcpy(dest, src, CLOCK_IDENTITY_LENGTH);
+}
+
+void
+copyPortIdentity( PortIdentity *dest, PortIdentity *src)
+{
+ copyClockIdentity(dest->clockIdentity, src->clockIdentity);
+ dest->portNumber = src->portNumber;
+}
+
+void
+unpackMsgHeader(Octet *buf, MsgHeader *header, PtpClock *ptpClock)
+{
+ int offset = 0;
+ MsgHeader* data = header;
+ #define OPERATE( name, size, type) \
+ unpack##type (buf + offset, &data->name, ptpClock); \
+ offset = offset + size;
+ #include "../def/message/header.def"
+}
+
+void
+packMsgHeader(MsgHeader *h, Octet *buf)
+{
+ int offset = 0;
+
+ /* set uninitalized bytes to zero */
+ h->reserved0 = 0;
+ h->reserved1 = 0;
+ h->reserved2 = 0;
+
+ #define OPERATE( name, size, type ) \
+ pack##type( &h->name, buf + offset ); \
+ offset = offset + size;
+ #include "../def/message/header.def"
+}
+
+void
+unpackManagementTLV(Octet *buf, int baseOffset, MsgManagement *m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv, sizeof(ManagementTLV));
+ /* read the management TLV */
+ #define OPERATE( name, size, type ) \
+ unpack##type( buf + baseOffset + MANAGEMENT_LENGTH + offset, &m->tlv->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/managementTLV/managementTLV.def"
+}
+
+void
+packManagementTLV(ManagementTLV *tlv, Octet *buf)
+{
+ int offset = 0;
+ #define OPERATE( name, size, type ) \
+ pack##type( &tlv->name, buf + MANAGEMENT_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/managementTLV/managementTLV.def"
+}
+
+void
+freeManagementTLV(MsgManagement *m)
+{
+ /* cleanup outgoing managementTLV */
+ if(m->tlv) {
+ if(m->tlv->dataField) {
+ if(m->tlv->tlvType == TLV_MANAGEMENT) {
+ freeMMTLV(m->tlv);
+ } else if(m->tlv->tlvType == TLV_MANAGEMENT_ERROR_STATUS) {
+ freeMMErrorStatusTLV(m->tlv);
+ }
+ free(m->tlv->dataField);
+ m->tlv->dataField = NULL;
+ }
+ free(m->tlv);
+ m->tlv = NULL;
+ }
+}
+
+void
+packMsgManagement(MsgManagement *m, Octet *buf)
+{
+ int offset = 0;
+ MsgManagement *data = m;
+
+ /* set unitialized bytes to zero */
+ m->reserved0 = 0;
+ m->reserved1 = 0;
+
+ #define OPERATE( name, size, type) \
+ pack##type (&data->name, buf + offset); \
+ offset = offset + size;
+ #include "../def/message/management.def"
+
+}
+
+void unpackMsgManagement(Octet *buf, MsgManagement *m, PtpClock *ptpClock)
+{
+ int offset = 0;
+ MsgManagement* data = m;
+ #define OPERATE( name, size, type) \
+ unpack##type (buf + offset, &data->name, ptpClock); \
+ offset = offset + size;
+ #include "../def/message/management.def"
+
+ #ifdef PTPD_DBG
+ msgManagement_display(data);
+ #endif /* PTPD_DBG */
+}
+
+void
+unpackSignalingTLV(Octet *buf, MsgSignaling *m, PtpClock* ptpClock)
+{
+ int offset = 0;
+ XMALLOC(m->tlv, sizeof(SignalingTLV));
+ /* read the signaling TLV */
+ #define OPERATE( name, size, type ) \
+ unpack##type( buf + SIGNALING_LENGTH + offset, &m->tlv->name, ptpClock ); \
+ offset = offset + size;
+ #include "../def/signalingTLV/signalingTLV.def"
+}
+
+void
+packSignalingTLV(SignalingTLV *tlv, Octet *buf)
+{
+ int offset = 0;
+ #define OPERATE( name, size, type ) \
+ pack##type( &tlv->name, buf + SIGNALING_LENGTH + offset ); \
+ offset = offset + size;
+ #include "../def/signalingTLV/signalingTLV.def"
+}
+
+void
+freeSignalingTLV(MsgSignaling *m)
+{
+ /* cleanup outgoing signaling TLV */
+ if(m->tlv) {
+ if(m->tlv->valueField) {
+ free(m->tlv->valueField);
+ m->tlv->valueField = NULL;
+ }
+ free(m->tlv);
+ m->tlv = NULL;
+ }
+}
+
+void
+packMsgSignaling(MsgSignaling *m, Octet *buf)
+{
+ int offset = 0;
+ MsgSignaling *data = m;
+
+ #define OPERATE( name, size, type) \
+ pack##type (&data->name, buf + offset); \
+ offset = offset + size;
+ #include "../def/message/signaling.def"
+
+}
+
+void
+unpackMsgSignaling(Octet *buf, MsgSignaling *m, PtpClock *ptpClock)
+{
+ int offset = 0;
+ MsgSignaling* data = m;
+ #define OPERATE( name, size, type) \
+ unpack##type (buf + offset, &data->name, ptpClock); \
+ offset = offset + size;
+ #include "../def/message/signaling.def"
+
+ #ifdef PTPD_DBG
+ msgSignaling_display(data);
+ #endif /* PTPD_DBG */
+}
+
+/*Unpack Header from IN buffer to msgTmpHeader field */
+void
+msgUnpackHeader(Octet * buf, MsgHeader * header)
+{
+ header->transportSpecific = (*(Nibble *) (buf + 0)) >> 4;
+ header->messageType = (*(Enumeration4 *) (buf + 0)) & 0x0F;
+ header->versionPTP = (*(UInteger4 *) (buf + 1)) & 0x0F;
+ /* force reserved bit to zero if not */
+ header->messageLength = flip16(*(UInteger16 *) (buf + 2));
+ header->domainNumber = (*(UInteger8 *) (buf + 4));
+ header->flagField0 = (*(Octet *) (buf + 6));
+ header->flagField1 = (*(Octet *) (buf + 7));
+ memcpy(&header->correctionField.msb, (buf + 8), 4);
+ memcpy(&header->correctionField.lsb, (buf + 12), 4);
+ header->correctionField.msb = flip32(header->correctionField.msb);
+ header->correctionField.lsb = flip32(header->correctionField.lsb);
+ copyClockIdentity(header->sourcePortIdentity.clockIdentity, (buf + 20));
+ header->sourcePortIdentity.portNumber =
+ flip16(*(UInteger16 *) (buf + 28));
+ header->sequenceId = flip16(*(UInteger16 *) (buf + 30));
+ header->controlField = (*(UInteger8 *) (buf + 32));
+ header->logMessageInterval = (*(Integer8 *) (buf + 33));
+
+#ifdef PTPD_DBG
+ msgHeader_display(header);
+#endif /* PTPD_DBG */
+}
+
+/*Pack header message into OUT buffer of ptpClock*/
+void
+msgPackHeader(Octet * buf, PtpClock * ptpClock)
+{
+
+ /* (spec annex D) */
+ *(UInteger8 *) (buf + 0) = ptpClock->portDS.transportSpecific << 4;
+ *(UInteger4 *) (buf + 1) = ptpClock->portDS.versionNumber;
+ *(UInteger8 *) (buf + 4) = ptpClock->defaultDS.domainNumber;
+ /* clear flag field - message packing functions should populate it */
+ memset((buf + 6), 0, 2);
+
+ memset((buf + 8), 0, 8);
+ copyClockIdentity((buf + 20), ptpClock->portDS.portIdentity.clockIdentity);
+ *(UInteger16 *) (buf + 28) = flip16(ptpClock->portDS.portIdentity.portNumber);
+ /* LogMessageInterval defaults to 0x7F, will be set to another value if needed as per table 24*/
+ *(UInteger8 *) (buf + 33) = 0x7F;
+}
+
+
+#ifndef PTPD_SLAVE_ONLY
+/*Pack SYNC message into OUT buffer of ptpClock*/
+void
+msgPackSync(Octet * buf, UInteger16 sequenceId, Timestamp * originTimestamp, PtpClock * ptpClock)
+{
+ msgPackHeader(buf, ptpClock);
+
+ /* changes in header */
+ *(char *)(buf + 0) = *(char *)(buf + 0) & 0xF0;
+ /* RAZ messageType */
+ *(char *)(buf + 0) = *(char *)(buf + 0) | 0x00;
+ /* Two step flag - table 20: Sync and PdelayResp only */
+ if (ptpClock->defaultDS.twoStepFlag)
+ *(UInteger8 *) (buf + 6) |= PTP_TWO_STEP;
+ /* Table 19 */
+ *(UInteger16 *) (buf + 2) = flip16(SYNC_LENGTH);
+ *(UInteger16 *) (buf + 30) = flip16(sequenceId);
+ *(UInteger8 *) (buf + 32) = 0x00;
+
+ /* Table 24 - unless it's multicast, logMessageInterval remains 0x7F */
+ if(rtOpts.transport == IEEE_802_3 || rtOpts.ipMode != IPMODE_UNICAST )
+ *(Integer8 *) (buf + 33) = ptpClock->portDS.logSyncInterval;
+ memset((buf + 8), 0, 8);
+
+ /* Sync message */
+ *(UInteger16 *) (buf + 34) = flip16(originTimestamp->secondsField.msb);
+ *(UInteger32 *) (buf + 36) = flip32(originTimestamp->secondsField.lsb);
+ *(UInteger32 *) (buf + 40) = flip32(originTimestamp->nanosecondsField);
+}
+#endif /* PTPD_SLAVE_ONLY */
+
+/*Unpack Sync message from IN buffer */
+void
+msgUnpackSync(Octet * buf, MsgSync * sync)
+{
+ sync->originTimestamp.secondsField.msb =
+ flip16(*(UInteger16 *) (buf + 34));
+ sync->originTimestamp.secondsField.lsb =
+ flip32(*(UInteger32 *) (buf + 36));
+ sync->originTimestamp.nanosecondsField =
+ flip32(*(UInteger32 *) (buf + 40));
+
+#ifdef PTPD_DBG
+ msgSync_display(sync);
+#endif /* PTPD_DBG */
+}
+
+
+/* When building slave only, this code does not get compiled */
+#ifndef PTPD_SLAVE_ONLY
+/*Pack Announce message into OUT buffer of ptpClock*/
+void
+msgPackAnnounce(Octet * buf, UInteger16 sequenceId, Timestamp * originTimestamp, PtpClock * ptpClock)
+{
+ UInteger16 stepsRemoved;
+
+ msgPackHeader(buf, ptpClock);
+
+ /* changes in header */
+ *(char *)(buf + 0) = *(char *)(buf + 0) & 0xF0;
+ /* RAZ messageType */
+ *(char *)(buf + 0) = *(char *)(buf + 0) | 0x0B;
+ /* Table 19 */
+ *(UInteger16 *) (buf + 2) = flip16(ANNOUNCE_LENGTH);
+ *(UInteger16 *) (buf + 30) = flip16(sequenceId);
+ *(UInteger8 *) (buf + 32) = 0x05;
+ /* Table 24: for Announce, logMessageInterval is never 0x7F */
+ *(Integer8 *) (buf + 33) = ptpClock->portDS.logAnnounceInterval;
+
+ /* Announce message */
+ *(UInteger16 *) (buf + 34) = flip16(originTimestamp->secondsField.msb);
+ *(UInteger32 *) (buf + 36) = flip32(originTimestamp->secondsField.lsb);
+ *(UInteger32 *) (buf + 40) = flip32(originTimestamp->nanosecondsField);
+
+ *(Integer16 *) (buf + 44) = flip16(ptpClock->timePropertiesDS.currentUtcOffset);
+ *(UInteger8 *) (buf + 47) = ptpClock->parentDS.grandmasterPriority1;
+ *(UInteger8 *) (buf + 48) = ptpClock->defaultDS.clockQuality.clockClass;
+ *(Enumeration8 *) (buf + 49) = ptpClock->defaultDS.clockQuality.clockAccuracy;
+ *(UInteger16 *) (buf + 50) =
+ flip16(ptpClock->defaultDS.clockQuality.offsetScaledLogVariance);
+ *(UInteger8 *) (buf + 52) = ptpClock->parentDS.grandmasterPriority2;
+ copyClockIdentity((buf + 53), ptpClock->parentDS.grandmasterIdentity);
+ /* resolve bugs #37 and #40 - alignment errors on ARMv5 */
+ stepsRemoved = flip16(ptpClock->currentDS.stepsRemoved);
+ memcpy(buf + 61, &stepsRemoved, sizeof(UInteger16));
+ *(Enumeration8 *) (buf + 63) = ptpClock->timePropertiesDS.timeSource;
+
+ /*
+ * TimePropertiesDS in FlagField, 2nd octet - spec 13.3.2.6 table 20
+ * Could / should have used constants here PTP_LI_61 etc, but this is clean
+ */
+ *(UInteger8*) (buf + 7) = ptpClock->timePropertiesDS.leap61 << 0;
+ *(UInteger8*) (buf + 7) |= (ptpClock->timePropertiesDS.leap59) << 1;
+ *(UInteger8*) (buf + 7) |= (ptpClock->timePropertiesDS.currentUtcOffsetValid) << 2;
+ *(UInteger8*) (buf + 7) |= (ptpClock->timePropertiesDS.ptpTimescale) << 3;
+ *(UInteger8*) (buf + 7) |= (ptpClock->timePropertiesDS.timeTraceable) << 4;
+ *(UInteger8*) (buf + 7) |= (ptpClock->timePropertiesDS.frequencyTraceable) << 5;
+}
+#endif /* PTPD_SLAVE_ONLY */
+
+/*Unpack Announce message from IN buffer of ptpClock to msgtmp.Announce*/
+void
+msgUnpackAnnounce(Octet * buf, MsgAnnounce * announce)
+{
+ UInteger16 stepsRemoved;
+
+ announce->originTimestamp.secondsField.msb =
+ flip16(*(UInteger16 *) (buf + 34));
+ announce->originTimestamp.secondsField.lsb =
+ flip32(*(UInteger32 *) (buf + 36));
+ announce->originTimestamp.nanosecondsField =
+ flip32(*(UInteger32 *) (buf + 40));
+ announce->currentUtcOffset = flip16(*(UInteger16 *) (buf + 44));
+ announce->grandmasterPriority1 = *(UInteger8 *) (buf + 47);
+ announce->grandmasterClockQuality.clockClass =
+ *(UInteger8 *) (buf + 48);
+ announce->grandmasterClockQuality.clockAccuracy =
+ *(Enumeration8 *) (buf + 49);
+ announce->grandmasterClockQuality.offsetScaledLogVariance =
+ flip16(*(UInteger16 *) (buf + 50));
+ announce->grandmasterPriority2 = *(UInteger8 *) (buf + 52);
+ copyClockIdentity(announce->grandmasterIdentity, (buf + 53));
+ /* resolve bugs #37 and #40 - alignment errors on ARMv5 */
+ memcpy(&stepsRemoved, buf + 61, sizeof(UInteger16));
+ announce->stepsRemoved = flip16(stepsRemoved);
+ announce->timeSource = *(Enumeration8 *) (buf + 63);
+
+ #ifdef PTPD_DBG
+ msgAnnounce_display(announce);
+ #endif /* PTPD_DBG */
+}
+
+#ifndef PTPD_SLAVE_ONLY /* does not get compiled when building slave only */
+/*pack Follow_up message into OUT buffer of ptpClock*/
+void
+msgPackFollowUp(Octet * buf, Timestamp * preciseOriginTimestamp, PtpClock * ptpClock, const UInteger16 sequenceId)
+{
+ msgPackHeader(buf, ptpClock);
+
+ /* changes in header */
+ *(char *)(buf + 0) = *(char *)(buf + 0) & 0xF0;
+ /* RAZ messageType */
+ *(char *)(buf + 0) = *(char *)(buf + 0) | 0x08;
+ /* Table 19 */
+ *(UInteger16 *) (buf + 2) = flip16(FOLLOW_UP_LENGTH);
+ *(UInteger16 *) (buf + 30) = flip16(sequenceId);
+ *(UInteger8 *) (buf + 32) = 0x02;
+
+ /* Table 24 - unless it's multicast, logMessageInterval remains 0x7F */
+ if(rtOpts.transport == IEEE_802_3 || rtOpts.ipMode != IPMODE_UNICAST)
+ *(Integer8 *) (buf + 33) = ptpClock->portDS.logSyncInterval;
+
+ /* Follow_up message */
+ *(UInteger16 *) (buf + 34) =
+ flip16(preciseOriginTimestamp->secondsField.msb);
+ *(UInteger32 *) (buf + 36) =
+ flip32(preciseOriginTimestamp->secondsField.lsb);
+ *(UInteger32 *) (buf + 40) =
+ flip32(preciseOriginTimestamp->nanosecondsField);
+}
+#endif /* PTPD_SLAVE_ONLY */
+
+/*Unpack Follow_up message from IN buffer of ptpClock to msgtmp.follow*/
+void
+msgUnpackFollowUp(Octet * buf, MsgFollowUp * follow)
+{
+ follow->preciseOriginTimestamp.secondsField.msb =
+ flip16(*(UInteger16 *) (buf + 34));
+ follow->preciseOriginTimestamp.secondsField.lsb =
+ flip32(*(UInteger32 *) (buf + 36));
+ follow->preciseOriginTimestamp.nanosecondsField =
+ flip32(*(UInteger32 *) (buf + 40));
+
+ #ifdef PTPD_DBG
+ msgFollowUp_display(follow);
+ #endif /* PTPD_DBG */
+}
+
+
+/*pack PdelayReq message into OUT buffer of ptpClock*/
+void
+msgPackPdelayReq(Octet * buf, Timestamp * originTimestamp, PtpClock * ptpClock)
+{
+ msgPackHeader(buf, ptpClock);
+
+ /* changes in header */
+ *(char *)(buf + 0) = *(char *)(buf + 0) & 0xF0;
+ /* RAZ messageType */
+ *(char *)(buf + 0) = *(char *)(buf + 0) | 0x02;
+ /* Table 19 */
+ *(UInteger16 *) (buf + 2) = flip16(PDELAY_REQ_LENGTH);
+ *(UInteger16 *) (buf + 30) = flip16(ptpClock->sentPdelayReqSequenceId);
+ *(UInteger8 *) (buf + 32) = 0x05;
+ /* Table 23 */
+ *(Integer8 *) (buf + 33) = 0x7F;
+ /* Table 24 */
+ memset((buf + 8), 0, 8);
+
+ /* Pdelay_req message */
+ *(UInteger16 *) (buf + 34) = flip16(originTimestamp->secondsField.msb);
+ *(UInteger32 *) (buf + 36) = flip32(originTimestamp->secondsField.lsb);
+ *(UInteger32 *) (buf + 40) = flip32(originTimestamp->nanosecondsField);
+
+ memset((buf + 44), 0, 10);
+ /* RAZ reserved octets */
+}
+
+/*pack delayReq message into OUT buffer of ptpClock*/
+void
+msgPackDelayReq(Octet * buf, Timestamp * originTimestamp, PtpClock * ptpClock)
+{
+ msgPackHeader(buf, ptpClock);
+
+ /* changes in header */
+ *(char *)(buf + 0) = *(char *)(buf + 0) & 0xF0;
+ /* RAZ messageType */
+ *(char *)(buf + 0) = *(char *)(buf + 0) | 0x01;
+ /* Table 19 */
+ *(UInteger16 *) (buf + 2) = flip16(DELAY_REQ_LENGTH);
+
+ /* -- PTP_UNICAST flag will be set in netsend* if needed */
+
+ *(UInteger16 *) (buf + 30) = flip16(ptpClock->sentDelayReqSequenceId);
+ *(UInteger8 *) (buf + 32) = 0x01;
+ /* Table 23 */
+ *(Integer8 *) (buf + 33) = 0x7F;
+ /* Table 24 */
+ memset((buf + 8), 0, 8);
+
+ /* Pdelay_req message */
+ *(UInteger16 *) (buf + 34) = flip16(originTimestamp->secondsField.msb);
+ *(UInteger32 *) (buf + 36) = flip32(originTimestamp->secondsField.lsb);
+ *(UInteger32 *) (buf + 40) = flip32(originTimestamp->nanosecondsField);
+}
+
+/*pack delayResp message into OUT buffer of ptpClock*/
+void
+msgPackDelayResp(Octet * buf, MsgHeader * header, Timestamp * receiveTimestamp, PtpClock * ptpClock)
+{
+ msgPackHeader(buf, ptpClock);
+
+ /* changes in header */
+ *(char *)(buf + 0) = *(char *)(buf + 0) & 0xF0;
+ /* RAZ messageType */
+ *(char *)(buf + 0) = *(char *)(buf + 0) | 0x09;
+ /* Table 19 */
+ *(UInteger16 *) (buf + 2) = flip16(DELAY_RESP_LENGTH);
+ *(UInteger8 *) (buf + 4) = header->domainNumber;
+
+ /* -- PTP_UNICAST flag will be set in netsend* if needed */
+
+ memset((buf + 8), 0, 8);
+
+ /* Copy correctionField of PdelayReqMessage */
+ *(Integer32 *) (buf + 8) = flip32(header->correctionField.msb);
+ *(Integer32 *) (buf + 12) = flip32(header->correctionField.lsb);
+
+ *(UInteger16 *) (buf + 30) = flip16(header->sequenceId);
+
+ *(UInteger8 *) (buf + 32) = 0x03;
+
+ /* Table 24 - unless it's multicast, logMessageInterval remains 0x7F */
+ /* really tempting to cheat here, at least for hybrid, but standard is a standard */
+ if ((header->flagField0 & PTP_UNICAST) != PTP_UNICAST) {
+ *(Integer8 *) (buf + 33) = ptpClock->portDS.logMinDelayReqInterval;
+ }
+
+ /* Pdelay_resp message */
+ *(UInteger16 *) (buf + 34) =
+ flip16(receiveTimestamp->secondsField.msb);
+ *(UInteger32 *) (buf + 36) = flip32(receiveTimestamp->secondsField.lsb);
+ *(UInteger32 *) (buf + 40) = flip32(receiveTimestamp->nanosecondsField);
+ copyClockIdentity((buf + 44), header->sourcePortIdentity.clockIdentity);
+ *(UInteger16 *) (buf + 52) =
+ flip16(header->sourcePortIdentity.portNumber);
+}
+
+/*pack PdelayResp message into OUT buffer of ptpClock*/
+void
+msgPackPdelayResp(Octet * buf, MsgHeader * header, Timestamp * requestReceiptTimestamp, PtpClock * ptpClock)
+{
+ msgPackHeader(buf, ptpClock);
+
+ /* changes in header */
+ *(char *)(buf + 0) = *(char *)(buf + 0) & 0xF0;
+ /* RAZ messageType */
+ *(char *)(buf + 0) = *(char *)(buf + 0) | 0x03;
+ /* Two step flag - table 20: Sync and PdelayResp only */
+ if (ptpClock->defaultDS.twoStepFlag)
+ *(UInteger8 *) (buf + 6) |= PTP_TWO_STEP;
+ /* Table 19 */
+ *(UInteger16 *) (buf + 2) = flip16(PDELAY_RESP_LENGTH);
+ *(UInteger8 *) (buf + 4) = header->domainNumber;
+ memset((buf + 8), 0, 8);
+
+
+ *(UInteger16 *) (buf + 30) = flip16(header->sequenceId);
+
+ *(UInteger8 *) (buf + 32) = 0x05;
+ /* Table 23 */
+ *(Integer8 *) (buf + 33) = 0x7F;
+ /* Table 24 */
+
+ /* Pdelay_resp message */
+ *(UInteger16 *) (buf + 34) = flip16(requestReceiptTimestamp->secondsField.msb);
+ *(UInteger32 *) (buf + 36) = flip32(requestReceiptTimestamp->secondsField.lsb);
+ *(UInteger32 *) (buf + 40) = flip32(requestReceiptTimestamp->nanosecondsField);
+ copyClockIdentity((buf + 44), header->sourcePortIdentity.clockIdentity);
+ *(UInteger16 *) (buf + 52) = flip16(header->sourcePortIdentity.portNumber);
+
+}
+
+
+/*Unpack delayReq message from IN buffer of ptpClock to msgtmp.req*/
+void
+msgUnpackDelayReq(Octet * buf, MsgDelayReq * delayreq)
+{
+ delayreq->originTimestamp.secondsField.msb =
+ flip16(*(UInteger16 *) (buf + 34));
+ delayreq->originTimestamp.secondsField.lsb =
+ flip32(*(UInteger32 *) (buf + 36));
+ delayreq->originTimestamp.nanosecondsField =
+ flip32(*(UInteger32 *) (buf + 40));
+
+ #ifdef PTPD_DBG
+ msgDelayReq_display(delayreq);
+ #endif /* PTPD_DBG */
+
+}
+
+
+/*Unpack PdelayReq message from IN buffer of ptpClock to msgtmp.req*/
+void
+msgUnpackPdelayReq(Octet * buf, MsgPdelayReq * pdelayreq)
+{
+ pdelayreq->originTimestamp.secondsField.msb =
+ flip16(*(UInteger16 *) (buf + 34));
+ pdelayreq->originTimestamp.secondsField.lsb =
+ flip32(*(UInteger32 *) (buf + 36));
+ pdelayreq->originTimestamp.nanosecondsField =
+ flip32(*(UInteger32 *) (buf + 40));
+
+ #ifdef PTPD_DBG
+ msgPdelayReq_display(pdelayreq);
+ #endif /* PTPD_DBG */
+
+}
+
+
+/*Unpack delayResp message from IN buffer of ptpClock to msgtmp.presp*/
+void
+msgUnpackDelayResp(Octet * buf, MsgDelayResp * resp)
+{
+ resp->receiveTimestamp.secondsField.msb =
+ flip16(*(UInteger16 *) (buf + 34));
+ resp->receiveTimestamp.secondsField.lsb =
+ flip32(*(UInteger32 *) (buf + 36));
+ resp->receiveTimestamp.nanosecondsField =
+ flip32(*(UInteger32 *) (buf + 40));
+ copyClockIdentity(resp->requestingPortIdentity.clockIdentity,
+ (buf + 44));
+ resp->requestingPortIdentity.portNumber =
+ flip16(*(UInteger16 *) (buf + 52));
+
+ #ifdef PTPD_DBG
+ msgDelayResp_display(resp);
+ #endif /* PTPD_DBG */
+}
+
+
+/*Unpack PdelayResp message from IN buffer of ptpClock to msgtmp.presp*/
+void
+msgUnpackPdelayResp(Octet * buf, MsgPdelayResp * presp)
+{
+ presp->requestReceiptTimestamp.secondsField.msb =
+ flip16(*(UInteger16 *) (buf + 34));
+ presp->requestReceiptTimestamp.secondsField.lsb =
+ flip32(*(UInteger32 *) (buf + 36));
+ presp->requestReceiptTimestamp.nanosecondsField =
+ flip32(*(UInteger32 *) (buf + 40));
+ copyClockIdentity(presp->requestingPortIdentity.clockIdentity,
+ (buf + 44));
+ presp->requestingPortIdentity.portNumber =
+ flip16(*(UInteger16 *) (buf + 52));
+
+ #ifdef PTPD_DBG
+ msgPdelayResp_display(presp);
+ #endif /* PTPD_DBG */
+}
+
+/*pack PdelayRespfollowup message into OUT buffer of ptpClock*/
+void
+msgPackPdelayRespFollowUp(Octet * buf, MsgHeader * header, Timestamp * responseOriginTimestamp, PtpClock * ptpClock, const UInteger16 sequenceId)
+{
+ msgPackHeader(buf, ptpClock);
+
+ /* changes in header */
+ *(char *)(buf + 0) = *(char *)(buf + 0) & 0xF0;
+ /* RAZ messageType */
+ *(char *)(buf + 0) = *(char *)(buf + 0) | 0x0A;
+ /* Table 19 */
+ *(UInteger16 *) (buf + 2) = flip16(PDELAY_RESP_FOLLOW_UP_LENGTH);
+ *(UInteger16 *) (buf + 30) = flip16(sequenceId);
+ *(UInteger8 *) (buf + 32) = 0x05;
+ /* Table 23 */
+ *(Integer8 *) (buf + 33) = 0x7F;
+ /* Table 24 */
+
+ /* Copy correctionField of PdelayReqMessage */
+ *(Integer32 *) (buf + 8) = flip32(header->correctionField.msb);
+ *(Integer32 *) (buf + 12) = flip32(header->correctionField.lsb);
+
+ /* Pdelay_resp_follow_up message */
+ *(UInteger16 *) (buf + 34) =
+ flip16(responseOriginTimestamp->secondsField.msb);
+ *(UInteger32 *) (buf + 36) =
+ flip32(responseOriginTimestamp->secondsField.lsb);
+ *(UInteger32 *) (buf + 40) =
+ flip32(responseOriginTimestamp->nanosecondsField);
+ copyClockIdentity((buf + 44), header->sourcePortIdentity.clockIdentity);
+ *(UInteger16 *) (buf + 52) =
+ flip16(header->sourcePortIdentity.portNumber);
+}
+
+/*Unpack PdelayResp message from IN buffer of ptpClock to msgtmp.presp*/
+void
+msgUnpackPdelayRespFollowUp(Octet * buf, MsgPdelayRespFollowUp * prespfollow)
+{
+ prespfollow->responseOriginTimestamp.secondsField.msb =
+ flip16(*(UInteger16 *) (buf + 34));
+ prespfollow->responseOriginTimestamp.secondsField.lsb =
+ flip32(*(UInteger32 *) (buf + 36));
+ prespfollow->responseOriginTimestamp.nanosecondsField =
+ flip32(*(UInteger32 *) (buf + 40));
+ copyClockIdentity(prespfollow->requestingPortIdentity.clockIdentity,
+ (buf + 44));
+ prespfollow->requestingPortIdentity.portNumber =
+ flip16(*(UInteger16 *) (buf + 52));
+
+#ifdef PTPD_DBG
+ msgPdelayRespFollowUp_display(prespfollow);
+#endif /* PTPD_DBG */
+}
+
+/* Pack Management message into OUT buffer */
+void
+msgPackManagementTLV(Octet *buf, MsgManagement *outgoing, PtpClock *ptpClock)
+{
+ DBGV("packing ManagementTLV message \n");
+
+ UInteger16 dataLength = 0;
+
+ switch(outgoing->tlv->managementId)
+ {
+ case MM_NULL_MANAGEMENT:
+ case MM_SAVE_IN_NON_VOLATILE_STORAGE:
+ case MM_RESET_NON_VOLATILE_STORAGE:
+ case MM_ENABLE_PORT:
+ case MM_DISABLE_PORT:
+ dataLength = 0;
+ break;
+ case MM_CLOCK_DESCRIPTION:
+ dataLength = packMMClockDescription(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMClockDescription_display(
+ (MMClockDescription*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_USER_DESCRIPTION:
+ dataLength = packMMUserDescription(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMUserDescription_display(
+ (MMUserDescription*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_INITIALIZE:
+ dataLength = packMMInitialize(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMInitialize_display(
+ (MMInitialize*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_DEFAULT_DATA_SET:
+ dataLength = packMMDefaultDataSet(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMDefaultDataSet_display(
+ (MMDefaultDataSet*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_CURRENT_DATA_SET:
+ dataLength = packMMCurrentDataSet(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMCurrentDataSet_display(
+ (MMCurrentDataSet*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_PARENT_DATA_SET:
+ dataLength = packMMParentDataSet(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMParentDataSet_display(
+ (MMParentDataSet*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_TIME_PROPERTIES_DATA_SET:
+ dataLength = packMMTimePropertiesDataSet(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMTimePropertiesDataSet_display(
+ (MMTimePropertiesDataSet*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_PORT_DATA_SET:
+ dataLength = packMMPortDataSet(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMPortDataSet_display(
+ (MMPortDataSet*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_PRIORITY1:
+ dataLength = packMMPriority1(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMPriority1_display(
+ (MMPriority1*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_PRIORITY2:
+ dataLength = packMMPriority2(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMPriority2_display(
+ (MMPriority2*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_DOMAIN:
+ dataLength = packMMDomain(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMDomain_display(
+ (MMDomain*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_SLAVE_ONLY:
+ dataLength = packMMSlaveOnly(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMSlaveOnly_display(
+ (MMSlaveOnly*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_LOG_ANNOUNCE_INTERVAL:
+ dataLength = packMMLogAnnounceInterval(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMLogAnnounceInterval_display(
+ (MMLogAnnounceInterval*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_ANNOUNCE_RECEIPT_TIMEOUT:
+ dataLength = packMMAnnounceReceiptTimeout(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMAnnounceReceiptTimeout_display(
+ (MMAnnounceReceiptTimeout*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_LOG_SYNC_INTERVAL:
+ dataLength = packMMLogSyncInterval(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMLogSyncInterval_display(
+ (MMLogSyncInterval*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_VERSION_NUMBER:
+ dataLength = packMMVersionNumber(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMVersionNumber_display(
+ (MMVersionNumber*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_TIME:
+ dataLength = packMMTime(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMTime_display(
+ (MMTime*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_CLOCK_ACCURACY:
+ dataLength = packMMClockAccuracy(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMClockAccuracy_display(
+ (MMClockAccuracy*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_UTC_PROPERTIES:
+ dataLength = packMMUtcProperties(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMUtcProperties_display(
+ (MMUtcProperties*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_TRACEABILITY_PROPERTIES:
+ dataLength = packMMTraceabilityProperties(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMTraceabilityProperties_display(
+ (MMTraceabilityProperties*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_UNICAST_NEGOTIATION_ENABLE:
+ dataLength = packMMUnicastNegotiationEnable(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMUnicastNegotiationEnable_display(
+ (MMUnicastNegotiationEnable*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_DELAY_MECHANISM:
+ dataLength = packMMDelayMechanism(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMDelayMechanism_display(
+ (MMDelayMechanism*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ case MM_LOG_MIN_PDELAY_REQ_INTERVAL:
+ dataLength = packMMLogMinPdelayReqInterval(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMLogMinPdelayReqInterval_display(
+ (MMLogMinPdelayReqInterval*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+ break;
+ default:
+ DBGV("packing management msg: unsupported id \n");
+ }
+
+ /* set the outgoing tlv lengthField to 2 + N where 2 is the managementId field
+ * and N is dataLength, the length of the management tlv dataField field.
+ * See Table 39 of the spec.
+ */
+ outgoing->tlv->lengthField = 2 + dataLength;
+
+ packManagementTLV((ManagementTLV*)outgoing->tlv, buf);
+}
+
+/* Pack ManagementErrorStatusTLV message into OUT buffer */
+void
+msgPackManagementErrorStatusTLV(Octet *buf, MsgManagement *outgoing,
+ PtpClock *ptpClock)
+{
+ DBGV("packing ManagementErrorStatusTLV message \n");
+
+ UInteger16 dataLength = 0;
+
+ dataLength = packMMErrorStatus(outgoing, buf);
+ #ifdef PTPD_DBG
+ mMErrorStatus_display((MMErrorStatus*)outgoing->tlv->dataField, ptpClock);
+ #endif /* PTPD_DBG */
+
+ /* set the outgoing tlv lengthField to 2 + (6 + N) where 2 is the
+ * managementErrorId field and (6 + N) is dataLength, where 6 is
+ * the managementId and reserved field and N is the displayData field
+ * and optional pad field. See Table 71 of the spec.
+ */
+ outgoing->tlv->lengthField = 2 + dataLength;
+
+ packManagementTLV((ManagementTLV*)outgoing->tlv, buf);
+}
+
+/* Pack singaling message into OUT buffer */
+void
+msgPackSignalingTLV(Octet *buf, MsgSignaling *outgoing, PtpClock *ptpClock)
+{
+ DBGV("packing SignalingTLV message \n");
+
+ UInteger16 dataLength = 0;
+
+ switch(outgoing->tlv->tlvType)
+ {
+ case TLV_REQUEST_UNICAST_TRANSMISSION:
+ dataLength = packSMRequestUnicastTransmission(outgoing, buf);
+ break;
+ case TLV_GRANT_UNICAST_TRANSMISSION:
+ dataLength = packSMGrantUnicastTransmission(outgoing, buf);
+ break;
+ case TLV_CANCEL_UNICAST_TRANSMISSION:
+ dataLength = packSMCancelUnicastTransmission(outgoing, buf);
+ break;
+ case TLV_ACKNOWLEDGE_CANCEL_UNICAST_TRANSMISSION:
+ dataLength = packSMAcknowledgeCancelUnicastTransmission(outgoing, buf);
+ break;
+ default:
+ DBGV("packing signaling msg: unsupported tlv type \n");
+ };
+ /* set the outgoing tlv lengthField to 2 + N where 2 is the managementId field
+ * and N is dataLength, the length of the management tlv dataField field.
+ * See Table 39 of the spec.
+ */
+ outgoing->tlv->lengthField = dataLength;
+ packSignalingTLV((SignalingTLV*)outgoing->tlv, buf);
+}
+
+void
+freeMMTLV(ManagementTLV* tlv) {
+ DBGV("cleanup managementTLV data\n");
+ switch(tlv->managementId)
+ {
+ case MM_CLOCK_DESCRIPTION:
+ DBGV("cleanup clock description \n");
+ freeMMClockDescription((MMClockDescription*)tlv->dataField);
+ break;
+ case MM_USER_DESCRIPTION:
+ DBGV("cleanup user description \n");
+ freeMMUserDescription((MMUserDescription*)tlv->dataField);
+ break;
+ case MM_NULL_MANAGEMENT:
+ case MM_SAVE_IN_NON_VOLATILE_STORAGE:
+ case MM_RESET_NON_VOLATILE_STORAGE:
+ case MM_INITIALIZE:
+ case MM_DEFAULT_DATA_SET:
+ case MM_CURRENT_DATA_SET:
+ case MM_PARENT_DATA_SET:
+ case MM_TIME_PROPERTIES_DATA_SET:
+ case MM_PORT_DATA_SET:
+ case MM_PRIORITY1:
+ case MM_PRIORITY2:
+ case MM_DOMAIN:
+ case MM_SLAVE_ONLY:
+ case MM_LOG_ANNOUNCE_INTERVAL:
+ case MM_ANNOUNCE_RECEIPT_TIMEOUT:
+ case MM_LOG_SYNC_INTERVAL:
+ case MM_VERSION_NUMBER:
+ case MM_ENABLE_PORT:
+ case MM_DISABLE_PORT:
+ case MM_TIME:
+ case MM_CLOCK_ACCURACY:
+ case MM_UTC_PROPERTIES:
+ case MM_TRACEABILITY_PROPERTIES:
+ case MM_UNICAST_NEGOTIATION_ENABLE:
+ case MM_DELAY_MECHANISM:
+ case MM_LOG_MIN_PDELAY_REQ_INTERVAL:
+ default:
+ DBGV("no managementTLV data to cleanup \n");
+ }
+}
+
+void
+freeMMErrorStatusTLV(ManagementTLV *tlv) {
+ DBGV("cleanup managementErrorStatusTLV data \n");
+ freeMMErrorStatus((MMErrorStatus*)tlv->dataField);
+}
+
+void
+msgPackManagement(Octet *buf, MsgManagement *outgoing, PtpClock *ptpClock)
+{
+ DBGV("packing management message \n");
+ packMsgManagement(outgoing, buf);
+
+}
+
+/*
+ * Unpack Management message from IN buffer of ptpClock to msgtmp.manage
+ * return TRUE if there are more packed TLVs left in the message
+ */
+Boolean
+msgUnpackManagement(Octet *buf, MsgManagement * manage, MsgHeader * header, PtpClock *ptpClock, const int tlvOffset)
+{
+ unpackMsgManagement(buf, manage, ptpClock);
+
+ if ( manage->header.messageLength >= (MANAGEMENT_LENGTH + tlvOffset + TL_LENGTH) )
+ {
+ unpackManagementTLV(buf, tlvOffset, manage, ptpClock);
+
+ /* at this point, we know what managementTLV we have, so return and
+ * let someone else handle the data */
+ manage->tlv->dataField = NULL;
+ return TRUE;
+ }
+ else /* no (more) TLV attached to this message */
+ {
+ manage->tlv = NULL;
+ return FALSE;
+
+ }
+
+}
+
+void
+msgPackSignaling(Octet *buf, MsgSignaling *outgoing, PtpClock *ptpClock)
+{
+ DBGV("packing signaling message \n");
+ packMsgSignaling(outgoing, buf);
+}
+
+/*
+ * Unpack Signaling message from IN buffer of ptpClock to msgtmp.signaling
+ * return TRUE if there are more packed TLVs left in the message.
+ */
+Boolean
+msgUnpackSignaling(Octet *buf, MsgSignaling * signaling, MsgHeader * header, PtpClock *ptpClock, const int tlvOffset)
+{
+ unpackMsgSignaling(buf, signaling, ptpClock);
+
+ if ( signaling->header.messageLength >= (SIGNALING_LENGTH + tlvOffset + TL_LENGTH) )
+ {
+ unpackSignalingTLV(buf + tlvOffset, signaling, ptpClock);
+
+ DBGV("Signaling seq %d: Found TLV type 0x%04x: %d bytes left\n", header->sequenceId,
+ signaling->tlv->tlvType,signaling->header.messageLength - SIGNALING_LENGTH - tlvOffset);
+
+ /* at this point, we know what managementTLV we have, so return and
+ * let someone else handle the data */
+ signaling->tlv->valueField = NULL;
+ return TRUE;
+ }
+ else /* no (more) TLVs attached to this message */
+ {
+ signaling->tlv = NULL;
+ DBGV("Signaling seq %d: No more TLVs in message\n", header->sequenceId);
+ return FALSE;
+ }
+
+}
+
+/**
+ * Dump the most recent packet in the daemon
+ *
+ * @param ptpClock The central clock structure
+ */
+void msgDump(PtpClock *ptpClock)
+{
+
+#if defined(freebsd)
+ static int dumped = 0;
+#endif /* FreeBSD */
+
+ msgDebugHeader(&ptpClock->msgTmpHeader);
+ switch (ptpClock->msgTmpHeader.messageType) {
+ case SYNC:
+ msgDebugSync(&ptpClock->msgTmp.sync);
+ break;
+
+ case ANNOUNCE:
+ msgDebugAnnounce(&ptpClock->msgTmp.announce);
+ break;
+
+ case FOLLOW_UP:
+ msgDebugFollowUp(&ptpClock->msgTmp.follow);
+ break;
+
+ case DELAY_REQ:
+ msgDebugDelayReq(&ptpClock->msgTmp.req);
+ break;
+
+ case DELAY_RESP:
+ msgDebugDelayResp(&ptpClock->msgTmp.resp);
+ break;
+
+ case MANAGEMENT:
+ msgDebugManagement(&ptpClock->msgTmp.manage);
+ break;
+
+ case SIGNALING:
+ NOTIFY("* msgDebugSignaling not implemented *\n");
+ /* TODO: IMPLEMENT ME */
+ /* msgDebugSignaling(&ptpClock->msgTmp.signaling); */
+ break;
+
+ default:
+ NOTIFY("msgDump:unrecognized message\n");
+ break;
+ }
+
+#if defined(freebsd)
+ /* Only dump the first time, after that just do a message. */
+ if (dumped != 0)
+ return;
+
+ dumped++;
+ NOTIFY("msgDump: core file created.\n");
+
+ switch(rfork(RFFDG|RFPROC|RFNOWAIT)) {
+ case -1:
+ NOTIFY("could not fork to core dump! errno: %s",
+ strerror(errno));
+ break;
+ case 0:
+ abort(); /* Generate a core dump */
+ default:
+ /* This default intentionally left blank. */
+ break;
+ }
+#endif /* FreeBSD */
+}
+
+/**
+ * Dump a PTP message header
+ *
+ * @param header a pre-filled msg header structure
+ */
+
+void msgDebugHeader(MsgHeader *header)
+{
+ NOTIFY("msgDebugHeader: messageType %d\n", header->messageType);
+ NOTIFY("msgDebugHeader: versionPTP %d\n", header->versionPTP);
+ NOTIFY("msgDebugHeader: messageLength %d\n", header->messageLength);
+ NOTIFY("msgDebugHeader: domainNumber %d\n", header->domainNumber);
+ NOTIFY("msgDebugHeader: flags %02hhx %02hhx\n",
+ header->flagField0, header->flagField1);
+ NOTIFY("msgDebugHeader: correctionfield %d\n", header->correctionField);
+ NOTIFY("msgDebugHeader: sourcePortIdentity.clockIdentity "
+ "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx%02hhx:%02hhx\n",
+ header->sourcePortIdentity.clockIdentity[0],
+ header->sourcePortIdentity.clockIdentity[1],
+ header->sourcePortIdentity.clockIdentity[2],
+ header->sourcePortIdentity.clockIdentity[3],
+ header->sourcePortIdentity.clockIdentity[4],
+ header->sourcePortIdentity.clockIdentity[5],
+ header->sourcePortIdentity.clockIdentity[6],
+ header->sourcePortIdentity.clockIdentity[7]);
+ NOTIFY("msgDebugHeader: sourcePortIdentity.portNumber %d\n",
+ header->sourcePortIdentity.portNumber);
+ NOTIFY("msgDebugHeader: sequenceId %d\n", header->sequenceId);
+ NOTIFY("msgDebugHeader: controlField %d\n", header->controlField);
+ NOTIFY("msgDebugHeader: logMessageIntervale %d\n",
+ header->logMessageInterval);
+
+}
+
+/**
+ * Dump the contents of a sync packet
+ *
+ * @param sync A pre-filled MsgSync structure
+ */
+
+void msgDebugSync(MsgSync *sync)
+{
+ NOTIFY("msgDebugSync: originTimestamp.seconds %u\n",
+ sync->originTimestamp.secondsField);
+ NOTIFY("msgDebugSync: originTimestamp.nanoseconds %d\n",
+ sync->originTimestamp.nanosecondsField);
+}
+
+/**
+ * Dump the contents of a announce packet
+ *
+ * @param sync A pre-filled MsgAnnounce structure
+ */
+
+void msgDebugAnnounce(MsgAnnounce *announce)
+{
+ NOTIFY("msgDebugAnnounce: originTimestamp.seconds %u\n",
+ announce->originTimestamp.secondsField);
+ NOTIFY("msgDebugAnnounce: originTimestamp.nanoseconds %d\n",
+ announce->originTimestamp.nanosecondsField);
+ NOTIFY("msgDebugAnnounce: currentUTCOffset %d\n",
+ announce->currentUtcOffset);
+ NOTIFY("msgDebugAnnounce: grandmasterPriority1 %d\n",
+ announce->grandmasterPriority1);
+ NOTIFY("msgDebugAnnounce: grandmasterClockQuality.clockClass %d\n",
+ announce->grandmasterClockQuality.clockClass);
+ NOTIFY("msgDebugAnnounce: grandmasterClockQuality.clockAccuracy %d\n",
+ announce->grandmasterClockQuality.clockAccuracy);
+ NOTIFY("msgDebugAnnounce: "
+ "grandmasterClockQuality.offsetScaledLogVariance %d\n",
+ announce->grandmasterClockQuality.offsetScaledLogVariance);
+ NOTIFY("msgDebugAnnounce: grandmasterPriority2 %d\n",
+ announce->grandmasterPriority2);
+ NOTIFY("msgDebugAnnounce: grandmasterClockIdentity "
+ "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx%02hhx:%02hhx\n",
+ announce->grandmasterIdentity[0],
+ announce->grandmasterIdentity[1],
+ announce->grandmasterIdentity[2],
+ announce->grandmasterIdentity[3],
+ announce->grandmasterIdentity[4],
+ announce->grandmasterIdentity[5],
+ announce->grandmasterIdentity[6],
+ announce->grandmasterIdentity[7]);
+ NOTIFY("msgDebugAnnounce: stepsRemoved %d\n",
+ announce->stepsRemoved);
+ NOTIFY("msgDebugAnnounce: timeSource %d\n",
+ announce->timeSource);
+}
+
+/**
+ * NOT IMPLEMENTED
+ *
+ * @param req
+ */
+void msgDebugDelayReq(MsgDelayReq *req) {}
+
+/**
+ * Dump the contents of a followup packet
+ *
+ * @param follow A pre-fille MsgFollowUp structure
+ */
+void msgDebugFollowUp(MsgFollowUp *follow)
+{
+ NOTIFY("msgDebugFollowUp: preciseOriginTimestamp.seconds %u\n",
+ follow->preciseOriginTimestamp.secondsField);
+ NOTIFY("msgDebugFollowUp: preciseOriginTimestamp.nanoseconds %d\n",
+ follow->preciseOriginTimestamp.nanosecondsField);
+}
+
+/**
+ * Dump the contents of a delay response packet
+ *
+ * @param resp a pre-filled MsgDelayResp structure
+ */
+void msgDebugDelayResp(MsgDelayResp *resp)
+{
+ NOTIFY("msgDebugDelayResp: delayReceiptTimestamp.seconds %u\n",
+ resp->receiveTimestamp.secondsField);
+ NOTIFY("msgDebugDelayResp: delayReceiptTimestamp.nanoseconds %d\n",
+ resp->receiveTimestamp.nanosecondsField);
+ NOTIFY("msgDebugDelayResp: requestingPortIdentity.clockIdentity "
+ "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx%02hhx:%02hhx\n",
+ resp->requestingPortIdentity.clockIdentity[0],
+ resp->requestingPortIdentity.clockIdentity[1],
+ resp->requestingPortIdentity.clockIdentity[2],
+ resp->requestingPortIdentity.clockIdentity[3],
+ resp->requestingPortIdentity.clockIdentity[4],
+ resp->requestingPortIdentity.clockIdentity[5],
+ resp->requestingPortIdentity.clockIdentity[6],
+ resp->requestingPortIdentity.clockIdentity[7]);
+ NOTIFY("msgDebugDelayResp: requestingPortIdentity.portNumber %d\n",
+ resp->requestingPortIdentity.portNumber);
+}
+
+/**
+ * Dump the contents of management packet
+ *
+ * @param manage a pre-filled MsgManagement structure
+ */
+
+void msgDebugManagement(MsgManagement *manage)
+{
+ NOTIFY("msgDebugDelayManage: targetPortIdentity.clockIdentity "
+ "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ manage->targetPortIdentity.clockIdentity[0],
+ manage->targetPortIdentity.clockIdentity[1],
+ manage->targetPortIdentity.clockIdentity[2],
+ manage->targetPortIdentity.clockIdentity[3],
+ manage->targetPortIdentity.clockIdentity[4],
+ manage->targetPortIdentity.clockIdentity[5],
+ manage->targetPortIdentity.clockIdentity[6],
+ manage->targetPortIdentity.clockIdentity[7]);
+ NOTIFY("msgDebugDelayManage: targetPortIdentity.portNumber %d\n",
+ manage->targetPortIdentity.portNumber);
+ NOTIFY("msgDebugManagement: startingBoundaryHops %d\n",
+ manage->startingBoundaryHops);
+ NOTIFY("msgDebugManagement: boundaryHops %d\n", manage->boundaryHops);
+ NOTIFY("msgDebugManagement: actionField %d\n", manage->actionField);
+ NOTIFY("msgDebugManagement: tvl %s\n", manage->tlv);
+}
diff --git a/rtemsbsd/ptpd/src/dep/net.c b/rtemsbsd/ptpd/src/dep/net.c
new file mode 100644
index 00000000..7e185728
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/net.c
@@ -0,0 +1,2300 @@
+/*-
+ * Copyright (c) 2014-2015 Wojciech Owczarek,
+ * George V. Neville-Neil
+ * Copyright (c) 2012-2013 George V. Neville-Neil,
+ * Wojciech Owczarek.
+ * Copyright (c) 2011-2012 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen,
+ * Inaqui Delgado,
+ * Rick Ratzel,
+ * National Instruments.
+ * Copyright (c) 2009-2010 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen
+ *
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file net.c
+ * @date Tue Jul 20 16:17:49 2010
+ *
+ * @brief Functions to interact with the network sockets and NIC driver.
+ *
+ *
+ */
+
+#include "../ptpd.h"
+
+#ifdef PTPD_PCAP
+#ifdef HAVE_PCAP_PCAP_H
+#include <pcap/pcap.h>
+#else /* !HAVE_PCAP_PCAP_H */
+/* Cases like RHEL5 and others where only pcap.h exists */
+#ifdef HAVE_PCAP_H
+#include <pcap.h>
+#endif /* HAVE_PCAP_H */
+#endif
+#define PCAP_TIMEOUT 1 /* expressed in milliseconds */
+#endif
+
+#if defined PTPD_SNMP
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#endif
+
+/* choose kernel-level nanoseconds or microseconds resolution on the client-side */
+#if !defined(SO_TIMESTAMPING) && !defined(SO_TIMESTAMPNS) && !defined(SO_TIMESTAMP) && !defined(SO_BINTIME)
+#error No kernel-level support for packet timestamping detected!
+#endif
+
+#ifdef SO_TIMESTAMPING
+#include <linux/net_tstamp.h>
+#include <linux/sockios.h>
+#include <linux/ethtool.h>
+#endif /* SO_TIMESTAMPING */
+
+/**
+ * shutdown the IPv4 multicast for specific address
+ *
+ * @param netPath
+ * @param multicastAddr
+ *
+ * @return TRUE if successful
+ */
+static Boolean
+netShutdownMulticastIPv4(NetPath * netPath, Integer32 multicastAddr)
+{
+ struct ip_mreq imr;
+
+ if(!multicastAddr || !netPath->interfaceAddr.s_addr) {
+ return TRUE;
+ }
+
+ /* Close General Multicast */
+ imr.imr_multiaddr.s_addr = multicastAddr;
+ imr.imr_interface.s_addr = netPath->interfaceAddr.s_addr;
+
+ setsockopt(netPath->eventSock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ &imr, sizeof(struct ip_mreq));
+ setsockopt(netPath->generalSock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ &imr, sizeof(struct ip_mreq));
+
+ return TRUE;
+}
+
+/**
+ * shutdown the multicast (both General and Peer)
+ *
+ * @param netPath
+ *
+ * @return TRUE if successful
+ */
+static Boolean
+netShutdownMulticast(NetPath * netPath)
+{
+ /* Close General Multicast */
+ netShutdownMulticastIPv4(netPath, netPath->multicastAddr);
+ netPath->multicastAddr = 0;
+
+ /* Close Peer Multicast */
+ netShutdownMulticastIPv4(netPath, netPath->peerMulticastAddr);
+ netPath->peerMulticastAddr = 0;
+
+ return TRUE;
+}
+
+/*
+ * For future use: Check if IPv4 address is multiast -
+ * If first 4 bits of an address are 0xE (1110), it's multicast
+ */
+/*
+static Boolean
+isIpMulticast(struct in_addr in)
+{
+ if((ntohl(in.s_addr) >> 28) == 0x0E )
+ return TRUE;
+ return FALSE;
+}
+*/
+
+/* shut down the UDP stuff */
+Boolean
+netShutdown(NetPath * netPath)
+{
+ netShutdownMulticast(netPath);
+
+ /* Close sockets */
+ if (netPath->eventSock >= 0)
+ close(netPath->eventSock);
+ netPath->eventSock = -1;
+
+ if (netPath->generalSock >= 0)
+ close(netPath->generalSock);
+ netPath->generalSock = -1;
+
+#ifdef PTPD_PCAP
+ if (netPath->pcapEvent != NULL) {
+ pcap_close(netPath->pcapEvent);
+ netPath->pcapEventSock = -1;
+ }
+ if (netPath->pcapGeneral != NULL) {
+ pcap_close(netPath->pcapGeneral);
+ netPath->pcapGeneralSock = -1;
+ }
+#endif
+
+ freeIpv4AccessList(&netPath->timingAcl);
+ freeIpv4AccessList(&netPath->managementAcl);
+
+ return TRUE;
+}
+
+/* Check if interface ifaceName exists. Return 1 on success, 0 when interface doesn't exists, -1 on failure.
+ */
+
+static int
+interfaceExists(char* ifaceName)
+{
+
+ int ret;
+ struct ifaddrs *ifaddr, *ifa;
+
+ if(!strlen(ifaceName)) {
+ DBG("interfaceExists called for an empty interface!");
+ return 0;
+ }
+
+ if(getifaddrs(&ifaddr) == -1) {
+ PERROR("Could not get interface list");
+ ret = -1;
+ goto end;
+
+ }
+
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+ if(ifa->ifa_name == NULL) continue;
+ if(!strcmp(ifaceName, ifa->ifa_name)) {
+ ret = 1;
+ goto end;
+ }
+
+ }
+
+ ret = 0;
+ DBG("Interface not found: %s\n", ifaceName);
+
+end:
+ freeifaddrs(ifaddr);
+ return ret;
+}
+
+static int
+getInterfaceFlags(char* ifaceName, unsigned int* flags)
+{
+
+ int ret;
+ struct ifaddrs *ifaddr, *ifa;
+
+ if(!strlen(ifaceName)) {
+ DBG("interfaceExists called for an empty interface!");
+ return 0;
+ }
+
+ if(getifaddrs(&ifaddr) == -1) {
+ PERROR("Could not get interface list");
+ ret = -1;
+ goto end;
+
+ }
+
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+ if(ifa->ifa_name == NULL) continue;
+ if(!strcmp(ifaceName, ifa->ifa_name)) {
+ *flags = ifa->ifa_flags;
+ ret = 1;
+ goto end;
+ }
+
+ }
+
+ ret = 0;
+ DBG("Interface not found: %s\n", ifaceName);
+
+end:
+ freeifaddrs(ifaddr);
+ return ret;
+}
+
+
+/* Try getting addr address of family family from interface ifaceName.
+ Return 1 on success, 0 when no suitable address available, -1 on failure.
+ */
+static int
+getInterfaceAddress(char* ifaceName, int family, struct sockaddr* addr) {
+
+ int ret;
+ struct ifaddrs *ifaddr, *ifa;
+
+ if(getifaddrs(&ifaddr) == -1) {
+ PERROR("Could not get interface list");
+ ret = -1;
+ goto end;
+
+ }
+
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+
+ if(ifa->ifa_name == NULL) continue;
+ /* ifa_addr not always present - link layer may come first */
+ if(ifa->ifa_addr == NULL) continue;
+
+ if(!strcmp(ifaceName, ifa->ifa_name) && ifa->ifa_addr->sa_family == family) {
+
+ memcpy(addr, ifa->ifa_addr, sizeof(struct sockaddr));
+ ret = 1;
+ goto end;
+
+ }
+
+ }
+
+ ret = 0;
+ DBG("Interface not found: %s\n", ifaceName);
+
+end:
+
+ freeifaddrs(ifaddr);
+ return ret;
+}
+
+
+/* Try getting hwAddrSize bytes of ifaceName hardware address,
+ and place them in hwAddr. Return 1 on success, 0 when no suitable
+ hw address available, -1 on failure.
+ */
+static int
+getHwAddress (char* ifaceName, unsigned char* hwAddr, int hwAddrSize)
+{
+
+ int ret;
+ if(!strlen(ifaceName))
+ return 0;
+
+/* BSD* - AF_LINK gives us access to the hw address via struct sockaddr_dl */
+#if defined(AF_LINK) && !defined(__sun)
+
+ struct ifaddrs *ifaddr, *ifa;
+
+ if(getifaddrs(&ifaddr) == -1) {
+ PERROR("Could not get interface list");
+ ret = -1;
+ goto end;
+
+ }
+
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+ if(ifa->ifa_name == NULL) continue;
+ if(ifa->ifa_addr == NULL) continue;
+ if(!strcmp(ifaceName, ifa->ifa_name) && ifa->ifa_addr->sa_family == AF_LINK) {
+
+ struct sockaddr_dl* sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ if(sdl->sdl_type == IFT_ETHER || sdl->sdl_type == IFT_L2VLAN) {
+
+ memcpy(hwAddr, LLADDR(sdl),
+ hwAddrSize <= sizeof(sdl->sdl_data) ?
+ hwAddrSize : sizeof(sdl->sdl_data));
+ ret = 1;
+ goto end;
+ } else {
+ DBGV("Unsupported hardware address family on %s\n", ifaceName);
+ ret = 0;
+ goto end;
+ }
+ }
+
+ }
+
+ ret = 0;
+ DBG("Interface not found: %s\n", ifaceName);
+
+end:
+
+ freeifaddrs(ifaddr);
+ return ret;
+
+
+#else
+/* Linux and Solaris family which also have SIOCGIFHWADDR/SIOCGLIFHWADDR */
+ int sockfd;
+ struct ifreq ifr;
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if(sockfd < 0) {
+ PERROR("Could not open test socket");
+ return -1;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+
+ strncpy(ifr.ifr_name, ifaceName, IFACE_NAME_LENGTH);
+
+ if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) < 0) {
+ DBGV("failed to request hardware address for %s", ifaceName);
+ ret = -1;
+ goto end;
+ }
+
+#ifdef HAVE_STRUCT_IFREQ_IFR_HWADDR
+ int af = ifr.ifr_hwaddr.sa_family;
+#else
+ int af = ifr.ifr_addr.sa_family;
+#endif /* HAVE_STRUCT_IFREQ_IFR_HWADDR */
+
+ if ( af == ARPHRD_ETHER
+ || af == ARPHRD_IEEE802
+#ifdef ARPHRD_INFINIBAND
+ || af == ARPHRD_INFINIBAND
+#endif
+ ) {
+#ifdef HAVE_STRUCT_IFREQ_IFR_HWADDR
+ memcpy(hwAddr, ifr.ifr_hwaddr.sa_data, hwAddrSize);
+#else
+ memcpy(hwAddr, ifr.ifr_addr.sa_data, hwAddrSize);
+#endif /* HAVE_STRUCT_IFREQ_IFR_HWADDR */
+
+ ret = 1;
+ } else {
+ DBGV("Unsupported hardware address family on %s\n", ifaceName);
+ ret = 0;
+ }
+end:
+ close(sockfd);
+ return ret;
+
+#endif /* AF_LINK */
+
+}
+
+static int getInterfaceIndex(char *ifaceName)
+{
+
+#ifndef SIOCGIFINDEX
+ return -1;
+#else
+ int sockfd;
+ struct ifreq ifr;
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if(sockfd < 0) {
+ PERROR("Could not retrieve interface index for %s",ifaceName);
+ return -1;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+
+ strncpy(ifr.ifr_name, ifaceName, IFACE_NAME_LENGTH);
+
+ if (ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0) {
+ DBGV("failed to request hardware address for %s", ifaceName);
+ close(sockfd);
+ return -1;
+
+ }
+
+ close(sockfd);
+
+#if defined(HAVE_STRUCT_IFREQ_IFR_INDEX)
+ return ifr.ifr_index;
+#elif defined(HAVE_STRUCT_IFREQ_IFR_IFINDEX)
+ return ifr.ifr_ifindex;
+#else
+ return 0;
+#endif
+
+#endif /* !SIOCGIFINDEX */
+
+}
+
+static Boolean getInterfaceInfo(char* ifaceName, InterfaceInfo* ifaceInfo)
+{
+
+ int res;
+
+ res = interfaceExists(ifaceName);
+
+ if (res == -1) {
+
+ return FALSE;
+
+ } else if (res == 0) {
+
+ ERROR("Interface %s does not exist.\n", ifaceName);
+ return FALSE;
+ }
+
+ res = getInterfaceAddress(ifaceName, ifaceInfo->addressFamily, &ifaceInfo->afAddress);
+
+ if (res == -1) {
+
+ return FALSE;
+
+ }
+
+ ifaceInfo->hasAfAddress = res;
+
+ res = getHwAddress(ifaceName, (unsigned char*)ifaceInfo->hwAddress, 6);
+
+ if (res == -1) {
+
+ return FALSE;
+
+ }
+
+ ifaceInfo->hasHwAddress = res;
+
+ res = getInterfaceFlags(ifaceName, &ifaceInfo->flags);
+
+ if (res == -1) {
+
+ return FALSE;
+
+ }
+
+ res = getInterfaceIndex(ifaceName);
+
+ if(res == -1) {
+ ifaceInfo->ifIndex = 0;
+ } else {
+ ifaceInfo->ifIndex = res;
+ }
+
+ return TRUE;
+
+
+}
+
+Boolean
+testInterface(char * ifaceName, const RunTimeOpts* rtOpts)
+{
+
+ InterfaceInfo info;
+
+ info.addressFamily = AF_INET;
+
+ if(getInterfaceInfo(ifaceName, &info) != 1)
+ return FALSE;
+
+ switch(rtOpts->transport) {
+
+ case UDP_IPV4:
+ if(!info.hasAfAddress) {
+ ERROR("Interface %s has no IPv4 address set\n", ifaceName);
+ return FALSE;
+ }
+ break;
+
+ case IEEE_802_3:
+ if(!info.hasHwAddress) {
+ ERROR("Interface %s has no supported hardware address - possibly not an Ethernet interface\n", ifaceName);
+ return FALSE;
+ }
+ break;
+
+ default:
+ ERROR("Unsupported transport: %d\n", rtOpts->transport);
+ return FALSE;
+
+ }
+
+ if(!(info.flags & IFF_UP) || !(info.flags & IFF_RUNNING))
+ WARNING("Interface %s seems to be down. PTPd will not operate correctly until it's up.\n", ifaceName);
+
+ if(info.flags & IFF_LOOPBACK)
+ WARNING("Interface %s is a loopback interface.\n", ifaceName);
+
+ if(!(info.flags & IFF_MULTICAST)
+ && rtOpts->transport==UDP_IPV4
+ && rtOpts->ipMode != IPMODE_UNICAST) {
+ WARNING("Interface %s is not multicast capable.\n", ifaceName);
+ }
+
+ return TRUE;
+
+}
+
+/**
+ * Init the multcast for specific IPv4 address
+ *
+ * @param netPath
+ * @param multicastAddr
+ *
+ * @return TRUE if successful
+ */
+static Boolean
+netInitMulticastIPv4(NetPath * netPath, Integer32 multicastAddr)
+{
+ struct ip_mreq imr;
+
+ /* multicast send only on specified interface */
+ imr.imr_multiaddr.s_addr = multicastAddr;
+ imr.imr_interface.s_addr = netPath->interfaceAddr.s_addr;
+
+ if (setsockopt(netPath->eventSock, IPPROTO_IP, IP_MULTICAST_IF,
+ &netPath->interfaceAddr, sizeof(struct in_addr)) < 0
+ || setsockopt(netPath->generalSock, IPPROTO_IP, IP_MULTICAST_IF,
+ &netPath->interfaceAddr, sizeof(struct in_addr))
+ < 0) {
+ PERROR("error while setting outgoig multicast interface "
+ "(IP_MULTICAST_IF)");
+ return FALSE;
+ }
+ /* join multicast group (for receiving) on specified interface */
+ if (setsockopt(netPath->eventSock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &imr, sizeof(struct ip_mreq)) < 0
+ || setsockopt(netPath->generalSock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &imr, sizeof(struct ip_mreq)) < 0) {
+ PERROR("failed to join the multicast group");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Init the multcast (both General and Peer)
+ *
+ * @param netPath
+ * @param rtOpts
+ *
+ * @return TRUE if successful
+ */
+static Boolean
+netInitMulticast(NetPath * netPath, const RunTimeOpts * rtOpts)
+{
+ struct in_addr netAddr;
+ char addrStr[NET_ADDRESS_LENGTH+1];
+
+ /* do not join multicast in unicast mode */
+ if(rtOpts->ipMode == IPMODE_UNICAST)
+ return TRUE;
+
+ /* Init General multicast IP address */
+ strncpy(addrStr, DEFAULT_PTP_DOMAIN_ADDRESS, NET_ADDRESS_LENGTH);
+ if (!inet_aton(addrStr, &netAddr)) {
+ ERROR("failed to encode multicast address: %s\n", addrStr);
+ return FALSE;
+ }
+
+ /* this allows for leaving groups only if joined */
+ netPath->joinedGeneral = TRUE;
+
+ netPath->multicastAddr = netAddr.s_addr;
+ if(!netInitMulticastIPv4(netPath, netPath->multicastAddr)) {
+ return FALSE;
+ }
+
+ /* End of General multicast Ip address init */
+
+ if(rtOpts->delayMechanism != P2P) {
+ return TRUE;
+ }
+
+ /* Init Peer multicast IP address */
+ strncpy(addrStr, PEER_PTP_DOMAIN_ADDRESS, NET_ADDRESS_LENGTH);
+ if (!inet_aton(addrStr, &netAddr)) {
+ ERROR("failed to encode multicast address: %s\n", addrStr);
+ return FALSE;
+ }
+
+ /* track if we have joined the p2p mcast group */
+ netPath->joinedPeer = TRUE;
+
+ netPath->peerMulticastAddr = netAddr.s_addr;
+ if(!netInitMulticastIPv4(netPath, netPath->peerMulticastAddr)) {
+ return FALSE;
+ }
+ /* End of Peer multicast Ip address init */
+
+ return TRUE;
+}
+
+static Boolean
+netSetMulticastTTL(int sockfd, int ttl) {
+
+#if defined(__OpenBSD__) || defined(__sun)
+ uint8_t temp = (uint8_t) ttl;
+#else
+ int temp = ttl;
+#endif
+
+ if (setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
+ &temp, sizeof(temp)) < 0) {
+ PERROR("Failed to set socket multicast time-to-live");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static Boolean
+netSetMulticastLoopback(NetPath * netPath, Boolean value) {
+#if defined(__OpenBSD__) || defined(__sun)
+ uint8_t temp = value ? 1 : 0;
+#else
+ int temp = value ? 1 : 0;
+#endif
+ DBG("Going to set multicast loopback with %d \n", temp);
+
+ if (setsockopt(netPath->eventSock, IPPROTO_IP, IP_MULTICAST_LOOP,
+ &temp, sizeof(temp)) < 0) {
+ PERROR("Failed to set multicast loopback");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#if defined(SO_TIMESTAMPING) && defined(SO_TIMESTAMPNS)
+static Boolean
+getTxTimestamp(NetPath* netPath,TimeInternal* timeStamp) {
+ extern PtpClock *G_ptpClock;
+ ssize_t length;
+ fd_set tmpSet;
+ struct timeval timeOut = {0,0};
+ int val = 1;
+ int i = 0;
+ if(netPath->txTimestampFailure)
+ goto failure;
+
+ FD_ZERO(&tmpSet);
+ FD_SET(netPath->eventSock, &tmpSet);
+
+ if(select(netPath->eventSock + 1, &tmpSet, NULL, NULL, &timeOut) > 0) {
+ if (FD_ISSET(netPath->eventSock, &tmpSet)) {
+
+ length = netRecvEvent(G_ptpClock->msgIbuf, timeStamp,
+ netPath, MSG_ERRQUEUE);
+ if (length > 0) {
+ DBG("getTxTimestamp: Grabbed sent msg via errqueue: %d bytes, at %d.%d\n", length, timeStamp->seconds, timeStamp->nanoseconds);
+ return TRUE;
+ } else if (length < 0) {
+ DBG("getTxTimestamp: Failed to poll error queue for SO_TIMESTAMPING transmit time\n");
+ G_ptpClock->counters.messageRecvErrors++;
+ } else if (length == 0) {
+ DBG("getTxTimestamp: Received no data from TX error queue\n");
+ }
+ }
+ }
+
+ /* we're desperate here, aren't we... */
+ for(i = 0; i < 3; i++) {
+ length = netRecvEvent(G_ptpClock->msgIbuf, timeStamp, netPath, MSG_ERRQUEUE);
+ if(length > 0) {
+ DBG("getTxTimestamp: SO_TIMESTAMPING - delayed TX timestamp caught\n");
+ return TRUE;
+ }
+ usleep(10);
+ }
+
+ /* try for the last time: sleep and poll the error queue, if nothing, consider SO_TIMESTAMPING inoperable */
+ usleep(LATE_TXTIMESTAMP_US);
+
+ length = netRecvEvent(G_ptpClock->msgIbuf, timeStamp, netPath, MSG_ERRQUEUE);
+
+ if(length > 0) {
+ DBG("getTxTimestamp: SO_TIMESTAMPING - even more delayed TX timestamp caught\n");
+ return TRUE;
+ } else {
+ DBG("getTxTimestamp: SO_TIMESTAMPING - TX timestamp retry failed - will use loop from now on\n");
+ }
+
+failure:
+ DBG("net.c: SO_TIMESTAMPING TX software timestamp failure - reverting to SO_TIMESTAMPNS\n");
+ /* unset SO_TIMESTAMPING first! otherwise we get an always-exiting select! */
+ val = 0;
+ if(setsockopt(netPath->eventSock, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(int)) < 0) {
+ DBG("getTxTimestamp: failed to unset SO_TIMESTAMPING");
+ }
+ val = 1;
+ if(setsockopt(netPath->eventSock, SOL_SOCKET, SO_TIMESTAMPNS, &val, sizeof(int)) < 0) {
+ DBG("getTxTimestamp: failed to revert to SO_TIMESTAMPNS");
+ }
+
+ return FALSE;
+}
+#endif /* SO_TIMESTAMPING */
+
+
+/**
+ * Initialize timestamping of packets
+ *
+ * @param netPath
+ *
+ * @return TRUE if successful
+ */
+static Boolean
+netInitTimestamping(NetPath * netPath, const RunTimeOpts * rtOpts)
+{
+
+ int val = 1;
+ Boolean result = TRUE;
+#if defined(SO_TIMESTAMPING) && defined(SO_TIMESTAMPNS)/* Linux - current API */
+ DBG("netInitTimestamping: trying to use SO_TIMESTAMPING\n");
+ val = SOF_TIMESTAMPING_TX_SOFTWARE |
+ SOF_TIMESTAMPING_RX_SOFTWARE |
+ SOF_TIMESTAMPING_SOFTWARE;
+
+/* unless compiled with PTPD_EXPERIMENTAL, check if we support the desired tstamp capabilities */
+#ifndef PTPD_EXPERIMENTAL
+#ifdef ETHTOOL_GET_TS_INFO
+
+ struct ethtool_ts_info tsInfo;
+ struct ifreq ifRequest;
+ int res;
+
+ memset(&tsInfo, 0, sizeof(tsInfo));
+ memset(&ifRequest, 0, sizeof(ifRequest));
+ tsInfo.cmd = ETHTOOL_GET_TS_INFO;
+ strncpy( ifRequest.ifr_name, rtOpts->ifaceName, IFNAMSIZ - 1);
+ ifRequest.ifr_data = (char *) &tsInfo;
+ res = ioctl(netPath->eventSock, SIOCETHTOOL, &ifRequest);
+
+ if (res < 0) {
+ PERROR("Could not retrieve ethtool timestamping capabilities for %s - reverting to SO_TIMESTAMPNS",
+ rtOpts->ifaceName);
+ val = 1;
+ netPath->txTimestampFailure = FALSE;
+ } else if((tsInfo.so_timestamping & val) != val) {
+ DBGV("Required SO_TIMESTAMPING flags not supported - reverting to SO_TIMESTAMPNS\n");
+ val = 1;
+ netPath->txTimestampFailure = TRUE;
+ }
+#else
+ netPath->txTimestampFailure = TRUE;
+ val = 1;
+#endif /* ETHTOOL_GET_TS_INFO */
+#endif /* PTPD_EXPERIMENTAL */
+
+ if(val == 1) {
+ if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_TIMESTAMPNS, &val, sizeof(int)) < 0) {
+ PERROR("netInitTimestamping: failed to enable SO_TIMESTAMPNS");
+ result = FALSE;
+ }
+ } else {
+ if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(int)) < 0) {
+ PERROR("netInitTimestamping: failed to enable SO_TIMESTAMPING");
+ result = FALSE;
+ }
+ }
+
+ if (result == TRUE) {
+ DBG("SO_TIMESTAMP%s initialised\n",(val==1)?"NS":"ING");
+ }
+
+#elif defined(SO_TIMESTAMPNS) /* Linux, Apple */
+ DBG("netInitTimestamping: trying to use SO_TIMESTAMPNS\n");
+
+ if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_TIMESTAMPNS, &val, sizeof(int)) < 0) {
+ PERROR("netInitTimestamping: failed to enable SO_TIMESTAMPNS");
+ result = FALSE;
+ }
+#elif defined(SO_BINTIME) /* FreeBSD */
+ DBG("netInitTimestamping: trying to use SO_BINTIME\n");
+
+ if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_BINTIME, &val, sizeof(int)) < 0) {
+ PERROR("netInitTimestamping: failed to enable SO_BINTIME");
+ result = FALSE;
+ }
+#else
+ result = FALSE;
+#endif
+
+/* fallback method */
+#if defined(SO_TIMESTAMP) /* Linux, Apple, FreeBSD */
+ if (!result) {
+ DBG("netInitTimestamping: trying to use SO_TIMESTAMP\n");
+
+ if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_TIMESTAMP, &val, sizeof(int)) < 0) {
+ PERROR("netInitTimestamping: failed to enable SO_TIMESTAMP");
+ result = FALSE;
+ }
+ result = TRUE;
+ }
+#endif
+
+ return result;
+}
+
+Boolean
+hostLookup(const char* hostname, Integer32* addr)
+{
+ if (hostname[0]) {
+ /* Attempt a DNS lookup first. */
+ struct hostent *host;
+#ifdef HAVE_GETHOSTBYNAME2
+ host = gethostbyname2(hostname, AF_INET);
+#else
+ host = getipnodebyname(hostname, AF_INET, AI_DEFAULT, &errno);
+#endif /* HAVE_GETHOSTBYNAME2 */
+
+ if (host != NULL) {
+ if (host->h_length != 4) {
+ PERROR("unicast host resolved to non ipv4"
+ "address");
+ return FALSE;
+ }
+ *addr =
+ *(uint32_t *)host->h_addr_list[0];
+ return TRUE;
+ } else {
+ struct in_addr netAddr;
+ /* Maybe it's a dotted quad. */
+ if (!inet_aton(hostname, &netAddr)) {
+ ERROR("failed to encode unicast address: %s\n",
+ hostname);
+ return FALSE;
+ *addr = netAddr.s_addr;
+ return TRUE;
+ }
+ }
+ }
+
+return FALSE;
+
+}
+
+/* parse a list of hosts to a list of IP addresses */
+static int parseUnicastConfig(const RunTimeOpts *rtOpts, int maxCount, UnicastDestination * output)
+{
+ char* token;
+ char* stash;
+ int found = 0;
+ int total = 0;
+ char* text_;
+ char* text__;
+ int tmp = 0;
+
+ if(strlen(rtOpts->unicastDestinations)==0) return 0;
+
+ text_=strdup(rtOpts->unicastDestinations);
+
+ for(text__=text_;found < maxCount; text__=NULL) {
+
+ token=strtok_r(text__,", ;\t",&stash);
+ if(token==NULL) break;
+ if(hostLookup(token, &output[found].transportAddress)) {
+ DBG("hostList %d host: %s addr %08x\n", found, token, output[found]);
+ found++;
+ }
+
+ }
+
+ if(text_ != NULL) {
+ free(text_);
+ }
+
+ if(!found) {
+ return 0;
+ }
+ total = found;
+
+ found = 0;
+
+ text_=strdup(rtOpts->unicastDomains);
+
+ for(text__=text_;found < total; text__=NULL) {
+
+ token=strtok_r(text__,", ;\t",&stash);
+ if(token==NULL) break;
+ if (sscanf(token,"%d", &tmp)) {
+ DBG("hostList %dth host: domain %d\n", found, tmp);
+ output[found].domainNumber = tmp;
+ found++;
+ }
+
+ }
+
+ if(text_ != NULL) {
+ free(text_);
+ }
+
+ found = 0;
+
+ text_=strdup(rtOpts->unicastLocalPreference);
+
+ for(text__=text_;found < total; text__=NULL) {
+
+ token=strtok_r(text__,", ;\t",&stash);
+ tmp = LOWEST_LOCALPREFERENCE;
+ if(token!=NULL) {
+ if (sscanf(token,"%d", &tmp) != 1) {
+ tmp = LOWEST_LOCALPREFERENCE;
+ }
+ }
+
+ DBG("hostList %dth host: preference %d\n", found, tmp);
+ output[found].localPreference = tmp;
+ found++;
+
+ }
+
+ if(text_ != NULL) {
+ free(text_);
+ }
+
+ return total;
+
+}
+
+
+
+/**
+ * Init all network transports
+ *
+ * @param netPath
+ * @param rtOpts
+ * @param ptpClock
+ *
+ * @return TRUE if successful
+ */
+Boolean
+netInit(NetPath * netPath, RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+
+#ifdef __QNXNTO__
+ unsigned char temp;
+#else
+ int temp;
+#endif
+ struct sockaddr_in addr;
+
+#ifdef PTPD_PCAP
+ struct bpf_program program;
+ char errbuf[PCAP_ERRBUF_SIZE];
+#endif
+
+ DBG("netInit\n");
+
+#ifdef PTPD_PCAP
+ netPath->pcapEvent = NULL;
+ netPath->pcapGeneral = NULL;
+ netPath->pcapEventSock = -1;
+ netPath->pcapGeneralSock = -1;
+#endif
+ netPath->generalSock = -1;
+ netPath->eventSock = -1;
+
+#ifdef PTPD_PCAP
+ if (rtOpts->transport == IEEE_802_3) {
+ netPath->headerOffset = PACKET_BEGIN_ETHER;
+#ifdef HAVE_STRUCT_ETHER_ADDR_OCTET
+ memcpy(netPath->etherDest.octet, ether_aton(PTP_ETHER_DST), ETHER_ADDR_LEN);
+ memcpy(netPath->peerEtherDest.octet, ether_aton(PTP_ETHER_PEER), ETHER_ADDR_LEN);
+#else
+ memcpy(netPath->etherDest.ether_addr_octet, ether_aton(PTP_ETHER_DST), ETHER_ADDR_LEN);
+ memcpy(netPath->peerEtherDest.ether_addr_octet, ether_aton(PTP_ETHER_PEER), ETHER_ADDR_LEN);
+#endif /* HAVE_STRUCT_ETHER_ADDR_OCTET */
+ } else
+#endif
+ netPath->headerOffset = PACKET_BEGIN_UDP;
+
+ /* open sockets */
+ if ((netPath->eventSock = socket(PF_INET, SOCK_DGRAM,
+ IPPROTO_UDP)) < 0
+ || (netPath->generalSock = socket(PF_INET, SOCK_DGRAM,
+ IPPROTO_UDP)) < 0) {
+ PERROR("failed to initialize sockets");
+ return FALSE;
+ }
+
+ /* let's see if we have another interface left before we die */
+ if(!testInterface(rtOpts->ifaceName, rtOpts)) {
+
+ /* backup not enabled - exit */
+ if(!rtOpts->backupIfaceEnabled)
+ return FALSE;
+
+ /* backup enabled - try the other interface */
+ ptpClock->runningBackupInterface = !ptpClock->runningBackupInterface;
+
+ rtOpts->ifaceName = (ptpClock->runningBackupInterface)?rtOpts->backupIfaceName : rtOpts->primaryIfaceName;
+
+ NOTICE("Last resort - attempting to switch to %s interface\n", ptpClock->runningBackupInterface ? "backup" : "primary");
+ /* if this fails, we have no reason to live */
+ if(!testInterface(rtOpts->ifaceName, rtOpts)) {
+ return FALSE;
+ }
+ }
+
+
+ netPath->interfaceInfo.addressFamily = AF_INET;
+
+ /* the if is here only to get rid of an unused result warning. */
+ if( getInterfaceInfo(rtOpts->ifaceName, &netPath->interfaceInfo)!= 1)
+ return FALSE;
+
+ /* No HW address, we'll use the protocol address to form interfaceID -> clockID */
+ if( !netPath->interfaceInfo.hasHwAddress && netPath->interfaceInfo.hasAfAddress ) {
+ uint32_t addr = ((struct sockaddr_in*)&(netPath->interfaceInfo.afAddress))->sin_addr.s_addr;
+ memcpy(netPath->interfaceID, &addr, 2);
+ memcpy(netPath->interfaceID + 4, &addr + 2, 2);
+ /* Initialise interfaceID with hardware address */
+ } else {
+ memcpy(&netPath->interfaceID, &netPath->interfaceInfo.hwAddress,
+ sizeof(netPath->interfaceID) <= sizeof(netPath->interfaceInfo.hwAddress) ?
+ sizeof(netPath->interfaceID) : sizeof(netPath->interfaceInfo.hwAddress)
+ );
+ }
+
+ DBG("Listening on IP: %s\n",inet_ntoa(
+ ((struct sockaddr_in*)&(netPath->interfaceInfo.afAddress))->sin_addr));
+
+#ifdef PTPD_PCAP
+ if (rtOpts->pcap == TRUE) {
+
+ netPath->txTimestampFailure = TRUE;
+
+ int promisc = (rtOpts->transport == IEEE_802_3 ) ? 1 : 0;
+
+ if ((netPath->pcapEvent = pcap_open_live(rtOpts->ifaceName,
+ PACKET_SIZE, promisc,
+ PCAP_TIMEOUT,
+ errbuf)) == NULL) {
+ PERROR("failed to open event pcap");
+ return FALSE;
+ }
+
+/* libpcap - new way - may be required for non-default buffer sizes */
+/*
+ netPath->pcapEvent = pcap_create(rtOpts->ifaceName, errbuf);
+ pcap_set_promisc(netPath->pcapEvent, promisc);
+ pcap_set_snaplen(netPath->pcapEvent, PACKET_SIZE);
+ pcap_set_timeout(netPath->pcapEvent, PCAP_TIMEOUT);
+ pcap_set_buffer_size(netPath->pcapEvent, 1024 * 2 * UNICAST_MAX_DESTINATIONS);
+ pcap_activate(netPath->pcapEvent);
+*/
+ if (pcap_compile(netPath->pcapEvent, &program,
+ ( rtOpts->transport == IEEE_802_3 ) ?
+ "ether proto 0x88f7":
+ ( rtOpts->ipMode == IPMODE_UNICAST ) ?
+ "udp port 319 and not multicast" :
+ ( rtOpts->ipMode != IPMODE_MULTICAST ) ?
+ "udp port 319" :
+ "host (224.0.1.129 or 224.0.0.107) and udp port 319" ,
+ 1, 0) < 0) {
+ PERROR("failed to compile pcap event filter");
+ pcap_perror(netPath->pcapEvent, "ptpd2");
+ return FALSE;
+ }
+ if (pcap_setfilter(netPath->pcapEvent, &program) < 0) {
+ PERROR("failed to set pcap event filter");
+ return FALSE;
+ }
+ pcap_freecode(&program);
+ if ((netPath->pcapEventSock =
+ pcap_get_selectable_fd(netPath->pcapEvent)) < 0) {
+ PERROR("failed to get pcap event fd");
+ return FALSE;
+ }
+ if ((netPath->pcapGeneral = pcap_open_live(rtOpts->ifaceName,
+ PACKET_SIZE, promisc,
+ PCAP_TIMEOUT,
+ errbuf)) == NULL) {
+ PERROR("failed to open general pcap");
+ return FALSE;
+ }
+ if (rtOpts->transport != IEEE_802_3) {
+ if (pcap_compile(netPath->pcapGeneral, &program,
+ ( rtOpts->ipMode == IPMODE_UNICAST ) ?
+ "udp port 320 and not multicast" :
+ ( rtOpts->ipMode != IPMODE_MULTICAST ) ?
+ "udp port 320" :
+ "host (224.0.1.129 or 224.0.0.107) and udp port 320" ,
+ 1, 0) < 0) {
+ PERROR("failed to compile pcap general filter");
+ pcap_perror(netPath->pcapGeneral, "ptpd2");
+ return FALSE;
+ }
+ if (pcap_setfilter(netPath->pcapGeneral, &program) < 0) {
+ PERROR("failed to set pcap general filter");
+ return FALSE;
+ }
+ pcap_freecode(&program);
+ if ((netPath->pcapGeneralSock =
+ pcap_get_selectable_fd(netPath->pcapGeneral)) < 0) {
+ PERROR("failed to get pcap general fd");
+ return FALSE;
+ }
+ }
+ }
+#endif
+
+#ifdef PTPD_PCAP
+ if(rtOpts->transport == IEEE_802_3) {
+ close(netPath->eventSock);
+ netPath->eventSock = -1;
+ close(netPath->generalSock);
+ netPath->generalSock = -1;
+ /* TX timestamp is not generated for PCAP mode and Ethernet transport */
+#ifdef SO_TIMESTAMPING
+ netPath->txTimestampFailure = TRUE;
+#endif /* SO_TIMESTAMPING */
+ } else {
+#endif
+ /* save interface address for IGMP refresh */
+ {
+ struct sockaddr_in* sin = (struct sockaddr_in*)&(netPath->interfaceInfo.afAddress);
+ netPath->interfaceAddr = sin->sin_addr;
+ }
+
+ DBG("Local IP address used : %s \n", inet_ntoa(netPath->interfaceAddr));
+
+ temp = 1; /* allow address reuse */
+ if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_REUSEADDR,
+ &temp, sizeof(int)) < 0
+ || setsockopt(netPath->generalSock, SOL_SOCKET, SO_REUSEADDR,
+ &temp, sizeof(int)) < 0) {
+ DBG("failed to set socket reuse\n");
+ }
+
+ /* disable UDP checksum validation (Linux) */
+#ifdef SO_NO_CHECK
+ if(rtOpts->disableUdpChecksums) {
+ if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_NO_CHECK , &temp,
+ sizeof(int)) < 0
+ || setsockopt(netPath->generalSock, SOL_SOCKET, SO_NO_CHECK , &temp,
+ sizeof(int)) < 0) {
+ WARNING("Could not disable UDP checksum validation\n");
+ }
+ }
+#endif /* SO_NO_CHECK */
+
+ /* bind sockets */
+ /*
+ * need INADDR_ANY to receive both unicast and multicast,
+ * but only need interface address for unicast
+ */
+
+ if(rtOpts->ipMode == IPMODE_UNICAST ||
+ rtOpts->ignore_daemon_lock) {
+ addr.sin_addr = netPath->interfaceAddr;
+ } else {
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(PTP_EVENT_PORT);
+ if (bind(netPath->eventSock, (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in)) < 0) {
+ PERROR("failed to bind event socket");
+ return FALSE;
+ }
+ addr.sin_port = htons(PTP_GENERAL_PORT);
+ if (bind(netPath->generalSock, (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in)) < 0) {
+ PERROR("failed to bind general socket");
+ return FALSE;
+ }
+
+#ifdef SO_RCVBUF
+ /* try increasing receive buffers for unicast Sync processing */
+ if(rtOpts->ipMode == IPMODE_UNICAST && !rtOpts->slaveOnly) {
+ uint32_t n = 0;
+ socklen_t nlen = sizeof(n);
+
+ if (getsockopt(netPath->eventSock, SOL_SOCKET, SO_RCVBUF, &n, &nlen) < 0) {
+ n = 0;
+ }
+
+ DBG("eventSock rcvbuff : %d\n", n);
+
+ if(n < (UNICAST_MAX_DESTINATIONS * 1024)) {
+ n = UNICAST_MAX_DESTINATIONS * 1024;
+ if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) < 0) {
+ DBG("Failed to increase event socket receive buffer\n");
+ }
+ }
+
+ if (getsockopt(netPath->generalSock, SOL_SOCKET, SO_RCVBUF, &n, &nlen) < 0) {
+ n = 0;
+ }
+
+ DBG("genetalSock rcvbuff : %d\n", n);
+
+ if(n < (UNICAST_MAX_DESTINATIONS * 1024)) {
+ n = UNICAST_MAX_DESTINATIONS * 1024;
+ if (setsockopt(netPath->generalSock, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) < 0) {
+ DBG("Failed to increase general socket receive buffer\n");
+ }
+ }
+ }
+#endif /* SO_RCVBUF */
+
+#ifdef USE_BINDTODEVICE
+#ifdef linux
+ /*
+ * The following code makes sure that the data is only
+ * received on the specified interface. Without this option,
+ * it's possible to receive PTP from another interface, and
+ * confuse the protocol. Calling bind() with the IP address
+ * of the device instead of INADDR_ANY does not work.
+ *
+ * More info:
+ * http://developerweb.net/viewtopic.php?id=6471
+ * http://stackoverflow.com/questions/1207746/problems-with-so-bindtodevice-linux-socket-option
+ */
+
+ /*
+ * wowczarek: 2.3.1-rc4 at jun0215: this breaks the manual packet looping,
+ * so may only be used for multicast-only
+ */
+
+ if ( rtOpts->ipMode == IPMODE_MULTICAST ) {
+ if (setsockopt(netPath->eventSock, SOL_SOCKET, SO_BINDTODEVICE,
+ rtOpts->ifaceName, strlen(rtOpts->ifaceName)) < 0
+ || setsockopt(netPath->generalSock, SOL_SOCKET, SO_BINDTODEVICE,
+ rtOpts->ifaceName, strlen(rtOpts->ifaceName)) < 0){
+ PERROR("failed to call SO_BINDTODEVICE on the interface");
+ return FALSE;
+ }
+ }
+
+#endif
+#endif
+
+ /* Set socket dscp */
+ if(rtOpts->dscpValue) {
+
+ if (setsockopt(netPath->eventSock, IPPROTO_IP, IP_TOS,
+ &rtOpts->dscpValue, sizeof(int)) < 0
+ || setsockopt(netPath->generalSock, IPPROTO_IP, IP_TOS,
+ &rtOpts->dscpValue, sizeof(int)) < 0) {
+ PERROR("Failed to set socket DSCP bits");
+ return FALSE;
+ }
+ }
+
+ if(rtOpts->unicastDestinationsSet) {
+
+ ptpClock->unicastDestinationCount = parseUnicastConfig(rtOpts,
+ UNICAST_MAX_DESTINATIONS, ptpClock->unicastDestinations);
+ DBG("configured %d unicast destinations\n",ptpClock->unicastDestinationCount);
+
+ }
+
+ if(rtOpts->delayMechanism==P2P && rtOpts->ipMode==IPMODE_UNICAST) {
+ ptpClock->unicastPeerDestination.transportAddress = 0;
+ if(rtOpts->unicastPeerDestinationSet &&
+ rtOpts->delayMechanism==P2P && !hostLookup(rtOpts->unicastPeerDestination,
+ &ptpClock->unicastPeerDestination.transportAddress)) {
+
+ ERROR("Could not parse P2P unicast destination %s:\n",
+ rtOpts->unicastPeerDestination);
+ return FALSE;
+
+ } else if(!rtOpts->unicastPeerDestinationSet) {
+
+ ERROR("No P2P unicast destination specified\n");
+ return FALSE;
+
+ }
+
+ }
+
+ if(rtOpts->ipMode != IPMODE_UNICAST) {
+
+ /* init UDP Multicast on both Default and Peer addresses */
+ if (!netInitMulticast(netPath, rtOpts))
+ return FALSE;
+
+ /* set socket time-to-live */
+ if(!netSetMulticastTTL(netPath->eventSock,rtOpts->ttl) ||
+ !netSetMulticastTTL(netPath->generalSock,rtOpts->ttl))
+ return FALSE;
+
+ /* start tracking TTL */
+ netPath->ttlEvent = rtOpts->ttl;
+ netPath->ttlGeneral = rtOpts->ttl;
+ }
+
+ /* try enabling the capture of destination address */
+
+#ifdef IP_PKTINFO
+ temp = 1;
+ setsockopt(netPath->eventSock, IPPROTO_IP, IP_PKTINFO, &temp, sizeof(int));
+#endif /* IP_PKTINFO */
+
+#ifdef IP_RECVDSTADDR
+ temp = 1;
+ setsockopt(netPath->eventSock, IPPROTO_IP, IP_RECVDSTADDR, &temp, sizeof(int));
+#endif
+
+
+#ifdef SO_TIMESTAMPING
+ /* Reset the failure indicator when (re)starting network */
+ netPath->txTimestampFailure = FALSE;
+ /* for SO_TIMESTAMPING we're receiving transmitted packets via ERRQUEUE */
+ temp = 0;
+#else
+ /* enable loopback */
+ temp = 1;
+#endif
+
+ /* make timestamps available through recvmsg() */
+ if (!netInitTimestamping(netPath,rtOpts)) {
+ ERROR("Failed to enable packet time stamping\n");
+ return FALSE;
+ }
+
+#ifdef SO_TIMESTAMPING
+ /* If we failed to initialise SO_TIMESTAMPING, enable mcast loopback */
+ if(netPath->txTimestampFailure)
+ temp = 1;
+#endif
+
+ if(!netSetMulticastLoopback(netPath, temp)) {
+ return FALSE;
+ }
+
+#ifdef PTPD_PCAP
+ }
+#endif
+
+ /* Compile ACLs */
+ if(rtOpts->timingAclEnabled) {
+ freeIpv4AccessList(&netPath->timingAcl);
+ netPath->timingAcl=createIpv4AccessList(rtOpts->timingAclPermitText,
+ rtOpts->timingAclDenyText, rtOpts->timingAclOrder);
+ }
+ if(rtOpts->managementAclEnabled) {
+ freeIpv4AccessList(&netPath->managementAcl);
+ netPath->managementAcl=createIpv4AccessList(rtOpts->managementAclPermitText,
+ rtOpts->managementAclDenyText, rtOpts->managementAclOrder);
+ }
+
+
+ return TRUE;
+}
+
+/*Check if data has been received*/
+int
+netSelect(TimeInternal * timeout, NetPath * netPath, fd_set *readfds)
+{
+ int ret, nfds;
+ struct timeval tv, *tv_ptr;
+
+
+#if defined PTPD_SNMP
+ extern const RunTimeOpts rtOpts;
+ struct timeval snmp_timer_wait = { 0, 0}; // initialise to avoid unused warnings when SNMP disabled
+ int snmpblock = 0;
+#endif
+
+ if (timeout) {
+ if(isTimeInternalNegative(timeout)) {
+ ERROR("Negative timeout attempted for select()\n");
+ return -1;
+ }
+ tv.tv_sec = timeout->seconds;
+ tv.tv_usec = timeout->nanoseconds / 1000;
+ tv_ptr = &tv;
+ } else {
+ tv_ptr = NULL;
+ }
+
+ FD_ZERO(readfds);
+ nfds = 0;
+#ifdef PTPD_PCAP
+ if (netPath->pcapEventSock >= 0) {
+ FD_SET(netPath->pcapEventSock, readfds);
+ if (netPath->pcapGeneralSock >= 0)
+ FD_SET(netPath->pcapGeneralSock, readfds);
+
+ nfds = netPath->pcapEventSock;
+ if (netPath->pcapEventSock < netPath->pcapGeneralSock)
+ nfds = netPath->pcapGeneralSock;
+
+ } else if (netPath->eventSock >= 0) {
+#endif
+ FD_SET(netPath->eventSock, readfds);
+ if (netPath->generalSock >= 0)
+ FD_SET(netPath->generalSock, readfds);
+
+ nfds = netPath->eventSock;
+ if (netPath->eventSock < netPath->generalSock)
+ nfds = netPath->generalSock;
+#ifdef PTPD_PCAP
+ }
+#endif
+ nfds++;
+
+#if defined PTPD_SNMP
+if (rtOpts.snmpEnabled) {
+ snmpblock = 1;
+ if (tv_ptr) {
+ snmpblock = 0;
+ memcpy(&snmp_timer_wait, tv_ptr, sizeof(struct timeval));
+ }
+ snmp_select_info(&nfds, readfds, &snmp_timer_wait, &snmpblock);
+ if (snmpblock == 0)
+ tv_ptr = &snmp_timer_wait;
+}
+#endif
+
+ ret = select(nfds, readfds, 0, 0, tv_ptr);
+
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+ }
+#if defined PTPD_SNMP
+if (rtOpts.snmpEnabled) {
+ /* Maybe we have received SNMP related data */
+ if (ret > 0) {
+ snmp_read(readfds);
+ } else if (ret == 0) {
+ snmp_timeout();
+ run_alarms();
+ }
+ netsnmp_check_outstanding_agent_requests();
+}
+#endif
+ return ret;
+}
+
+/**
+ * store received data from network to "buf" , get and store the
+ * SO_TIMESTAMP value in "time" for an event message
+ *
+ * @note Should this function be merged with netRecvGeneral(), below?
+ * Jan Breuer: I think that netRecvGeneral should be
+ * simplified. Timestamp returned by this function is never
+ * used. According to this, netInitTimestamping can be also simplified
+ * to initialize timestamping only on eventSock.
+ *
+ * @param buf
+ * @param time
+ * @param netPath
+ *
+ * @return
+ */
+
+ssize_t
+netRecvEvent(Octet * buf, TimeInternal * time, NetPath * netPath, int flags)
+{
+ ssize_t ret = 0;
+ struct msghdr msg;
+ struct iovec vec[1];
+ struct sockaddr_in from_addr;
+
+#ifdef PTPD_PCAP
+ struct pcap_pkthdr *pkt_header;
+ const u_char *pkt_data;
+#endif
+
+#if defined(__QNXNTO__) && defined(PTPD_EXPERIMENTAL)
+ TimeInternal tmpTime;
+ /* get system time interpolated with TSC / clockCycles as soon as we have data on the socket */
+ getTime(&tmpTime);
+#endif
+
+ union {
+ struct cmsghdr cm;
+ char control[256];
+ } cmsg_un;
+
+ struct cmsghdr *cmsg;
+
+#if defined(SO_TIMESTAMPNS) || defined(SO_TIMESTAMPING)
+ struct timespec * ts;
+#elif defined(SO_BINTIME)
+ struct bintime * bt;
+ struct timespec ts;
+#endif
+
+#if defined(SO_TIMESTAMP)
+ struct timeval * tv;
+#endif
+ Boolean timestampValid = FALSE;
+ netPath->lastDestAddr = 0;
+#ifdef PTPD_PCAP
+ if (netPath->pcapEvent == NULL) { /* Using sockets */
+#endif
+ vec[0].iov_base = buf;
+ vec[0].iov_len = PACKET_SIZE;
+
+ memset(&msg, 0, sizeof(msg));
+ memset(&from_addr, 0, sizeof(from_addr));
+ memset(buf, 0, PACKET_SIZE);
+ memset(&cmsg_un, 0, sizeof(cmsg_un));
+
+ msg.msg_name = (caddr_t)&from_addr;
+ msg.msg_namelen = sizeof(from_addr);
+ msg.msg_iov = vec;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsg_un.control;
+ msg.msg_controllen = sizeof(cmsg_un.control);
+ msg.msg_flags = 0;
+
+ ret = recvmsg(netPath->eventSock, &msg, flags | MSG_DONTWAIT);
+ if (ret <= 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ return ret;
+ };
+ if (msg.msg_flags & MSG_TRUNC) {
+ ERROR("received truncated message\n");
+ return 0;
+ }
+ /* get time stamp of packet */
+ if (!time) {
+ ERROR("null receive time stamp argument\n");
+ return 0;
+ }
+ if (msg.msg_flags & MSG_CTRUNC) {
+ ERROR("received truncated ancillary data\n");
+ return 0;
+ }
+
+#if defined(HAVE_DECL_MSG_ERRQUEUE) && HAVE_DECL_MSG_ERRQUEUE
+ if(!(flags & MSG_ERRQUEUE))
+#endif
+ netPath->lastSourceAddr = from_addr.sin_addr.s_addr;
+
+ netPath->receivedPacketsTotal++;
+
+ /* do not report "from self" */
+ if(!netPath->lastSourceAddr || (netPath->lastSourceAddr != netPath->interfaceAddr.s_addr)) {
+ netPath->receivedPackets++;
+ }
+
+ if (msg.msg_controllen <= 0) {
+ ERROR("received short ancillary data (%ld/%ld)\n",
+ (long)msg.msg_controllen, (long)sizeof(cmsg_un.control));
+
+ return 0;
+ }
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+
+#ifdef IP_PKTINFO
+ if ((cmsg->cmsg_level == IPPROTO_IP) &&
+ (cmsg->cmsg_type == IP_PKTINFO)) {
+ struct in_pktinfo *pi =
+ (struct in_pktinfo *) CMSG_DATA(cmsg);
+ netPath->lastDestAddr = pi->ipi_addr.s_addr;
+ DBG("IP_PKTINFO Dst: %s\n", inet_ntoa(pi->ipi_addr));
+ }
+#endif
+
+#ifdef IP_RECVDSTADDR
+ if ((cmsg->cmsg_level == IPPROTO_IP) &&
+ (cmsg->cmsg_type == IP_RECVDSTADDR)) {
+ struct in_addr *pa = (struct in_addr *) CMSG_DATA(cmsg);
+ netPath->lastDestAddr = pa->s_addr;
+ DBG("IP_RECVDSTADDR Dst: %s\n", inet_ntoa(*pa));
+ }
+#endif
+
+
+ if (cmsg->cmsg_level == SOL_SOCKET) {
+#if defined(SO_TIMESTAMPING) && defined(SO_TIMESTAMPNS)
+ if(cmsg->cmsg_type == SO_TIMESTAMPING ||
+ cmsg->cmsg_type == SO_TIMESTAMPNS) {
+ ts = (struct timespec *)CMSG_DATA(cmsg);
+ time->seconds = ts->tv_sec;
+ time->nanoseconds = ts->tv_nsec;
+ timestampValid = TRUE;
+ DBG("rcvevent: SO_TIMESTAMP%s %s time stamp: %us %dns\n", netPath->txTimestampFailure ?
+ "NS" : "ING",
+ (flags & MSG_ERRQUEUE) ? "(TX)" : "(RX)" , time->seconds, time->nanoseconds);
+ break;
+ }
+#elif defined(SO_TIMESTAMPNS)
+ if(cmsg->cmsg_type == SCM_TIMESTAMPNS) {
+ ts = (struct timespec *)CMSG_DATA(cmsg);
+ time->seconds = ts->tv_sec;
+ time->nanoseconds = ts->tv_nsec;
+ timestampValid = TRUE;
+ DBGV("kernel NANO recv time stamp %us %dns\n",
+ time->seconds, time->nanoseconds);
+ break;
+ }
+#elif defined(SO_BINTIME)
+ if(cmsg->cmsg_type == SCM_BINTIME) {
+ bt = (struct bintime *)CMSG_DATA(cmsg);
+ bintime2timespec(bt, &ts);
+ time->seconds = ts.tv_sec;
+ time->nanoseconds = ts.tv_nsec;
+ timestampValid = TRUE;
+ DBGV("kernel NANO recv time stamp %us %dns\n",
+ time->seconds, time->nanoseconds);
+ break;
+ }
+#endif
+
+#if defined(SO_TIMESTAMP)
+ if(cmsg->cmsg_type == SCM_TIMESTAMP) {
+ tv = (struct timeval *)CMSG_DATA(cmsg);
+ time->seconds = tv->tv_sec;
+ time->nanoseconds = tv->tv_usec * 1000;
+ timestampValid = TRUE;
+ DBGV("kernel MICRO recv time stamp %us %dns\n",
+ time->seconds, time->nanoseconds);
+ }
+#endif
+ }
+
+ }
+
+
+ if (!timestampValid) {
+ /*
+ * do not try to get by with recording the time here, better
+ * to fail because the time recorded could be well after the
+ * message receive, which would put a big spike in the
+ * offset signal sent to the clock servo
+ */
+ DBG("netRecvEvent: no receive time stamp\n");
+ return 0;
+ }
+#ifdef PTPD_PCAP
+ }
+#endif
+
+#ifdef PTPD_PCAP
+ else { /* Using PCAP */
+ /* Discard packet on socket */
+ if (netPath->eventSock >= 0) {
+ recv(netPath->eventSock, buf, PACKET_SIZE, MSG_DONTWAIT);
+ }
+
+ if ((ret = pcap_next_ex(netPath->pcapEvent, &pkt_header,
+ &pkt_data)) < 1) {
+ if (ret < 0)
+ INFO("netRecvEvent: pcap_next_ex failed %s\n",
+ pcap_geterr(netPath->pcapEvent));
+ return 0;
+ }
+
+ /* Make sure this is IP (could dot1q get here?) */
+ if( ntohs(*(u_short *)(pkt_data + 12)) != ETHERTYPE_IP) {
+ if( ntohs(*(u_short *)(pkt_data + 12)) != PTP_ETHER_TYPE) {
+ DBG("PCAP payload ethertype received not IP or PTP: 0x%04x\n",
+ ntohs(*(u_short *)(pkt_data + 12)));
+ /* do not count packets if from self */
+ } else if(memcmp(&netPath->interfaceInfo.hwAddress, pkt_data + 6, 6)) {
+ netPath->receivedPackets++;
+ }
+ } else {
+ /* Retrieve source IP from the payload - 14 eth + 12 IP */
+ netPath->lastSourceAddr = *(Integer32 *)(pkt_data + 26);
+ /* Retrieve destination IP from the payload - 14 eth + 16 IP */
+ netPath->lastDestAddr = *(Integer32 *)(pkt_data + 30);
+ /* do not count packets from self */
+ if(netPath->lastSourceAddr != netPath->interfaceAddr.s_addr) {
+ netPath->receivedPackets++;
+ }
+ }
+
+ netPath->receivedPacketsTotal++;
+
+ /* XXX Total cheat */
+ memcpy(buf, pkt_data + netPath->headerOffset,
+ pkt_header->caplen - netPath->headerOffset);
+ time->seconds = pkt_header->ts.tv_sec;
+ time->nanoseconds = pkt_header->ts.tv_usec * 1000;
+ timestampValid = TRUE;
+ DBGV("netRecvEvent: kernel PCAP recv time stamp %us %dns\n",
+ time->seconds, time->nanoseconds);
+ fflush(NULL);
+ ret = pkt_header->caplen - netPath->headerOffset;
+ }
+#endif
+
+#if defined(__QNXNTO__) && defined(PTPD_EXPERIMENTAL)
+ *time = tmpTime;
+#endif
+
+ return ret;
+}
+
+
+
+/**
+ *
+ * store received data from network to "buf" get and store the
+ * SO_TIMESTAMP value in "time" for a general message
+ *
+ * @param buf
+ * @param time
+ * @param netPath
+ *
+ * @return
+ */
+
+ssize_t
+netRecvGeneral(Octet * buf, NetPath * netPath)
+{
+ ssize_t ret = 0;
+ struct sockaddr_in from_addr;
+
+#ifdef PTPD_PCAP
+ struct pcap_pkthdr *pkt_header;
+ const u_char *pkt_data;
+#endif
+ socklen_t from_addr_len = sizeof(from_addr);
+
+ netPath->lastSourceAddr = 0;
+
+#ifdef PTPD_PCAP
+ if (netPath->pcapGeneral == NULL) {
+#endif
+ ret=recvfrom(netPath->generalSock, buf, PACKET_SIZE, MSG_DONTWAIT, (struct sockaddr*)&from_addr, &from_addr_len);
+ netPath->lastSourceAddr = from_addr.sin_addr.s_addr;
+
+ /* do not report "from self" */
+ if(!netPath->lastSourceAddr || (netPath->lastSourceAddr != netPath->interfaceAddr.s_addr)) {
+ netPath->receivedPackets++;
+ }
+ netPath->receivedPacketsTotal++;
+ return ret;
+#ifdef PTPD_PCAP
+ }
+#endif
+
+#ifdef PTPD_PCAP
+ else { /* Using PCAP */
+ /* Discard packet on socket */
+ if (netPath->generalSock >= 0)
+ recv(netPath->generalSock, buf, PACKET_SIZE, MSG_DONTWAIT);
+
+
+ if (( ret = pcap_next_ex(netPath->pcapGeneral, &pkt_header,
+ &pkt_data)) < 1) {
+ if (ret < 0)
+ DBGV("netRecvGeneral: pcap_next_ex failed %d %s\n",
+ ret, pcap_geterr(netPath->pcapGeneral));
+ return 0;
+ }
+
+ /* Make sure this is IP (could dot1q get here?) */
+ if( ntohs(*(u_short *)(pkt_data + 12)) != ETHERTYPE_IP) {
+ if( ntohs(*(u_short *)(pkt_data + 12)) != PTP_ETHER_TYPE) {
+ DBG("PCAP payload ethertype received not IP or PTP: 0x%04x\n",
+ ntohs(*(u_short *)(pkt_data + 12)));
+ /* do not count packets if from self */
+ } else if(memcmp(&netPath->interfaceInfo.hwAddress, pkt_data + 6, 6)) {
+ netPath->receivedPackets++;
+ }
+ } else {
+ /* Retrieve source IP from the payload - 14 eth + 12 IP */
+ netPath->lastSourceAddr = *(Integer32 *)(pkt_data + 26);
+ /* Retrieve destination IP from the payload - 14 eth + 16 IP */
+ netPath->lastDestAddr = *(Integer32 *)(pkt_data + 30);
+ /* do not count packets from self */
+ if(netPath->lastSourceAddr != netPath->interfaceAddr.s_addr) {
+ netPath->receivedPackets++;
+ }
+ }
+
+ netPath->receivedPacketsTotal++;
+
+ /* XXX Total cheat */
+ memcpy(buf, pkt_data + netPath->headerOffset,
+ pkt_header->caplen - netPath->headerOffset);
+ fflush(NULL);
+ ret = pkt_header->caplen - netPath->headerOffset;
+ }
+#endif
+ return ret;
+}
+
+
+#ifdef PTPD_PCAP
+ssize_t
+netSendPcapEther(Octet * buf, UInteger16 length,
+ struct ether_addr * dst, struct ether_addr * src,
+ pcap_t * pcap) {
+ Octet ether[ETHER_HDR_LEN + PACKET_SIZE];
+#ifdef HAVE_STRUCT_ETHER_ADDR_OCTET
+ memcpy(ether, dst->octet, ETHER_ADDR_LEN);
+ memcpy(ether + ETHER_ADDR_LEN, src->octet, ETHER_ADDR_LEN);
+#else
+ memcpy(ether, dst->ether_addr_octet, ETHER_ADDR_LEN);
+ memcpy(ether + ETHER_ADDR_LEN, src->ether_addr_octet, ETHER_ADDR_LEN);
+#endif /* HAVE_STRUCT_ETHER_ADDR_OCTET */
+ *((short *)ðer[2 * ETHER_ADDR_LEN]) = htons(PTP_ETHER_TYPE);
+ memcpy(ether + ETHER_HDR_LEN, buf, length);
+
+ return pcap_inject(pcap, ether, ETHER_HDR_LEN + length);
+}
+#endif
+
+//
+// destinationAddress: destination:
+// if filled, send to this unicast dest;
+// if zero, sending to multicast.
+//
+///
+/// TODO: merge these 2 functions into one
+///
+ssize_t
+netSendEvent(Octet * buf, UInteger16 length, NetPath * netPath,
+ const RunTimeOpts *rtOpts, Integer32 destinationAddress, TimeInternal * tim)
+{
+ ssize_t ret;
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(PTP_EVENT_PORT);
+
+#if defined(__QNXNTO__) && defined(PTPD_EXPERIMENTAL)
+ TimeInternal tmpTime;
+ /* get system time interpolated with TSC / clockCycles as soon as we have data on the socket */
+ getTime(&tmpTime);
+#endif
+
+#ifdef PTPD_PCAP
+
+ /* In PCAP Ethernet mode, we use pcapEvent for receiving all messages
+ * and pcapGeneral for sending all messages
+ */
+ if ((netPath->pcapGeneral != NULL) && (rtOpts->transport == IEEE_802_3 )) {
+ ret = netSendPcapEther(buf, length,
+ &netPath->etherDest,
+ (struct ether_addr *)netPath->interfaceID,
+ netPath->pcapGeneral);
+ if (ret <= 0)
+ DBG("Error sending ether multicast event message\n");
+ else {
+ netPath->sentPackets++;
+ netPath->sentPacketsTotal++;
+ }
+ } else {
+#endif
+ if (destinationAddress ) {
+ addr.sin_addr.s_addr = destinationAddress;
+ /*
+ * This function is used for PTP only anyway - for now.
+ * If we're sending to a unicast address, set the UNICAST flag.
+ * Transport API in LibCCK / 2.4 uses a callback for this,
+ * so the client can do something with the payload before it's sent,
+ * depending if it's unicast or multicast.
+ */
+ *(char *)(buf + 6) |= PTP_UNICAST;
+
+ ret = sendto(netPath->eventSock, buf, length, 0,
+ (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in));
+ if (ret <= 0)
+ DBG("Error sending unicast event message\n");
+ else {
+ netPath->sentPackets++;
+ netPath->sentPacketsTotal++;
+ }
+#ifndef SO_TIMESTAMPING
+#if defined(__QNXNTO__) && defined(PTPD_EXPERIMENTAL)
+ *tim = tmpTime;
+#else
+ /*
+ * Need to forcibly loop back the packet since
+ * we are not using multicast.
+ */
+ addr.sin_addr.s_addr = netPath->interfaceAddr.s_addr;
+ ret = sendto(netPath->eventSock, buf, length, 0,
+ (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in));
+ if (ret <= 0)
+ DBGV("Error looping back unicast event message\n");
+#endif
+
+#else
+
+#ifdef PTPD_PCAP
+ if((netPath->pcapEvent == NULL) && !netPath->txTimestampFailure) {
+#else
+ if(!netPath->txTimestampFailure) {
+#endif /* PTPD_PCAP */
+ if(!getTxTimestamp(netPath, tim)) {
+ netPath->txTimestampFailure = TRUE;
+ if (tim) {
+ clearTime(tim);
+ }
+ }
+ }
+
+ if(netPath->txTimestampFailure)
+ {
+ /* We've had a TX timestamp receipt timeout - falling back to packet looping */
+ addr.sin_addr.s_addr = netPath->interfaceAddr.s_addr;
+ ret = sendto(netPath->eventSock, buf, length, 0,
+ (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in));
+ if (ret <= 0)
+ DBG("Error looping back unicast event message\n");
+ }
+#endif /* SO_TIMESTAMPING */
+ } else {
+ addr.sin_addr.s_addr = netPath->multicastAddr;
+ /* Is TTL OK? */
+ if(netPath->ttlEvent != rtOpts->ttl) {
+ /* Try restoring TTL */
+ /* set socket time-to-live */
+ if (netSetMulticastTTL(netPath->eventSock,rtOpts->ttl)) {
+ netPath->ttlEvent = rtOpts->ttl;
+ }
+ }
+ ret = sendto(netPath->eventSock, buf, length, 0,
+ (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in));
+ if (ret <= 0)
+ DBG("Error sending multicast event message\n");
+ else {
+ netPath->sentPackets++;
+ netPath->sentPacketsTotal++;
+ }
+#ifdef SO_TIMESTAMPING
+
+#ifdef PTPD_PCAP
+ if((netPath->pcapEvent == NULL) && !netPath->txTimestampFailure) {
+#else
+ if(!netPath->txTimestampFailure) {
+#endif /* PTPD_PCAP */
+ if(!getTxTimestamp(netPath, tim)) {
+ if (tim) {
+ clearTime(tim);
+ }
+
+ netPath->txTimestampFailure = TRUE;
+
+ /* Try re-enabling MULTICAST_LOOP */
+ netSetMulticastLoopback(netPath, TRUE);
+ }
+ }
+#endif /* SO_TIMESTAMPING */
+ }
+
+#ifdef PTPD_PCAP
+ }
+#endif
+ return ret;
+}
+
+ssize_t
+netSendGeneral(Octet * buf, UInteger16 length, NetPath * netPath,
+ const const RunTimeOpts *rtOpts, Integer32 destinationAddress)
+{
+ ssize_t ret;
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(PTP_GENERAL_PORT);
+
+#ifdef PTPD_PCAP
+ if ((netPath->pcapGeneral != NULL) && (rtOpts->transport == IEEE_802_3)) {
+ ret = netSendPcapEther(buf, length,
+ &netPath->etherDest,
+ (struct ether_addr *)netPath->interfaceID,
+ netPath->pcapGeneral);
+
+ if (ret <= 0)
+ DBG("Error sending ether multicast general message\n");
+ else {
+ netPath->sentPackets++;
+ netPath->sentPacketsTotal++;
+ }
+ } else {
+#endif
+ if(destinationAddress) {
+
+ addr.sin_addr.s_addr = destinationAddress;
+ /*
+ * This function is used for PTP only anyway...
+ * If we're sending to a unicast address, set the UNICAST flag.
+ */
+ *(char *)(buf + 6) |= PTP_UNICAST;
+
+ ret = sendto(netPath->generalSock, buf, length, 0,
+ (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in));
+ if (ret <= 0)
+ DBG("Error sending unicast general message\n");
+ else {
+ netPath->sentPackets++;
+ netPath->sentPacketsTotal++;
+ }
+ } else {
+ addr.sin_addr.s_addr = netPath->multicastAddr;
+
+ /* Is TTL OK? */
+ if(netPath->ttlGeneral != rtOpts->ttl) {
+ /* Try restoring TTL */
+ if (netSetMulticastTTL(netPath->generalSock,rtOpts->ttl)) {
+ netPath->ttlGeneral = rtOpts->ttl;
+ }
+ }
+
+ ret = sendto(netPath->generalSock, buf, length, 0,
+ (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in));
+ if (ret <= 0)
+ DBG("Error sending multicast general message\n");
+ else {
+ netPath->sentPackets++;
+ netPath->sentPacketsTotal++;
+ }
+ }
+
+#ifdef PTPD_PCAP
+ }
+#endif
+ return ret;
+}
+
+ssize_t
+netSendPeerGeneral(Octet * buf, UInteger16 length, NetPath * netPath, const RunTimeOpts *rtOpts, Integer32 dst)
+{
+
+ ssize_t ret;
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(PTP_GENERAL_PORT);
+
+#ifdef PTPD_PCAP
+ if ((netPath->pcapGeneral != NULL) && (rtOpts->transport == IEEE_802_3)) {
+ ret = netSendPcapEther(buf, length,
+ &netPath->peerEtherDest,
+ (struct ether_addr *)netPath->interfaceID,
+ netPath->pcapGeneral);
+
+ if (ret <= 0)
+ DBG("error sending ether multicast general message\n");
+
+ } else if (dst)
+#else
+ if (dst)
+#endif
+ {
+ addr.sin_addr.s_addr = dst;
+
+ /*
+ * This function is used for PTP only anyway...
+ * If we're sending to a unicast address, set the UNICAST flag.
+ */
+ *(char *)(buf + 6) |= PTP_UNICAST;
+
+ ret = sendto(netPath->generalSock, buf, length, 0,
+ (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in));
+ if (ret <= 0)
+ DBG("Error sending unicast peer general message\n");
+
+ } else {
+ addr.sin_addr.s_addr = netPath->peerMulticastAddr;
+
+ /* is TTL already 1 ? */
+ if(netPath->ttlGeneral != 1) {
+ /* Try setting TTL to 1 */
+ if (netSetMulticastTTL(netPath->generalSock,1)) {
+ netPath->ttlGeneral = 1;
+ }
+ }
+ ret = sendto(netPath->generalSock, buf, length, 0,
+ (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in));
+ if (ret <= 0)
+ DBG("Error sending multicast peer general message\n");
+
+ }
+
+ if (ret > 0) {
+ netPath->sentPackets++;
+ netPath->sentPacketsTotal++;
+ }
+
+ return ret;
+
+}
+
+ssize_t
+netSendPeerEvent(Octet * buf, UInteger16 length, NetPath * netPath, const RunTimeOpts *rtOpts, Integer32 dst, TimeInternal * tim)
+{
+ ssize_t ret;
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(PTP_EVENT_PORT);
+
+#ifdef PTPD_PCAP
+ if ((netPath->pcapGeneral != NULL) && (rtOpts->transport == IEEE_802_3)) {
+ ret = netSendPcapEther(buf, length,
+ &netPath->peerEtherDest,
+ (struct ether_addr *)netPath->interfaceID,
+ netPath->pcapGeneral);
+
+ if (ret <= 0)
+ DBG("error sending ether multicast general message\n");
+ } else if (dst)
+#else
+ if (dst)
+#endif
+ {
+ addr.sin_addr.s_addr = dst;
+
+ /*
+ * This function is used for PTP only anyway...
+ * If we're sending to a unicast address, set the UNICAST flag.
+ */
+ *(char *)(buf + 6) |= PTP_UNICAST;
+
+ ret = sendto(netPath->eventSock, buf, length, 0,
+ (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in));
+ if (ret <= 0)
+ DBG("Error sending unicast peer event message\n");
+
+#ifndef SO_TIMESTAMPING
+ /*
+ * Need to forcibly loop back the packet since
+ * we are not using multicast.
+ */
+ addr.sin_addr.s_addr = netPath->interfaceAddr.s_addr;
+
+ ret = sendto(netPath->eventSock, buf, length, 0,
+ (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in));
+ if (ret <= 0)
+ DBG("Error looping back unicast peer event message\n");
+#else
+
+#ifdef PTPD_PCAP
+ if((netPath->pcapEvent == NULL) && !netPath->txTimestampFailure) {
+#else
+ if(!netPath->txTimestampFailure) {
+#endif /* PTPD_PCAP */
+ if(!getTxTimestamp(netPath, tim)) {
+ netPath->txTimestampFailure = TRUE;
+ if (tim) {
+ clearTime(tim);
+ }
+ }
+ }
+
+ if(netPath->txTimestampFailure) {
+ /* We've had a TX timestamp receipt timeout - falling back to packet looping */
+ addr.sin_addr.s_addr = netPath->interfaceAddr.s_addr;
+ ret = sendto(netPath->eventSock, buf, length, 0,
+ (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in));
+ if (ret <= 0)
+ DBG("Error looping back unicast event message\n");
+ }
+#endif /* SO_TIMESTAMPING */
+
+ } else {
+ addr.sin_addr.s_addr = netPath->peerMulticastAddr;
+
+ /* is TTL already 1 ? */
+ if(netPath->ttlEvent != 1) {
+ /* Try setting TTL to 1 */
+ if (netSetMulticastTTL(netPath->eventSock,1)) {
+ netPath->ttlEvent = 1;
+ }
+ }
+ ret = sendto(netPath->eventSock, buf, length, 0,
+ (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in));
+ if (ret <= 0)
+ DBG("Error sending multicast peer event message\n");
+#ifdef SO_TIMESTAMPING
+ if(!netPath->txTimestampFailure) {
+ if(!getTxTimestamp(netPath, tim)) {
+ if (tim) {
+ clearTime(tim);
+ }
+
+ netPath->txTimestampFailure = TRUE;
+
+ /* Try re-enabling MULTICAST_LOOP */
+ netSetMulticastLoopback(netPath, TRUE);
+ }
+ }
+#endif /* SO_TIMESTAMPING */
+ }
+
+ if (ret > 0) {
+ netPath->sentPackets++;
+ netPath->sentPacketsTotal++;
+ }
+
+ return ret;
+}
+
+
+
+/*
+ * refresh IGMP on a timeout
+ */
+/*
+ * @return TRUE if successful
+ */
+Boolean
+netRefreshIGMP(NetPath * netPath, const RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ DBG("netRefreshIGMP\n");
+
+ if(netPath->joinedGeneral) {
+ netShutdownMulticastIPv4(netPath, netPath->multicastAddr);
+ netPath->multicastAddr = 0;
+ }
+
+ if(netPath->joinedPeer) {
+ netShutdownMulticastIPv4(netPath, netPath->peerMulticastAddr);
+ netPath->peerMulticastAddr = 0;
+ }
+
+ /* suspend process 100 milliseconds, to make sure the kernel sends the IGMP_leave properly */
+ usleep(100*1000);
+
+ if (!netInitMulticast(netPath, rtOpts)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/rtemsbsd/ptpd/src/dep/ntpengine/ntp_isc_md5.c b/rtemsbsd/ptpd/src/dep/ntpengine/ntp_isc_md5.c
new file mode 100644
index 00000000..e4e34ac9
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/ntpengine/ntp_isc_md5.c
@@ -0,0 +1,273 @@
+/*
+ * This file contains the minimal set of declarations and constants
+ * extracted from the ISC MD5 code and ntpdc code included with NTP 4
+ * distribution version 4.2.6p5 to allow computing the MD5 digest
+ * for NTP type 7 packets
+ */
+
+/* Original copyright notice */
+
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "../../ptpd.h"
+
+#include "ntp_isc_md5.h"
+
+void ntp_memset (char *, int, int);
+
+#define memcmp(a, b, c) bcmp(a, b, (int)(c))
+#define memmove(t, f, c) bcopy(f, t, (int)(c))
+#define memcpy(t, f, c) bcopy(f, t, (int)(c))
+#define memset(a, x, c) if (0 == (x)) \
+ bzero(a, (int)(c)); \
+ else \
+ ntp_memset((char *)(a), x, c)
+
+static void
+byteSwap(uint32_t *buf, unsigned words)
+{
+ unsigned char *p = (unsigned char *)buf;
+
+ do {
+ *buf++ = (uint32_t)((unsigned)p[3] << 8 | p[2]) << 16 |
+ ((unsigned)p[1] << 8 | p[0]);
+ p += 4;
+ } while (--words);
+}
+
+/*!
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void
+isc_md5_init(isc_md5_t *ctx) {
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bytes[0] = 0;
+ ctx->bytes[1] = 0;
+}
+
+void
+isc_md5_invalidate(isc_md5_t *ctx) {
+ memset(ctx, 0, sizeof(isc_md5_t));
+}
+
+/*@{*/
+/*! The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+/*@}*/
+
+/*! This is the central step in the MD5 algorithm. */
+#define MD5STEP(f,w,x,y,z,in,s) \
+ (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
+
+/*!
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+transform(uint32_t buf[4], uint32_t const in[16]) {
+ register uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+/*!
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void
+isc_md5_update(isc_md5_t *ctx, const unsigned char *buf, unsigned int len) {
+ uint32_t t;
+
+ /* Update byte count */
+
+ t = ctx->bytes[0];
+ if ((ctx->bytes[0] = t + len) < t)
+ ctx->bytes[1]++; /* Carry from low to high */
+
+ t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */
+ if (t > len) {
+ memcpy((unsigned char *)ctx->in + 64 - t, buf, len);
+ return;
+ }
+ /* First chunk is an odd size */
+ memcpy((unsigned char *)ctx->in + 64 - t, buf, t);
+ byteSwap(ctx->in, 16);
+ transform(ctx->buf, ctx->in);
+ buf += t;
+ len -= t;
+
+ /* Process data in 64-byte chunks */
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteSwap(ctx->in, 16);
+ transform(ctx->buf, ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+ memcpy(ctx->in, buf, len);
+}
+
+/*!
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void
+isc_md5_final(isc_md5_t *ctx, unsigned char *digest) {
+ int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */
+ unsigned char *p = (unsigned char *)ctx->in + count;
+
+ /* Set the first char of padding to 0x80. There is always room. */
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 56 bytes (-8..55) */
+ count = 56 - 1 - count;
+
+ if (count < 0) { /* Padding forces an extra block */
+ memset(p, 0, count + 8);
+ byteSwap(ctx->in, 16);
+ transform(ctx->buf, ctx->in);
+ p = (unsigned char *)ctx->in;
+ count = 56;
+ }
+ memset(p, 0, count);
+ byteSwap(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ctx->in[14] = ctx->bytes[0] << 3;
+ ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
+ transform(ctx->buf, ctx->in);
+
+ byteSwap(ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(isc_md5_t)); /* In case it's sensitive */
+}
+
+/*
+ * MD5authencrypt - generate message digest
+ *
+ * Returns length of MAC including key ID and digest.
+ */
+
+int
+MD5authencrypt(
+ char *key, /* key pointer */
+ uint32_t *pkt, /* packet pointer */
+ int length,
+ keyid_t keyid
+ )
+{
+ u_char digest[64];
+ u_int len;
+ PTPD_EVP_MD_CTX ctx;
+ pkt[length / 4] = htonl(keyid);
+ EVP_DigestInit(&ctx);
+ EVP_DigestUpdate(&ctx, (u_char *)key, (u_int)strlen(key));
+ EVP_DigestUpdate(&ctx, (u_char *)pkt, (u_int)length);
+ EVP_DigestFinal(&ctx, digest, &len);
+ memmove((u_char *)pkt + length + 4, digest, len);
+ return (len + 4);
+}
diff --git a/rtemsbsd/ptpd/src/dep/ntpengine/ntp_isc_md5.h b/rtemsbsd/ptpd/src/dep/ntpengine/ntp_isc_md5.h
new file mode 100644
index 00000000..236c0e4b
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/ntpengine/ntp_isc_md5.h
@@ -0,0 +1,99 @@
+/*
+ * This file contains the minimal set of declarations and constants
+ * extracted from the ISC MD5 code included with NTP 4 distribution
+ * version 4.2.6p5 to allow computing the MAC for NTP type 7 packets
+ */
+
+/* Original copyright notice */
+
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: md5.h,v 1.16 2007/06/19 23:47:18 tbox Exp $ */
+
+/*! \file isc/md5.h
+ * \brief This is the header file for the MD5 message-digest algorithm.
+ *
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ * Changed so as no longer to depend on Colin Plumb's `usual.h'
+ * header definitions; now uses stuff from dpkg's config.h
+ * - Ian Jackson <ijackson at nyx.cs.du.edu>.
+ * Still in the public domain.
+ */
+
+#ifndef ISC_MD5_H
+#define ISC_MD5_H 1
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif /* HAVE_STRINGS_H */
+
+#define ISC_MD5_DIGESTLENGTH 16U
+
+
+typedef struct {
+ uint32_t buf[4];
+ uint32_t bytes[2];
+ uint32_t in[16];
+} isc_md5_t;
+
+void
+isc_md5_init(isc_md5_t *ctx);
+
+void
+isc_md5_invalidate(isc_md5_t *ctx);
+
+void
+isc_md5_update(isc_md5_t *ctx, const unsigned char *buf, unsigned int len);
+
+void
+isc_md5_final(isc_md5_t *ctx, unsigned char *digest);
+
+ typedef isc_md5_t MD5_CTX;
+# define MD5Init(c) isc_md5_init(c)
+# define MD5Update(c, p, s) isc_md5_update(c, p, s)
+# define MD5Final(d, c) isc_md5_final((c), (d)) /* swapped */
+ typedef MD5_CTX PTPD_EVP_MD_CTX;
+# define EVP_DigestInit(c) MD5Init(c)
+# define EVP_DigestUpdate(c, p, s) MD5Update(c, p, s)
+# define EVP_DigestFinal(c, d, pdl) \
+ do { \
+ MD5Final((d), (c)); \
+ *(pdl) = 16; \
+ } while (0)
+
+#define NID_md5 4
+#define JAN_1970 2208988800UL
+#define FRAC 4294967296LL
+
+int MD5authencrypt( char *key, uint32_t *pkt, int length, keyid_t keyid );
+
+#endif /* ISC_MD5_H */
+
diff --git a/rtemsbsd/ptpd/src/dep/ntpengine/ntpdcontrol.c b/rtemsbsd/ptpd/src/dep/ntpengine/ntpdcontrol.c
new file mode 100644
index 00000000..0c83adb1
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/ntpengine/ntpdcontrol.c
@@ -0,0 +1,874 @@
+/*-
+ * Copyright (c) 2013-2015 Wojciech Owczarek,
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file ntpdcontrol.c
+ * @date Tue Jul 20 22:19:20 2013
+ *
+ * @brief Functions allowing remote control of the ntpd daemon
+ * using mode 7 packets
+ *
+ */
+
+/*
+ * This code is largely based on the source code of the ntpdc utility from the
+ * NTP distribution version 4.2.6p5 and is mostly a bridge between PTPd2 APIs
+ * and NTPDc code - functionality so far is limited to setting and clearing system
+ * flags to allow failover to- and -from (local) NTP
+ */
+
+/* Original NTP4 copyright notice */
+
+/***********************************************************************
+ * *
+ * Copyright (c) University of Delaware 1992-2011 *
+ * *
+ * Permission to use, copy, modify, and distribute this software and *
+ * its documentation for any purpose with or without fee is hereby *
+ * granted, provided that the above copyright notice appears in all *
+ * copies and that both the copyright notice and this permission *
+ * notice appear in supporting documentation, and that the name *
+ * University of Delaware not be used in advertising or publicity *
+ * pertaining to distribution of the software without specific, *
+ * written prior permission. The University of Delaware makes no *
+ * representations about the suitability this software for any *
+ * purpose. It is provided "as is" without express or implied *
+ * warranty. *
+ * *
+ ***********************************************************************/
+
+#include "../../ptpd.h"
+
+#define NTP_PORT 123
+
+char *ntpdc_pktdata;
+
+Boolean
+ntpInit(NTPoptions* options, NTPcontrol* control)
+{
+
+ int res = TRUE;
+ TimingService service = control->timingService;
+
+ control->sockFD = -1;
+ if(!options->enableEngine)
+ return FALSE;
+
+ memset(control, 0, sizeof(*control));
+ /* preserve TimingService... temporary */
+ control->timingService = service;
+
+ if(!hostLookup(options->hostAddress, &control->serverAddress)) {
+ control->serverAddress = 0;
+ return FALSE;
+ }
+
+ if ((control->sockFD = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0 ) {
+ PERROR("failed to initalize NTP control socket");
+ return FALSE;
+ }
+
+ /* This will attempt to read the ntpd control flags for the first time */
+ res = ntpdInControl(options, control);
+
+ if (res != INFO_YES && res != INFO_NO) {
+ return FALSE;
+ }
+
+ DBGV("NTPd original flags: %d\n", control->originalFlags);
+ return TRUE;
+}
+
+Boolean
+ntpShutdown(NTPoptions* options, NTPcontrol* control)
+{
+
+ /* Attempt reverting ntpd flags to the original value */
+ if(control->flagsCaptured) {
+ /* we only control the kernel and ntp flags */
+ /* just to avoid -Wunused* */
+#ifdef RUNTIME_DEBUG
+ int resC = ntpdClearFlags(options, control, ~(control->originalFlags) & (INFO_FLAG_KERNEL | INFO_FLAG_NTP));
+ int resS = ntpdSetFlags(options, control, control->originalFlags & (INFO_FLAG_KERNEL | INFO_FLAG_NTP));
+ DBGV("Attempting to revert NTPd flags to %d - result: clear %d set %d\n", control->originalFlags,
+ resC, resS);
+#else
+ ntpdClearFlags(options, control, ~(control->originalFlags) & (INFO_FLAG_KERNEL | INFO_FLAG_NTP));
+ ntpdSetFlags(options, control, control->originalFlags & (INFO_FLAG_KERNEL | INFO_FLAG_NTP));
+#endif /* RUNTIME_DEBUG */
+ }
+
+ if (control->sockFD > 0)
+ close(control->sockFD);
+ control->sockFD = -1;
+
+ return TRUE;
+}
+
+
+static ssize_t
+ntpSend(NTPcontrol* control, Octet * buf, UInteger16 length)
+{
+ ssize_t ret = -1;
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(NTP_PORT);
+
+ if (control->serverAddress) {
+
+ addr.sin_addr.s_addr = control->serverAddress;
+
+ ret = sendto(control->sockFD, buf, length, 0,
+ (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in));
+ if (ret <= 0)
+ INFO("error sending NTP control message\n");
+
+ }
+ return ret;
+}
+
+
+/*
+ * Default values we use
+ */
+#define DEFHOST "localhost" /* default host name */
+#define DEFTIMEOUT (5) /* 5 second time out */
+#define DEFSTIMEOUT (2) /* 2 second time out after first */
+#define DEFDELAY 0x51EB852 /* 20 milliseconds, l_fp fraction */
+#define LENHOSTNAME 256 /* host name is 256 characters long */
+#define MAXCMDS 100 /* maximum commands on cmd line */
+#define MAXHOSTS 200 /* maximum hosts on cmd line */
+#define MAXLINE 512 /* maximum line length */
+#define MAXTOKENS (1+1+MAXARGS+MOREARGS+2) /* maximum number of usable tokens */
+#define SCREENWIDTH 78 /* nominal screen width in columns */
+
+#define INITDATASIZE (sizeof(struct resp_pkt) * 16)
+#define INCDATASIZE (sizeof(struct resp_pkt) * 8)
+
+/*
+ * These are used to help the magic with old and new versions of ntpd.
+ */
+#define IMPL_XNTPD 3
+int impl_ver = IMPL_XNTPD;
+#define REQ_LEN_NOMAC (offsetof(struct req_pkt, keyid))
+static int req_pkt_size = REQ_LEN_NOMAC;
+#define ERR_INCOMPLETE 16
+#define ERR_TIMEOUT 17
+
+static void
+get_systime(
+ l_fp *now /* system time */
+ )
+{
+ double dtemp;
+
+#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK)
+ struct timespec ts; /* seconds and nanoseconds */
+
+ /*
+ * Convert Unix clock from seconds and nanoseconds to seconds.
+ */
+# ifdef HAVE_CLOCK_GETTIME
+ clock_gettime(CLOCK_REALTIME, &ts);
+# else
+ getclock(TIMEOFDAY, &ts);
+# endif
+ now->l_i = ts.tv_sec + JAN_1970;
+ dtemp = ts.tv_nsec / 1e9;
+
+#else /* HAVE_CLOCK_GETTIME || HAVE_GETCLOCK */
+ struct timeval tv; /* seconds and microseconds */
+
+ /*
+ * Convert Unix clock from seconds and microseconds to seconds.
+ */
+ gettimeofday(&tv, NULL);
+ now->l_i = tv.tv_sec + JAN_1970;
+ dtemp = tv.tv_usec / 1e6;
+
+#endif /* HAVE_CLOCK_GETTIME || HAVE_GETCLOCK */
+
+ /*
+ * Renormalize to seconds past 1900 and fraction.
+ */
+
+// dtemp += sys_residual;
+ if (dtemp >= 1) {
+ dtemp -= 1;
+ now->l_i++;
+ } else if (dtemp < -1) {
+ dtemp += 1;
+ now->l_i--;
+ }
+ dtemp *= FRAC;
+ now->l_uf = (uint32_t)dtemp;
+}
+
+static int
+NTPDCrequest(
+ NTPoptions* options,
+ NTPcontrol* control,
+ int reqcode,
+ int auth,
+ u_int qitems,
+ size_t qsize,
+ char *qdata
+ )
+{
+
+ l_fp delay_time = { .l_ui=0, .l_uf=0x51EB852};
+
+ struct req_pkt qpkt;
+ size_t datasize;
+ size_t reqsize;
+ l_fp ts;
+ l_fp * ptstamp;
+ int maclen;
+ static char *key;
+
+ key=calloc(21,sizeof(char));
+ strncpy(key,options->key,20);
+
+ memset(&qpkt, 0, sizeof(qpkt));
+ qpkt.rm_vn_mode = RM_VN_MODE(0, 0, 0);
+ qpkt.implementation = (u_char)3;
+ qpkt.request = (u_char)reqcode;
+ datasize = qitems * qsize;
+ if (datasize && qdata != NULL) {
+ memcpy(qpkt.data, qdata, datasize);
+ qpkt.err_nitems = ERR_NITEMS(0, qitems);
+ qpkt.mbz_itemsize = MBZ_ITEMSIZE(qsize);
+ } else {
+ qpkt.err_nitems = ERR_NITEMS(0, 0);
+ qpkt.mbz_itemsize = MBZ_ITEMSIZE(qsize); /* allow for optional first item */
+ }
+
+ if (!auth) {
+ qpkt.auth_seq = AUTH_SEQ(0, 0);
+ return ntpSend(control, (Octet *)&qpkt, req_pkt_size);
+ }
+
+ qpkt.auth_seq = AUTH_SEQ(1, 0);
+
+ reqsize = req_pkt_size;
+
+ ptstamp = (void *)((u_char *)&qpkt + reqsize);
+ ptstamp--;
+ get_systime(&ts);
+ L_ADD(&ts, &delay_time);
+ HTONL_FP(&ts, ptstamp);
+
+ maclen = MD5authencrypt(key, (void *)&qpkt, reqsize,options->keyId);
+ free(key);
+ if (!maclen || (maclen != (16 + sizeof(keyid_t))))
+ {
+ ERROR("Error while computing NTP MD5 hash\n");
+ return 1;
+ }
+
+ return ntpSend(control, (Octet *)&qpkt, reqsize + maclen);
+
+}
+
+
+/*
+ * checkitems - utility to print a message if no items were returned
+ */
+/*
+static int
+checkitems(
+ int items
+
+ )
+{
+ if (items == 0) {
+
+ return 0;
+ }
+ return 1;
+}
+*/
+
+/*
+ * checkitemsize - utility to print a message if the item size is wrong
+ */
+static int
+checkitemsize(
+ int itemsize,
+ int expected
+ )
+{
+ if (itemsize != expected) {
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * check1item - check to make sure we have exactly one item
+ */
+static int
+check1item(
+ int items
+
+ )
+{
+ if (items == 0) {
+ return 0;
+ }
+ if (items > 1) {
+
+ return 0;
+ }
+ return 1;
+}
+
+static int
+NTPDCresponse(
+ NTPoptions* options,
+ NTPcontrol* control,
+ int reqcode,
+ int *ritems,
+ int *rsize,
+ char **rdata,
+ int esize
+ )
+{
+ struct resp_pkt rpkt;
+ struct timeval tvo;
+ int items;
+ int i;
+ int size;
+ int datasize;
+ char *datap;
+ char *tmp_data;
+ char haveseq[MAXSEQ+1];
+ int firstpkt;
+ int lastseq;
+ int numrecv;
+ int seq;
+ fd_set fds;
+ int n;
+ int pad;
+ int retries = NTP_EINTR_RETRIES;
+
+ int ntpdc_pktdatasize;
+
+ int implcode=(u_char)3;
+
+ static struct timeval tvout = { DEFTIMEOUT, 0 }; /* time out for reads */
+ static struct timeval tvsout = { DEFSTIMEOUT, 0 }; /* secondary time out */
+
+// static char *currenthost = "localhost";
+
+ ntpdc_pktdatasize = INITDATASIZE;
+ ntpdc_pktdata = realloc(ntpdc_pktdata,INITDATASIZE);
+
+ /*
+ * This is pretty tricky. We may get between 1 and many packets
+ * back in response to the request. We peel the data out of
+ * each packet and collect it in one long block. When the last
+ * packet in the sequence is received we'll know how many we
+ * should have had. Note we use one long time out, should reconsider.
+ */
+ *ritems = 0;
+ *rsize = 0;
+ firstpkt = 1;
+ numrecv = 0;
+
+ lastseq = 999; /* too big to be a sequence number */
+ memset(haveseq, 0, sizeof(haveseq));
+
+
+ again:
+ if (firstpkt)
+ tvo = tvout;
+ else
+ tvo = tvsout;
+ do {
+ FD_ZERO(&fds);
+ FD_SET(control->sockFD, &fds);
+ n = select(control->sockFD+1, &fds, (fd_set *)0, (fd_set *)0, &tvo);
+ if(n == -1) {
+ if(errno == EINTR) {
+ DBG("NTPDCresponse(): EINTR caught\n");
+ retries--;
+ } else {
+ retries = 0;
+ }
+ }
+ } while ((n == -1) && retries);
+
+ if (n == -1) {
+ DBG("NTPDCresponse(): select failed - not EINTR: %s\n", strerror(errno));
+ return -1;
+ }
+ if (n == 0) {
+ DBG("NTP response select timeout");
+ if (firstpkt) {
+ return ERR_TIMEOUT;
+ } else {
+ return ERR_INCOMPLETE;
+ }
+ }
+
+ n = recv(control->sockFD, (char *)&rpkt, sizeof(rpkt), 0);
+
+ if (n == -1) {
+ DBG("NTP response recv failed\n");
+ return -1;
+ }
+
+
+ /*
+ * Check for format errors. Bug proofing.
+ */
+ if (n < RESP_HEADER_SIZE) {
+ goto again;
+ }
+
+
+
+ if (INFO_VERSION(rpkt.rm_vn_mode) > NTP_VERSION ||
+ INFO_VERSION(rpkt.rm_vn_mode) < NTP_OLDVERSION) {
+/* if (debug)
+ printf("Packet received with version %d\n",
+ INFO_VERSION(rpkt.rm_vn_mode));
+*/
+
+ goto again;
+ }
+
+ if (INFO_MODE(rpkt.rm_vn_mode) != MODE_PRIVATE) {
+/* if (debug)
+ printf("Packet received with mode %d\n",
+ INFO_MODE(rpkt.rm_vn_mode));
+*/
+
+ goto again;
+
+ }
+ if (INFO_IS_AUTH(rpkt.auth_seq)) {
+/*
+ if (debug)
+ printf("Encrypted packet received\n");
+*/
+ goto again;
+ }
+ if (!ISRESPONSE(rpkt.rm_vn_mode)) {
+/*
+ if (debug)
+ printf("Received request packet, wanted response\n");
+*/
+ goto again;
+ }
+ if (INFO_MBZ(rpkt.mbz_itemsize) != 0) {
+/*
+ if (debug)
+ printf("Received packet with nonzero MBZ field!\n");
+*/
+ goto again;
+ }
+
+ /*
+ * Check implementation/request. Could be old data getting to us.
+ */
+
+ if (rpkt.implementation != implcode || rpkt.request != reqcode) {
+/*
+ if (debug)
+ printf(
+ "Received implementation/request of %d/%d, wanted %d/%d",
+ rpkt.implementation, rpkt.request,
+ implcode, reqcode);
+*/
+ goto again;
+ }
+
+ /*
+ * Check the error code. If non-zero, return it.
+ */
+ if (INFO_ERR(rpkt.err_nitems) != INFO_OKAY) {
+/*
+ if (debug && ISMORE(rpkt.rm_vn_mode)) {
+ printf("Error code %d received on not-final packet\n",
+ INFO_ERR(rpkt.err_nitems));
+ }
+*/
+ return (int)INFO_ERR(rpkt.err_nitems);
+ }
+
+ /*
+ * Collect items and size. Make sure they make sense.
+ */
+ items = INFO_NITEMS(rpkt.err_nitems);
+
+ size = INFO_ITEMSIZE(rpkt.mbz_itemsize);
+ if (esize > size)
+ pad = esize - size;
+ else
+ pad = 0;
+ datasize = items * size;
+
+ if ((size_t)datasize > (n-RESP_HEADER_SIZE)) {
+/* if (debug)
+ printf(
+ "Received items %d, size %d (total %d), data in packet is %lu\n",
+ items, size, datasize, (u_long)(n-RESP_HEADER_SIZE));
+*/
+ goto again;
+
+ }
+
+ /*
+ * If this isn't our first packet, make sure the size matches
+ * the other ones.
+ */
+ if (!firstpkt && esize != *rsize) {
+/* if (debug)
+ printf("Received itemsize %d, previous %d\n",
+ size, *rsize);
+*/
+ goto again;
+ }
+ /*
+ * If we've received this before, +toss it
+ */
+ seq = INFO_SEQ(rpkt.auth_seq);
+ if (haveseq[seq]) {
+/* if (debug)
+ printf("Received duplicate sequence number %d\n", seq);
+*/
+ goto again;
+ }
+ haveseq[seq] = 1;
+
+ /*
+ * If this is the last in the sequence, record that.
+ */
+ if (!ISMORE(rpkt.rm_vn_mode)) {
+ if (lastseq != 999) {
+ DBGV("NTPDC Received second end sequence packet\n");
+ goto again;
+ }
+ lastseq = seq;
+ }
+
+ *rdata = datap = ntpdc_pktdata;
+
+ /*
+ * So far, so good. Copy this data into the output array.
+ */
+ if ((datap + datasize + (pad * items)) > (ntpdc_pktdata + ntpdc_pktdatasize)) {
+ int offset = datap - ntpdc_pktdata;
+
+ ntpdc_pktdatasize += INCDATASIZE;
+ ntpdc_pktdata = realloc(ntpdc_pktdata, (size_t)ntpdc_pktdatasize);
+ *rdata = ntpdc_pktdata; /* might have been realloced ! */
+ datap = ntpdc_pktdata + offset;
+ }
+ /*
+ * We now move the pointer along according to size and number of
+ * items. This is so we can play nice with older implementations
+ */
+
+ tmp_data = rpkt.data;
+ for (i = 0; i < items; i++) {
+ memcpy(datap, tmp_data, (unsigned)size);
+ tmp_data += size;
+ memset(datap + size, 0, pad);
+ datap += size + pad;
+ }
+
+ if (firstpkt) {
+ firstpkt = 0;
+ *rsize = size + pad;
+ }
+ *ritems += items;
+
+ /*
+ * Finally, check the count of received packets. If we've got them
+ * all, return
+ */
+ ++numrecv;
+
+ if (numrecv <= lastseq)
+ goto again;
+
+ return INFO_OKAY;
+}
+
+static int
+NTPDCquery(
+ NTPoptions* options,
+ NTPcontrol* control,
+ int reqcode,
+ int auth,
+ int qitems,
+ int qsize,
+ char *qdata,
+ int *ritems,
+ int *rsize,
+ char **rdata,
+ int quiet_mask,
+ int esize
+ )
+{
+ int res;
+ int retries = NTP_EINTR_RETRIES;
+ char junk[512];
+ fd_set fds;
+ struct timeval tvzero;
+// int implcode=(u_char)3;
+
+ /*
+ * Poll the socket and clear out any pending data
+ */
+again:
+ do {
+ tvzero.tv_sec = tvzero.tv_usec = 0;
+ FD_ZERO(&fds);
+ FD_SET(control->sockFD, &fds);
+ res = select(control->sockFD+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero);
+
+ if (res == -1) {
+ if((errno == EINTR) && retries ) {
+ DBG("NTPDCquery(): select EINTR caught");
+ retries--;
+ res = 1;
+ continue;
+ }
+ DBG("NTPDCquery(): select() error - not EINTR: %s\n", strerror(errno));
+ return -1;
+ } else if (res > 0) {
+ (void) recv(control->sockFD, junk, sizeof junk, 0);
+ }
+ } while (res > 0);
+
+ /*
+ * send a request
+ */
+ res = NTPDCrequest(options, control, reqcode, auth, qitems, qsize, qdata);
+ if (res <= 0) {
+ return res;
+ }
+ /*
+ * Get the response. If we got a standard error, print a message
+ */
+ res = NTPDCresponse(options, control, reqcode, ritems, rsize, rdata, esize);
+
+ /*
+ * Try to be compatible with older implementations of ntpd.
+ */
+ if (res == INFO_ERR_FMT && req_pkt_size != 48) {
+#if defined(RUNTIME_DEBUG) || defined (PTPD_DBGV)
+ int oldsize = req_pkt_size;
+#endif /* RUNTIME_DEBUG */
+
+ switch(req_pkt_size) {
+ case REQ_LEN_NOMAC:
+ req_pkt_size = 160;
+ break;
+ case 160:
+ req_pkt_size = 48;
+ break;
+ }
+ if (impl_ver == IMPL_XNTPD) {
+ DBGV(
+ "NTPDC ***Warning changing to older implementation\n");
+ return INFO_ERR_IMPL;
+ }
+
+ DBGV(
+ "NTPDC ***Warning changing the request packet size from %d to %d\n",
+ oldsize, req_pkt_size);
+ goto again;
+ }
+
+ return res;
+}
+
+int
+ntpdControlFlags(NTPoptions* options, NTPcontrol* control, int req, int flags)
+{
+ struct conf_sys_flags sys;
+ int items;
+ int itemsize;
+ char *dummy;
+ int res;
+
+ sys.flags = 0;
+ res = 0;
+ sys.flags = flags;
+
+ sys.flags = htonl(sys.flags);
+
+ res = NTPDCquery(options, control, req, 1, 1,
+ sizeof(struct conf_sys_flags), (char *)&sys, &items,
+ &itemsize, &dummy, 0, sizeof(struct conf_sys_flags));
+
+ if ((res != INFO_OKAY) && (sys.flags == 0))
+ return 0;
+
+ if (res != INFO_OKAY) {
+
+ switch (res) {
+
+ case -1:
+ if(!control->requestFailed) ERROR("Cannot connect to NTP daemon\n");
+ break;
+
+ case INFO_ERR_AUTH:
+
+ if(!control->requestFailed) ERROR("NTP permission denied: check key id, password and NTP configuration\n");
+ break;
+
+ case ERR_TIMEOUT:
+
+ if(!control->requestFailed) ERROR("Timeout while connecting to NTP daemon\n");
+ break;
+
+ default:
+ ERROR("NTP protocol error\n");
+
+ }
+ }
+
+ return res;
+
+}
+
+int
+ntpdSetFlags(NTPoptions* options, NTPcontrol* control, int flags)
+{
+
+ int res;
+ ntpdc_pktdata = malloc(INITDATASIZE);
+ DBGV("Setting NTP flags %d\n", flags);
+ res=ntpdControlFlags(options, control, REQ_SET_SYS_FLAG, flags);
+ free(ntpdc_pktdata);
+ return res;
+
+}
+
+int
+ntpdClearFlags(NTPoptions* options, NTPcontrol* control, int flags)
+{
+
+ int res;
+ ntpdc_pktdata = malloc(INITDATASIZE);
+ DBGV("Clearing NTP flags %d\n", flags);
+ res=ntpdControlFlags(options, control, REQ_CLR_SYS_FLAG, flags);
+ free(ntpdc_pktdata);
+ return res;
+}
+
+
+int
+ntpdInControl(NTPoptions* options, NTPcontrol* control)
+{
+ struct info_sys *is;
+ int items;
+ int itemsize;
+ int res;
+ ntpdc_pktdata = malloc(INITDATASIZE);
+ res = NTPDCquery(options, control, REQ_SYS_INFO, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (void *)&is, 0,
+ sizeof(struct info_sys));
+
+ if ( res != 0 )
+ goto end;
+
+ if (!check1item(items)) {
+
+ res=INFO_ERR_EMPTY;
+ goto end;
+ }
+
+
+ if (!checkitemsize(itemsize, sizeof(struct info_sys)) &&
+ !checkitemsize(itemsize, v4sizeof(struct info_sys))) {
+
+ res=INFO_ERR_EMPTY;
+ goto end;
+ }
+
+ if (is->flags & INFO_FLAG_NTP) DBGV("NTP flag seen: ntp\n");
+ if (is->flags & INFO_FLAG_KERNEL) DBGV("NTP flag seen: kernel\n");
+
+ if(!control->flagsCaptured) {
+ control->originalFlags = is->flags;
+ /* we only control the kernel and ntp flags */
+ control->originalFlags &= (INFO_FLAG_KERNEL | INFO_FLAG_NTP);
+ control->flagsCaptured = TRUE;
+ res = INFO_YES;
+ goto end;
+ }
+
+ if ((is->flags & INFO_FLAG_NTP) || (is->flags & INFO_FLAG_KERNEL))
+ {
+ res=INFO_YES;
+ } else {
+
+ res=INFO_NO;
+ }
+
+ end:
+
+ free(ntpdc_pktdata);
+
+
+ if (res != INFO_YES && res != INFO_NO) {
+
+ switch (res) {
+
+ case -1:
+ DBG("Could not connect to NTP daemon\n");
+ break;
+
+ case ERR_TIMEOUT:
+
+ DBG("Timeout while connecting to NTP daemon\n");
+ break;
+
+ case INFO_ERR_AUTH:
+
+ DBG("NTP permission denied: check NTP key id, key and if key is trusted and is a request key\n");
+ break;
+
+ default:
+ ERROR("NTP protocol error\n");
+
+ }
+ }
+ return res;
+
+}
+
diff --git a/rtemsbsd/ptpd/src/dep/ntpengine/ntpdcontrol.h b/rtemsbsd/ptpd/src/dep/ntpengine/ntpdcontrol.h
new file mode 100644
index 00000000..fde9707e
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/ntpengine/ntpdcontrol.h
@@ -0,0 +1,362 @@
+/*
+ * ntpdcontrol.h - definitions for the ntpd remote query and control facility
+ */
+
+#ifndef NTPDCONTROL_H
+#define NTPDCONTROL_H
+
+#include "../../ptpd.h"
+
+#include "../../timingdomain.h"
+
+typedef struct {
+
+ Boolean enableEngine;
+ Boolean enableControl;
+ Boolean enableFailover;
+ int failoverTimeout;
+ int checkInterval;
+ int keyId;
+ char key[20];
+ Octet hostAddress[MAXHOSTNAMELEN];
+} NTPoptions;
+
+typedef struct {
+ Boolean operational;
+ Boolean enabled;
+ Boolean isRequired;
+ Boolean inControl;
+ Boolean isFailOver;
+ Boolean checkFailed;
+ Boolean requestFailed;
+ Boolean flagsCaptured;
+ int originalFlags;
+ Integer32 serverAddress;
+ Integer32 sockFD;
+ struct TimingService timingService;
+} NTPcontrol;
+
+Boolean ntpInit(NTPoptions* options, NTPcontrol* control);
+Boolean ntpShutdown(NTPoptions* options, NTPcontrol* control);
+int ntpdControlFlags(NTPoptions* options, NTPcontrol* control, int req, int flags);
+int ntpdSetFlags(NTPoptions* options, NTPcontrol* control, int flags);
+int ntpdClearFlags(NTPoptions* options, NTPcontrol* control, int flags);
+int ntpdInControl(NTPoptions* options, NTPcontrol* control);
+//Boolean ntpdControl(NTPoptions* options, NTPcontrol* control, Boolean quiet);
+
+
+#define NTPCONTROL_YES 128
+#define NTPCONTROL_NO 129
+#define NTPCONTROL_AUTHERR 18
+#define NTPCONTROL_TIMEOUT 19
+#define NTPCONTROL_PROTOERR 20
+#define NTPCONTROL_NETERR 21
+
+
+typedef struct {
+ union {
+ uint32_t Xl_ui;
+ int32_t Xl_i;
+ } Ul_i;
+ union {
+ uint32_t Xl_uf;
+ int32_t Xl_f;
+ } Ul_f;
+} l_fp;
+
+#define l_ui Ul_i.Xl_ui /* unsigned integral part */
+#define l_i Ul_i.Xl_i /* signed integral part */
+#define l_uf Ul_f.Xl_uf /* unsigned fractional part */
+#define l_f Ul_f.Xl_f /* signed fractional part */
+
+#define M_ADD(r_i, r_f, a_i, a_f) /* r += a */ \
+ do { \
+ register uint32_t lo_tmp; \
+ register uint32_t hi_tmp; \
+ \
+ lo_tmp = ((r_f) & 0xffff) + ((a_f) & 0xffff); \
+ hi_tmp = (((r_f) >> 16) & 0xffff) + (((a_f) >> 16) & 0xffff); \
+ if (lo_tmp & 0x10000) \
+ hi_tmp++; \
+ (r_f) = ((hi_tmp & 0xffff) << 16) | (lo_tmp & 0xffff); \
+ \
+ (r_i) += (a_i); \
+ if (hi_tmp & 0x10000) \
+ (r_i)++; \
+ } while (0)
+
+
+#define L_ADD(r, a) M_ADD((r)->l_ui, (r)->l_uf, (a)->l_ui, (a)->l_uf)
+
+#define HTONL_FP(h, n) do { (n)->l_ui = htonl((h)->l_ui); \
+ (n)->l_uf = htonl((h)->l_uf); } while (0)
+
+typedef unsigned short associd_t; /* association ID */
+typedef int32_t keyid_t; /* cryptographic key ID */
+typedef uint32_t tstamp_t; /* NTP seconds timestamp */
+
+
+typedef int32_t s_fp;
+typedef uint32_t u_fp;
+typedef char s_char;
+#define MAX_MAC_LEN (6 * sizeof(uint32_t)) /* SHA */
+
+/*
+ * A request packet. These are almost a fixed length.
+ */
+struct req_pkt {
+ u_char rm_vn_mode; /* response, more, version, mode */
+ u_char auth_seq; /* key, sequence number */
+ u_char implementation; /* implementation number */
+ u_char request; /* request number */
+ u_short err_nitems; /* error code/number of data items */
+ u_short mbz_itemsize; /* item size */
+ char data[128 + 48]; /* data area [32 prev](176 byte max) */
+ /* struct conf_peer must fit */
+ l_fp tstamp; /* time stamp, for authentication */
+ keyid_t keyid; /* (optional) encryption key */
+ char mac[MAX_MAC_LEN-sizeof(keyid_t)]; /* (optional) auth code */
+};
+
+/*
+ * The req_pkt_tail structure is used by ntpd to adjust for different
+ * packet sizes that may arrive.
+ */
+struct req_pkt_tail {
+ l_fp tstamp; /* time stamp, for authentication */
+ keyid_t keyid; /* (optional) encryption key */
+ char mac[MAX_MAC_LEN-sizeof(keyid_t)]; /* (optional) auth code */
+};
+
+/* MODE_PRIVATE request packet header length before optional items. */
+#define REQ_LEN_HDR (offsetof(struct req_pkt, data))
+/* MODE_PRIVATE request packet fixed length without MAC. */
+#define REQ_LEN_NOMAC (offsetof(struct req_pkt, keyid))
+/* MODE_PRIVATE req_pkt_tail minimum size (16 octet digest) */
+#define REQ_TAIL_MIN \
+ (sizeof(struct req_pkt_tail) - (MAX_MAC_LEN - MAX_MD5_LEN))
+
+/*
+ * A MODE_PRIVATE response packet. The length here is variable, this
+ * is a maximally sized one. Note that this implementation doesn't
+ * authenticate responses.
+ */
+#define RESP_HEADER_SIZE (offsetof(struct resp_pkt, data))
+#define RESP_DATA_SIZE (500)
+
+struct resp_pkt {
+ u_char rm_vn_mode; /* response, more, version, mode */
+ u_char auth_seq; /* key, sequence number */
+ u_char implementation; /* implementation number */
+ u_char request; /* request number */
+ u_short err_nitems; /* error code/number of data items */
+ u_short mbz_itemsize; /* item size */
+ char data[RESP_DATA_SIZE]; /* data area */
+};
+
+
+/*
+ * Information error codes
+ */
+#define INFO_OKAY 0
+#define INFO_ERR_IMPL 1 /* incompatable implementation */
+#define INFO_ERR_REQ 2 /* unknown request code */
+#define INFO_ERR_FMT 3 /* format error */
+#define INFO_ERR_NODATA 4 /* no data for this request */
+#define INFO_ERR_AUTH 7 /* authentication failure */
+#define INFO_ERR_EMPTY 8 /* wowczarek: problem with response items */
+#define INFO_YES 126 /* wowczarek: NTP is controlling the OS clock */
+#define INFO_NO 127 /* wowczarek: NTP is not controlling the OS clock */
+/*
+ * Maximum sequence number.
+ */
+#define MAXSEQ 127
+
+/*
+ * Bit setting macros for multifield items.
+ */
+#define RESP_BIT 0x80
+#define MORE_BIT 0x40
+
+#define ISRESPONSE(rm_vn_mode) (((rm_vn_mode)&RESP_BIT)!=0)
+#define ISMORE(rm_vn_mode) (((rm_vn_mode)&MORE_BIT)!=0)
+#define INFO_VERSION(rm_vn_mode) ((u_char)(((rm_vn_mode)>>3)&0x7))
+#define INFO_MODE(rm_vn_mode) ((rm_vn_mode)&0x7)
+
+#define RM_VN_MODE(resp, more, version) \
+ ((u_char)(((resp)?RESP_BIT:0)\
+ |((more)?MORE_BIT:0)\
+ |((version?version:(NTP_OLDVERSION+1))<<3)\
+ |(MODE_PRIVATE)))
+
+#define INFO_IS_AUTH(auth_seq) (((auth_seq) & 0x80) != 0)
+#define INFO_SEQ(auth_seq) ((auth_seq)&0x7f)
+#define AUTH_SEQ(auth, seq) ((u_char)((((auth)!=0)?0x80:0)|((seq)&0x7f)))
+
+#define INFO_ERR(err_nitems) ((u_short)((ntohs(err_nitems)>>12)&0xf))
+#define INFO_NITEMS(err_nitems) ((u_short)(ntohs(err_nitems)&0xfff))
+#define ERR_NITEMS(err, nitems) (htons((u_short)((((u_short)(err)<<12)&0xf000)\
+ |((u_short)(nitems)&0xfff))))
+
+#define INFO_MBZ(mbz_itemsize) ((ntohs(mbz_itemsize)>>12)&0xf)
+#define INFO_ITEMSIZE(mbz_itemsize) ((u_short)(ntohs(mbz_itemsize)&0xfff))
+#define MBZ_ITEMSIZE(itemsize) (htons((u_short)(itemsize)))
+
+
+/*
+ * Implementation numbers. One for universal use and one for ntpd.
+ */
+#define IMPL_UNIV 0
+#define IMPL_XNTPD_OLD 2 /* Used by pre ipv6 ntpdc */
+#define IMPL_XNTPD 3 /* Used by post ipv6 ntpdc */
+
+/*
+ * Some limits related to authentication. Frames which are
+ * authenticated must include a time stamp which differs from
+ * the receive time stamp by no more than 10 seconds.
+ */
+#define INFO_TS_MAXSKEW 10.
+#define NTP_OLDVERSION ((u_char)1)
+#define MODE_PRIVATE 7
+/*
+ * Universal request codes go here. There aren't any.
+ */
+
+/*
+ * NTPD request codes go here.
+ */
+#define REQ_PEER_LIST 0 /* return list of peers */
+#define REQ_PEER_LIST_SUM 1 /* return summary info for all peers */
+#define REQ_PEER_INFO 2 /* get standard information on peer */
+#define REQ_PEER_STATS 3 /* get statistics for peer */
+#define REQ_SYS_INFO 4 /* get system information */
+#define REQ_SYS_STATS 5 /* get system stats */
+#define REQ_IO_STATS 6 /* get I/O stats */
+#define REQ_MEM_STATS 7 /* stats related to peer list maint */
+#define REQ_LOOP_INFO 8 /* info from the loop filter */
+#define REQ_TIMER_STATS 9 /* get timer stats */
+#define REQ_CONFIG 10 /* configure a new peer */
+#define REQ_UNCONFIG 11 /* unconfigure an existing peer */
+#define REQ_SET_SYS_FLAG 12 /* set system flags */
+#define REQ_CLR_SYS_FLAG 13 /* clear system flags */
+#define REQ_MONITOR 14 /* (not used) */
+#define REQ_NOMONITOR 15 /* (not used) */
+#define REQ_GET_RESTRICT 16 /* return restrict list */
+#define REQ_RESADDFLAGS 17 /* add flags to restrict list */
+#define REQ_RESSUBFLAGS 18 /* remove flags from restrict list */
+#define REQ_UNRESTRICT 19 /* remove entry from restrict list */
+#define REQ_MON_GETLIST 20 /* return data collected by monitor */
+#define REQ_RESET_STATS 21 /* reset stat counters */
+#define REQ_RESET_PEER 22 /* reset peer stat counters */
+#define REQ_REREAD_KEYS 23 /* reread the encryption key file */
+#define REQ_DO_DIRTY_HACK 24 /* (not used) */
+#define REQ_DONT_DIRTY_HACK 25 /* (not used) */
+#define REQ_TRUSTKEY 26 /* add a trusted key */
+#define REQ_UNTRUSTKEY 27 /* remove a trusted key */
+#define REQ_AUTHINFO 28 /* return authentication info */
+#define REQ_TRAPS 29 /* return currently set traps */
+#define REQ_ADD_TRAP 30 /* add a trap */
+#define REQ_CLR_TRAP 31 /* clear a trap */
+#define REQ_REQUEST_KEY 32 /* define a new request keyid */
+#define REQ_CONTROL_KEY 33 /* define a new control keyid */
+#define REQ_GET_CTLSTATS 34 /* get stats from the control module */
+#define REQ_GET_LEAPINFO 35 /* (not used) */
+#define REQ_GET_CLOCKINFO 36 /* get clock information */
+#define REQ_SET_CLKFUDGE 37 /* set clock fudge factors */
+#define REQ_GET_KERNEL 38 /* get kernel pll/pps information */
+#define REQ_GET_CLKBUGINFO 39 /* get clock debugging info */
+#define REQ_SET_PRECISION 41 /* (not used) */
+#define REQ_MON_GETLIST_1 42 /* return collected v1 monitor data */
+#define REQ_HOSTNAME_ASSOCID 43 /* Here is a hostname + assoc_id */
+#define REQ_IF_STATS 44 /* get interface statistics */
+#define REQ_IF_RELOAD 45 /* reload interface list */
+
+/* Determine size of pre-v6 version of structures */
+#define v4sizeof(type) offsetof(type, v6_flag)
+
+/*
+ * Flags in the peer information returns
+ */
+#define INFO_FLAG_CONFIG 0x1
+#define INFO_FLAG_SYSPEER 0x2
+#define INFO_FLAG_BURST 0x4
+#define INFO_FLAG_REFCLOCK 0x8
+#define INFO_FLAG_PREFER 0x10
+#define INFO_FLAG_AUTHENABLE 0x20
+#define INFO_FLAG_SEL_CANDIDATE 0x40
+#define INFO_FLAG_SHORTLIST 0x80
+#define INFO_FLAG_IBURST 0x100
+
+/*
+ * Flags in the system information returns
+ */
+#define INFO_FLAG_BCLIENT 0x1
+#define INFO_FLAG_AUTHENTICATE 0x2
+#define INFO_FLAG_NTP 0x4
+#define INFO_FLAG_KERNEL 0x8
+#define INFO_FLAG_MONITOR 0x40
+#define INFO_FLAG_FILEGEN 0x80
+#define INFO_FLAG_CAL 0x10
+#define INFO_FLAG_PPS_SYNC 0x20
+
+
+
+/*
+ * System info. Mostly the sys.* variables, plus a few unique to
+ * the implementation.
+ */
+struct info_sys {
+ uint32_t peer; /* system peer address (v4) */
+ u_char peer_mode; /* mode we are syncing to peer in */
+ u_char leap; /* system leap bits */
+ u_char stratum; /* our stratum */
+ s_char precision; /* local clock precision */
+ s_fp rootdelay; /* delay from sync source */
+ u_fp rootdispersion; /* dispersion from sync source */
+ uint32_t refid; /* reference ID of sync source */
+ l_fp reftime; /* system reference time */
+ uint32_t poll; /* system poll interval */
+ u_char flags; /* system flags */
+ u_char unused1; /* unused */
+ u_char unused2; /* unused */
+ u_char unused3; /* unused */
+ s_fp bdelay; /* default broadcast offset */
+ s_fp frequency; /* frequency residual (scaled ppm) */
+ l_fp authdelay; /* default authentication delay */
+ u_fp stability; /* clock stability (scaled ppm) */
+ u_int v6_flag; /* is this v6 or not */
+ u_int unused4; /* unused, padding for peer6 */
+ struct in6_addr peer6; /* system peer address (v6) */
+};
+
+/*
+ * Structure for carrying system flags.
+ */
+struct conf_sys_flags {
+ uint32_t flags;
+};
+
+/*
+ * System flags we can set/clear
+ */
+#define SYS_FLAG_BCLIENT 0x01
+#define SYS_FLAG_PPS 0x02
+#define SYS_FLAG_NTP 0x04
+#define SYS_FLAG_KERNEL 0x08
+#define SYS_FLAG_MONITOR 0x10
+#define SYS_FLAG_FILEGEN 0x20
+#define SYS_FLAG_AUTH 0x40
+#define SYS_FLAG_CAL 0x80
+
+#define NTP_VERSION ((u_char)4)
+
+#define DEFTIMEOUT (5) /* 5 second time out */
+#define DEFSTIMEOUT (2) /* 2 second time out after first */
+
+#define INITDATASIZE (sizeof(struct resp_pkt) * 16)
+#define INCDATASIZE (sizeof(struct resp_pkt) * 8)
+
+/* how many time select() will retry on EINTR */
+#define NTP_EINTR_RETRIES 5
+
+#endif /* NTPDCONTROL_H */
diff --git a/rtemsbsd/ptpd/src/dep/outlierfilter.c b/rtemsbsd/ptpd/src/dep/outlierfilter.c
new file mode 100644
index 00000000..fe3efe10
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/outlierfilter.c
@@ -0,0 +1,369 @@
+/*-
+ * Copyright (c) 2014 Wojciech Owczarek,
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file outlierfilter.c
+ * @date Fri Aug 22 16:18:33 2014
+ *
+ * @brief Code to handle outlier filter routines
+ *
+ * Outlier filter mechanics extracted into separate source
+ * in preparation for 2.4's OOP-like model
+ */
+
+#include "../ptpd.h"
+
+#ifdef LOCAL_PREFIX
+#undef LOCAL_PREFIX
+#endif
+
+#define LOCAL_PREFIX "OutlierFilter"
+
+static int outlierFilterInit(OutlierFilter *filter, OutlierFilterConfig *config, const char* id);
+static int outlierFilterReset(OutlierFilter *filter);
+static int outlierFilterShutdown(OutlierFilter *filter);
+static int outlierFilterTune(OutlierFilter *filter);
+static void outlierFilterUpdate(OutlierFilter *filter);
+static Boolean outlierFilterConfigure(OutlierFilter *filter, OutlierFilterConfig *config);
+static Boolean outlierFilterFilter(OutlierFilter *filter, double sample);
+static int outlierFilterDisplay(OutlierFilter *filter);
+
+int
+outlierFilterSetup(OutlierFilter *filter)
+{
+
+ if(filter == NULL) {
+ return 0;
+ }
+
+ memset(filter, 0, sizeof(OutlierFilter));
+
+ filter->init = outlierFilterInit;
+ filter->shutdown = outlierFilterShutdown;
+ filter->reset = outlierFilterReset;
+ filter->filter = outlierFilterFilter;
+ filter->display = outlierFilterDisplay;
+ filter->configure = outlierFilterConfigure;
+ filter->update = outlierFilterUpdate;
+
+ return 1;
+
+}
+
+static int
+outlierFilterInit(OutlierFilter *filter, OutlierFilterConfig *config, const char* id) {
+
+ filter->config = *config;
+
+ if (config->enabled) {
+ filter->rawStats = createDoubleMovingStdDev(config->capacity);
+ strncpy(filter->id, id, OUTLIERFILTER_MAX_DESC);
+ strncpy(filter->rawStats->identifier, id, 10);
+ filter->filteredStats = createDoubleMovingMean(config->capacity);
+ filter->threshold = config->threshold;
+ } else {
+ filter->rawStats = NULL;
+ filter->filteredStats = NULL;
+ }
+ resetDoublePermanentMean(&filter->outlierStats);
+ resetDoublePermanentMean(&filter->acceptedStats);
+
+ filter->delayCredit = filter->config.delayCredit;
+
+ return 1;
+
+}
+
+static int
+outlierFilterReset(OutlierFilter *filter) {
+
+ resetDoubleMovingStdDev(filter->rawStats);
+ resetDoubleMovingMean(filter->filteredStats);
+ filter->lastOutlier = FALSE;
+ filter->threshold = filter->config.threshold;
+ resetDoublePermanentMean(&filter->outlierStats);
+ resetDoublePermanentMean(&filter->acceptedStats);
+ filter->delay = 0;
+ filter->totalDelay = 0;
+ filter->delayCredit = filter->config.delayCredit;
+ filter->blocking = FALSE;
+
+ return 1;
+
+}
+
+static int
+outlierFilterShutdown(OutlierFilter *filter) {
+
+ if(filter->rawStats != NULL ) {
+ freeDoubleMovingStdDev(&filter->rawStats);
+ }
+
+ if(filter->filteredStats != NULL ) {
+ freeDoubleMovingMean(&filter->filteredStats);
+ }
+ resetDoublePermanentMean(&filter->outlierStats);
+ resetDoublePermanentMean(&filter->acceptedStats);
+
+ return 1;
+
+}
+
+static
+int outlierFilterTune(OutlierFilter *filter) {
+
+ DBG("tuning outlier filter: %s\n", filter->id);
+
+ if(!filter->config.autoTune || (filter->autoTuneSamples < 1)) {
+ return 1;
+ }
+
+ filter->autoTuneScore = round(((filter->autoTuneOutliers + 0.0) / (filter->autoTuneSamples + 0.0)) * 100.0);
+
+ if(filter->autoTuneScore < filter->config.minPercent) {
+
+ filter->threshold -= filter->config.thresholdStep;
+
+ if(filter->threshold < filter->config.minThreshold) {
+ filter->threshold = filter->config.minThreshold;
+ }
+
+ }
+
+ if(filter->autoTuneScore > filter->config.maxPercent) {
+
+ filter->threshold += filter->config.thresholdStep;
+
+ if(filter->threshold > filter->config.maxThreshold) {
+ filter->threshold = filter->config.maxThreshold;
+ }
+
+ }
+
+ DBG("%s filter autotune: counter %d, samples %d, outliers, %d percentage %d, new threshold %.02f\n",
+ filter->id, filter->rawStats->meanContainer->counter, filter->autoTuneSamples,
+ filter->autoTuneOutliers, filter->autoTuneScore,
+ filter->threshold);
+
+ filter->autoTuneSamples = 0;
+ filter->autoTuneOutliers = 0;
+
+ return 1;
+
+}
+
+static void
+outlierFilterUpdate(OutlierFilter *filter)
+{
+
+ /* not used yet */
+
+
+}
+
+static Boolean
+outlierFilterConfigure(OutlierFilter *filter, OutlierFilterConfig *config)
+{
+
+ /* restart needed */
+ if(filter->config.capacity != config->capacity) {
+ return TRUE;
+ }
+
+ filter->config = *config;
+
+ filter->reset(filter);
+
+ return FALSE;
+
+}
+
+/* 2 x fairy dust, 3 x unicorn droppings, 1 x magic beanstalk juice. blend, spray on the affected area twice per day */
+static Boolean
+outlierFilterFilter(OutlierFilter *filter, double sample)
+{
+
+ /* true = accepted - this is to tell the user if we advised to throw away the sample */
+ Boolean ret = TRUE;
+
+ /* step change: outlier mean - accepted mean from last sampling period */
+ double step = 0.0;
+
+ if(!filter->config.enabled) {
+ filter->output = sample;
+ return TRUE;
+ }
+
+ step = fabs(filter->outlierStats.mean - filter->acceptedStats.bufferedMean);
+
+ if(filter->config.autoTune) {
+ filter->autoTuneSamples++;
+ }
+
+ /* no outlier first - more convenient this way */
+ if(!isDoublePeircesOutlier(filter->rawStats, sample, filter->threshold) && (filter->delay == 0)) {
+
+ filter->lastOutlier = FALSE;
+ filter->output = sample;
+
+ /* filter is about to accept after a blocking period */
+ if(filter->consecutiveOutliers) {
+ DBG_LOCAL_ID(filter,"consecutive: %d, mean: %.09fm accepted bmean: %.09f\n", filter->consecutiveOutliers,
+ filter->outlierStats.mean,filter->acceptedStats.bufferedMean);
+
+ /* we are about to open up but the offset has risen above step level, we will block again, but not forever */
+ if(filter->config.stepDelay &&
+ (fabs(filter->acceptedStats.bufferedMean) < ((filter->config.stepThreshold + 0.0) / 1E9)) &&
+ (step > ((filter->config.stepLevel + 0.0) / 1E9))) {
+ /* if we're to enter blocking, we need 2 * consecutiveOutliers credit */
+ /* if we're already blocking, we just need enough credit */
+ /* if we're already blocking, make sure we block no more than maxDelay */
+ if((filter->blocking && ((filter->config.maxDelay > filter->totalDelay) && (filter->delayCredit >= filter->consecutiveOutliers))) ||
+ (!filter->blocking && (filter->delayCredit >= filter->consecutiveOutliers * 2 ))) {
+ if(!filter->blocking) {
+ INFO_LOCAL_ID(filter,"%.03f us step detected, filter will now block\n", step * 1E6);
+ }
+ DBG_LOCAL_ID(filter,"step: %.09f, credit left %d, requesting %d\n",step,
+ filter->delayCredit, filter->consecutiveOutliers);
+ filter->delay = filter->consecutiveOutliers;
+ filter->totalDelay += filter->consecutiveOutliers;
+ filter->delayCredit -= filter->consecutiveOutliers;
+ filter->blocking = TRUE;
+ resetDoublePermanentMean(&filter->outlierStats);
+ filter->lastOutlier = TRUE;
+ DBG_LOCAL_ID(filter,"maxdelay: %d, totaldelay: %d\n",filter->config.maxDelay, filter->totalDelay);
+ return FALSE;
+
+
+ /* much love for the ultra magnetic, cause everybody knows you never got enough credit */
+ /* we either ran out of credit while blocking, or we did not have enough to start with */
+ } else {
+ if(filter->blocking) {
+ INFO_LOCAL_ID(filter,"blocking time exhausted, filter will stop blocking\n");
+ } else {
+ INFO_LOCAL_ID(filter,"%.03f us step detected but filter cannot block\n", step * 1E6);
+ }
+ DBG_LOCAL_ID(filter,"credit out (has %d, needed %d)\n", filter->delayCredit, filter->consecutiveOutliers);
+ }
+ /* NO STEP */
+ } else {
+
+ if (filter->blocking) {
+ INFO_LOCAL_ID(filter,"step event over, filter will stop blocking\n");
+ }
+ filter->blocking = FALSE;
+ }
+
+ if(filter->totalDelay != 0) {
+ DBG_LOCAL_ID(filter,"Total waited %d\n", filter->totalDelay);
+ filter->totalDelay = 0;
+ }
+ }
+
+ filter->consecutiveOutliers = 0;
+ resetDoublePermanentMean(&filter->outlierStats);
+ feedDoublePermanentMean(&filter->acceptedStats, sample);
+
+ /* it's an outlier, Sir! */
+ } else {
+
+ filter->lastOutlier = TRUE;
+ feedDoublePermanentMean(&filter->outlierStats, sample);
+
+ if(filter->delay) {
+ DBG_LOCAL_ID(filter,"delay left: %d\n", filter->delay);
+ filter->delay--;
+ return FALSE;
+ }
+
+ filter->autoTuneOutliers++;
+ filter->consecutiveOutliers++;
+
+ if(filter->config.discard) {
+ ret = FALSE;
+ } else {
+ filter->output = filter->filteredStats->mean;
+ }
+
+
+ DBG_LOCAL_ID(filter,"Outlier: %.09f\n", sample);
+ /* Allow [weight] * [deviation from mean] to influence std dev in the next outlier checks */
+ sample = filter->rawStats->meanContainer->mean + filter->config.weight * ( sample - filter->rawStats->meanContainer->mean);
+
+ }
+
+ /* keep stats containers updated */
+ feedDoubleMovingStdDev(filter->rawStats, sample);
+ feedDoubleMovingMean(filter->filteredStats, filter->output);
+
+ /* re-tune filter twice per window */
+ if( (filter->rawStats->meanContainer->counter % ( filter->rawStats->meanContainer->capacity / 2)) == 0) {
+ outlierFilterTune(filter);
+ }
+
+ /* replenish filter credit once per window */
+ if( filter->config.stepDelay && ((filter->rawStats->meanContainer->counter % filter->rawStats->meanContainer->capacity) == 0)) {
+ filter->delayCredit += filter->config.creditIncrement;
+ if(filter->delayCredit >= filter->config.delayCredit) {
+ filter->delayCredit = filter->config.delayCredit;
+ }
+ DBG_LOCAL_ID(filter,"credit added, now %d\n", filter->delayCredit);
+ }
+
+ return ret;
+
+}
+
+
+static int outlierFilterDisplay(OutlierFilter *filter) {
+
+ char *id = filter->id;
+
+ INFO("%s outlier filter info:\n",
+ id);
+ INFO(" %s.threshold : %.02f\n",
+ id, filter->config.threshold);
+ INFO(" %s.autotune : %s\n",
+ id, (filter->config.autoTune) ? "Y" : "N");
+
+ if(!filter->config.autoTune) {
+ return 0;
+ }
+
+ INFO(" %s.percent : %d\n",
+ id, filter->autoTuneScore);
+ INFO(" %s.minThreshold : %.02f\n",
+ id, filter->config.minThreshold);
+ INFO(" %s.maxThreshold : %.02f\n",
+ id, filter->config.maxThreshold);
+ INFO(" %s.thresholdStep : %.02f\n",
+ id, filter->config.thresholdStep);
+
+
+ return 1;
+}
+
diff --git a/rtemsbsd/ptpd/src/dep/outlierfilter.h b/rtemsbsd/ptpd/src/dep/outlierfilter.h
new file mode 100644
index 00000000..873a8ff5
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/outlierfilter.h
@@ -0,0 +1,125 @@
+#ifndef OUTLIERFILTER_H_
+#define OUTLIERFILTER_H_
+
+#include <dep/statistics.h>
+
+#define OUTLIERFILTER_MAX_DESC 20
+
+/*-
+ * Copyright (c) 2014 Wojciech Owczarek,
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file outlierfilter.h
+ * @date Fri Aug 22 16:18:33 2014
+ *
+ * @brief Function definitions for the outlier filter
+ *
+ */
+
+typedef struct {
+
+ Boolean enabled;
+ Boolean discard;
+ Boolean autoTune;
+ Boolean stepDelay;
+
+ /* the user may have some conditions under which we should not filter.
+ this is to hint them that we would like to always filter.
+ */
+ Boolean alwaysFilter;
+
+ int capacity;
+ double threshold;
+ double weight;
+
+ int minPercent;
+ int maxPercent;
+ double thresholdStep;
+
+ double minThreshold;
+ double maxThreshold;
+
+ /* if absolute value outside this threshold, do not filter. negative = disabled */
+ double maxAcceptable;
+
+ /* accepted sample threshold at which step detection is active */
+ int32_t stepThreshold;
+ /* value which is considered a step */
+ int32_t stepLevel;
+
+ /* delay credit - gets used for waiting when step detected */
+ int delayCredit;
+
+ /* credit replenishment unit */
+ int creditIncrement;
+
+ /* safeguard - maximum credit */
+ int maxDelay;
+
+} OutlierFilterConfig;
+
+typedef struct OutlierFilter OutlierFilter;
+
+struct OutlierFilter {
+
+ char id [OUTLIERFILTER_MAX_DESC + 1];
+ OutlierFilterConfig config;
+
+ DoubleMovingStdDev* rawStats;
+ DoubleMovingMean* filteredStats;
+ DoublePermanentMean outlierStats;
+ DoublePermanentMean acceptedStats;
+ Boolean lastOutlier;
+ double threshold;
+ double output;
+ int autoTuneSamples;
+ int autoTuneOutliers;
+ int autoTuneScore;
+ int consecutiveOutliers;
+ int delay;
+ int totalDelay;
+ int delayCredit;
+ Boolean blocking;
+
+ /* 'methods' */
+
+ int (*init) (OutlierFilter *filter, OutlierFilterConfig *config, const char *id);
+ int (*shutdown) (OutlierFilter *filter);
+ int (*reset) (OutlierFilter *filter);
+ Boolean (*filter) (OutlierFilter *filter, double sample);
+ Boolean (*configure) (OutlierFilter *filter, OutlierFilterConfig *config);
+ int (*display) (OutlierFilter *filter);
+ void (*update) (OutlierFilter *filter);
+
+};
+
+
+
+/* OutlierFilter *oFilterCreate(OutlierFilterConfig *options, const char *id); */
+int outlierFilterSetup(OutlierFilter *filter);
+
+#endif /*OUTLIERFILTER_H_*/
diff --git a/rtemsbsd/ptpd/src/dep/ptpd_dep.h b/rtemsbsd/ptpd/src/dep/ptpd_dep.h
new file mode 100644
index 00000000..10663afb
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/ptpd_dep.h
@@ -0,0 +1,516 @@
+/**
+ * @file ptpd_dep.h
+ *
+ * @brief External definitions for inclusion elsewhere.
+ *
+ *
+ */
+
+#ifndef PTPD_DEP_H_
+#define PTPD_DEP_H_
+
+#ifdef RUNTIME_DEBUG
+#undef PTPD_DBGV
+#define PTPD_DBGV
+#endif
+
+ /** \name System messages*/
+ /**\{*/
+
+
+// Syslog ordering. We define extra debug levels above LOG_DEBUG for internal use - but message() doesn't pass these to SysLog
+
+// extended from <sys/syslog.h>
+#define LOG_DEBUG1 7
+#define LOG_DEBUG2 8
+#define LOG_DEBUG3 9
+#define LOG_DEBUGV 9
+
+
+#define EMERGENCY(x, ...) logMessage(LOG_EMERG, x, ##__VA_ARGS__)
+#define ALERT(x, ...) logMessage(LOG_ALERT, x, ##__VA_ARGS__)
+#define CRITICAL(x, ...) logMessage(LOG_CRIT, x, ##__VA_ARGS__)
+#define ERROR(x, ...) logMessage(LOG_ERR, x, ##__VA_ARGS__)
+#define PERROR(x, ...) logMessage(LOG_ERR, x " (strerror: %m)\n", ##__VA_ARGS__)
+#define WARNING(x, ...) logMessage(LOG_WARNING, x, ##__VA_ARGS__)
+#define NOTIFY(x, ...) logMessage(LOG_NOTICE, x, ##__VA_ARGS__)
+#define NOTICE(x, ...) logMessage(LOG_NOTICE, x, ##__VA_ARGS__)
+#define INFO(x, ...) logMessage(LOG_INFO, x, ##__VA_ARGS__)
+
+#define EMERGENCY_LOCAL(x, ...) EMERGENCY(LOCAL_PREFIX": " x,##__VA_ARGS__)
+#define ALERT_LOCAL(x, ...) ALERT(LOCAL_PREFIX": " x, ##__VA_ARGS__)
+#define CRITICAL_LOCAL(x, ...) CRITICAL(LOCAL_PREFIX": " x, ##__VA_ARGS__)
+#define ERROR_LOCAL(x, ...) ERROR(LOCAL_PREFIX": " x, ##__VA_ARGS__)
+#define PERROR_LOCAL(x, ...) PERROR(LOCAL_PREFIX": " x, ##__VA_ARGS__)
+#define WARNING_LOCAL(x, ...) WARNING(LOCAL_PREFIX": " x, ##__VA_ARGS__)
+#define NOTIFY_LOCAL(x, ...) NOTIFY(LOCAL_PREFIX": " x, ##__VA_ARGS__)
+#define NOTIC_LOCALE(x, ...) NOTICE(LOCAL_PREFIX": " x, ##__VA_ARGS__)
+#define INFO_LOCAL(x, ...) INFO(LOCAL_PREFIX": " x, ##__VA_ARGS__)
+
+#define EMERGENCY_LOCAL_ID(o,x, ...) EMERGENCY(LOCAL_PREFIX".%s: "x,o->id,##__VA_ARGS__)
+#define ALERT_LOCAL_ID(o,x, ...) ALERT(LOCAL_PREFIX".%s: "x,o->id, ##__VA_ARGS__)
+#define CRITICAL_LOCAL_ID(o,x, ...) CRITICAL(LOCAL_PREFIX".%s: "x,o->id, ##__VA_ARGS__)
+#define ERROR_LOCAL_ID(o,x, ...) ERROR(LOCAL_PREFIX".%s: "x,o->id, ##__VA_ARGS__)
+#define PERROR_LOCAL_ID(o,x, ...) PERROR(LOCAL_PREFIX".%s: "x,o->id, ##__VA_ARGS__)
+#define WARNING_LOCAL_ID(o,x, ...) WARNING(LOCAL_PREFIX".%s: "x,o->id, ##__VA_ARGS__)
+#define NOTIFY_LOCAL_ID(o,x, ...) NOTIFY(LOCAL_PREFIX".%s: "x,o->id, ##__VA_ARGS__)
+#define NOTICE_LOCAL_ID(o,x, ...) NOTICE(LOCAL_PREFIX".%s: "x,o->id, ##__VA_ARGS__)
+#define INFO_LOCAL_ID(o,x, ...) INFO(LOCAL_PREFIX".%s: "x,o->id, ##__VA_ARGS__)
+
+#if defined(__FILE__) && defined(__LINE__)
+#define MARKER INFO("Marker: %s:%d\n", __FILE__, __LINE__)
+#else
+#define MARKER INFO("Marker\n")
+#endif
+
+#include <assert.h>
+
+
+/*
+ list of per-module defines:
+
+./dep/sys.c:#define PRINT_MAC_ADDRESSES
+./dep/timer.c:#define US_TIMER_INTERVAL 125000
+*/
+#define USE_BINDTODEVICE
+
+
+
+// enable this line to show debug numbers in nanoseconds instead of microseconds
+// #define DEBUG_IN_NS
+
+#define DBG_UNIT_US (1000)
+#define DBG_UNIT_NS (1)
+
+#ifdef DEBUG_IN_NS
+#define DBG_UNIT DBG_UNIT_NS
+#else
+#define DBG_UNIT DBG_UNIT_US
+#endif
+
+
+
+
+/** \}*/
+
+/** \name Debug messages*/
+ /**\{*/
+
+#ifdef PTPD_DBGV
+#undef PTPD_DBG
+#undef PTPD_DBG2
+#define PTPD_DBG
+#define PTPD_DBG2
+
+#define DBGV(x, ...) logMessage(LOG_DEBUGV, x, ##__VA_ARGS__)
+#define DBGV_LOCAL(x, ...) DBGV(LOCAL_PREFIX": " x,##__VA_ARGS__)
+#define DBGV_LOCAL_ID(o,x, ...) DBGV(LOCAL_PREFIX".%s:"x,o->id,##__VA_ARGS__)
+#else
+#define DBGV(x, ...)
+#define DBGV_LOCAL(x, ...)
+#define DBGV_LOCAL_ID(x, ...)
+#endif
+
+/*
+ * new debug level DBG2:
+ * this is above DBG(), but below DBGV() (to avoid changing hundreds of lines)
+ */
+
+
+#ifdef PTPD_DBG2
+#undef PTPD_DBG
+#define PTPD_DBG
+#define DBG2(x, ...) logMessage(LOG_DEBUG2, x, ##__VA_ARGS__)
+#define DBG2_LOCAL(x, ...) DBG2(LOCAL_PREFIX": " x,##__VA_ARGS__)
+#define DBG2_LOCAL_ID(o,x, ...) DBG2(LOCAL_PREFIX".%s:"x,o->id,##__VA_ARGS__)
+
+#else
+
+#define DBG2(x, ...)
+#define DBG2_LOCAL(x, ...)
+#define DBG2_LOCAL_ID(x, ...)
+#endif
+
+#ifdef PTPD_DBG
+#define DBG(x, ...) logMessage(LOG_DEBUG, x, ##__VA_ARGS__)
+#define DBG_LOCAL(x, ...) DBG(LOCAL_PREFIX": " x,##__VA_ARGS__)
+#define DBG_LOCAL_ID(o,x, ...) DBG(LOCAL_PREFIX".%s:"x,o->id,##__VA_ARGS__)
+#else
+#define DBG(x, ...)
+#define DBG_LOCAL(x, ...)
+#define DBG_LOCAL_ID(x, ...)
+#endif
+
+/** \}*/
+
+/** \name Endian corrections*/
+ /**\{*/
+
+#if defined(PTPD_MSBF)
+#define shift8(x,y) ( (x) << ((3-y)<<3) )
+#define shift16(x,y) ( (x) << ((1-y)<<4) )
+#elif defined(PTPD_LSBF)
+#define shift8(x,y) ( (x) << ((y)<<3) )
+#define shift16(x,y) ( (x) << ((y)<<4) )
+#endif
+
+#define flip16(x) htons(x)
+#define flip32(x) htonl(x)
+
+/* i don't know any target platforms that do not have htons and htonl,
+ but here are generic funtions just in case */
+/*
+#if defined(PTPD_MSBF)
+#define flip16(x) (x)
+#define flip32(x) (x)
+#elif defined(PTPD_LSBF)
+static inline Integer16 flip16(Integer16 x)
+{
+ return (((x) >> 8) & 0x00ff) | (((x) << 8) & 0xff00);
+}
+
+static inline Integer32 flip32(x)
+{
+ return (((x) >> 24) & 0x000000ff) | (((x) >> 8 ) & 0x0000ff00) |
+ (((x) << 8 ) & 0x00ff0000) | (((x) << 24) & 0xff000000);
+}
+#endif
+*/
+
+/** \}*/
+
+
+/** \name Bit array manipulations*/
+ /**\{*/
+
+#define getFlag(x,y) !!( *(UInteger8*)((x)+((y)<8?1:0)) & (1<<((y)<8?(y):(y)-8)) )
+#define setFlag(x,y) ( *(UInteger8*)((x)+((y)<8?1:0)) |= 1<<((y)<8?(y):(y)-8) )
+#define clearFlag(x,y) ( *(UInteger8*)((x)+((y)<8?1:0)) &= ~(1<<((y)<8?(y):(y)-8)) )
+/** \}*/
+
+#define DEFAULT_TOKEN_DELIM ", ;\t"
+
+/*
+ * foreach loop across substrings from var, delimited by delim, placing
+ * each token in targetvar on iteration, using id variable name prefix
+ * to allow nesting (each loop uses an individual set of variables)
+ */
+#define foreach_token_begin(id, var, targetvar, delim) {\
+ char* id_stash; \
+ char* id_text_; \
+ char* id_text__; \
+ char* targetvar; \
+ id_text_=strdup(var); \
+ for(id_text__ = id_text_;; id_text__=NULL) { \
+ targetvar = strtok_r(id_text__, delim, &id_stash); \
+ if(targetvar==NULL) break;
+
+#define foreach_token_end(id) } \
+ if(id_text_ != NULL) { \
+ free(id_text_); \
+ }\
+}
+
+/** \name msg.c
+ *-Pack and unpack PTP messages */
+ /**\{*/
+
+void msgUnpackHeader(Octet * buf,MsgHeader*);
+void msgUnpackAnnounce (Octet * buf,MsgAnnounce*);
+void msgUnpackSync(Octet * buf,MsgSync*);
+void msgUnpackFollowUp(Octet * buf,MsgFollowUp*);
+void msgUnpackDelayReq(Octet * buf, MsgDelayReq * delayreq);
+void msgUnpackDelayResp(Octet * buf,MsgDelayResp *);
+void msgUnpackPdelayReq(Octet * buf,MsgPdelayReq*);
+void msgUnpackPdelayResp(Octet * buf,MsgPdelayResp*);
+void msgUnpackPdelayRespFollowUp(Octet * buf,MsgPdelayRespFollowUp*);
+Boolean msgUnpackManagement(Octet * buf,MsgManagement*, MsgHeader*, PtpClock *ptpClock, const int tlvOffset);
+Boolean msgUnpackSignaling(Octet * buf,MsgSignaling*, MsgHeader*, PtpClock *ptpClock, const int tlvOffset);
+void msgPackHeader(Octet * buf,PtpClock*);
+#ifndef PTPD_SLAVE_ONLY
+void msgPackAnnounce(Octet * buf, UInteger16, Timestamp*, PtpClock*);
+void msgPackSync(Octet * buf, UInteger16, Timestamp*, PtpClock*);
+#endif /* PTPD_SLAVE_ONLY */
+void msgPackFollowUp(Octet * buf,Timestamp*,PtpClock*, const UInteger16);
+void msgPackDelayReq(Octet * buf,Timestamp *,PtpClock *);
+void msgPackDelayResp(Octet * buf,MsgHeader *,Timestamp *,PtpClock *);
+void msgPackPdelayReq(Octet * buf,Timestamp*,PtpClock*);
+void msgPackPdelayResp(Octet * buf,MsgHeader*,Timestamp*,PtpClock*);
+void msgPackPdelayRespFollowUp(Octet * buf,MsgHeader*,Timestamp*,PtpClock*, const UInteger16);
+void msgPackManagement(Octet * buf,MsgManagement*,PtpClock*);
+void msgPackSignaling(Octet * buf,MsgSignaling*,PtpClock*);
+void msgPackManagementRespAck(Octet *,MsgManagement*,PtpClock*);
+void msgPackManagementTLV(Octet *,MsgManagement*, PtpClock*);
+void msgPackSignalingTLV(Octet *,MsgSignaling*, PtpClock*);
+void msgPackManagementErrorStatusTLV(Octet *,MsgManagement*,PtpClock*);
+
+void freeMMErrorStatusTLV(ManagementTLV*);
+void freeMMTLV(ManagementTLV*);
+
+void msgDump(PtpClock *ptpClock);
+void msgDebugHeader(MsgHeader *header);
+void msgDebugSync(MsgSync *sync);
+void msgDebugAnnounce(MsgAnnounce *announce);
+void msgDebugDelayReq(MsgDelayReq *req);
+void msgDebugFollowUp(MsgFollowUp *follow);
+void msgDebugDelayResp(MsgDelayResp *resp);
+void msgDebugManagement(MsgManagement *manage);
+
+void copyClockIdentity( ClockIdentity dest, ClockIdentity src);
+void copyPortIdentity( PortIdentity * dest, PortIdentity * src);
+
+void unpackMsgSignaling(Octet *, MsgSignaling*, PtpClock*);
+void packMsgSignaling(MsgSignaling*, Octet *);
+void unpackSignalingTLV(Octet*, MsgSignaling*, PtpClock*);
+void packSignalingTLV(SignalingTLV*, Octet*);
+void freeSignalingTLV(MsgSignaling*);
+
+void unpackMsgManagement(Octet *, MsgManagement*, PtpClock*);
+void packMsgManagement(MsgManagement*, Octet *);
+void unpackManagementTLV(Octet*, int, MsgManagement*, PtpClock*);
+void packManagementTLV(ManagementTLV*, Octet*);
+void freeManagementTLV(MsgManagement*);
+
+int unpackMMClockDescription( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMClockDescription( MsgManagement*, Octet*);
+void freeMMClockDescription( MMClockDescription*);
+int unpackMMUserDescription( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMUserDescription( MsgManagement*, Octet*);
+void freeMMUserDescription( MMUserDescription*);
+int unpackMMErrorStatus( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMErrorStatus( MsgManagement*, Octet*);
+void freeMMErrorStatus( MMErrorStatus*);
+int unpackMMInitialize( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMInitialize( MsgManagement*, Octet*);
+int unpackMMDefaultDataSet( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMDefaultDataSet( MsgManagement*, Octet*);
+int unpackMMCurrentDataSet( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMCurrentDataSet( MsgManagement*, Octet*);
+int unpackMMParentDataSet( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMParentDataSet( MsgManagement*, Octet*);
+int unpackMMTimePropertiesDataSet( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMTimePropertiesDataSet( MsgManagement*, Octet*);
+int unpackMMPortDataSet( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMPortDataSet( MsgManagement*, Octet*);
+int unpackMMPriority1( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMPriority1( MsgManagement*, Octet*);
+int unpackMMPriority2( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMPriority2( MsgManagement*, Octet*);
+int unpackMMDomain( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMDomain( MsgManagement*, Octet*);
+int unpackMMSlaveOnly( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMSlaveOnly( MsgManagement*, Octet* );
+int unpackMMLogAnnounceInterval( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMLogAnnounceInterval( MsgManagement*, Octet*);
+int unpackMMAnnounceReceiptTimeout( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMAnnounceReceiptTimeout( MsgManagement*, Octet*);
+int unpackMMLogSyncInterval( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMLogSyncInterval( MsgManagement*, Octet*);
+int unpackMMVersionNumber( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMVersionNumber( MsgManagement*, Octet*);
+int unpackMMTime( Octet* buf, int, MsgManagement*, PtpClock * );
+UInteger16 packMMTime( MsgManagement*, Octet*);
+int unpackMMClockAccuracy( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMClockAccuracy( MsgManagement*, Octet*);
+int unpackMMUtcProperties( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMUtcProperties( MsgManagement*, Octet*);
+int unpackMMTraceabilityProperties( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMTraceabilityProperties( MsgManagement*, Octet*);
+int unpackMMTimescaleProperties( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMTimescaleProperties( MsgManagement*, Octet*);
+int unpackMMUnicastNegotiationEnable( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMUnicastNegotiationEnable( MsgManagement*, Octet*);
+int unpackMMDelayMechanism( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMDelayMechanism( MsgManagement*, Octet*);
+int unpackMMLogMinPdelayReqInterval( Octet* buf, int, MsgManagement*, PtpClock* );
+UInteger16 packMMLogMinPdelayReqInterval( MsgManagement*, Octet*);
+
+/* Signaling TLV packing / unpacking functions */
+void unpackSMRequestUnicastTransmission( Octet* buf, MsgSignaling*, PtpClock* );
+UInteger16 packSMRequestUnicastTransmission( MsgSignaling*, Octet*);
+void unpackSMGrantUnicastTransmission( Octet* buf, MsgSignaling*, PtpClock* );
+UInteger16 packSMGrantUnicastTransmission( MsgSignaling*, Octet*);
+void unpackSMCancelUnicastTransmission( Octet* buf, MsgSignaling*, PtpClock* );
+UInteger16 packSMCancelUnicastTransmission( MsgSignaling*, Octet*);
+void unpackSMAcknowledgeCancelUnicastTransmission( Octet* buf, MsgSignaling*, PtpClock* );
+UInteger16 packSMAcknowledgeCancelUnicastTransmission( MsgSignaling*, Octet*);
+
+void unpackPortAddress( Octet* buf, PortAddress*, PtpClock*);
+void packPortAddress( PortAddress*, Octet*);
+void freePortAddress( PortAddress*);
+void unpackPTPText( Octet* buf, PTPText*, PtpClock*);
+void packPTPText( PTPText*, Octet*);
+void freePTPText( PTPText*);
+void unpackPhysicalAddress( Octet* buf, PhysicalAddress*, PtpClock*);
+void packPhysicalAddress( PhysicalAddress*, Octet*);
+void freePhysicalAddress( PhysicalAddress*);
+void unpackClockIdentity( Octet* buf, ClockIdentity *c, PtpClock*);
+void packClockIdentity( ClockIdentity *c, Octet* buf);
+void freeClockIdentity( ClockIdentity *c);
+void unpackClockQuality( Octet* buf, ClockQuality *c, PtpClock*);
+void packClockQuality( ClockQuality *c, Octet* buf);
+void freeClockQuality( ClockQuality *c);
+void unpackTimeInterval( Octet* buf, TimeInterval *t, PtpClock*);
+void packTimeInterval( TimeInterval *t, Octet* buf);
+void freeTimeInterval( TimeInterval *t);
+void unpackPortIdentity( Octet* buf, PortIdentity *p, PtpClock*);
+void packPortIdentity( PortIdentity *p, Octet* buf);
+void freePortIdentity( PortIdentity *p);
+void unpackTimestamp( Octet* buf, Timestamp *t, PtpClock*);
+void packTimestamp( Timestamp *t, Octet* buf);
+void freeTimestamp( Timestamp *t);
+UInteger16 msgPackManagementResponse(Octet * buf,MsgHeader*,MsgManagement*,PtpClock*);
+/** \}*/
+
+/** \name net.c (Unix API dependent)
+ * -Init network stuff, send and receive datas*/
+ /**\{*/
+
+Boolean testInterface(char* ifaceName, const RunTimeOpts* rtOpts);
+Boolean netInit(NetPath*,RunTimeOpts*,PtpClock*);
+Boolean netShutdown(NetPath*);
+int netSelect(TimeInternal*,NetPath*,fd_set*);
+ssize_t netRecvEvent(Octet*,TimeInternal*,NetPath*,int);
+ssize_t netRecvGeneral(Octet*,NetPath*);
+ssize_t netSendEvent(Octet*,UInteger16,NetPath*,const RunTimeOpts*,Integer32,TimeInternal*);
+ssize_t netSendGeneral(Octet*,UInteger16,NetPath*,const RunTimeOpts*,Integer32 );
+ssize_t netSendPeerGeneral(Octet*,UInteger16,NetPath*,const RunTimeOpts*, Integer32);
+ssize_t netSendPeerEvent(Octet*,UInteger16,NetPath*,const RunTimeOpts*,Integer32,TimeInternal*);
+Boolean netRefreshIGMP(NetPath *, const RunTimeOpts *, PtpClock *);
+Boolean hostLookup(const char* hostname, Integer32* addr);
+
+/** \}*/
+
+#if defined PTPD_SNMP
+/** \name snmp.c (SNMP subsystem)
+ * -Handle SNMP subsystem*/
+ /**\{*/
+
+void snmpInit(RunTimeOpts *, PtpClock *);
+void snmpShutdown();
+void eventHandler_snmp(AlarmEntry *alarm);
+void alarmHandler_snmp(AlarmEntry *alarm);
+
+//void sendNotif(int eventType, PtpEventData *eventData);
+
+
+/** \}*/
+#endif
+
+/** \name servo.c
+ * -Clock servo*/
+ /**\{*/
+
+void initClock(const RunTimeOpts*,PtpClock*);
+void updatePeerDelay (one_way_delay_filter*, const RunTimeOpts*,PtpClock*,TimeInternal*,Boolean);
+void updateDelay (one_way_delay_filter*, const RunTimeOpts*, PtpClock*,TimeInternal*);
+void updateOffset(TimeInternal*,TimeInternal*,
+ offset_from_master_filter*,const RunTimeOpts*,PtpClock*,TimeInternal*);
+void checkOffset(const RunTimeOpts*, PtpClock*);
+void updateClock(const RunTimeOpts*,PtpClock*);
+void stepClock(const RunTimeOpts * rtOpts, PtpClock * ptpClock);
+
+/** \}*/
+
+/** \name startup.c (Unix API dependent)
+ * -Handle with runtime options*/
+ /**\{*/
+int setCpuAffinity(int cpu);
+int logToFile(RunTimeOpts * rtOpts);
+int recordToFile(RunTimeOpts * rtOpts);
+PtpClock * ptpdStartup(int,char**,Integer16*,RunTimeOpts*);
+
+void ptpdShutdown(PtpClock * ptpClock);
+void checkSignals(RunTimeOpts * rtOpts, PtpClock * ptpClock);
+void restartSubsystems(RunTimeOpts *rtOpts, PtpClock *ptpClock);
+void applyConfig(dictionary *baseConfig, RunTimeOpts *rtOpts, PtpClock *ptpClock);
+
+void enable_runtime_debug(void );
+void disable_runtime_debug(void );
+
+void ntpSetup(RunTimeOpts *rtOpts, PtpClock *ptpClock);
+
+#define D_ON do { enable_runtime_debug(); } while (0);
+#define D_OFF do { disable_runtime_debug( ); } while (0);
+
+
+/** \}*/
+
+/** \name sys.c (Unix API dependent)
+ * -Manage timing system API*/
+ /**\{*/
+
+/* new debug methods to debug time variables */
+char *time2st(const TimeInternal * p);
+void DBG_time(const char *name, const TimeInternal p);
+
+
+void logMessage(int priority, const char *format, ...);
+void updateLogSize(LogFileHandler* handler);
+Boolean maintainLogSize(LogFileHandler* handler);
+int restartLog(LogFileHandler* handler, Boolean quiet);
+void restartLogging(RunTimeOpts* rtOpts);
+void stopLogging(RunTimeOpts* rtOpts);
+void logStatistics(PtpClock *ptpClock);
+void periodicUpdate(const RunTimeOpts *rtOpts, PtpClock *ptpClock);
+void displayStatus(PtpClock *ptpClock, const char *prefixMessage);
+void displayPortIdentity(PortIdentity *port, const char *prefixMessage);
+int snprint_PortIdentity(char *s, int max_len, const PortIdentity *id);
+Boolean nanoSleep(TimeInternal*);
+void getTime(TimeInternal*);
+void getTimeMonotonic(TimeInternal*);
+void setTime(TimeInternal*);
+#ifdef linux
+void setRtc(TimeInternal *);
+#endif /* linux */
+double getRand(void);
+int lockFile(int fd);
+int checkLockStatus(int fd, short lockType, int *lockPid);
+int checkFileLockable(const char *fileName, int *lockPid);
+Boolean checkOtherLocks(RunTimeOpts *rtOpts);
+
+void recordSync(UInteger16 sequenceId, TimeInternal * time);
+
+void adjFreq_wrapper(const RunTimeOpts * rtOpts, PtpClock * ptpClock, double adj);
+
+Boolean adjFreq(double);
+double getAdjFreq(void);
+
+#ifdef HAVE_SYS_TIMEX_H
+void informClockSource(PtpClock* ptpClock);
+
+/* Helper function to manage ntpadjtime / adjtimex flags */
+void setTimexFlags(int flags, Boolean quiet);
+void unsetTimexFlags(int flags, Boolean quiet);
+int getTimexFlags(void);
+Boolean checkTimexFlags(int flags);
+
+#if defined(MOD_TAI) && NTP_API == 4
+void setKernelUtcOffset(int utc_offset);
+Boolean getKernelUtcOffset(int *utc_offset);
+#endif /* MOD_TAI */
+
+#endif /* HAVE_SYS_TIMEX_H */
+
+/* Observed drift save / recovery functions */
+void restoreDrift(PtpClock * ptpClock, const RunTimeOpts * rtOpts, Boolean quiet);
+void saveDrift(PtpClock * ptpClock, const RunTimeOpts * rtOpts, Boolean quiet);
+
+int parseLeapFile(char * path, LeapSecondInfo *info);
+
+void
+resetWarnings(const RunTimeOpts * rtOpts, PtpClock * ptpClock);
+
+void setupPIservo(PIservo* servo, const RunTimeOpts* rtOpts);
+void resetPIservo(PIservo* servo);
+double runPIservo(PIservo* servo, const Integer32 input);
+
+#ifdef PTPD_STATISTICS
+void updatePtpEngineStats (PtpClock* ptpClock, const RunTimeOpts* rtOpts);
+#endif /* PTPD_STATISTICS */
+
+void writeStatusFile(PtpClock *ptpClock, const RunTimeOpts *rtOpts, Boolean quiet);
+void updateXtmp (TimeInternal oldTime, TimeInternal newTime);
+
+
+#endif /*PTPD_DEP_H_*/
diff --git a/rtemsbsd/ptpd/src/dep/servo.c b/rtemsbsd/ptpd/src/dep/servo.c
new file mode 100644
index 00000000..f348763a
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/servo.c
@@ -0,0 +1,1262 @@
+/*-
+ * Copyright (c) 2012-2015 Wojciech Owczarek,
+ * Copyright (c) 2011-2012 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen,
+ * Inaqui Delgado,
+ * Rick Ratzel,
+ * National Instruments.
+ * Copyright (c) 2009-2010 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen
+ *
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file servo.c
+ * @date Tue Jul 20 16:19:19 2010
+ *
+ * @brief Code which implements the clock servo in software.
+ *
+ *
+ */
+
+#include "../ptpd.h"
+
+#define CLAMP(var,bound) {\
+ if(var < -bound) {\
+ var = -bound;\
+ }\
+ if(var > bound) {\
+ var = bound;\
+ }\
+}
+
+#ifdef PTPD_STATISTICS
+static void checkServoStable(PtpClock *ptpClock, const RunTimeOpts *rtOpts);
+#endif
+
+void
+resetWarnings(const RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ ptpClock->warned_operator_slow_slewing = 0;
+ ptpClock->warned_operator_fast_slewing = 0;
+ ptpClock->warnedUnicastCapacity = FALSE;
+ //ptpClock->seen_servo_stable_first_time = FALSE;
+}
+
+void
+initClock(const RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ DBG("initClock\n");
+
+ /* If we've been suppressing ntpdc error messages, show them once again */
+ ptpClock->ntpControl.requestFailed = FALSE;
+ ptpClock->disabled = rtOpts->portDisabled;
+
+/* do not reset frequency here - restoreDrift will do it if necessary */
+/* 2.3.1: restoreDrift now always compiled - this is no longer needed */
+#if 0
+ ptpClock->servo.observedDrift = 0;
+#endif
+ /* clear vars */
+
+ /* clean more original filter variables */
+ clearTime(&ptpClock->currentDS.offsetFromMaster);
+ clearTime(&ptpClock->currentDS.meanPathDelay);
+ clearTime(&ptpClock->delaySM);
+ clearTime(&ptpClock->delayMS);
+
+ ptpClock->ofm_filt.y = 0;
+ ptpClock->ofm_filt.nsec_prev = 0;
+
+ ptpClock->mpd_filt.s_exp = 0; /* clears one-way delay filter */
+ ptpClock->offsetFirstUpdated = FALSE;
+
+ ptpClock->char_last_msg='I';
+
+ resetWarnings(rtOpts, ptpClock);
+
+ /* For Hybrid mode */
+// ptpClock->masterAddr = 0;
+
+ ptpClock->maxDelayRejected = 0;
+
+}
+
+void
+updateDelay(one_way_delay_filter * mpd_filt, const RunTimeOpts * rtOpts, PtpClock * ptpClock, TimeInternal * correctionField)
+{
+
+ /* updates paused, leap second pending - do nothing */
+ if(ptpClock->leapSecondInProgress)
+ return;
+
+ DBGV("updateDelay\n");
+
+ /* todo: do all intermediate calculations on temp vars */
+ TimeInternal prev_meanPathDelay = ptpClock->currentDS.meanPathDelay;
+
+ ptpClock->char_last_msg = 'D';
+
+ Boolean maxDelayHit = FALSE;
+
+ {
+
+#ifdef PTPD_STATISTICS
+ /* if maxDelayStableOnly configured, only check once servo is stable */
+ Boolean checkThreshold = rtOpts-> maxDelayStableOnly ?
+ (ptpClock->servo.isStable && rtOpts->maxDelay) :
+ (rtOpts->maxDelay);
+#else
+ Boolean checkThreshold = rtOpts->maxDelay;
+#endif
+ //perform basic checks, using local variables only
+ TimeInternal slave_to_master_delay;
+
+
+ /* calc 'slave_to_master_delay' */
+ subTime(&slave_to_master_delay, &ptpClock->delay_req_receive_time,
+ &ptpClock->delay_req_send_time);
+
+ if (checkThreshold && /* If maxDelay is 0 then it's OFF */
+ ptpClock->offsetFirstUpdated) {
+
+ if ((slave_to_master_delay.nanoseconds < 0) &&
+ (abs(slave_to_master_delay.nanoseconds) > rtOpts->maxDelay)) {
+ INFO("updateDelay aborted, "
+ "delay (sec: %d ns: %d) is negative\n",
+ slave_to_master_delay.seconds,
+ slave_to_master_delay.nanoseconds);
+ INFO("send (sec: %d ns: %d)\n",
+ ptpClock->delay_req_send_time.seconds,
+ ptpClock->delay_req_send_time.nanoseconds);
+ INFO("recv (sec: %d n s: %d)\n",
+ ptpClock->delay_req_receive_time.seconds,
+ ptpClock->delay_req_receive_time.nanoseconds);
+ goto finish;
+ }
+
+ if (slave_to_master_delay.seconds && checkThreshold) {
+ INFO("updateDelay aborted, slave to master delay %d.%d greater than 1 second\n",
+ slave_to_master_delay.seconds,
+ slave_to_master_delay.nanoseconds);
+ if (rtOpts->displayPackets)
+ msgDump(ptpClock);
+ goto finish;
+ }
+
+ if (slave_to_master_delay.nanoseconds > rtOpts->maxDelay) {
+ ptpClock->counters.maxDelayDrops++;
+ DBG("updateDelay aborted, slave to master delay %d greater than "
+ "administratively set maximum %d\n",
+ slave_to_master_delay.nanoseconds,
+ rtOpts->maxDelay);
+ if(rtOpts->maxDelayMaxRejected) {
+ maxDelayHit = TRUE;
+ /* if we blocked maxDelayMaxRejected samples, reset the slave to unblock the filter */
+ if(++ptpClock->maxDelayRejected > rtOpts->maxDelayMaxRejected) {
+ WARNING("%d consecutive measurements above %d threshold - resetting slave\n",
+ rtOpts->maxDelayMaxRejected, slave_to_master_delay.nanoseconds);
+ toState(PTP_LISTENING, rtOpts, ptpClock);
+ }
+ }
+
+ if (rtOpts->displayPackets)
+ msgDump(ptpClock);
+ goto finish;
+ } else {
+ ptpClock->maxDelayRejected=0;
+ }
+ }
+ }
+
+ /*
+ * The packet has passed basic checks, so we'll:
+ * - update the global delaySM variable
+ * - calculate a new filtered MPD
+ */
+ if (ptpClock->offsetFirstUpdated) {
+ Integer16 s;
+
+ /*
+ * calc 'slave_to_master_delay' (Master to Slave delay is
+ * already computed in updateOffset )
+ */
+
+ DBG("==> UpdateDelay(): %s\n",
+ dump_TimeInternal2("Req_RECV:", &ptpClock->delay_req_receive_time,
+ "Req_SENT:", &ptpClock->delay_req_send_time));
+
+ /* raw value before filtering */
+ subTime(&ptpClock->rawDelaySM, &ptpClock->delay_req_receive_time,
+ &ptpClock->delay_req_send_time);
+
+#ifdef PTPD_STATISTICS
+
+/* testing only: step detection */
+#if 0
+ TimeInternal bob;
+ bob.nanoseconds = -1000000;
+ bob.seconds = 0;
+ if(ptpClock->addOffset) {
+ addTime(&ptpClock->rawDelaySM, &ptpClock->rawDelaySM, &bob);
+ }
+#endif
+
+ /* run the delayMS stats filter */
+ if(rtOpts->filterSMOpts.enabled) {
+ if(!feedDoubleMovingStatFilter(ptpClock->filterSM, timeInternalToDouble(&ptpClock->rawDelaySM))) {
+ return;
+ }
+ ptpClock->rawDelaySM = doubleToTimeInternal(ptpClock->filterSM->output);
+ }
+
+ /* run the delaySM outlier filter */
+ if(!rtOpts->noAdjust && ptpClock->oFilterSM.config.enabled && (ptpClock->oFilterSM.config.alwaysFilter || !ptpClock->servo.runningMaxOutput) ) {
+ if(ptpClock->oFilterSM.filter(&ptpClock->oFilterSM, timeInternalToDouble(&ptpClock->rawDelaySM))) {
+ ptpClock->delaySM = doubleToTimeInternal(ptpClock->oFilterSM.output);
+ } else {
+ ptpClock->counters.delaySMOutliersFound++;
+ /* If the outlier filter has blocked the sample, "reverse" the last maxDelay action */
+ if (maxDelayHit) {
+ ptpClock->maxDelayRejected--;
+ }
+ goto finish;
+ }
+ } else {
+ ptpClock->delaySM = ptpClock->rawDelaySM;
+ }
+
+
+
+#else
+ subTime(&ptpClock->delaySM, &ptpClock->delay_req_receive_time,
+ &ptpClock->delay_req_send_time);
+#endif
+
+ /* update MeanPathDelay */
+ addTime(&ptpClock->currentDS.meanPathDelay, &ptpClock->delaySM,
+ &ptpClock->delayMS);
+
+ /* Subtract correctionField */
+ subTime(&ptpClock->currentDS.meanPathDelay, &ptpClock->currentDS.meanPathDelay,
+ correctionField);
+
+ /* Compute one-way delay */
+ div2Time(&ptpClock->currentDS.meanPathDelay);
+
+ if (ptpClock->currentDS.meanPathDelay.seconds) {
+ DBG("update delay: cannot filter with large OFM, "
+ "clearing filter\n");
+ INFO("Servo: Ignoring delayResp because of large OFM\n");
+
+ mpd_filt->s_exp = mpd_filt->nsec_prev = 0;
+ /* revert back to previous value */
+ ptpClock->currentDS.meanPathDelay = prev_meanPathDelay;
+ goto finish;
+ }
+
+ if(ptpClock->currentDS.meanPathDelay.nanoseconds < 0){
+ DBG("update delay: found negative value for OWD, "
+ "so ignoring this value: %d\n",
+ ptpClock->currentDS.meanPathDelay.nanoseconds);
+ /* revert back to previous value */
+ ptpClock->currentDS.meanPathDelay = prev_meanPathDelay;
+ goto finish;
+ }
+
+ /* avoid overflowing filter */
+ s = rtOpts->s;
+ while (abs(mpd_filt->y) >> (31 - s))
+ --s;
+
+ /* crank down filter cutoff by increasing 's_exp' */
+ if (mpd_filt->s_exp < 1)
+ mpd_filt->s_exp = 1;
+ else if (mpd_filt->s_exp < 1 << s)
+ ++mpd_filt->s_exp;
+ else if (mpd_filt->s_exp > 1 << s)
+ mpd_filt->s_exp = 1 << s;
+
+ /* filter 'meanPathDelay' */
+ double fy =
+ (double)((mpd_filt->s_exp - 1.0) *
+ mpd_filt->y / (mpd_filt->s_exp + 0.0) +
+ (ptpClock->currentDS.meanPathDelay.nanoseconds / 2.0 +
+ mpd_filt->nsec_prev / 2.0) / (mpd_filt->s_exp + 0.0));
+
+ mpd_filt->nsec_prev = ptpClock->currentDS.meanPathDelay.nanoseconds;
+
+ mpd_filt->y = round(fy);
+
+ ptpClock->currentDS.meanPathDelay.nanoseconds = mpd_filt->y;
+
+ DBGV("delay filter %d, %d\n", mpd_filt->y, mpd_filt->s_exp);
+ } else {
+ DBG("Ignoring delayResp because we didn't receive any sync yet\n");
+ ptpClock->counters.discardedMessages++;
+ }
+
+finish:
+
+DBG("UpdateDelay: Max delay hit: %d\n", maxDelayHit);
+
+#ifdef PTPD_STATISTICS
+ /* don't churn on stats containers with the old value if we've discarded an outlier */
+ if(!(ptpClock->oFilterSM.config.enabled && ptpClock->oFilterSM.config.discard && ptpClock->oFilterSM.lastOutlier)) {
+ feedDoublePermanentStdDev(&ptpClock->slaveStats.mpdStats, timeInternalToDouble(&ptpClock->currentDS.meanPathDelay));
+ feedDoublePermanentMedian(&ptpClock->slaveStats.mpdMedianContainer, timeInternalToDouble(&ptpClock->currentDS.meanPathDelay));
+ if(!ptpClock->slaveStats.mpdStatsUpdated) {
+ if(timeInternalToDouble(&ptpClock->currentDS.meanPathDelay) != 0.0){
+ ptpClock->slaveStats.mpdMax = timeInternalToDouble(&ptpClock->currentDS.meanPathDelay);
+ ptpClock->slaveStats.mpdMin = timeInternalToDouble(&ptpClock->currentDS.meanPathDelay);
+ ptpClock->slaveStats.mpdStatsUpdated = TRUE;
+ }
+ } else {
+ ptpClock->slaveStats.mpdMax = max(ptpClock->slaveStats.mpdMax, timeInternalToDouble(&ptpClock->currentDS.meanPathDelay));
+ ptpClock->slaveStats.mpdMin = min(ptpClock->slaveStats.mpdMin, timeInternalToDouble(&ptpClock->currentDS.meanPathDelay));
+ }
+ }
+#endif /* PTPD_STATISTICS */
+
+ logStatistics(ptpClock);
+
+}
+
+void
+updatePeerDelay(one_way_delay_filter * mpd_filt, const RunTimeOpts * rtOpts, PtpClock * ptpClock, TimeInternal * correctionField, Boolean twoStep)
+{
+ Integer16 s;
+
+ /* updates paused, leap second pending - do nothing */
+ if(ptpClock->leapSecondInProgress)
+ return;
+
+
+ DBGV("updatePeerDelay\n");
+
+ ptpClock->char_last_msg = 'P';
+
+ if (twoStep) {
+ /* calc 'slave_to_master_delay' */
+ subTime(&ptpClock->pdelayMS,
+ &ptpClock->pdelay_resp_receive_time,
+ &ptpClock->pdelay_resp_send_time);
+ subTime(&ptpClock->pdelaySM,
+ &ptpClock->pdelay_req_receive_time,
+ &ptpClock->pdelay_req_send_time);
+
+ /* update 'one_way_delay' */
+ addTime(&ptpClock->portDS.peerMeanPathDelay,
+ &ptpClock->pdelayMS,
+ &ptpClock->pdelaySM);
+
+ /* Subtract correctionField */
+ subTime(&ptpClock->portDS.peerMeanPathDelay,
+ &ptpClock->portDS.peerMeanPathDelay, correctionField);
+
+ /* Compute one-way delay */
+ div2Time(&ptpClock->portDS.peerMeanPathDelay);
+ } else {
+ /* One step clock */
+
+ subTime(&ptpClock->portDS.peerMeanPathDelay,
+ &ptpClock->pdelay_resp_receive_time,
+ &ptpClock->pdelay_req_send_time);
+
+ /* Subtract correctionField */
+ subTime(&ptpClock->portDS.peerMeanPathDelay,
+ &ptpClock->portDS.peerMeanPathDelay, correctionField);
+
+ /* Compute one-way delay */
+ div2Time(&ptpClock->portDS.peerMeanPathDelay);
+ }
+
+ if (ptpClock->portDS.peerMeanPathDelay.seconds) {
+ /* cannot filter with secs, clear filter */
+ mpd_filt->s_exp = mpd_filt->nsec_prev = 0;
+ return;
+ }
+ /* avoid overflowing filter */
+ s = rtOpts->s;
+ while (abs(mpd_filt->y) >> (31 - s))
+ --s;
+
+ /* crank down filter cutoff by increasing 's_exp' */
+ if (mpd_filt->s_exp < 1)
+ mpd_filt->s_exp = 1;
+ else if (mpd_filt->s_exp < 1 << s)
+ ++mpd_filt->s_exp;
+ else if (mpd_filt->s_exp > 1 << s)
+ mpd_filt->s_exp = 1 << s;
+
+ /* filter 'meanPathDelay' */
+ mpd_filt->y = (mpd_filt->s_exp - 1) *
+ mpd_filt->y / mpd_filt->s_exp +
+ (ptpClock->portDS.peerMeanPathDelay.nanoseconds / 2 +
+ mpd_filt->nsec_prev / 2) / mpd_filt->s_exp;
+
+ mpd_filt->nsec_prev = ptpClock->portDS.peerMeanPathDelay.nanoseconds;
+ ptpClock->portDS.peerMeanPathDelay.nanoseconds = mpd_filt->y;
+
+ DBGV("delay filter %d, %d\n", mpd_filt->y, mpd_filt->s_exp);
+
+
+ if(ptpClock->portDS.portState == PTP_SLAVE)
+ logStatistics(ptpClock);
+}
+
+void
+updateOffset(TimeInternal * send_time, TimeInternal * recv_time,
+ offset_from_master_filter * ofm_filt, const RunTimeOpts * rtOpts, PtpClock * ptpClock, TimeInternal * correctionField)
+{
+
+ ptpClock->clockControl.offsetOK = FALSE;
+
+ Boolean maxDelayHit = FALSE;
+
+ DBGV("UTCOffset: %d | leap 59: %d | leap61: %d\n",
+ ptpClock->timePropertiesDS.currentUtcOffset,ptpClock->timePropertiesDS.leap59,ptpClock->timePropertiesDS.leap61);
+
+ /* prepare time constant for servo*/
+ ptpClock->servo.maxdT = rtOpts->servoMaxdT;
+ if(ptpClock->portDS.logSyncInterval == UNICAST_MESSAGEINTERVAL) {
+ ptpClock->servo.dT = 1;
+
+ if(rtOpts->unicastNegotiation && ptpClock->parentGrants && ptpClock->parentGrants->grantData[SYNC].granted) {
+ ptpClock->servo.dT = pow(2,ptpClock->parentGrants->grantData[SYNC].logInterval);
+ }
+
+ } else {
+ ptpClock->servo.dT = pow(2, ptpClock->portDS.logSyncInterval);
+ }
+
+#ifdef PTPD_STATISTICS
+ /* multiply interval if interval filter used on delayMS */
+ if(rtOpts->filterMSOpts.enabled && rtOpts->filterMSOpts.windowType != WINDOW_SLIDING) {
+ ptpClock->servo.dT *= rtOpts->filterMSOpts.windowSize;
+ }
+#endif
+ /* updates paused, leap second pending - do nothing */
+ if(ptpClock->leapSecondInProgress)
+ return;
+
+ DBGV("==> updateOffset\n");
+
+ {
+
+#ifdef PTPD_STATISTICS
+ /* if maxDelayStableOnly configured, only check once servo is stable */
+ Boolean checkThreshold = (rtOpts->maxDelayStableOnly ?
+ (ptpClock->servo.isStable && rtOpts->maxDelay) :
+ (rtOpts->maxDelay));
+#else
+ Boolean checkThreshold = rtOpts->maxDelay;
+#endif
+
+ //perform basic checks, using only local variables
+ TimeInternal master_to_slave_delay;
+
+
+ /* calc 'master_to_slave_delay' */
+ subTime(&master_to_slave_delay, recv_time, send_time);
+
+ if (checkThreshold) { /* If maxDelay is 0 then it's OFF */
+ if (master_to_slave_delay.seconds && checkThreshold) {
+ INFO("updateOffset aborted, master to slave delay greater than 1"
+ " second.\n");
+ /* msgDump(ptpClock); */
+ return;
+ }
+
+ if (abs(master_to_slave_delay.nanoseconds) > rtOpts->maxDelay) {
+ ptpClock->counters.maxDelayDrops++;
+ DBG("updateOffset aborted, master to slave delay %d greater than "
+ "administratively set maximum %d\n",
+ master_to_slave_delay.nanoseconds,
+ rtOpts->maxDelay);
+ if(rtOpts->maxDelayMaxRejected) {
+ maxDelayHit = TRUE;
+ /* if we blocked maxDelayMaxRejected samples, reset the slave to unblock the filter */
+ if(++ptpClock->maxDelayRejected > rtOpts->maxDelayMaxRejected) {
+ WARNING("%d consecutive delay measurements above %d threshold - resetting slave\n",
+ rtOpts->maxDelayMaxRejected, master_to_slave_delay.nanoseconds);
+ toState(PTP_LISTENING, rtOpts, ptpClock);
+ }
+ } else {
+ ptpClock->maxDelayRejected=0;
+ }
+ if (rtOpts->displayPackets)
+ msgDump(ptpClock);
+ return;
+ }
+ }}
+
+ // used for stats feedback
+ ptpClock->char_last_msg='S';
+
+ /*
+ * The packet has passed basic checks, so we'll:
+ * - run the filters
+ * - update the global delayMS variable
+ * - calculate the new filtered OFM
+ */
+
+ /* raw value before filtering */
+ subTime(&ptpClock->rawDelayMS, recv_time, send_time);
+
+DBG("UpdateOffset: max delay hit: %d\n", maxDelayHit);
+
+#ifdef PTPD_STATISTICS
+
+/* testing only: step detection */
+/*
+ TimeInternal bob;
+ bob.nanoseconds = 1000000;
+ bob.seconds = 0;
+ if(ptpClock->addOffset) {
+ addTime(&ptpClock->rawDelayMS, &ptpClock->rawDelayMS, &bob);
+ }
+*/
+ /* run the delayMS stats filter */
+ if(rtOpts->filterMSOpts.enabled) {
+ /* FALSE if filter wants to skip the update */
+ if(!feedDoubleMovingStatFilter(ptpClock->filterMS, timeInternalToDouble(&ptpClock->rawDelayMS))) {
+ goto finish;
+ }
+ ptpClock->rawDelayMS = doubleToTimeInternal(ptpClock->filterMS->output);
+ }
+
+ /* run the delayMS outlier filter */
+ if(!rtOpts->noAdjust && ptpClock->oFilterMS.config.enabled && (ptpClock->oFilterMS.config.alwaysFilter || !ptpClock->servo.runningMaxOutput)) {
+ if(ptpClock->oFilterMS.filter(&ptpClock->oFilterMS, timeInternalToDouble(&ptpClock->rawDelayMS))) {
+ ptpClock->delayMS = doubleToTimeInternal(ptpClock->oFilterMS.output);
+ } else {
+ ptpClock->counters.delayMSOutliersFound++;
+ /* If the outlier filter has blocked the sample, "reverse" the last maxDelay action */
+ if (maxDelayHit) {
+ ptpClock->maxDelayRejected--;
+ }
+ goto finish;
+ }
+ } else {
+ ptpClock->delayMS = ptpClock->rawDelayMS;
+ }
+#else
+ /* Used just for End to End mode. */
+ subTime(&ptpClock->delayMS, recv_time, send_time);
+#endif
+
+ /* Take care of correctionField */
+ subTime(&ptpClock->delayMS,
+ &ptpClock->delayMS, correctionField);
+
+ /* update 'offsetFromMaster' */
+ if (ptpClock->portDS.delayMechanism == P2P) {
+ subTime(&ptpClock->currentDS.offsetFromMaster,
+ &ptpClock->delayMS,
+ &ptpClock->portDS.peerMeanPathDelay);
+ /* (End to End mode or disabled - if disabled, meanpath delay is zero) */
+ } else if (ptpClock->portDS.delayMechanism == E2E ||
+ ptpClock->portDS.delayMechanism == DELAY_DISABLED ) {
+
+ subTime(&ptpClock->currentDS.offsetFromMaster,
+ &ptpClock->delayMS,
+ &ptpClock->currentDS.meanPathDelay);
+ }
+
+ if (ptpClock->currentDS.offsetFromMaster.seconds) {
+ /* cannot filter with secs, clear filter */
+ ofm_filt->nsec_prev = 0;
+ ptpClock->offsetFirstUpdated = TRUE;
+ ptpClock->clockControl.offsetOK = TRUE;
+ SET_ALARM(ALRM_OFM_SECONDS, TRUE);
+ goto finish;
+ } else {
+ SET_ALARM(ALRM_OFM_SECONDS, FALSE);
+ if(rtOpts->ofmAlarmThreshold) {
+ if( abs(ptpClock->currentDS.offsetFromMaster.nanoseconds)
+ > rtOpts->ofmAlarmThreshold) {
+ SET_ALARM(ALRM_OFM_THRESHOLD, TRUE);
+ } else {
+ SET_ALARM(ALRM_OFM_THRESHOLD, FALSE);
+ }
+ }
+ }
+
+ /* filter 'offsetFromMaster' */
+ ofm_filt->y = ptpClock->currentDS.offsetFromMaster.nanoseconds / 2 +
+ ofm_filt->nsec_prev / 2;
+ ofm_filt->nsec_prev = ptpClock->currentDS.offsetFromMaster.nanoseconds;
+ ptpClock->currentDS.offsetFromMaster.nanoseconds = ofm_filt->y;
+
+ /* Apply the offset shift */
+ subTime(&ptpClock->currentDS.offsetFromMaster, &ptpClock->currentDS.offsetFromMaster,
+ &rtOpts->ofmShift);
+
+ DBGV("offset filter %d\n", ofm_filt->y);
+
+ /*
+ * Offset must have been computed at least one time before
+ * computing end to end delay
+ */
+ ptpClock->offsetFirstUpdated = TRUE;
+ ptpClock->clockControl.offsetOK = TRUE;
+
+#ifdef PTPD_STATISTICS
+ if(!ptpClock->oFilterMS.lastOutlier) {
+ feedDoublePermanentStdDev(&ptpClock->slaveStats.ofmStats, timeInternalToDouble(&ptpClock->currentDS.offsetFromMaster));
+ feedDoublePermanentMedian(&ptpClock->slaveStats.ofmMedianContainer, timeInternalToDouble(&ptpClock->currentDS.offsetFromMaster));
+ if(!ptpClock->slaveStats.ofmStatsUpdated) {
+ if(timeInternalToDouble(&ptpClock->currentDS.offsetFromMaster) != 0.0){
+ ptpClock->slaveStats.ofmMax = timeInternalToDouble(&ptpClock->currentDS.offsetFromMaster);
+ ptpClock->slaveStats.ofmMin = timeInternalToDouble(&ptpClock->currentDS.offsetFromMaster);
+ ptpClock->slaveStats.ofmStatsUpdated = TRUE;
+ }
+ } else {
+ ptpClock->slaveStats.ofmMax = max(ptpClock->slaveStats.ofmMax, timeInternalToDouble(&ptpClock->currentDS.offsetFromMaster));
+ ptpClock->slaveStats.ofmMin = min(ptpClock->slaveStats.ofmMin, timeInternalToDouble(&ptpClock->currentDS.offsetFromMaster));
+ }
+
+
+ }
+#endif /* PTPD_STATISTICS */
+
+finish:
+ logStatistics(ptpClock);
+
+ DBGV("\n--Offset Correction-- \n");
+ DBGV("Raw offset from master: %10ds %11dns\n",
+ ptpClock->delayMS.seconds,
+ ptpClock->delayMS.nanoseconds);
+
+ DBGV("\n--Offset and Delay filtered-- \n");
+
+ if (ptpClock->portDS.delayMechanism == P2P) {
+ DBGV("one-way delay averaged (P2P): %10ds %11dns\n",
+ ptpClock->portDS.peerMeanPathDelay.seconds,
+ ptpClock->portDS.peerMeanPathDelay.nanoseconds);
+ } else if (ptpClock->portDS.delayMechanism == E2E) {
+ DBGV("one-way delay averaged (E2E): %10ds %11dns\n",
+ ptpClock->currentDS.meanPathDelay.seconds,
+ ptpClock->currentDS.meanPathDelay.nanoseconds);
+ }
+
+ DBGV("offset from master: %10ds %11dns\n",
+ ptpClock->currentDS.offsetFromMaster.seconds,
+ ptpClock->currentDS.offsetFromMaster.nanoseconds);
+ DBGV("observed drift: %10d\n", ptpClock->servo.observedDrift);
+
+}
+
+void
+stepClock(const RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ if(rtOpts->noAdjust){
+ WARNING("Could not step clock - clock adjustment disabled\n");
+ return;
+ }
+
+ SET_ALARM(ALRM_CLOCK_STEP, TRUE);
+
+ ptpClock->clockControl.stepRequired = FALSE;
+
+ TimeInternal oldTime, newTime;
+ /*No need to reset the frequency offset: if we're far off, it will quickly get back to a high value */
+ getTime(&oldTime);
+ subTime(&newTime, &oldTime, &ptpClock->currentDS.offsetFromMaster);
+
+ setTime(&newTime);
+
+ ptpClock->clockStatus.majorChange = TRUE;
+
+ initClock(rtOpts, ptpClock);
+
+ if(ptpClock->defaultDS.clockQuality.clockClass > 127)
+ restoreDrift(ptpClock, rtOpts, TRUE);
+
+ ptpClock->servo.runningMaxOutput = FALSE;
+ toState(PTP_FAULTY, rtOpts, ptpClock); /* make a full protocol reset */
+
+ if(rtOpts->calibrationDelay) {
+ ptpClock->isCalibrated = FALSE;
+ }
+
+}
+
+void
+warn_operator_fast_slewing(const RunTimeOpts * rtOpts, PtpClock * ptpClock, double adj)
+{
+ if(ptpClock->warned_operator_fast_slewing == 0){
+ if ((adj >= rtOpts->servoMaxPpb) || ((adj <= -rtOpts->servoMaxPpb))){
+ ptpClock->warned_operator_fast_slewing = 1;
+ NOTICE("Servo: Going to slew the clock with the maximum frequency adjustment\n");
+ }
+ }
+
+}
+
+void
+warn_operator_slow_slewing(const RunTimeOpts * rtOpts, PtpClock * ptpClock )
+{
+ if(ptpClock->warned_operator_slow_slewing == 0){
+ ptpClock->warned_operator_slow_slewing = 1;
+ ptpClock->warned_operator_fast_slewing = 1;
+
+ /* rule of thumb: at tick rate 10000, slewing at the maximum speed took 0.5ms per second */
+ float estimated = (((abs(ptpClock->currentDS.offsetFromMaster.seconds)) + 0.0) * 2.0 * 1000.0 / 3600.0);
+
+
+ ALERT("Servo: %d seconds offset detected, will take %.1f hours to slew\n",
+ ptpClock->currentDS.offsetFromMaster.seconds,
+ estimated
+ );
+
+ }
+}
+
+/*
+ * this is a wrapper around adjFreq to abstract extra operations
+ */
+
+void
+adjFreq_wrapper(const RunTimeOpts * rtOpts, PtpClock * ptpClock, double adj)
+{
+ if (rtOpts->noAdjust){
+ DBGV("adjFreq2: noAdjust on, returning\n");
+ return;
+ }
+
+/*
+ * adjFreq simulation for QNX: correct clock by x ns per tick over clock adjust interval,
+ * to make it equal adj ns per second. Makes sense only if intervals are regular.
+ */
+
+#ifdef __QNXNTO__
+
+ struct _clockadjust clockadj;
+ struct _clockperiod period;
+ if (ClockPeriod (CLOCK_REALTIME, 0, &period, 0) < 0)
+ return;
+
+ CLAMP(adj,ptpClock->servo.maxOutput);
+
+ /* adjust clock for the duration of 0.9 clock update period in ticks (so we're done before the next) */
+ clockadj.tick_count = 0.9 * ptpClock->servo.dT * 1E9 / (period.nsec + 0.0);
+
+ /* scale adjustment per second to adjustment per single tick */
+ clockadj.tick_nsec_inc = (adj * ptpClock->servo.dT / clockadj.tick_count) / 0.9;
+
+ DBGV("QNX: adj: %.09f, dt: %.09f, ticks per dt: %d, inc per tick %d\n",
+ adj, ptpClock->servo.dT, clockadj.tick_count, clockadj.tick_nsec_inc);
+
+ if (ClockAdjust(CLOCK_REALTIME, &clockadj, NULL) < 0) {
+ DBGV("QNX: failed to call ClockAdjust: %s\n", strerror(errno));
+ }
+/* regular adjFreq */
+#elif defined(HAVE_SYS_TIMEX_H)
+ DBG2(" adjFreq2: call adjfreq to %.09f us \n", adj / DBG_UNIT);
+ adjFreq(adj);
+/* otherwise use adjtime */
+#else
+ struct timeval tv;
+
+ CLAMP(adj,ptpClock->servo.maxOutput);
+
+ tv.tv_sec = 0;
+ tv.tv_usec = (adj / 1000);
+ if((ptpClock->servo.dT > 0) && (ptpClock->servo.dT < 1.0)) {
+ tv.tv_usec *= ptpClock->servo.dT;
+ }
+ adjtime(&tv, NULL);
+#endif
+}
+
+/* check if it's OK to update the clock, deal with panic mode, call for clock step */
+void checkOffset(const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+
+ /* unless offset OK */
+ ptpClock->clockControl.updateOK = FALSE;
+
+ /* if false, updateOffset does not want us to continue */
+ if(!ptpClock->clockControl.offsetOK) {
+ DBGV("checkOffset: !offsetOK\n");
+ return;
+ }
+
+ if(rtOpts->noAdjust) {
+ DBGV("checkOffset: noAdjust\n");
+ /* in case if noAdjust has changed */
+ ptpClock->clockControl.available = FALSE;
+ return;
+ }
+
+ /* Leap second pending: do not update clock */
+ if(ptpClock->leapSecondInProgress) {
+ /* let the watchdog know that we still want to hold the clock control */
+ DBGV("checkOffset: leapSecondInProgress\n");
+ ptpClock->clockControl.activity = TRUE;
+ return;
+ }
+
+ /* check if we are allowed to step the clock */
+ if(!ptpClock->pastStartup && (
+ rtOpts->stepForce || (rtOpts->stepOnce && ptpClock->currentDS.offsetFromMaster.seconds)
+ )) {
+ if(rtOpts->stepForce) WARNING("First clock update - will step the clock\n");
+ if(rtOpts->stepOnce) WARNING("First clock update and offset >= 1 second - will step the clock\n");
+ ptpClock->clockControl.stepRequired = TRUE;
+ ptpClock->clockControl.updateOK = TRUE;
+ ptpClock->pastStartup = TRUE;
+ return;
+ }
+
+ /* check if offset within allowed limit */
+ if (rtOpts->maxOffset && ((abs(ptpClock->currentDS.offsetFromMaster.nanoseconds) > abs(rtOpts->maxOffset)) ||
+ ptpClock->currentDS.offsetFromMaster.seconds)) {
+ INFO("Offset %d.%09d greater than "
+ "administratively set maximum %d\n. Will not update clock",
+ ptpClock->currentDS.offsetFromMaster.seconds,
+ ptpClock->currentDS.offsetFromMaster.nanoseconds, rtOpts->maxOffset);
+ return;
+ }
+
+ /* offset above 1 second */
+ if (ptpClock->currentDS.offsetFromMaster.seconds) {
+
+ if(!rtOpts->enablePanicMode) {
+ if (!rtOpts->noResetClock)
+ CRITICAL("Offset above 1 second (%.09f s). Clock will step.\n",
+ timeInternalToDouble(&ptpClock->currentDS.offsetFromMaster));
+ ptpClock->clockControl.stepRequired = TRUE;
+ ptpClock->clockControl.updateOK = TRUE;
+ ptpClock->pastStartup = TRUE;
+ return;
+ }
+
+ /* still in panic mode, do nothing */
+ if(ptpClock->panicMode) {
+ DBG("checkOffset: still in panic mode\n");
+ /* if we have not released clock control, keep the heartbeats going */
+ if (ptpClock->clockControl.available) {
+ ptpClock->clockControl.activity = TRUE;
+ /* if for some reason we don't have clock control and not configured to release it, re-acquire it */
+ } else if(!rtOpts->panicModeReleaseClock) {
+ ptpClock->clockControl.available = TRUE;
+ ptpClock->clockControl.activity = TRUE;
+ /* and vice versa - in case the setting has changed */
+ } else {
+ ptpClock->clockControl.available = FALSE;
+ }
+ return;
+ }
+
+ if(ptpClock->panicOver) {
+ if (rtOpts->noResetClock)
+ CRITICAL("Panic mode timeout - accepting current offset. Clock will be slewed at maximum rate.\n");
+ else
+ CRITICAL("Panic mode timeout - accepting current offset. Clock will step.\n");
+ ptpClock->panicOver = FALSE;
+ timerStop(&ptpClock->timers[PANIC_MODE_TIMER]);
+ ptpClock->clockControl.available = TRUE;
+ ptpClock->clockControl.stepRequired = TRUE;
+ ptpClock->clockControl.updateOK = TRUE;
+ ptpClock->pastStartup = TRUE;
+ ptpClock->isCalibrated = FALSE;
+ return;
+ }
+
+ CRITICAL("Offset above 1 second (%.09f s) - entering panic mode. Clock updates paused.\n",
+ timeInternalToDouble(&ptpClock->currentDS.offsetFromMaster));
+ ptpClock->panicMode = TRUE;
+ ptpClock->panicModeTimeLeft = 6 * rtOpts->panicModeDuration;
+ timerStart(&ptpClock->timers[PANIC_MODE_TIMER], 10);
+ /* do not release if not configured to do so */
+ if(rtOpts->panicModeReleaseClock) {
+ ptpClock->clockControl.available = FALSE;
+ }
+ return;
+
+ }
+
+ /* offset below 1 second - exit panic mode if no threshold or if below threshold,
+ * but make sure we stayed in panic mode for at least one interval,
+ * so that we avoid flapping.
+ */
+ if(rtOpts->enablePanicMode && ptpClock->panicMode &&
+ (ptpClock->panicModeTimeLeft != (2 * rtOpts->panicModeDuration)) ) {
+
+ if (rtOpts->panicModeExitThreshold == 0) {
+ ptpClock->panicMode = FALSE;
+ ptpClock->panicOver = FALSE;
+ timerStop(&ptpClock->timers[PANIC_MODE_TIMER]);
+ NOTICE("Offset below 1 second again: resuming clock control\n");
+ /* we can control the clock again */
+ ptpClock->clockControl.available = TRUE;
+ } else if ( abs(ptpClock->currentDS.offsetFromMaster.nanoseconds) < rtOpts->panicModeExitThreshold ) {
+ ptpClock->panicMode = FALSE;
+ ptpClock->panicOver = FALSE;
+ timerStop(&ptpClock->timers[PANIC_MODE_TIMER]);
+ NOTICE("Offset below %d ns threshold: resuming clock control\n",
+ ptpClock->currentDS.offsetFromMaster.nanoseconds);
+ /* we can control the clock again */
+ ptpClock->clockControl.available = TRUE;
+ }
+
+ }
+
+ /* can this even happen if offset is < 1 sec? */
+ if(rtOpts->enablePanicMode && ptpClock->panicOver) {
+ ptpClock->panicMode = FALSE;
+ ptpClock->panicOver = FALSE;
+ timerStop(&ptpClock->timers[PANIC_MODE_TIMER]);
+ NOTICE("Panic mode timeout and offset below 1 second again: resuming clock control\n");
+ /* we can control the clock again */
+ ptpClock->clockControl.available = TRUE;
+ }
+
+
+
+ ptpClock->clockControl.updateOK = TRUE;
+
+}
+
+void
+updateClock(const RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+
+ if(rtOpts->noAdjust) {
+ ptpClock->clockControl.available = FALSE;
+ DBGV("updateClock: noAdjust - skipped clock update\n");
+ return;
+ }
+
+ if(!ptpClock->clockControl.updateOK) {
+ DBGV("updateClock: !clockUpdateOK - skipped clock update\n");
+ return;
+ }
+
+ DBGV("==> updateClock\n");
+
+ if(ptpClock->clockControl.stepRequired) {
+ if (!rtOpts->noResetClock) {
+ stepClock(rtOpts, ptpClock);
+ ptpClock->clockControl.stepRequired = FALSE;
+ return;
+ } else {
+ if(ptpClock->currentDS.offsetFromMaster.nanoseconds > 0)
+ ptpClock->servo.observedDrift = rtOpts->servoMaxPpb;
+ else
+ ptpClock->servo.observedDrift = -rtOpts->servoMaxPpb;
+ warn_operator_slow_slewing(rtOpts, ptpClock);
+ adjFreq_wrapper(rtOpts, ptpClock, -ptpClock->servo.observedDrift);
+ ptpClock->clockControl.stepRequired = FALSE;
+ }
+ return;
+ }
+
+ if (ptpClock->clockControl.granted) {
+
+ /* only run the servo if we are calibrted - if calibration delay configured */
+
+ if((!rtOpts->calibrationDelay) || ptpClock->isCalibrated) {
+
+ /* Adjust the clock first -> the PI controller runs here */
+ adjFreq_wrapper(rtOpts, ptpClock, runPIservo(&ptpClock->servo, ptpClock->currentDS.offsetFromMaster.nanoseconds));
+ }
+ warn_operator_fast_slewing(rtOpts, ptpClock, ptpClock->servo.observedDrift);
+ /* let the clock source know it's being synced */
+ ptpClock->clockStatus.inSync = TRUE;
+ ptpClock->clockStatus.clockOffset = (ptpClock->currentDS.offsetFromMaster.seconds * 1E9 +
+ ptpClock->currentDS.offsetFromMaster.nanoseconds) / 1000;
+ ptpClock->clockStatus.update = TRUE;
+ }
+
+ SET_ALARM(ALRM_FAST_ADJ, ptpClock->servo.runningMaxOutput);
+
+ /* we are ready to control the clock */
+ ptpClock->clockControl.available = TRUE;
+ ptpClock->clockControl.activity = TRUE;
+
+ /* Clock has been updated - or was eligible for an update - restart the timeout timer*/
+ if(rtOpts->clockUpdateTimeout > 0) {
+ DBG("Restarted clock update timeout timer\n");
+ timerStart(&ptpClock->timers[CLOCK_UPDATE_TIMER],rtOpts->clockUpdateTimeout);
+ }
+
+ ptpClock->pastStartup = TRUE;
+#ifdef PTPD_STATISTICS
+ feedDoublePermanentStdDev(&ptpClock->servo.driftStats, ptpClock->servo.observedDrift);
+ feedDoublePermanentMedian(&ptpClock->servo.driftMedianContainer, ptpClock->servo.observedDrift);
+ if(!ptpClock->servo.statsUpdated) {
+ if(ptpClock->servo.observedDrift != 0.0){
+ ptpClock->servo.driftMax = ptpClock->servo.observedDrift;
+ ptpClock->servo.driftMin = ptpClock->servo.observedDrift;
+ ptpClock->servo.statsUpdated = TRUE;
+ }
+ } else {
+ ptpClock->servo.driftMax = max(ptpClock->servo.driftMax, ptpClock->servo.observedDrift);
+ ptpClock->servo.driftMin = min(ptpClock->servo.driftMin, ptpClock->servo.observedDrift);
+ }
+#endif
+
+}
+
+void
+setupPIservo(PIservo* servo, const const RunTimeOpts* rtOpts)
+{
+ servo->maxOutput = rtOpts->servoMaxPpb;
+ servo->kP = rtOpts->servoKP;
+ servo->kI = rtOpts->servoKI;
+ servo->dTmethod = rtOpts->servoDtMethod;
+#ifdef PTPD_STATISTICS
+ servo->stabilityThreshold = rtOpts->servoStabilityThreshold;
+ servo->stabilityPeriod = rtOpts->servoStabilityPeriod;
+ servo->stabilityTimeout = (60 / rtOpts->statsUpdateInterval) * rtOpts->servoStabilityTimeout;
+#endif
+}
+
+void
+resetPIservo(PIservo* servo)
+{
+/* not needed: restoreDrift handles this */
+/* servo->observedDrift = 0; */
+ servo->input = 0;
+ servo->output = 0;
+ servo->lastUpdate.seconds = 0;
+ servo->lastUpdate.nanoseconds = 0;
+}
+
+double
+runPIservo(PIservo* servo, const Integer32 input)
+{
+
+ double dt;
+
+ TimeInternal now, delta;
+
+ switch (servo->dTmethod) {
+
+ case DT_MEASURED:
+
+ getTimeMonotonic(&now);
+ if(servo->lastUpdate.seconds == 0 &&
+ servo->lastUpdate.nanoseconds == 0) {
+ dt = servo->dT;
+ } else {
+ subTime(&delta, &now, &servo->lastUpdate);
+ dt = delta.seconds + delta.nanoseconds / 1E9;
+ }
+
+ /* Don't use dT longer then max update interval multiplier */
+ if(dt > (servo->maxdT * servo->dT))
+ dt = (servo->maxdT + 0.0) * servo->dT;
+
+ break;
+
+ case DT_CONSTANT:
+
+ dt = servo->dT;
+
+ break;
+
+ case DT_NONE:
+ default:
+ dt = 1.0;
+ break;
+ }
+
+ if(dt <= 0.0)
+ dt = 1.0;
+
+ servo->input = input;
+
+ if (servo->kP < 0.000001)
+ servo->kP = 0.000001;
+ if (servo->kI < 0.000001)
+ servo->kI = 0.000001;
+
+ servo->observedDrift +=
+ dt * ((input + 0.0 ) * servo->kI);
+
+ if(servo->observedDrift >= servo->maxOutput) {
+ servo->observedDrift = servo->maxOutput;
+ servo->runningMaxOutput = TRUE;
+#ifdef PTPD_STATISTICS
+ servo->stableCount = 0;
+ servo->updateCount = 0;
+ servo->isStable = FALSE;
+#endif /* PTPD_STATISTICS */
+ }
+ else if(servo->observedDrift <= -servo->maxOutput) {
+ servo->observedDrift = -servo->maxOutput;
+ servo->runningMaxOutput = TRUE;
+#ifdef PTPD_STATISTICS
+ servo->stableCount = 0;
+ servo->updateCount = 0;
+ servo->isStable = FALSE;
+#endif /* PTPD_STATISTICS */
+ } else {
+ servo->runningMaxOutput = FALSE;
+ }
+
+ servo->output = (servo->kP * (input + 0.0) ) + servo->observedDrift;
+
+ if(servo->dTmethod == DT_MEASURED)
+ servo->lastUpdate = now;
+
+ DBGV("Servo dt: %.09f, input (ofm): %d, output(adj): %.09f, accumulator (observed drift): %.09f\n", dt, input, servo->output, servo->observedDrift);
+
+ return -servo->output;
+
+}
+
+#ifdef PTPD_STATISTICS
+static void
+checkServoStable(PtpClock *ptpClock, const RunTimeOpts *rtOpts)
+{
+
+ DBG("servo stablecount: %d\n",ptpClock->servo.stableCount);
+
+ /* if not calibrated, do nothing */
+ if( !rtOpts->calibrationDelay || ptpClock->isCalibrated ) {
+ ++ptpClock->servo.updateCount;
+ } else {
+ return;
+ }
+
+ /* check if we're below the threshold or not */
+ if(ptpClock->servo.runningMaxOutput || !ptpClock->acceptedUpdates ||
+ (ptpClock->servo.driftStdDev > ptpClock->servo.stabilityThreshold)) {
+ ptpClock->servo.stableCount = 0;
+ } else if (ptpClock->servo.driftStdDev <= ptpClock->servo.stabilityThreshold) {
+ ptpClock->servo.stableCount++;
+ }
+
+ /* Servo considered stable - drift std dev below threshold for n measurements - saving drift*/
+ if(ptpClock->servo.stableCount >= ptpClock->servo.stabilityPeriod) {
+
+ if(!ptpClock->servo.isStable) {
+ NOTICE("Clock servo now within stability threshold of %.03f ppb\n",
+ ptpClock->servo.stabilityThreshold);
+ }
+
+ saveDrift(ptpClock, rtOpts, ptpClock->servo.isStable);
+
+ ptpClock->servo.isStable = TRUE;
+ ptpClock->servo.stableCount = 0;
+ ptpClock->servo.updateCount = 0;
+
+ /* servo not stable within max interval */
+ } else if(ptpClock->servo.updateCount >= ptpClock->servo.stabilityTimeout) {
+
+ ptpClock->servo.stableCount = 0;
+ ptpClock->servo.updateCount = 0;
+
+ if(ptpClock->servo.isStable) {
+ WARNING("Clock servo outside stability threshold (%.03f ppb dev > %.03f ppb thr). Too many warnings may mean the threshold is too low.\n",
+ ptpClock->servo.driftStdDev,
+ ptpClock->servo.stabilityThreshold);
+ ptpClock->servo.isStable = FALSE;
+ } else {
+ if(ptpClock->servo.runningMaxOutput) {
+ WARNING("Clock servo outside stability threshold after %d seconds - running at maximum rate.\n",
+ rtOpts->statsUpdateInterval * ptpClock->servo.stabilityTimeout);
+ } else {
+ WARNING("Clock servo outside stability threshold %d seconds after last check. Saving current observed drift.\n",
+ rtOpts->statsUpdateInterval * ptpClock->servo.stabilityTimeout);
+ saveDrift(ptpClock, rtOpts, FALSE);
+ }
+ }
+ }
+
+}
+
+void
+updatePtpEngineStats (PtpClock* ptpClock, const RunTimeOpts* rtOpts)
+{
+
+ DBG("Refreshing slave engine stats counters\n");
+
+ DBG("samples used: %d/%d = %.03f\n", ptpClock->acceptedUpdates, ptpClock->offsetUpdates, (ptpClock->acceptedUpdates + 0.0) / (ptpClock->offsetUpdates + 0.0));
+
+ ptpClock->slaveStats.mpdMean = ptpClock->slaveStats.mpdStats.meanContainer.mean;
+ ptpClock->slaveStats.mpdStdDev = ptpClock->slaveStats.mpdStats.stdDev;
+ ptpClock->slaveStats.mpdMedian = ptpClock->slaveStats.mpdMedianContainer.median;
+ ptpClock->slaveStats.mpdMinFinal = ptpClock->slaveStats.mpdMin;
+ ptpClock->slaveStats.mpdMaxFinal = ptpClock->slaveStats.mpdMax;
+ ptpClock->slaveStats.ofmMean = ptpClock->slaveStats.ofmStats.meanContainer.mean;
+ ptpClock->slaveStats.ofmStdDev = ptpClock->slaveStats.ofmStats.stdDev;
+ ptpClock->slaveStats.ofmMedian = ptpClock->slaveStats.ofmMedianContainer.median;
+ ptpClock->slaveStats.ofmMinFinal = ptpClock->slaveStats.ofmMin;
+ ptpClock->slaveStats.ofmMaxFinal = ptpClock->slaveStats.ofmMax;
+
+ ptpClock->slaveStats.statsCalculated = TRUE;
+ ptpClock->servo.driftMean = ptpClock->servo.driftStats.meanContainer.mean;
+ ptpClock->servo.driftStdDev = ptpClock->servo.driftStats.stdDev;
+ ptpClock->servo.driftMedian = ptpClock->servo.driftMedianContainer.median;
+ ptpClock->servo.statsCalculated = TRUE;
+ ptpClock->servo.driftMinFinal = ptpClock->servo.driftMin;
+ ptpClock->servo.driftMaxFinal = ptpClock->servo.driftMax;
+
+ resetDoublePermanentMean(&ptpClock->oFilterMS.acceptedStats);
+ resetDoublePermanentMean(&ptpClock->oFilterSM.acceptedStats);
+
+ if(ptpClock->slaveStats.mpdStats.meanContainer.count >= 10.0) {
+ resetDoublePermanentStdDev(&ptpClock->slaveStats.mpdStats);
+ resetDoublePermanentMedian(&ptpClock->slaveStats.mpdMedianContainer);
+ ptpClock->slaveStats.ofmStatsUpdated = FALSE;
+ ptpClock->slaveStats.mpdStatsUpdated = FALSE;
+ }
+ if(ptpClock->slaveStats.ofmStats.meanContainer.count >= 10.0) {
+ resetDoublePermanentStdDev(&ptpClock->slaveStats.ofmStats);
+ resetDoublePermanentMedian(&ptpClock->slaveStats.ofmMedianContainer);
+ }
+ if(ptpClock->servo.driftStats.meanContainer.count >= 10.0)
+ resetDoublePermanentStdDev(&ptpClock->servo.driftStats);
+
+ resetDoublePermanentMedian(&ptpClock->servo.driftMedianContainer);
+ ptpClock->servo.statsUpdated = FALSE;
+
+ if(rtOpts->servoStabilityDetection && ptpClock->clockControl.granted) {
+ checkServoStable(ptpClock, rtOpts);
+ }
+
+ ptpClock->offsetUpdates = 0;
+ ptpClock->acceptedUpdates = 0;
+
+}
+
+#endif /* PTPD_STATISTICS */
diff --git a/rtemsbsd/ptpd/src/dep/snmp.c b/rtemsbsd/ptpd/src/dep/snmp.c
new file mode 100644
index 00000000..ae15eafd
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/snmp.c
@@ -0,0 +1,2186 @@
+/*-
+ * Copyright (c) 2015 Wojciech Owczarek
+ * Copyright (c) 2012 The IMS Company
+ * Vincent Bernat
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file snmp.c
+ * @author Vincent Bernat <bernat at luffy.cx>
+ * @date Sat Jun 23 23:08:05 2012
+ *
+ * @brief SNMP related functions
+ */
+
+#include "../ptpd.h"
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+
+//static void sendNotif(int event);
+static void sendNotif(int eventType, PtpEventData *eventData);
+
+/* Hard to get header... */
+extern int header_generic(struct variable *, oid *, size_t *, int,
+ size_t *, WriteMethod **);
+
+
+enum {
+
+ /* PTPBASE-MIB core */
+ PTPBASE_SYSTEM_PROFILE = 1,
+ PTPBASE_DOMAIN_CLOCK_PORTS_TOTAL,
+ PTPBASE_SYSTEM_DOMAIN_TOTALS,
+ PTPBASE_CLOCK_CURRENT_DS_STEPS_REMOVED,
+ PTPBASE_CLOCK_CURRENT_DS_OFFSET_FROM_MASTER,
+ PTPBASE_CLOCK_CURRENT_DS_MEAN_PATH_DELAY,
+ PTPBASE_CLOCK_PARENT_DS_PARENT_PORT_ID,
+ PTPBASE_CLOCK_PARENT_DS_PARENT_STATS,
+ PTPBASE_CLOCK_PARENT_DS_OFFSET,
+ PTPBASE_CLOCK_PARENT_DS_CLOCK_PH_CH_RATE,
+ PTPBASE_CLOCK_PARENT_DS_GM_CLOCK_IDENTITY,
+ PTPBASE_CLOCK_PARENT_DS_GM_CLOCK_PRIO1,
+ PTPBASE_CLOCK_PARENT_DS_GM_CLOCK_PRIO2,
+ PTPBASE_CLOCK_PARENT_DS_GM_CLOCK_QUALITY_CLASS,
+ PTPBASE_CLOCK_PARENT_DS_GM_CLOCK_QUALITY_ACCURACY,
+ PTPBASE_CLOCK_PARENT_DS_GM_CLOCK_QUALITY_OFFSET,
+ PTPBASE_CLOCK_DEFAULT_DS_TWO_STEP_FLAG,
+ PTPBASE_CLOCK_DEFAULT_DS_CLOCK_IDENTITY,
+ PTPBASE_CLOCK_DEFAULT_DS_PRIO1,
+ PTPBASE_CLOCK_DEFAULT_DS_PRIO2,
+ PTPBASE_CLOCK_DEFAULT_DS_SLAVE_ONLY,
+ PTPBASE_CLOCK_DEFAULT_DS_QUALITY_CLASS,
+ PTPBASE_CLOCK_DEFAULT_DS_QUALITY_ACCURACY,
+ PTPBASE_CLOCK_DEFAULT_DS_QUALITY_OFFSET,
+ PTPBASE_CLOCK_DEFAULT_DS_DOMAIN_NUMBER, /* PTPd addition */
+ PTPBASE_CLOCK_TIME_PROPERTIES_DS_CURRENT_UTC_OFFSET_VALID,
+ PTPBASE_CLOCK_TIME_PROPERTIES_DS_CURRENT_UTC_OFFSET,
+ PTPBASE_CLOCK_TIME_PROPERTIES_DS_LEAP59,
+ PTPBASE_CLOCK_TIME_PROPERTIES_DS_LEAP61,
+ PTPBASE_CLOCK_TIME_PROPERTIES_DS_TIME_TRACEABLE,
+ PTPBASE_CLOCK_TIME_PROPERTIES_DS_FREQ_TRACEABLE,
+ PTPBASE_CLOCK_TIME_PROPERTIES_DS_PTP_TIMESCALE,
+ PTPBASE_CLOCK_TIME_PROPERTIES_DS_SOURCE,
+ PTPBASE_CLOCK_PORT_NAME,
+ PTPBASE_CLOCK_PORT_ROLE,
+ PTPBASE_CLOCK_PORT_SYNC_ONE_STEP,
+ PTPBASE_CLOCK_PORT_CURRENT_PEER_ADDRESS_TYPE,
+ PTPBASE_CLOCK_PORT_CURRENT_PEER_ADDRESS,
+ PTPBASE_CLOCK_PORT_NUM_ASSOCIATED_PORTS,
+ PTPBASE_CLOCK_PORT_DS_PORT_NAME,
+ PTPBASE_CLOCK_PORT_DS_PORT_IDENTITY,
+ PTPBASE_CLOCK_PORT_DS_ANNOUNCEMENT_INTERVAL,
+ PTPBASE_CLOCK_PORT_DS_ANNOUNCE_RCT_TIMEOUT,
+ PTPBASE_CLOCK_PORT_DS_SYNC_INTERVAL,
+ PTPBASE_CLOCK_PORT_DS_MIN_DELAY_REQ_INTERVAL,
+ PTPBASE_CLOCK_PORT_DS_PEER_DELAY_REQ_INTERVAL,
+ PTPBASE_CLOCK_PORT_DS_DELAY_MECH,
+ PTPBASE_CLOCK_PORT_DS_PEER_MEAN_PATH_DELAY,
+ PTPBASE_CLOCK_PORT_DS_GRANT_DURATION,
+ PTPBASE_CLOCK_PORT_DS_PTP_VERSION,
+ PTPBASE_CLOCK_PORT_DS_PEER_MEAN_PATH_DELAY_STRING, /* PTPd addition */
+ PTPBASE_CLOCK_PORT_DS_LAST_MISMATCHED_DOMAIN, /* PTPd addition */
+ PTPBASE_CLOCK_PORT_RUNNING_NAME,
+ PTPBASE_CLOCK_PORT_RUNNING_STATE,
+ PTPBASE_CLOCK_PORT_RUNNING_ROLE,
+ PTPBASE_CLOCK_PORT_RUNNING_INTERFACE_INDEX,
+ PTPBASE_CLOCK_PORT_RUNNING_IPVERSION,
+ PTPBASE_CLOCK_PORT_RUNNING_ENCAPSULATION_TYPE,
+ PTPBASE_CLOCK_PORT_RUNNING_TX_MODE,
+ PTPBASE_CLOCK_PORT_RUNNING_RX_MODE,
+ PTPBASE_CLOCK_PORT_RUNNING_PACKETS_RECEIVED,
+ PTPBASE_CLOCK_PORT_RUNNING_PACKETS_SENT,
+
+ /* PTPd additions to PTPBASE-MIB */
+ PTPBASE_CLOCK_CURRENT_DS_OFFSET_FROM_MASTER_STRING,
+ PTPBASE_CLOCK_CURRENT_DS_MEAN_PATH_DELAY_STRING,
+ PTPBASE_CLOCK_CURRENT_DS_OFFSET_FROM_MASTER_THRESHOLD,
+
+ PTPBASE_CLOCK_PARENT_DS_PARENT_PORT_ADDRESS_TYPE,
+ PTPBASE_CLOCK_PARENT_DS_PARENT_PORT_ADDRESS,
+
+ /* ptpbasePtpPortMessageCounters */
+ PTPBASE_PORT_MESSAGE_COUNTERS_CLEAR,
+ PTPBASE_PORT_MESSAGE_COUNTERS_CLEAR_ALL,
+ PTPBASE_PORT_MESSAGE_COUNTERS_TOTAL_SENT,
+ PTPBASE_PORT_MESSAGE_COUNTERS_TOTAL_RECEIVED,
+ PTPBASE_PORT_MESSAGE_COUNTERS_ANNOUNCE_SENT,
+ PTPBASE_PORT_MESSAGE_COUNTERS_ANNOUNCE_RECEIVED,
+ PTPBASE_PORT_MESSAGE_COUNTERS_SYNC_SENT,
+ PTPBASE_PORT_MESSAGE_COUNTERS_SYNC_RECEIVED,
+ PTPBASE_PORT_MESSAGE_COUNTERS_FOLLOWUP_SENT,
+ PTPBASE_PORT_MESSAGE_COUNTERS_FOLLOWUP_RECEIVED,
+ PTPBASE_PORT_MESSAGE_COUNTERS_DELAYREQ_SENT,
+ PTPBASE_PORT_MESSAGE_COUNTERS_DELAYREQ_RECEIVED,
+ PTPBASE_PORT_MESSAGE_COUNTERS_DELAYRESP_SENT,
+ PTPBASE_PORT_MESSAGE_COUNTERS_DELAYRESP_RECEIVED,
+ PTPBASE_PORT_MESSAGE_COUNTERS_PDELAYREQ_SENT,
+ PTPBASE_PORT_MESSAGE_COUNTERS_PDELAYREQ_RECEIVED,
+ PTPBASE_PORT_MESSAGE_COUNTERS_PDELAYRESP_SENT,
+ PTPBASE_PORT_MESSAGE_COUNTERS_PDELAYRESP_RECEIVED,
+ PTPBASE_PORT_MESSAGE_COUNTERS_PDELAYRESP_FOLLOWUP_SENT,
+ PTPBASE_PORT_MESSAGE_COUNTERS_PDELAYRESP_FOLLOWUP_RECEIVED,
+ PTPBASE_PORT_MESSAGE_COUNTERS_SIGNALING_SENT,
+ PTPBASE_PORT_MESSAGE_COUNTERS_SIGNALING_RECEIVED,
+ PTPBASE_PORT_MESSAGE_COUNTERS_MANAGEMENT_SENT,
+ PTPBASE_PORT_MESSAGE_COUNTERS_MANAGEMENT_RECEIVED,
+ PTPBASE_PORT_MESSAGE_COUNTERS_DISCARDED_MESSAGES,
+ PTPBASE_PORT_MESSAGE_COUNTERS_UNKNOWN_MESSAGES,
+ /* ptpBasePtpProtocolCounters */
+ PTPBASE_PORT_PROTOCOL_COUNTERS_CLEAR,
+ PTPBASE_PORT_PROTOCOL_COUNTERS_FOREIGN_ADDED,
+ PTPBASE_PORT_PROTOCOL_COUNTERS_FOREIGN_COUNT,
+ PTPBASE_PORT_PROTOCOL_COUNTERS_FOREIGN_REMOVED,
+ PTPBASE_PORT_PROTOCOL_COUNTERS_FOREIGN_OVERFLOWS,
+ PTPBASE_PORT_PROTOCOL_COUNTERS_STATE_TRANSITIONS,
+ PTPBASE_PORT_PROTOCOL_COUNTERS_BEST_MASTER_CHANGES,
+ PTPBASE_PORT_PROTOCOL_COUNTERS_ANNOUNCE_TIMEOUTS,
+ /* ptpBasePtpErrorCounters */
+ PTPBASE_PORT_ERROR_COUNTERS_CLEAR,
+ PTPBASE_PORT_ERROR_COUNTERS_MESSAGE_RECV,
+ PTPBASE_PORT_ERROR_COUNTERS_MESSAGE_SEND,
+ PTPBASE_PORT_ERROR_COUNTERS_MESSAGE_FORMAT,
+ PTPBASE_PORT_ERROR_COUNTERS_PROTOCOL,
+ PTPBASE_PORT_ERROR_COUNTERS_VERSION_MISMATCH,
+ PTPBASE_PORT_ERROR_COUNTERS_DOMAIN_MISMATCH,
+ PTPBASE_PORT_ERROR_COUNTERS_SEQUENCE_MISMATCH,
+ PTPBASE_PORT_ERROR_COUNTERS_DELAYMECH_MISMATCH,
+ /* ptpBasePtpUnicastNegotiationCounters */
+ PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_CLEAR,
+ PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_REQUESTED,
+ PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_GRANTED,
+ PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_DENIED,
+ PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_CANCEL_SENT,
+ PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_CANCEL_RECEIVED,
+ PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_CANCEL_ACK_SENT,
+ PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_CANCEL_ACK_RECEIVED,
+ /* ptpBasePtpPerformanceCounters */
+ PTPBASE_PORT_PERFORMANCE_COUNTERS_MESSAGE_SEND_RATE,
+ PTPBASE_PORT_PERFORMANCE_COUNTERS_MESSAGE_RECEIVE_RATE,
+ /* ptpBasePtpSecurityCounters */
+ PTPBASE_PORT_SECURITY_COUNTERS_CLEAR,
+ PTPBASE_PORT_SECURITY_COUNTERS_TIMING_ACL_DISCARDED,
+ PTPBASE_PORT_SECURITY_COUNTERS_MANAGEMENT_ACL_DISCARDED,
+ /* ptpBaseSlaveOfmStatistics */
+ PTPBASE_SLAVE_OFM_STATS_CURRENT_VALUE,
+ PTPBASE_SLAVE_OFM_STATS_CURRENT_VALUE_STRING,
+ PTPBASE_SLAVE_OFM_STATS_PERIOD_SECONDS,
+ PTPBASE_SLAVE_OFM_STATS_VALID,
+ PTPBASE_SLAVE_OFM_STATS_MIN,
+ PTPBASE_SLAVE_OFM_STATS_MAX,
+ PTPBASE_SLAVE_OFM_STATS_MEAN,
+ PTPBASE_SLAVE_OFM_STATS_STDDEV,
+ PTPBASE_SLAVE_OFM_STATS_MEDIAN,
+ PTPBASE_SLAVE_OFM_STATS_MIN_STRING,
+ PTPBASE_SLAVE_OFM_STATS_MAX_STRING,
+ PTPBASE_SLAVE_OFM_STATS_MEAN_STRING,
+ PTPBASE_SLAVE_OFM_STATS_STDDEV_STRING,
+ PTPBASE_SLAVE_OFM_STATS_MEDIAN_STRING,
+ /* ptpBaseSlaveMpdStatistics */
+ PTPBASE_SLAVE_MPD_STATS_CURRENT_VALUE,
+ PTPBASE_SLAVE_MPD_STATS_CURRENT_VALUE_STRING,
+ PTPBASE_SLAVE_MPD_STATS_PERIOD_SECONDS,
+ PTPBASE_SLAVE_MPD_STATS_VALID,
+ PTPBASE_SLAVE_MPD_STATS_MIN,
+ PTPBASE_SLAVE_MPD_STATS_MAX,
+ PTPBASE_SLAVE_MPD_STATS_MEAN,
+ PTPBASE_SLAVE_MPD_STATS_STDDEV,
+ PTPBASE_SLAVE_MPD_STATS_MEDIAN,
+ PTPBASE_SLAVE_MPD_STATS_MIN_STRING,
+ PTPBASE_SLAVE_MPD_STATS_MAX_STRING,
+ PTPBASE_SLAVE_MPD_STATS_MEAN_STRING,
+ PTPBASE_SLAVE_MPD_STATS_STDDEV_STRING,
+ PTPBASE_SLAVE_MPD_STATS_MEDIAN_STRING,
+ /* ptpBaseSlaveFreqAdjStatistics */
+ PTPBASE_SLAVE_FREQADJ_STATS_CURRENT_VALUE,
+ PTPBASE_SLAVE_FREQADJ_STATS_PERIOD_SECONDS,
+ PTPBASE_SLAVE_FREQADJ_STATS_VALID,
+ PTPBASE_SLAVE_FREQADJ_STATS_MIN,
+ PTPBASE_SLAVE_FREQADJ_STATS_MAX,
+ PTPBASE_SLAVE_FREQADJ_STATS_MEAN,
+ PTPBASE_SLAVE_FREQADJ_STATS_STDDEV,
+ PTPBASE_SLAVE_FREQADJ_STATS_MEDIAN,
+ /* ptpBasePtpdSpecificCounters */
+ PTPBASE_PTPD_SPECIFIC_COUNTERS_CLEAR,
+ PTPBASE_PTPD_SPECIFIC_COUNTERS_IGNORED_ANNOUNCE,
+ PTPBASE_PTPD_SPECIFIC_COUNTERS_CONSECUTIVE_SEQUENCE_ERRORS,
+ PTPBASE_PTPD_SPECIFIC_COUNTERS_DELAYMS_OUTLIERS_FOUND,
+ PTPBASE_PTPD_SPECIFIC_COUNTERS_DELAYSM_OUTLIERS_FOUND,
+ PTPBASE_PTPD_SPECIFIC_COUNTERS_MAX_DELAY_DROPS,
+ /* ptpBasePtpdSpecificData */
+ PTPBASE_PTPD_SPECIFIC_DATA_RAW_DELAYMS,
+ PTPBASE_PTPD_SPECIFIC_DATA_RAW_DELAYMS_STRING,
+ PTPBASE_PTPD_SPECIFIC_DATA_RAW_DELAYSM,
+ PTPBASE_PTPD_SPECIFIC_DATA_RAW_DELAYSM_STRING
+};
+
+/* trap / notification definitions */
+enum {
+ PTPBASE_NOTIFS_EXPECTED_PORT_STATE,
+ PTPBASE_NOTIFS_UNEXPECTED_PORT_STATE,
+ PTPBASE_NOTIFS_SLAVE_OFFSET_THRESHOLD_EXCEEDED,
+ PTPBASE_NOTIFS_SLAVE_OFFSET_THRESHOLD_ACCEPTABLE,
+ PTPBASE_NOTIFS_SLAVE_CLOCK_STEP,
+ PTPBASE_NOTIFS_SLAVE_NO_SYNC,
+ PTPBASE_NOTIFS_SLAVE_RECEIVING_SYNC,
+ PTPBASE_NOTIFS_SLAVE_NO_DELAY,
+ PTPBASE_NOTIFS_SLAVE_RECEIVING_DELAY,
+ PTPBASE_NOTIFS_BEST_MASTER_CHANGE,
+ PTPBASE_NOTIFS_NETWORK_FAULT,
+ PTPBASE_NOTIFS_NETWORK_FAULT_CLEARED,
+ PTPBASE_NOTIFS_FREQADJ_FAST,
+ PTPBASE_NOTIFS_FREQADJ_NORMAL,
+ PTPBASE_NOTIFS_OFFSET_SECONDS,
+ PTPBASE_NOTIFS_OFFSET_SUB_SECONDS,
+ PTPBASE_NOTIFS_TIMEPROPERTIESDS_CHANGE,
+ PTPBASE_NOTIFS_DOMAIN_MISMATCH,
+ PTPBASE_NOTIFS_DOMAIN_MISMATCH_CLEARED,
+};
+
+#define SNMP_PTP_ORDINARY_CLOCK 1
+#define SNMP_PTP_CLOCK_INSTANCE 1 /* Only one instance */
+#define SNMP_PTP_PORT_MASTER 1
+#define SNMP_PTP_PORT_SLAVE 2
+#define SNMP_IPv4 1
+#define SNMP_PTP_TX_UNICAST 1
+#define SNMP_PTP_TX_MULTICAST 2
+#define SNMP_PTP_TX_MULTICAST_MIX 3
+
+#define TRUTHVALUE_TRUE 1
+#define TRUTHVALUE_FALSE 2
+
+#define TO_TRUTHVALUE(a) \
+ (a ? TRUTHVALUE_TRUE : TRUTHVALUE_FALSE)
+
+#define PTPBASE_MIB_OID \
+ 1, 3, 6, 1, 4, 1, 46649, 1, 1
+#define PTPBASE_MIB_INDEX2 \
+ snmpPtpClock->defaultDS.domainNumber, SNMP_PTP_CLOCK_INSTANCE
+#define PTPBASE_MIB_INDEX3 \
+ snmpPtpClock->defaultDS.domainNumber, SNMP_PTP_ORDINARY_CLOCK, SNMP_PTP_CLOCK_INSTANCE
+#define PTPBASE_MIB_INDEX4 \
+ snmpPtpClock->defaultDS.domainNumber, SNMP_PTP_ORDINARY_CLOCK, SNMP_PTP_CLOCK_INSTANCE, snmpPtpClock->portDS.portIdentity.portNumber
+
+static oid ptp_oid[] = { PTPBASE_MIB_OID };
+
+static PtpClock *snmpPtpClock;
+static RunTimeOpts *snmpRtOpts;
+
+/* Helper functions to build header_*indexed_table() functions. Those
+ functions keep an internal state. They are not reentrant!
+*/
+/* {{{ */
+struct snmpHeaderIndex {
+ struct variable *vp;
+ oid *name; /* Requested/returned OID */
+ size_t *length; /* Length of above OID */
+ int exact;
+ oid best[MAX_OID_LEN]; /* Best OID */
+ size_t best_len; /* Best OID length */
+ void *entity; /* Best entity */
+};
+
+static int
+snmpHeaderInit(struct snmpHeaderIndex *idx,
+ struct variable *vp, oid *name, size_t *length,
+ int exact, size_t *var_len, WriteMethod **write_method)
+{
+ /* If the requested OID name is less than OID prefix we
+ handle, adjust it to our prefix. */
+ if ((snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) {
+ memcpy(name, vp->name, sizeof(oid) * vp->namelen);
+ *length = vp->namelen;
+ }
+ /* Now, we can only handle OID matching our prefix. Those two
+ tests are not really necessary since NetSNMP won't give us
+ OID "above" our prefix. But this makes unit tests
+ easier. */
+ if (*length < vp->namelen) return 0;
+ if (memcmp(name, vp->name, vp->namelen * sizeof(oid))) return 0;
+
+ if(write_method != NULL) *write_method = 0;
+ *var_len = sizeof(long);
+
+ /* Initialize our header index structure */
+ idx->vp = vp;
+ idx->name = name;
+ idx->length = length;
+ idx->exact = exact;
+ idx->best_len = 0;
+ idx->entity = NULL;
+ return 1;
+}
+
+static void
+snmpHeaderIndexAdd(struct snmpHeaderIndex *idx,
+ oid *index, size_t len, void *entity)
+{
+ int result;
+ oid *target;
+ size_t target_len;
+
+ target = idx->name + idx->vp->namelen;
+ target_len = *idx->length - idx->vp->namelen;
+ if ((result = snmp_oid_compare(index, len, target, target_len)) < 0)
+ return; /* Too small. */
+ if (idx->exact) {
+ if (result == 0) {
+ memcpy(idx->best, index, sizeof(oid) * len);
+ idx->best_len = len;
+ idx->entity = entity;
+ return;
+ }
+ return;
+ }
+ if (result == 0)
+ return; /* Too small. */
+ if (idx->best_len == 0 ||
+ (snmp_oid_compare(index, len,
+ idx->best,
+ idx->best_len) < 0)) {
+ memcpy(idx->best, index, sizeof(oid) * len);
+ idx->best_len = len;
+ idx->entity = entity;
+ }
+}
+
+static void*
+snmpHeaderIndexBest(struct snmpHeaderIndex *idx)
+{
+ if (idx->entity == NULL)
+ return NULL;
+ if (idx->exact) {
+ if (snmp_oid_compare(idx->name + idx->vp->namelen,
+ *idx->length - idx->vp->namelen,
+ idx->best, idx->best_len) == 0)
+ return idx->entity;
+ return NULL;
+ }
+ memcpy(idx->name + idx->vp->namelen,
+ idx->best, sizeof(oid) * idx->best_len);
+ *idx->length = idx->vp->namelen + idx->best_len;
+ return idx->entity;
+}
+/* }}} */
+
+
+#define SNMP_LOCAL_VARIABLES \
+ static unsigned long long_ret; \
+ static struct counter64 counter64_ret; \
+ static uint32_t ipaddr; \
+ Integer32 i32_ret; \
+ Integer64 bigint; \
+ struct snmpHeaderIndex idx; \
+ static char tmpStr[64]; \
+ (void)long_ret; \
+ (void)counter64_ret; \
+ (void)ipaddr; \
+ (void)bigint; \
+ (void)i32_ret; \
+ (void)tmpStr; \
+ (void)idx
+#define SNMP_INDEXED_TABLE \
+ if (!snmpHeaderInit(&idx, vp, name, \
+ length, exact, \
+ var_len, \
+ write_method)) \
+ return NULL
+#define SNMP_ADD_INDEX(index, len, w) \
+ snmpHeaderIndexAdd(&idx, index, len, w)
+#define SNMP_BEST_MATCH \
+ snmpHeaderIndexBest(&idx)
+#define SNMP_OCTETSTR(V, L) \
+ ( *var_len = L, \
+ (u_char *)V )
+#define SNMP_COUNTER64(V) \
+ ( counter64_ret.low = (V) & 0xffffffff, \
+ counter64_ret.high = (V) >> 32, \
+ *var_len = sizeof (counter64_ret), \
+ (u_char*)&counter64_ret )
+#define SNMP_TIMEINTERNAL(V) \
+ ( *var_len = sizeof(counter64_ret), \
+ internalTime_to_integer64(V, &bigint), \
+ counter64_ret.low = htonl(bigint.lsb), \
+ counter64_ret.high = htonl(bigint.msb), \
+ (u_char *)&counter64_ret )
+#define SNMP_INTEGER(V) \
+ ( long_ret = (V), \
+ *var_len = sizeof (long_ret), \
+ (u_char*)&long_ret )
+#define SNMP_IPADDR(A) \
+ ( ipaddr = A, \
+ *var_len = sizeof (ipaddr), \
+ (u_char*)&ipaddr )
+#define SNMP_GAUGE SNMP_INTEGER
+#define SNMP_UNSIGNED SNMP_INTEGER
+#define SNMP_TRUE SNMP_INTEGER(1)
+#define SNMP_FALSE SNMP_INTEGER(2)
+#define SNMP_BOOLEAN(V) \
+ (V == TRUE)?SNMP_TRUE:SNMP_FALSE
+#define SNMP_SIGNATURE struct variable *vp, \
+ oid *name, \
+ size_t *length, \
+ int exact, \
+ size_t *var_len, \
+ WriteMethod **write_method
+
+/**
+ * Handle SNMP scalar values.
+ */
+static u_char*
+snmpScalars(SNMP_SIGNATURE) {
+ SNMP_LOCAL_VARIABLES;
+ if (header_generic(vp, name, length, exact, var_len, write_method))
+ return NULL;
+
+ switch (vp->magic) {
+ case PTPBASE_SYSTEM_PROFILE:
+ return SNMP_INTEGER(1);
+ }
+
+ return NULL;
+}
+
+/**
+ * Handle ptpbaseSystemTable
+ */
+static u_char*
+snmpSystemTable(SNMP_SIGNATURE) {
+ oid index[2];
+ SNMP_LOCAL_VARIABLES;
+ SNMP_INDEXED_TABLE;
+
+ /* We only have one index: one domain, one instance */
+ index[0] = snmpPtpClock->defaultDS.domainNumber;
+ index[1] = SNMP_PTP_CLOCK_INSTANCE;
+ SNMP_ADD_INDEX(index, 2, snmpPtpClock);
+
+ if (!SNMP_BEST_MATCH) return NULL;
+
+ switch (vp->magic) {
+ case PTPBASE_DOMAIN_CLOCK_PORTS_TOTAL:
+ return SNMP_GAUGE(snmpPtpClock->defaultDS.numberPorts);
+ }
+
+ return NULL;
+}
+
+/**
+ * Handle ptpbaseSystemDomainTable
+ */
+static u_char*
+snmpSystemDomainTable(SNMP_SIGNATURE) {
+ oid index[1];
+ SNMP_LOCAL_VARIABLES;
+ SNMP_INDEXED_TABLE;
+
+ /* We only have one index: ordinary clock */
+ index[0] = SNMP_PTP_ORDINARY_CLOCK;
+ SNMP_ADD_INDEX(index, 1, snmpPtpClock);
+
+ if (!SNMP_BEST_MATCH) return NULL;
+
+ switch (vp->magic) {
+ case PTPBASE_SYSTEM_DOMAIN_TOTALS:
+ /* We only handle one domain... */
+ return SNMP_UNSIGNED(1);
+ }
+
+ return NULL;
+}
+
+/**
+ * Handle various ptpbaseClock*DSTable
+ */
+static u_char*
+snmpClockDSTable(SNMP_SIGNATURE) {
+ oid index[3];
+ SNMP_LOCAL_VARIABLES;
+ SNMP_INDEXED_TABLE;
+
+ memset(tmpStr, 0, sizeof(tmpStr));
+
+ /* We only have one valid index */
+ index[0] = snmpPtpClock->defaultDS.domainNumber;
+ index[1] = SNMP_PTP_ORDINARY_CLOCK;
+ index[2] = SNMP_PTP_CLOCK_INSTANCE;
+ SNMP_ADD_INDEX(index, 3, snmpPtpClock);
+
+ if (!SNMP_BEST_MATCH) return NULL;
+
+ switch (vp->magic) {
+ /* ptpbaseClockCurrentDSTable */
+ case PTPBASE_CLOCK_CURRENT_DS_STEPS_REMOVED:
+ return SNMP_UNSIGNED(snmpPtpClock->currentDS.stepsRemoved);
+ case PTPBASE_CLOCK_CURRENT_DS_OFFSET_FROM_MASTER:
+ return SNMP_TIMEINTERNAL(snmpPtpClock->currentDS.offsetFromMaster);
+ case PTPBASE_CLOCK_CURRENT_DS_MEAN_PATH_DELAY:
+ return SNMP_TIMEINTERNAL(snmpPtpClock->currentDS.meanPathDelay);
+ /* PTPd: offsets as string */
+ case PTPBASE_CLOCK_CURRENT_DS_OFFSET_FROM_MASTER_STRING:
+ snprintf(tmpStr, 64, "%.09f", timeInternalToDouble(&snmpPtpClock->currentDS.offsetFromMaster));
+ return SNMP_OCTETSTR(&tmpStr, strlen(tmpStr));
+ case PTPBASE_CLOCK_CURRENT_DS_MEAN_PATH_DELAY_STRING:
+ snprintf(tmpStr, 64, "%.09f", timeInternalToDouble(&snmpPtpClock->currentDS.meanPathDelay));
+ return SNMP_OCTETSTR(&tmpStr, strlen(tmpStr));
+ case PTPBASE_CLOCK_CURRENT_DS_OFFSET_FROM_MASTER_THRESHOLD:
+ return SNMP_INTEGER(snmpRtOpts->ofmAlarmThreshold);
+
+ /* ptpbaseClockParentDSTable */
+ case PTPBASE_CLOCK_PARENT_DS_PARENT_PORT_ID:
+ return SNMP_OCTETSTR(&snmpPtpClock->parentDS.parentPortIdentity,
+ sizeof(PortIdentity));
+ case PTPBASE_CLOCK_PARENT_DS_PARENT_STATS:
+ return SNMP_BOOLEAN(snmpPtpClock->parentDS.parentStats);
+ case PTPBASE_CLOCK_PARENT_DS_OFFSET:
+ return SNMP_INTEGER(snmpPtpClock->parentDS.observedParentOffsetScaledLogVariance);
+ case PTPBASE_CLOCK_PARENT_DS_CLOCK_PH_CH_RATE:
+ return SNMP_INTEGER(snmpPtpClock->parentDS.observedParentClockPhaseChangeRate);
+ case PTPBASE_CLOCK_PARENT_DS_GM_CLOCK_IDENTITY:
+ return SNMP_OCTETSTR(&snmpPtpClock->parentDS.grandmasterIdentity,
+ sizeof(ClockIdentity));
+ case PTPBASE_CLOCK_PARENT_DS_GM_CLOCK_PRIO1:
+ return SNMP_UNSIGNED(snmpPtpClock->parentDS.grandmasterPriority1);
+ case PTPBASE_CLOCK_PARENT_DS_GM_CLOCK_PRIO2:
+ return SNMP_UNSIGNED(snmpPtpClock->parentDS.grandmasterPriority2);
+ case PTPBASE_CLOCK_PARENT_DS_GM_CLOCK_QUALITY_CLASS:
+ return SNMP_UNSIGNED(snmpPtpClock->parentDS.grandmasterClockQuality.clockClass);
+ case PTPBASE_CLOCK_PARENT_DS_GM_CLOCK_QUALITY_ACCURACY:
+ return SNMP_INTEGER(snmpPtpClock->parentDS.grandmasterClockQuality.clockAccuracy);
+ case PTPBASE_CLOCK_PARENT_DS_GM_CLOCK_QUALITY_OFFSET:
+ return SNMP_INTEGER(snmpPtpClock->parentDS.grandmasterClockQuality.offsetScaledLogVariance);
+ /* PTPd addition */
+ case PTPBASE_CLOCK_PARENT_DS_PARENT_PORT_ADDRESS_TYPE:
+ /* Only supports IPv4 */
+ return SNMP_INTEGER(SNMP_IPv4);
+ case PTPBASE_CLOCK_PARENT_DS_PARENT_PORT_ADDRESS:
+ if(snmpRtOpts->transport != UDP_IPV4)
+ return SNMP_IPADDR(0);
+ if(!snmpPtpClock->bestMaster)
+ return SNMP_IPADDR(0);
+ return SNMP_IPADDR(snmpPtpClock->bestMaster->sourceAddr);
+ /* ptpbaseClockDefaultDSTable */
+ case PTPBASE_CLOCK_DEFAULT_DS_TWO_STEP_FLAG:
+ return SNMP_BOOLEAN(snmpPtpClock->defaultDS.twoStepFlag);
+ case PTPBASE_CLOCK_DEFAULT_DS_CLOCK_IDENTITY:
+ return SNMP_OCTETSTR(&snmpPtpClock->defaultDS.clockIdentity,
+ sizeof(ClockIdentity));
+ case PTPBASE_CLOCK_DEFAULT_DS_PRIO1:
+ return SNMP_UNSIGNED(snmpPtpClock->defaultDS.priority1);
+ case PTPBASE_CLOCK_DEFAULT_DS_PRIO2:
+ return SNMP_UNSIGNED(snmpPtpClock->defaultDS.priority2);
+ case PTPBASE_CLOCK_DEFAULT_DS_SLAVE_ONLY:
+ return SNMP_BOOLEAN(snmpPtpClock->defaultDS.slaveOnly);
+ case PTPBASE_CLOCK_DEFAULT_DS_QUALITY_CLASS:
+ return SNMP_UNSIGNED(snmpPtpClock->defaultDS.clockQuality.clockClass);
+ case PTPBASE_CLOCK_DEFAULT_DS_QUALITY_ACCURACY:
+ return SNMP_INTEGER(snmpPtpClock->defaultDS.clockQuality.clockAccuracy);
+ case PTPBASE_CLOCK_DEFAULT_DS_QUALITY_OFFSET:
+ return SNMP_INTEGER(snmpPtpClock->defaultDS.clockQuality.offsetScaledLogVariance);
+ /* PTPd addition */
+ case PTPBASE_CLOCK_DEFAULT_DS_DOMAIN_NUMBER:
+ return SNMP_INTEGER(snmpPtpClock->defaultDS.domainNumber);
+ /* ptpbaseClockTimePropertiesDSTable */
+ case PTPBASE_CLOCK_TIME_PROPERTIES_DS_CURRENT_UTC_OFFSET_VALID:
+ return SNMP_BOOLEAN(snmpPtpClock->timePropertiesDS.currentUtcOffsetValid);
+ case PTPBASE_CLOCK_TIME_PROPERTIES_DS_CURRENT_UTC_OFFSET:
+ return SNMP_INTEGER(snmpPtpClock->timePropertiesDS.currentUtcOffset);
+ case PTPBASE_CLOCK_TIME_PROPERTIES_DS_LEAP59:
+ return SNMP_BOOLEAN(snmpPtpClock->timePropertiesDS.leap59);
+ case PTPBASE_CLOCK_TIME_PROPERTIES_DS_LEAP61:
+ return SNMP_BOOLEAN(snmpPtpClock->timePropertiesDS.leap61);
+ case PTPBASE_CLOCK_TIME_PROPERTIES_DS_TIME_TRACEABLE:
+ return SNMP_BOOLEAN(snmpPtpClock->timePropertiesDS.timeTraceable);
+ case PTPBASE_CLOCK_TIME_PROPERTIES_DS_FREQ_TRACEABLE:
+ return SNMP_BOOLEAN(snmpPtpClock->timePropertiesDS.frequencyTraceable);
+ case PTPBASE_CLOCK_TIME_PROPERTIES_DS_PTP_TIMESCALE:
+ return SNMP_BOOLEAN(snmpPtpClock->timePropertiesDS.ptpTimescale);
+ case PTPBASE_CLOCK_TIME_PROPERTIES_DS_SOURCE:
+ return SNMP_INTEGER(snmpPtpClock->timePropertiesDS.timeSource);
+ }
+
+ return NULL;
+}
+
+/**
+ * Handle ptpbaseClockPort*Table
+ */
+static u_char*
+snmpClockPortTable(SNMP_SIGNATURE) {
+ oid index[4];
+ SNMP_LOCAL_VARIABLES;
+ SNMP_INDEXED_TABLE;
+
+ /* We only have one valid index */
+ index[0] = snmpPtpClock->defaultDS.domainNumber;
+ index[1] = SNMP_PTP_ORDINARY_CLOCK;
+ index[2] = SNMP_PTP_CLOCK_INSTANCE;
+ index[3] = snmpPtpClock->portDS.portIdentity.portNumber;
+ SNMP_ADD_INDEX(index, 4, snmpPtpClock);
+
+ if (!SNMP_BEST_MATCH) return NULL;
+
+ switch (vp->magic) {
+ /* ptpbaseClockPortTable */
+ case PTPBASE_CLOCK_PORT_NAME:
+ case PTPBASE_CLOCK_PORT_DS_PORT_NAME:
+ case PTPBASE_CLOCK_PORT_RUNNING_NAME:
+ return SNMP_OCTETSTR(snmpPtpClock->userDescription,
+ strlen(snmpPtpClock->userDescription));
+ case PTPBASE_CLOCK_PORT_ROLE:
+ return SNMP_INTEGER((snmpPtpClock->portDS.portState == PTP_MASTER)?
+ SNMP_PTP_PORT_MASTER:SNMP_PTP_PORT_SLAVE);
+ case PTPBASE_CLOCK_PORT_SYNC_ONE_STEP:
+ return (snmpPtpClock->defaultDS.twoStepFlag == TRUE)?SNMP_FALSE:SNMP_TRUE;
+ case PTPBASE_CLOCK_PORT_CURRENT_PEER_ADDRESS_TYPE:
+ /* Only supports IPv4 */
+ return SNMP_INTEGER(SNMP_IPv4);
+ case PTPBASE_CLOCK_PORT_CURRENT_PEER_ADDRESS:
+ if(snmpRtOpts->transport != UDP_IPV4)
+ return SNMP_IPADDR(0);
+ return(SNMP_IPADDR(snmpPtpClock->netPath.interfaceAddr.s_addr));
+ case PTPBASE_CLOCK_PORT_NUM_ASSOCIATED_PORTS:
+ if(snmpPtpClock->portDS.portState == PTP_MASTER && snmpRtOpts->unicastNegotiation) {
+ return SNMP_INTEGER(snmpPtpClock->slaveCount);
+ }
+ if(snmpPtpClock->portDS.portState == PTP_MASTER && snmpPtpClock->unicastDestinationCount) {
+ return SNMP_INTEGER(snmpPtpClock->unicastDestinationCount);
+ }
+ if(snmpPtpClock->portDS.portState == PTP_SLAVE) {
+ return SNMP_INTEGER(snmpPtpClock->number_foreign_records);
+ }
+ return SNMP_INTEGER(0);
+ /* ptpbaseClockPortDSTable */
+ case PTPBASE_CLOCK_PORT_DS_PORT_IDENTITY:
+ return SNMP_OCTETSTR(&snmpPtpClock->portDS.portIdentity,
+ sizeof(PortIdentity));
+ case PTPBASE_CLOCK_PORT_DS_ANNOUNCEMENT_INTERVAL:
+ /* TODO: is it really logAnnounceInterval? */
+ return SNMP_INTEGER(snmpPtpClock->portDS.logAnnounceInterval);
+ case PTPBASE_CLOCK_PORT_DS_ANNOUNCE_RCT_TIMEOUT:
+ return SNMP_INTEGER(snmpPtpClock->portDS.announceReceiptTimeout);
+ case PTPBASE_CLOCK_PORT_DS_SYNC_INTERVAL:
+ /* TODO: is it really logSyncInterval? */
+ return SNMP_INTEGER(snmpPtpClock->portDS.logSyncInterval);
+ case PTPBASE_CLOCK_PORT_DS_MIN_DELAY_REQ_INTERVAL:
+ /* TODO: is it really logMinDelayReqInterval? */
+ return SNMP_INTEGER(snmpPtpClock->portDS.logMinDelayReqInterval);
+ case PTPBASE_CLOCK_PORT_DS_PEER_DELAY_REQ_INTERVAL:
+ /* TODO: is it really logMinPdelayReqInterval? */
+ return SNMP_INTEGER(snmpPtpClock->portDS.logMinPdelayReqInterval);
+ case PTPBASE_CLOCK_PORT_DS_DELAY_MECH:
+ return SNMP_INTEGER(snmpPtpClock->portDS.delayMechanism);
+ case PTPBASE_CLOCK_PORT_DS_PEER_MEAN_PATH_DELAY:
+ return SNMP_TIMEINTERNAL(snmpPtpClock->portDS.peerMeanPathDelay);
+ case PTPBASE_CLOCK_PORT_DS_GRANT_DURATION:
+ if(snmpRtOpts->unicastNegotiation && snmpPtpClock->parentGrants) {
+ return SNMP_UNSIGNED(snmpPtpClock->parentGrants->grantData[SYNC_INDEXED].duration);
+ }
+ return SNMP_UNSIGNED(0);
+ case PTPBASE_CLOCK_PORT_DS_PTP_VERSION:
+ return SNMP_INTEGER(snmpPtpClock->portDS.versionNumber);
+ case PTPBASE_CLOCK_PORT_DS_PEER_MEAN_PATH_DELAY_STRING:
+ snprintf(tmpStr, 64, "%.09f", timeInternalToDouble(&snmpPtpClock->portDS.peerMeanPathDelay));
+ return SNMP_OCTETSTR(&tmpStr, strlen(tmpStr));
+ case PTPBASE_CLOCK_PORT_DS_LAST_MISMATCHED_DOMAIN:
+ return SNMP_INTEGER(snmpPtpClock->portDS.lastMismatchedDomain);
+
+ /* ptpbaseClockPortRunningTable */
+ case PTPBASE_CLOCK_PORT_RUNNING_STATE:
+ return SNMP_INTEGER(snmpPtpClock->portDS.portState);
+ case PTPBASE_CLOCK_PORT_RUNNING_ROLE:
+ return SNMP_INTEGER((snmpPtpClock->portDS.portState == PTP_MASTER)?
+ SNMP_PTP_PORT_MASTER:SNMP_PTP_PORT_SLAVE);
+ case PTPBASE_CLOCK_PORT_RUNNING_INTERFACE_INDEX:
+ return SNMP_INTEGER(snmpPtpClock->netPath.interfaceInfo.ifIndex);
+ case PTPBASE_CLOCK_PORT_RUNNING_IPVERSION:
+ /* IPv4 only */
+ return SNMP_INTEGER(4);
+ case PTPBASE_CLOCK_PORT_RUNNING_ENCAPSULATION_TYPE:
+ /* None. Moreover, the format is not really described in the MIB... */
+ return SNMP_INTEGER(0);
+ case PTPBASE_CLOCK_PORT_RUNNING_TX_MODE:
+ case PTPBASE_CLOCK_PORT_RUNNING_RX_MODE:
+ if (snmpRtOpts->ipMode == IPMODE_UNICAST)
+ return SNMP_INTEGER(SNMP_PTP_TX_UNICAST);
+ if (snmpRtOpts->ipMode == IPMODE_HYBRID)
+ return SNMP_INTEGER(SNMP_PTP_TX_MULTICAST_MIX);
+ return SNMP_INTEGER(SNMP_PTP_TX_MULTICAST);
+ case PTPBASE_CLOCK_PORT_RUNNING_PACKETS_RECEIVED:
+
+ return SNMP_COUNTER64(snmpPtpClock->netPath.receivedPacketsTotal);
+ case PTPBASE_CLOCK_PORT_RUNNING_PACKETS_SENT:
+ return SNMP_COUNTER64(snmpPtpClock->netPath.sentPacketsTotal);
+ }
+
+
+ return NULL;
+}
+
+/* clear counter sets based on oid. WARNING: USES MAGIC NUMBERS... */
+static int
+snmpWriteClearCounters (int action, u_char *var_val, u_char var_val_type, size_t var_val_len,
+ u_char *statP, oid *name, size_t name_len) {
+
+ /* table: 6 oids from end (index fields, entry, field) */
+ oid myOid1 = name[name_len - 1 - 6];
+ /* field: 4 oids from end (index fields) */
+ oid myOid2 = name[name_len - 1 - 4];
+
+ if(var_val_type != ASN_INTEGER) {
+ return SNMP_ERR_WRONGTYPE;
+ }
+
+ if(var_val_len > sizeof(long)) {
+ return SNMP_ERR_WRONGLENGTH;
+ }
+
+ if(action==COMMIT) {
+
+ long *val = (long*) var_val;
+
+ if (*val == TRUTHVALUE_TRUE) {
+
+ switch (myOid1) {
+
+ case 12: /* message counters */
+ /* all counters */
+ if(myOid2 == 5) {
+ memset(&snmpPtpClock->counters, 0, sizeof(PtpdCounters));
+ return SNMP_ERR_NOERROR;
+ }
+ /* message counters */
+ if(myOid2 == 6) {
+ snmpPtpClock->counters.announceMessagesSent = 0;
+ snmpPtpClock->counters.announceMessagesReceived = 0;
+ snmpPtpClock->counters.syncMessagesSent = 0;
+ snmpPtpClock->counters.syncMessagesReceived = 0;
+ snmpPtpClock->counters.followUpMessagesSent = 0;
+ snmpPtpClock->counters.followUpMessagesReceived = 0;
+ snmpPtpClock->counters.delayReqMessagesSent = 0;
+ snmpPtpClock->counters.delayReqMessagesReceived = 0;
+ snmpPtpClock->counters.delayRespMessagesSent = 0;
+ snmpPtpClock->counters.delayRespMessagesReceived = 0;
+ snmpPtpClock->counters.pdelayReqMessagesSent = 0;
+ snmpPtpClock->counters.pdelayReqMessagesReceived = 0;
+ snmpPtpClock->counters.pdelayRespMessagesSent = 0;
+ snmpPtpClock->counters.pdelayRespMessagesReceived = 0;
+ snmpPtpClock->counters.pdelayRespFollowUpMessagesSent = 0;
+ snmpPtpClock->counters.pdelayRespFollowUpMessagesReceived = 0;
+ snmpPtpClock->counters.signalingMessagesSent = 0;
+ snmpPtpClock->counters.signalingMessagesReceived = 0;
+ snmpPtpClock->counters.managementMessagesSent = 0;
+ snmpPtpClock->counters.managementMessagesReceived = 0;
+ snmpPtpClock->counters.discardedMessages = 0;
+ snmpPtpClock->counters.unknownMessages = 0;
+ return SNMP_ERR_NOERROR;
+ }
+ break;
+ case 13: /* protocol counters */
+ /* clear counters */
+ if(myOid2 == 5) {
+ snmpPtpClock->counters.foreignAdded = 0;
+ /* snmpPtpClock->counters.foreignCount = 0; */ /* we don't clear this */
+ snmpPtpClock->counters.foreignRemoved = 0;
+ snmpPtpClock->counters.foreignOverflows = 0;
+ snmpPtpClock->counters.stateTransitions = 0;
+ snmpPtpClock->counters.bestMasterChanges = 0;
+ snmpPtpClock->counters.announceTimeouts = 0;
+ return SNMP_ERR_NOERROR;
+ }
+ break;
+ case 14: /* error counters */
+ /* clear counters */
+ if(myOid2 == 5) {
+ snmpPtpClock->counters.messageRecvErrors = 0;
+ snmpPtpClock->counters.messageSendErrors = 0;
+ snmpPtpClock->counters.messageFormatErrors = 0;
+ snmpPtpClock->counters.protocolErrors = 0;
+ snmpPtpClock->counters.versionMismatchErrors = 0;
+ snmpPtpClock->counters.domainMismatchErrors = 0;
+ snmpPtpClock->counters.sequenceMismatchErrors = 0;
+ snmpPtpClock->counters.delayMechanismMismatchErrors = 0;
+ return SNMP_ERR_NOERROR;
+ }
+ break;
+ case 15: /* unicast negotiation counters */
+ /* clear counters */
+ if(myOid2 == 5) {
+ snmpPtpClock->counters.unicastGrantsRequested = 0;
+ snmpPtpClock->counters.unicastGrantsGranted = 0;
+ snmpPtpClock->counters.unicastGrantsDenied = 0;
+ snmpPtpClock->counters.unicastGrantsCancelSent = 0;
+ snmpPtpClock->counters.unicastGrantsCancelReceived = 0;
+ snmpPtpClock->counters.unicastGrantsCancelAckSent = 0;
+ snmpPtpClock->counters.unicastGrantsCancelAckReceived = 0;
+ return SNMP_ERR_NOERROR;
+ }
+ break;
+ case 17: /* security counters */
+ /* clear counters */
+ if(myOid2 == 5) {
+ snmpPtpClock->counters.aclTimingMessagesDiscarded = 0;
+ snmpPtpClock->counters.aclManagementMessagesDiscarded = 0;
+ return SNMP_ERR_NOERROR;
+ }
+ break;
+ case 21: /* ptpd counters */
+ /* clear counters */
+ if(myOid2 == 5) {
+ snmpPtpClock->counters.consecutiveSequenceErrors = 0;
+ snmpPtpClock->counters.ignoredAnnounce = 0;
+#ifdef PTPD_STATISTICS
+ snmpPtpClock->counters.delayMSOutliersFound = 0;
+ snmpPtpClock->counters.delaySMOutliersFound = 0;
+#endif
+ snmpPtpClock->counters.maxDelayDrops = 0;
+ return SNMP_ERR_NOERROR;
+ }
+ break;
+ default:
+ return SNMP_ERR_WRONGVALUE;
+ }
+ return SNMP_ERR_WRONGVALUE;
+ }
+
+ return SNMP_ERR_WRONGVALUE;
+
+ }
+
+ return SNMP_ERR_NOERROR;
+
+}
+
+/**
+ * Handle ptpbasePtpPortMessageCounters
+ */
+static u_char*
+snmpPtpPortMessageCountersTable(SNMP_SIGNATURE) {
+ oid index[4];
+ SNMP_LOCAL_VARIABLES;
+ SNMP_INDEXED_TABLE;
+
+ /* We only have one valid index */
+ index[0] = snmpPtpClock->defaultDS.domainNumber;
+ index[1] = SNMP_PTP_ORDINARY_CLOCK;
+ index[2] = SNMP_PTP_CLOCK_INSTANCE;
+ index[3] = snmpPtpClock->portDS.portIdentity.portNumber;
+ SNMP_ADD_INDEX(index, 4, snmpPtpClock);
+
+ if (!SNMP_BEST_MATCH) return NULL;
+
+ switch (vp->magic) {
+ case PTPBASE_PORT_MESSAGE_COUNTERS_CLEAR:
+ *write_method = snmpWriteClearCounters;
+ return SNMP_FALSE;
+ case PTPBASE_PORT_MESSAGE_COUNTERS_CLEAR_ALL:
+ *write_method = snmpWriteClearCounters;
+ return SNMP_FALSE;
+ case PTPBASE_PORT_MESSAGE_COUNTERS_TOTAL_SENT:
+ return SNMP_INTEGER(snmpPtpClock->netPath.sentPacketsTotal);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_TOTAL_RECEIVED:
+ return SNMP_INTEGER(snmpPtpClock->netPath.receivedPacketsTotal);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_ANNOUNCE_SENT:
+ return SNMP_INTEGER(snmpPtpClock->counters.announceMessagesSent);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_ANNOUNCE_RECEIVED:
+ return SNMP_INTEGER(snmpPtpClock->counters.announceMessagesReceived);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_SYNC_SENT:
+ return SNMP_INTEGER(snmpPtpClock->counters.syncMessagesSent);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_SYNC_RECEIVED:
+ return SNMP_INTEGER(snmpPtpClock->counters.syncMessagesReceived);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_FOLLOWUP_SENT:
+ return SNMP_INTEGER(snmpPtpClock->counters.followUpMessagesSent);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_FOLLOWUP_RECEIVED:
+ return SNMP_INTEGER(snmpPtpClock->counters.followUpMessagesReceived);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_DELAYREQ_SENT:
+ return SNMP_INTEGER(snmpPtpClock->counters.delayReqMessagesSent);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_DELAYREQ_RECEIVED:
+ return SNMP_INTEGER(snmpPtpClock->counters.delayReqMessagesReceived);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_DELAYRESP_SENT:
+ return SNMP_INTEGER(snmpPtpClock->counters.delayRespMessagesSent);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_DELAYRESP_RECEIVED:
+ return SNMP_INTEGER(snmpPtpClock->counters.delayRespMessagesReceived);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_PDELAYREQ_SENT:
+ return SNMP_INTEGER(snmpPtpClock->counters.pdelayReqMessagesSent);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_PDELAYREQ_RECEIVED:
+ return SNMP_INTEGER(snmpPtpClock->counters.pdelayReqMessagesReceived);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_PDELAYRESP_SENT:
+ return SNMP_INTEGER(snmpPtpClock->counters.pdelayRespMessagesSent);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_PDELAYRESP_RECEIVED:
+ return SNMP_INTEGER(snmpPtpClock->counters.pdelayRespMessagesReceived);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_PDELAYRESP_FOLLOWUP_SENT:
+ return SNMP_INTEGER(snmpPtpClock->counters.pdelayRespFollowUpMessagesSent);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_PDELAYRESP_FOLLOWUP_RECEIVED:
+ return SNMP_INTEGER(snmpPtpClock->counters.pdelayRespFollowUpMessagesReceived);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_SIGNALING_SENT:
+ return SNMP_INTEGER(snmpPtpClock->counters.signalingMessagesSent);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_SIGNALING_RECEIVED:
+ return SNMP_INTEGER(snmpPtpClock->counters.signalingMessagesReceived);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_MANAGEMENT_SENT:
+ return SNMP_INTEGER(snmpPtpClock->counters.managementMessagesSent);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_MANAGEMENT_RECEIVED:
+ return SNMP_INTEGER(snmpPtpClock->counters.managementMessagesReceived);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_DISCARDED_MESSAGES:
+ return SNMP_INTEGER(snmpPtpClock->counters.discardedMessages);
+ case PTPBASE_PORT_MESSAGE_COUNTERS_UNKNOWN_MESSAGES:
+ return SNMP_INTEGER(snmpPtpClock->counters.unknownMessages);
+
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Handle ptpbasePtpPortProtocolCounters
+ */
+static u_char*
+snmpPtpPortProtocolCountersTable(SNMP_SIGNATURE) {
+ oid index[4];
+ SNMP_LOCAL_VARIABLES;
+ SNMP_INDEXED_TABLE;
+
+ /* We only have one valid index */
+ index[0] = snmpPtpClock->defaultDS.domainNumber;
+ index[1] = SNMP_PTP_ORDINARY_CLOCK;
+ index[2] = SNMP_PTP_CLOCK_INSTANCE;
+ index[3] = snmpPtpClock->portDS.portIdentity.portNumber;
+ SNMP_ADD_INDEX(index, 4, snmpPtpClock);
+
+ if (!SNMP_BEST_MATCH) return NULL;
+
+ switch (vp->magic) {
+ case PTPBASE_PORT_PROTOCOL_COUNTERS_CLEAR:
+ *write_method = snmpWriteClearCounters;
+ return SNMP_FALSE;
+ case PTPBASE_PORT_PROTOCOL_COUNTERS_FOREIGN_ADDED:
+ return SNMP_INTEGER(snmpPtpClock->counters.foreignAdded);
+ case PTPBASE_PORT_PROTOCOL_COUNTERS_FOREIGN_COUNT:
+ return SNMP_INTEGER(snmpPtpClock->counters.foreignCount);
+ case PTPBASE_PORT_PROTOCOL_COUNTERS_FOREIGN_REMOVED:
+ return SNMP_INTEGER(snmpPtpClock->counters.foreignRemoved);
+ case PTPBASE_PORT_PROTOCOL_COUNTERS_FOREIGN_OVERFLOWS:
+ return SNMP_INTEGER(snmpPtpClock->counters.foreignOverflows);
+ case PTPBASE_PORT_PROTOCOL_COUNTERS_STATE_TRANSITIONS:
+ return SNMP_INTEGER(snmpPtpClock->counters.stateTransitions);
+ case PTPBASE_PORT_PROTOCOL_COUNTERS_BEST_MASTER_CHANGES:
+ return SNMP_INTEGER(snmpPtpClock->counters.bestMasterChanges);
+ case PTPBASE_PORT_PROTOCOL_COUNTERS_ANNOUNCE_TIMEOUTS:
+ return SNMP_INTEGER(snmpPtpClock->counters.announceTimeouts);
+ }
+
+ return NULL;
+}
+
+/**
+ * Handle ptpbasePtpPortErrorCounters
+ */
+static u_char*
+snmpPtpPortErrorCountersTable(SNMP_SIGNATURE) {
+ oid index[4];
+ SNMP_LOCAL_VARIABLES;
+ SNMP_INDEXED_TABLE;
+
+ /* We only have one valid index */
+ index[0] = snmpPtpClock->defaultDS.domainNumber;
+ index[1] = SNMP_PTP_ORDINARY_CLOCK;
+ index[2] = SNMP_PTP_CLOCK_INSTANCE;
+ index[3] = snmpPtpClock->portDS.portIdentity.portNumber;
+ SNMP_ADD_INDEX(index, 4, snmpPtpClock);
+
+ if (!SNMP_BEST_MATCH) return NULL;
+
+ switch (vp->magic) {
+ case PTPBASE_PORT_ERROR_COUNTERS_CLEAR:
+ *write_method = snmpWriteClearCounters;
+ return SNMP_FALSE;
+ case PTPBASE_PORT_ERROR_COUNTERS_MESSAGE_RECV:
+ return SNMP_INTEGER(snmpPtpClock->counters.messageRecvErrors);
+ case PTPBASE_PORT_ERROR_COUNTERS_MESSAGE_SEND:
+ return SNMP_INTEGER(snmpPtpClock->counters.messageSendErrors);
+ case PTPBASE_PORT_ERROR_COUNTERS_MESSAGE_FORMAT:
+ return SNMP_INTEGER(snmpPtpClock->counters.messageFormatErrors);
+ case PTPBASE_PORT_ERROR_COUNTERS_PROTOCOL:
+ return SNMP_INTEGER(snmpPtpClock->counters.protocolErrors);
+ case PTPBASE_PORT_ERROR_COUNTERS_VERSION_MISMATCH:
+ return SNMP_INTEGER(snmpPtpClock->counters.versionMismatchErrors);
+ case PTPBASE_PORT_ERROR_COUNTERS_DOMAIN_MISMATCH:
+ return SNMP_INTEGER(snmpPtpClock->counters.domainMismatchErrors);
+ case PTPBASE_PORT_ERROR_COUNTERS_SEQUENCE_MISMATCH:
+ return SNMP_INTEGER(snmpPtpClock->counters.sequenceMismatchErrors);
+ case PTPBASE_PORT_ERROR_COUNTERS_DELAYMECH_MISMATCH:
+ return SNMP_INTEGER(snmpPtpClock->counters.delayMechanismMismatchErrors);
+ }
+
+ return NULL;
+}
+
+/**
+ * Handle ptpbasePtpPortUnicastNegotiationCounters
+ */
+static u_char*
+snmpPtpPortUnicastNegotiationCountersTable(SNMP_SIGNATURE) {
+ oid index[4];
+ SNMP_LOCAL_VARIABLES;
+ SNMP_INDEXED_TABLE;
+
+ /* We only have one valid index */
+ index[0] = snmpPtpClock->defaultDS.domainNumber;
+ index[1] = SNMP_PTP_ORDINARY_CLOCK;
+ index[2] = SNMP_PTP_CLOCK_INSTANCE;
+ index[3] = snmpPtpClock->portDS.portIdentity.portNumber;
+ SNMP_ADD_INDEX(index, 4, snmpPtpClock);
+
+ if (!SNMP_BEST_MATCH) return NULL;
+
+ switch (vp->magic) {
+ case PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_CLEAR:
+ *write_method = snmpWriteClearCounters;
+ return SNMP_FALSE;
+ case PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_REQUESTED:
+ return SNMP_INTEGER(snmpPtpClock->counters.unicastGrantsRequested);
+ case PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_GRANTED:
+ return SNMP_INTEGER(snmpPtpClock->counters.unicastGrantsGranted);
+ case PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_DENIED:
+ return SNMP_INTEGER(snmpPtpClock->counters.unicastGrantsDenied);
+ case PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_CANCEL_SENT:
+ return SNMP_INTEGER(snmpPtpClock->counters.unicastGrantsCancelSent);
+ case PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_CANCEL_RECEIVED:
+ return SNMP_INTEGER(snmpPtpClock->counters.unicastGrantsCancelReceived);
+ case PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_CANCEL_ACK_SENT:
+ return SNMP_INTEGER(snmpPtpClock->counters.unicastGrantsCancelAckSent);
+ case PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_CANCEL_ACK_RECEIVED:
+ return SNMP_INTEGER(snmpPtpClock->counters.unicastGrantsCancelAckReceived);
+ }
+
+ return NULL;
+}
+
+/**
+ * Handle ptpbasePtpPortPerformanceCounters
+ */
+static u_char*
+snmpPtpPortPerformanceCountersTable(SNMP_SIGNATURE) {
+ oid index[4];
+ SNMP_LOCAL_VARIABLES;
+ SNMP_INDEXED_TABLE;
+
+ /* We only have one valid index */
+ index[0] = snmpPtpClock->defaultDS.domainNumber;
+ index[1] = SNMP_PTP_ORDINARY_CLOCK;
+ index[2] = SNMP_PTP_CLOCK_INSTANCE;
+ index[3] = snmpPtpClock->portDS.portIdentity.portNumber;
+ SNMP_ADD_INDEX(index, 4, snmpPtpClock);
+
+ if (!SNMP_BEST_MATCH) return NULL;
+
+ switch (vp->magic) {
+ case PTPBASE_PORT_PERFORMANCE_COUNTERS_MESSAGE_SEND_RATE:
+ return SNMP_INTEGER(snmpPtpClock->counters.messageSendRate);
+ case PTPBASE_PORT_PERFORMANCE_COUNTERS_MESSAGE_RECEIVE_RATE:
+ return SNMP_INTEGER(snmpPtpClock->counters.messageReceiveRate);
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Handle ptpbasePtpPortSecurityCounters
+ */
+static u_char*
+snmpPtpPortSecurityCountersTable(SNMP_SIGNATURE) {
+ oid index[4];
+ SNMP_LOCAL_VARIABLES;
+ SNMP_INDEXED_TABLE;
+
+ /* We only have one valid index */
+ index[0] = snmpPtpClock->defaultDS.domainNumber;
+ index[1] = SNMP_PTP_ORDINARY_CLOCK;
+ index[2] = SNMP_PTP_CLOCK_INSTANCE;
+ index[3] = snmpPtpClock->portDS.portIdentity.portNumber;
+ SNMP_ADD_INDEX(index, 4, snmpPtpClock);
+
+ if (!SNMP_BEST_MATCH) return NULL;
+
+ switch (vp->magic) {
+ case PTPBASE_PORT_SECURITY_COUNTERS_CLEAR:
+ *write_method = snmpWriteClearCounters;
+ return SNMP_FALSE;
+ case PTPBASE_PORT_SECURITY_COUNTERS_TIMING_ACL_DISCARDED:
+ return SNMP_INTEGER(snmpPtpClock->counters.aclTimingMessagesDiscarded);
+ case PTPBASE_PORT_SECURITY_COUNTERS_MANAGEMENT_ACL_DISCARDED:
+ return SNMP_INTEGER(snmpPtpClock->counters.aclManagementMessagesDiscarded);
+ }
+
+ return NULL;
+}
+
+/**
+ * Handle slaveOfmStatisticsTable
+ */
+static u_char*
+snmpSlaveOfmStatsTable(SNMP_SIGNATURE) {
+ oid index[3];
+ SNMP_LOCAL_VARIABLES;
+ SNMP_INDEXED_TABLE;
+
+ memset(tmpStr, 0, sizeof(tmpStr));
+
+ /* We only have one valid index */
+ index[0] = snmpPtpClock->defaultDS.domainNumber;
+ index[1] = SNMP_PTP_ORDINARY_CLOCK;
+ index[2] = SNMP_PTP_CLOCK_INSTANCE;
+ SNMP_ADD_INDEX(index, 3, snmpPtpClock);
+
+ if (!SNMP_BEST_MATCH) return NULL;
+
+ switch (vp->magic) {
+ case PTPBASE_SLAVE_OFM_STATS_CURRENT_VALUE:
+ return SNMP_TIMEINTERNAL(snmpPtpClock->currentDS.offsetFromMaster);
+ case PTPBASE_SLAVE_OFM_STATS_CURRENT_VALUE_STRING:
+ snprintf(tmpStr, 64, "%.09f", timeInternalToDouble(&snmpPtpClock->currentDS.offsetFromMaster));
+ return SNMP_OCTETSTR(&tmpStr, strlen(tmpStr));
+#ifdef PTPD_STATISTICS
+ case PTPBASE_SLAVE_OFM_STATS_PERIOD_SECONDS:
+ return SNMP_INTEGER(snmpRtOpts->statsUpdateInterval);
+ case PTPBASE_SLAVE_OFM_STATS_VALID:
+ return SNMP_BOOLEAN(snmpPtpClock->slaveStats.statsCalculated);
+ case PTPBASE_SLAVE_OFM_STATS_MIN:
+ return SNMP_TIMEINTERNAL(doubleToTimeInternal(snmpPtpClock->slaveStats.ofmMinFinal));
+ case PTPBASE_SLAVE_OFM_STATS_MAX:
+ return SNMP_TIMEINTERNAL(doubleToTimeInternal(snmpPtpClock->slaveStats.ofmMaxFinal));
+ case PTPBASE_SLAVE_OFM_STATS_MEAN:
+ return SNMP_TIMEINTERNAL(doubleToTimeInternal(snmpPtpClock->slaveStats.ofmMean));
+ case PTPBASE_SLAVE_OFM_STATS_STDDEV:
+ return SNMP_TIMEINTERNAL(doubleToTimeInternal(snmpPtpClock->slaveStats.ofmStdDev));
+ case PTPBASE_SLAVE_OFM_STATS_MEDIAN:
+ return SNMP_TIMEINTERNAL(doubleToTimeInternal(snmpPtpClock->slaveStats.ofmMedian));
+ case PTPBASE_SLAVE_OFM_STATS_MIN_STRING:
+ snprintf(tmpStr, 64, "%.09f", snmpPtpClock->slaveStats.ofmMinFinal);
+ return SNMP_OCTETSTR(&tmpStr, strlen(tmpStr));
+ case PTPBASE_SLAVE_OFM_STATS_MAX_STRING:
+ snprintf(tmpStr, 64, "%.09f", snmpPtpClock->slaveStats.ofmMaxFinal);
+ return SNMP_OCTETSTR(&tmpStr, strlen(tmpStr));
+ case PTPBASE_SLAVE_OFM_STATS_MEAN_STRING:
+ snprintf(tmpStr, 64, "%.09f", snmpPtpClock->slaveStats.ofmMean);
+ return SNMP_OCTETSTR(&tmpStr, strlen(tmpStr));
+ case PTPBASE_SLAVE_OFM_STATS_STDDEV_STRING:
+ snprintf(tmpStr, 64, "%.09f", snmpPtpClock->slaveStats.ofmStdDev);
+ return SNMP_OCTETSTR(&tmpStr, strlen(tmpStr));
+ case PTPBASE_SLAVE_OFM_STATS_MEDIAN_STRING:
+ snprintf(tmpStr, 64, "%.09f", snmpPtpClock->slaveStats.ofmMedian);
+ return SNMP_OCTETSTR(&tmpStr, strlen(tmpStr));
+#endif
+ }
+
+ return NULL;
+}
+
+/**
+ * Handle slaveMpdStatisticsTable
+ */
+static u_char*
+snmpSlaveMpdStatsTable(SNMP_SIGNATURE) {
+ oid index[3];
+ SNMP_LOCAL_VARIABLES;
+ SNMP_INDEXED_TABLE;
+
+ memset(tmpStr, 0, sizeof(tmpStr));
+
+ /* We only have one valid index */
+ index[0] = snmpPtpClock->defaultDS.domainNumber;
+ index[1] = SNMP_PTP_ORDINARY_CLOCK;
+ index[2] = SNMP_PTP_CLOCK_INSTANCE;
+ SNMP_ADD_INDEX(index, 3, snmpPtpClock);
+
+ if (!SNMP_BEST_MATCH) return NULL;
+
+ switch (vp->magic) {
+ case PTPBASE_SLAVE_MPD_STATS_CURRENT_VALUE:
+ return SNMP_TIMEINTERNAL(snmpPtpClock->currentDS.meanPathDelay);
+ case PTPBASE_SLAVE_MPD_STATS_CURRENT_VALUE_STRING:
+ snprintf(tmpStr, 64, "%.09f", timeInternalToDouble(&snmpPtpClock->currentDS.meanPathDelay));
+ return SNMP_OCTETSTR(&tmpStr, strlen(tmpStr));
+#ifdef PTPD_STATISTICS
+ case PTPBASE_SLAVE_MPD_STATS_PERIOD_SECONDS:
+ return SNMP_INTEGER(snmpRtOpts->statsUpdateInterval);
+ case PTPBASE_SLAVE_MPD_STATS_VALID:
+ return SNMP_BOOLEAN(snmpPtpClock->slaveStats.statsCalculated);
+ case PTPBASE_SLAVE_MPD_STATS_MIN:
+ return SNMP_TIMEINTERNAL(doubleToTimeInternal(snmpPtpClock->slaveStats.mpdMinFinal));
+ case PTPBASE_SLAVE_MPD_STATS_MAX:
+ return SNMP_TIMEINTERNAL(doubleToTimeInternal(snmpPtpClock->slaveStats.mpdMaxFinal));
+ case PTPBASE_SLAVE_MPD_STATS_MEAN:
+ return SNMP_TIMEINTERNAL(doubleToTimeInternal(snmpPtpClock->slaveStats.mpdMean));
+ case PTPBASE_SLAVE_MPD_STATS_STDDEV:
+ return SNMP_TIMEINTERNAL(doubleToTimeInternal(snmpPtpClock->slaveStats.mpdStdDev));
+ case PTPBASE_SLAVE_MPD_STATS_MEDIAN:
+ return SNMP_TIMEINTERNAL(doubleToTimeInternal(snmpPtpClock->slaveStats.mpdMedian));
+ case PTPBASE_SLAVE_MPD_STATS_MIN_STRING:
+ snprintf(tmpStr, 64, "%.09f", snmpPtpClock->slaveStats.mpdMinFinal);
+ return SNMP_OCTETSTR(&tmpStr, strlen(tmpStr));
+ case PTPBASE_SLAVE_MPD_STATS_MAX_STRING:
+ snprintf(tmpStr, 64, "%.09f", snmpPtpClock->slaveStats.mpdMaxFinal);
+ return SNMP_OCTETSTR(&tmpStr, strlen(tmpStr));
+ case PTPBASE_SLAVE_MPD_STATS_MEAN_STRING:
+ snprintf(tmpStr, 64, "%.09f", snmpPtpClock->slaveStats.mpdMean);
+ return SNMP_OCTETSTR(&tmpStr, strlen(tmpStr));
+ case PTPBASE_SLAVE_MPD_STATS_STDDEV_STRING:
+ snprintf(tmpStr, 64, "%.09f", snmpPtpClock->slaveStats.mpdStdDev);
+ return SNMP_OCTETSTR(&tmpStr, strlen(tmpStr));
+ case PTPBASE_SLAVE_MPD_STATS_MEDIAN_STRING:
+ snprintf(tmpStr, 64, "%.09f", snmpPtpClock->slaveStats.mpdMedian);
+ return SNMP_OCTETSTR(&tmpStr, strlen(tmpStr));
+#endif
+ }
+
+ return NULL;
+}
+
+/**
+ * Handle slaveFreqAdjStatisticsTable
+ */
+static u_char*
+snmpSlaveFreqAdjStatsTable(SNMP_SIGNATURE) {
+ oid index[3];
+ SNMP_LOCAL_VARIABLES;
+ SNMP_INDEXED_TABLE;
+
+ memset(tmpStr, 0, sizeof(tmpStr));
+
+ /* We only have one valid index */
+ index[0] = snmpPtpClock->defaultDS.domainNumber;
+ index[1] = SNMP_PTP_ORDINARY_CLOCK;
+ index[2] = SNMP_PTP_CLOCK_INSTANCE;
+ SNMP_ADD_INDEX(index, 3, snmpPtpClock);
+
+ if (!SNMP_BEST_MATCH) return NULL;
+
+ switch (vp->magic) {
+ case PTPBASE_SLAVE_FREQADJ_STATS_CURRENT_VALUE:
+ return SNMP_INTEGER(snmpPtpClock->servo.observedDrift);
+#ifdef PTPD_STATISTICS
+ case PTPBASE_SLAVE_FREQADJ_STATS_PERIOD_SECONDS:
+ return SNMP_INTEGER(snmpRtOpts->statsUpdateInterval);
+ case PTPBASE_SLAVE_FREQADJ_STATS_VALID:
+ return SNMP_BOOLEAN(snmpPtpClock->servo.statsCalculated);
+ case PTPBASE_SLAVE_FREQADJ_STATS_MIN:
+ return SNMP_INTEGER(snmpPtpClock->servo.driftMinFinal);
+ case PTPBASE_SLAVE_FREQADJ_STATS_MAX:
+ return SNMP_INTEGER(snmpPtpClock->servo.driftMaxFinal);
+ case PTPBASE_SLAVE_FREQADJ_STATS_MEAN:
+ return SNMP_INTEGER(snmpPtpClock->servo.driftMean);
+ case PTPBASE_SLAVE_FREQADJ_STATS_STDDEV:
+ return SNMP_INTEGER(snmpPtpClock->servo.driftStdDev);
+ case PTPBASE_SLAVE_FREQADJ_STATS_MEDIAN:
+ return SNMP_INTEGER(snmpPtpClock->servo.driftMedian);
+#endif
+ }
+
+ return NULL;
+}
+
+
+
+/**
+ * Handle ptpbasePtpdSpecificCounters
+ */
+static u_char*
+snmpPtpdSpecificCountersTable(SNMP_SIGNATURE) {
+ oid index[4];
+ SNMP_LOCAL_VARIABLES;
+ SNMP_INDEXED_TABLE;
+
+ /* We only have one valid index */
+ index[0] = snmpPtpClock->defaultDS.domainNumber;
+ index[1] = SNMP_PTP_ORDINARY_CLOCK;
+ index[2] = SNMP_PTP_CLOCK_INSTANCE;
+ index[3] = snmpPtpClock->portDS.portIdentity.portNumber;
+ SNMP_ADD_INDEX(index, 4, snmpPtpClock);
+
+ if (!SNMP_BEST_MATCH) return NULL;
+
+ switch (vp->magic) {
+ case PTPBASE_PTPD_SPECIFIC_COUNTERS_CLEAR:
+ *write_method = snmpWriteClearCounters;
+ return SNMP_FALSE;
+ case PTPBASE_PTPD_SPECIFIC_COUNTERS_IGNORED_ANNOUNCE:
+ return SNMP_INTEGER(snmpPtpClock->counters.ignoredAnnounce);
+ case PTPBASE_PTPD_SPECIFIC_COUNTERS_CONSECUTIVE_SEQUENCE_ERRORS:
+ return SNMP_INTEGER(snmpPtpClock->counters.consecutiveSequenceErrors);
+#ifdef PTPD_STATISTICS
+ case PTPBASE_PTPD_SPECIFIC_COUNTERS_DELAYMS_OUTLIERS_FOUND:
+ return SNMP_INTEGER(snmpPtpClock->counters.delayMSOutliersFound);
+ case PTPBASE_PTPD_SPECIFIC_COUNTERS_DELAYSM_OUTLIERS_FOUND:
+ return SNMP_INTEGER(snmpPtpClock->counters.delaySMOutliersFound);
+#endif
+ case PTPBASE_PTPD_SPECIFIC_COUNTERS_MAX_DELAY_DROPS:
+ return SNMP_INTEGER(snmpPtpClock->counters.maxDelayDrops);
+ }
+
+ return NULL;
+}
+
+/**
+ * Handle ptpBasePtpdSpecificData
+ */
+static u_char*
+snmpPtpdSpecificDataTable(SNMP_SIGNATURE) {
+ oid index[3];
+ SNMP_LOCAL_VARIABLES;
+ SNMP_INDEXED_TABLE;
+
+ memset(tmpStr, 0, sizeof(tmpStr));
+
+ /* We only have one valid index */
+ index[0] = snmpPtpClock->defaultDS.domainNumber;
+ index[1] = SNMP_PTP_ORDINARY_CLOCK;
+ index[2] = SNMP_PTP_CLOCK_INSTANCE;
+ SNMP_ADD_INDEX(index, 3, snmpPtpClock);
+
+ if (!SNMP_BEST_MATCH) return NULL;
+
+#ifdef PTPD_STATISTICS
+ switch (vp->magic) {
+ case PTPBASE_PTPD_SPECIFIC_DATA_RAW_DELAYMS:
+ return SNMP_TIMEINTERNAL(snmpPtpClock->rawDelayMS);
+ case PTPBASE_PTPD_SPECIFIC_DATA_RAW_DELAYMS_STRING:
+ snprintf(tmpStr, 64, "%.09f", timeInternalToDouble(&snmpPtpClock->rawDelayMS));
+ return SNMP_OCTETSTR(&tmpStr, strlen(tmpStr));
+ case PTPBASE_PTPD_SPECIFIC_DATA_RAW_DELAYSM:
+ return SNMP_TIMEINTERNAL(snmpPtpClock->rawDelaySM);
+ case PTPBASE_PTPD_SPECIFIC_DATA_RAW_DELAYSM_STRING:
+ snprintf(tmpStr, 64, "%.09f", timeInternalToDouble(&snmpPtpClock->rawDelaySM));
+ return SNMP_OCTETSTR(&tmpStr, strlen(tmpStr));
+ }
+#endif
+
+ return NULL;
+}
+
+
+
+/**
+ * MIB definition
+ */
+static struct variable7 snmpVariables[] = {
+ /* ptpbaseSystemTable */
+ { PTPBASE_DOMAIN_CLOCK_PORTS_TOTAL, ASN_GAUGE, HANDLER_CAN_RONLY,
+ snmpSystemTable, 5, {1, 1, 1, 1, 3}},
+ /* ptpbaseSystemDomainTable */
+ { PTPBASE_SYSTEM_DOMAIN_TOTALS, ASN_UNSIGNED, HANDLER_CAN_RONLY,
+ snmpSystemDomainTable, 5, {1, 1, 2, 1, 2}},
+ /* Scalars */
+ { PTPBASE_SYSTEM_PROFILE, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpScalars, 3, {1, 1, 3}},
+ /* ptpbaseClockCurrentDSTable */
+ { PTPBASE_CLOCK_CURRENT_DS_STEPS_REMOVED, ASN_UNSIGNED, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 1, 1, 4}},
+ { PTPBASE_CLOCK_CURRENT_DS_OFFSET_FROM_MASTER, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 1, 1, 5}},
+ { PTPBASE_CLOCK_CURRENT_DS_MEAN_PATH_DELAY, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 1, 1, 6}},
+ /* PTPd enhancements */
+ { PTPBASE_CLOCK_CURRENT_DS_OFFSET_FROM_MASTER_STRING, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 1, 1, 7}},
+ { PTPBASE_CLOCK_CURRENT_DS_MEAN_PATH_DELAY_STRING, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 1, 1, 8}},
+ { PTPBASE_CLOCK_CURRENT_DS_OFFSET_FROM_MASTER_THRESHOLD, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 1, 1, 9}},
+
+ /* ptpbaseClockParentDSTable */
+ { PTPBASE_CLOCK_PARENT_DS_PARENT_PORT_ID, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 2, 1, 4}},
+ { PTPBASE_CLOCK_PARENT_DS_PARENT_STATS, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 2, 1, 5}},
+ { PTPBASE_CLOCK_PARENT_DS_OFFSET, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 2, 1, 6}},
+ { PTPBASE_CLOCK_PARENT_DS_CLOCK_PH_CH_RATE, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 2, 1, 7}},
+ { PTPBASE_CLOCK_PARENT_DS_GM_CLOCK_IDENTITY, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 2, 1, 8}},
+ { PTPBASE_CLOCK_PARENT_DS_GM_CLOCK_PRIO1, ASN_UNSIGNED, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 2, 1, 9}},
+ { PTPBASE_CLOCK_PARENT_DS_GM_CLOCK_PRIO2, ASN_UNSIGNED, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 2, 1, 10}},
+ { PTPBASE_CLOCK_PARENT_DS_GM_CLOCK_QUALITY_CLASS, ASN_UNSIGNED, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 2, 1, 11}},
+ { PTPBASE_CLOCK_PARENT_DS_GM_CLOCK_QUALITY_ACCURACY, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 2, 1, 12}},
+ { PTPBASE_CLOCK_PARENT_DS_GM_CLOCK_QUALITY_OFFSET, ASN_UNSIGNED, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 2, 1, 13}},
+
+ /* PTPd enhancements */
+ { PTPBASE_CLOCK_PARENT_DS_PARENT_PORT_ADDRESS_TYPE, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 2, 1, 14}},
+ { PTPBASE_CLOCK_PARENT_DS_PARENT_PORT_ADDRESS, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 2, 1, 15}},
+
+ /* ptpbaseClockDefaultDSTable */
+ { PTPBASE_CLOCK_DEFAULT_DS_TWO_STEP_FLAG, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 3, 1, 4}},
+ { PTPBASE_CLOCK_DEFAULT_DS_CLOCK_IDENTITY, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 3, 1, 5}},
+ { PTPBASE_CLOCK_DEFAULT_DS_PRIO1, ASN_UNSIGNED, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 3, 1, 6}},
+ { PTPBASE_CLOCK_DEFAULT_DS_PRIO2, ASN_UNSIGNED, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 3, 1, 7}},
+ { PTPBASE_CLOCK_DEFAULT_DS_SLAVE_ONLY, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 3, 1, 8}},
+ { PTPBASE_CLOCK_DEFAULT_DS_QUALITY_CLASS, ASN_UNSIGNED, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 3, 1, 9}},
+ { PTPBASE_CLOCK_DEFAULT_DS_QUALITY_ACCURACY, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 3, 1, 10}},
+ { PTPBASE_CLOCK_DEFAULT_DS_QUALITY_OFFSET, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 3, 1, 11}},
+ /* PTPd addition - this is part of the index, but if you use snmpgetnext, you guess the indexes */
+ { PTPBASE_CLOCK_DEFAULT_DS_DOMAIN_NUMBER, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 3, 1, 12}},
+ /* ptpbaseClockRunningTable: no data available for this table? */
+ /* ptpbaseClockTimePropertiesDSTable */
+ { PTPBASE_CLOCK_TIME_PROPERTIES_DS_CURRENT_UTC_OFFSET_VALID, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 5, 1, 4}},
+ { PTPBASE_CLOCK_TIME_PROPERTIES_DS_CURRENT_UTC_OFFSET, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 5, 1, 5}},
+ { PTPBASE_CLOCK_TIME_PROPERTIES_DS_LEAP59, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 5, 1, 6}},
+ { PTPBASE_CLOCK_TIME_PROPERTIES_DS_LEAP61, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 5, 1, 7}},
+ { PTPBASE_CLOCK_TIME_PROPERTIES_DS_TIME_TRACEABLE, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 5, 1, 8}},
+ { PTPBASE_CLOCK_TIME_PROPERTIES_DS_FREQ_TRACEABLE, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 5, 1, 9}},
+ { PTPBASE_CLOCK_TIME_PROPERTIES_DS_PTP_TIMESCALE, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 5, 1, 10}},
+ { PTPBASE_CLOCK_TIME_PROPERTIES_DS_SOURCE, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockDSTable, 5, {1, 2, 5, 1, 11}},
+ /* ptpbaseClockPortTable */
+ { PTPBASE_CLOCK_PORT_NAME, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 7, 1, 5}},
+ { PTPBASE_CLOCK_PORT_ROLE, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 7, 1, 6}},
+ { PTPBASE_CLOCK_PORT_SYNC_ONE_STEP, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 7, 1, 7}},
+ { PTPBASE_CLOCK_PORT_CURRENT_PEER_ADDRESS_TYPE, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 7, 1, 8}},
+ { PTPBASE_CLOCK_PORT_CURRENT_PEER_ADDRESS, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 7, 1, 9}},
+ { PTPBASE_CLOCK_PORT_NUM_ASSOCIATED_PORTS, ASN_GAUGE, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 7, 1, 10}},
+ /* ptpbaseClockPortDSTable */
+ { PTPBASE_CLOCK_PORT_DS_PORT_NAME, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 8, 1, 5}},
+ { PTPBASE_CLOCK_PORT_DS_PORT_IDENTITY, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 8, 1, 6}},
+ { PTPBASE_CLOCK_PORT_DS_ANNOUNCEMENT_INTERVAL, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 8, 1, 7}},
+ { PTPBASE_CLOCK_PORT_DS_ANNOUNCE_RCT_TIMEOUT, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 8, 1, 8}},
+ { PTPBASE_CLOCK_PORT_DS_SYNC_INTERVAL, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 8, 1, 9}},
+ { PTPBASE_CLOCK_PORT_DS_MIN_DELAY_REQ_INTERVAL, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 8, 1, 10}},
+ { PTPBASE_CLOCK_PORT_DS_PEER_DELAY_REQ_INTERVAL, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 8, 1, 11}},
+ { PTPBASE_CLOCK_PORT_DS_DELAY_MECH, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 8, 1, 12}},
+ { PTPBASE_CLOCK_PORT_DS_PEER_MEAN_PATH_DELAY, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 8, 1, 13}},
+ { PTPBASE_CLOCK_PORT_DS_GRANT_DURATION, ASN_UNSIGNED, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 8, 1, 14}},
+ { PTPBASE_CLOCK_PORT_DS_PTP_VERSION, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 8, 1, 15}},
+ { PTPBASE_CLOCK_PORT_DS_PEER_MEAN_PATH_DELAY_STRING, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 8, 1, 16}},
+ { PTPBASE_CLOCK_PORT_DS_LAST_MISMATCHED_DOMAIN, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 8, 1, 17}},
+ /* ptpbaseClockPortRunningTable */
+ { PTPBASE_CLOCK_PORT_RUNNING_NAME, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 9, 1, 5}},
+ { PTPBASE_CLOCK_PORT_RUNNING_STATE, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 9, 1, 6}},
+ { PTPBASE_CLOCK_PORT_RUNNING_ROLE, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 9, 1, 7}},
+ { PTPBASE_CLOCK_PORT_RUNNING_INTERFACE_INDEX, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 9, 1, 8}},
+ { PTPBASE_CLOCK_PORT_RUNNING_IPVERSION, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 9, 1, 9}},
+ { PTPBASE_CLOCK_PORT_RUNNING_ENCAPSULATION_TYPE, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 9, 1, 10}},
+ { PTPBASE_CLOCK_PORT_RUNNING_TX_MODE, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 9, 1, 11}},
+ { PTPBASE_CLOCK_PORT_RUNNING_RX_MODE, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 9, 1, 12}},
+ { PTPBASE_CLOCK_PORT_RUNNING_PACKETS_RECEIVED, ASN_COUNTER64, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 9, 1, 13}},
+ { PTPBASE_CLOCK_PORT_RUNNING_PACKETS_SENT, ASN_COUNTER64, HANDLER_CAN_RONLY,
+ snmpClockPortTable, 5, {1, 2, 9, 1, 14}},
+ /* ptpbasePtpPortMessageCounters */
+ { PTPBASE_PORT_MESSAGE_COUNTERS_CLEAR_ALL, ASN_INTEGER, HANDLER_CAN_RWRITE,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 5}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_CLEAR, ASN_INTEGER, HANDLER_CAN_RWRITE,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 6}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_TOTAL_SENT, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 7}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_TOTAL_RECEIVED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 8}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_ANNOUNCE_SENT, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 9}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_ANNOUNCE_RECEIVED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 10}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_SYNC_SENT, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 11}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_SYNC_RECEIVED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 12}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_FOLLOWUP_SENT, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 13}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_FOLLOWUP_RECEIVED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 14}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_DELAYREQ_SENT, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 15}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_DELAYREQ_RECEIVED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 16}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_DELAYRESP_SENT, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 17}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_DELAYRESP_RECEIVED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 18}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_PDELAYREQ_SENT, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 19}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_PDELAYREQ_RECEIVED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 20}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_PDELAYRESP_SENT, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 21}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_PDELAYRESP_RECEIVED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 22}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_PDELAYRESP_FOLLOWUP_SENT, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 23}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_PDELAYRESP_FOLLOWUP_RECEIVED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 24}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_SIGNALING_SENT, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 25}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_SIGNALING_RECEIVED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 26}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_MANAGEMENT_SENT, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 27}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_MANAGEMENT_RECEIVED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 28}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_DISCARDED_MESSAGES, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 29}},
+ { PTPBASE_PORT_MESSAGE_COUNTERS_UNKNOWN_MESSAGES, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortMessageCountersTable, 5, {1, 2, 12, 1, 30}},
+ /* ptpbasePtpPortProtocolCounters */
+ { PTPBASE_PORT_PROTOCOL_COUNTERS_CLEAR, ASN_INTEGER, HANDLER_CAN_RWRITE,
+ snmpPtpPortProtocolCountersTable, 5, {1, 2, 13, 1, 5}},
+ { PTPBASE_PORT_PROTOCOL_COUNTERS_FOREIGN_ADDED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortProtocolCountersTable, 5, {1, 2, 13, 1, 6}},
+ { PTPBASE_PORT_PROTOCOL_COUNTERS_FOREIGN_COUNT, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortProtocolCountersTable, 5, {1, 2, 13, 1, 7}},
+ { PTPBASE_PORT_PROTOCOL_COUNTERS_FOREIGN_REMOVED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortProtocolCountersTable, 5, {1, 2, 13, 1, 8}},
+ { PTPBASE_PORT_PROTOCOL_COUNTERS_FOREIGN_OVERFLOWS, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortProtocolCountersTable, 5, {1, 2, 13, 1, 9}},
+ { PTPBASE_PORT_PROTOCOL_COUNTERS_STATE_TRANSITIONS, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortProtocolCountersTable, 5, {1, 2, 13, 1, 10}},
+ { PTPBASE_PORT_PROTOCOL_COUNTERS_BEST_MASTER_CHANGES, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortProtocolCountersTable, 5, {1, 2, 13, 1, 11}},
+ { PTPBASE_PORT_PROTOCOL_COUNTERS_ANNOUNCE_TIMEOUTS, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortProtocolCountersTable, 5, {1, 2, 13, 1, 12}},
+ /* ptpbasePtpPortErrorCounters */
+ { PTPBASE_PORT_ERROR_COUNTERS_CLEAR, ASN_INTEGER, HANDLER_CAN_RWRITE,
+ snmpPtpPortErrorCountersTable, 5, {1, 2, 14, 1, 5}},
+ { PTPBASE_PORT_ERROR_COUNTERS_MESSAGE_RECV, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortErrorCountersTable, 5, {1, 2, 14, 1, 6}},
+ { PTPBASE_PORT_ERROR_COUNTERS_MESSAGE_SEND, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortErrorCountersTable, 5, {1, 2, 14, 1, 7}},
+ { PTPBASE_PORT_ERROR_COUNTERS_MESSAGE_FORMAT, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortErrorCountersTable, 5, {1, 2, 14, 1, 8}},
+ { PTPBASE_PORT_ERROR_COUNTERS_PROTOCOL, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortErrorCountersTable, 5, {1, 2, 14, 1, 9}},
+ { PTPBASE_PORT_ERROR_COUNTERS_VERSION_MISMATCH, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortErrorCountersTable, 5, {1, 2, 14, 1, 10}},
+ { PTPBASE_PORT_ERROR_COUNTERS_DOMAIN_MISMATCH, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortErrorCountersTable, 5, {1, 2, 14, 1, 11}},
+ { PTPBASE_PORT_ERROR_COUNTERS_SEQUENCE_MISMATCH, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortErrorCountersTable, 5, {1, 2, 14, 1, 12}},
+ { PTPBASE_PORT_ERROR_COUNTERS_DELAYMECH_MISMATCH, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortErrorCountersTable, 5, {1, 2, 14, 1, 13}},
+ /* ptpbasePtpPortUnicastNegotiationCounters */
+ { PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_CLEAR, ASN_INTEGER, HANDLER_CAN_RWRITE,
+ snmpPtpPortUnicastNegotiationCountersTable, 5, {1, 2, 15, 1, 5}},
+ { PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_REQUESTED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortUnicastNegotiationCountersTable, 5, {1, 2, 15, 1, 6}},
+ { PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_GRANTED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortUnicastNegotiationCountersTable, 5, {1, 2, 15, 1, 7}},
+ { PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_DENIED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortUnicastNegotiationCountersTable, 5, {1, 2, 15, 1, 8}},
+ { PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_CANCEL_SENT, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortUnicastNegotiationCountersTable, 5, {1, 2, 15, 1, 9}},
+ { PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_CANCEL_RECEIVED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortUnicastNegotiationCountersTable, 5, {1, 2, 15, 1, 10}},
+ { PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_CANCEL_ACK_SENT, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortUnicastNegotiationCountersTable, 5, {1, 2, 15, 1, 11}},
+ { PTPBASE_PORT_UNICAST_NEGOTIATION_COUNTERS_GRANTS_CANCEL_ACK_RECEIVED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortUnicastNegotiationCountersTable, 5, {1, 2, 15, 1, 12}},
+ /* ptpbasePtpPortPerformanceCounters - no clear here */
+ { PTPBASE_PORT_PERFORMANCE_COUNTERS_MESSAGE_SEND_RATE, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortPerformanceCountersTable, 5, {1, 2, 16, 1, 5}},
+ { PTPBASE_PORT_PERFORMANCE_COUNTERS_MESSAGE_RECEIVE_RATE, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortPerformanceCountersTable, 5, {1, 2, 16, 1, 6}},
+ /* ptpbasePtpPortSecurityCounters */
+ { PTPBASE_PORT_SECURITY_COUNTERS_CLEAR, ASN_INTEGER, HANDLER_CAN_RWRITE,
+ snmpPtpPortSecurityCountersTable, 5, {1, 2, 17, 1, 5}},
+ { PTPBASE_PORT_SECURITY_COUNTERS_TIMING_ACL_DISCARDED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortSecurityCountersTable, 5, {1, 2, 17, 1, 6}},
+ { PTPBASE_PORT_SECURITY_COUNTERS_MANAGEMENT_ACL_DISCARDED, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpPortSecurityCountersTable, 5, {1, 2, 17, 1, 7}},
+ /* ptpBaseSlaveOfmStatistics */
+ { PTPBASE_SLAVE_OFM_STATS_CURRENT_VALUE, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveOfmStatsTable, 5, {1, 2, 18, 1, 4}},
+ { PTPBASE_SLAVE_OFM_STATS_CURRENT_VALUE_STRING, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveOfmStatsTable, 5, {1, 2, 18, 1, 5}},
+ { PTPBASE_SLAVE_OFM_STATS_PERIOD_SECONDS, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpSlaveOfmStatsTable, 5, {1, 2, 18, 1, 6}},
+ { PTPBASE_SLAVE_OFM_STATS_VALID, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpSlaveOfmStatsTable, 5, {1, 2, 18, 1, 7}},
+ { PTPBASE_SLAVE_OFM_STATS_MIN, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveOfmStatsTable, 5, {1, 2, 18, 1, 8}},
+ { PTPBASE_SLAVE_OFM_STATS_MAX, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveOfmStatsTable, 5, {1, 2, 18, 1, 9}},
+ { PTPBASE_SLAVE_OFM_STATS_MEAN, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveOfmStatsTable, 5, {1, 2, 18, 1, 10}},
+ { PTPBASE_SLAVE_OFM_STATS_STDDEV, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveOfmStatsTable, 5, {1, 2, 18, 1, 11}},
+ { PTPBASE_SLAVE_OFM_STATS_MEDIAN, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveOfmStatsTable, 5, {1, 2, 18, 1, 12}},
+ { PTPBASE_SLAVE_OFM_STATS_MIN_STRING, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveOfmStatsTable, 5, {1, 2, 18, 1, 13}},
+ { PTPBASE_SLAVE_OFM_STATS_MAX_STRING, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveOfmStatsTable, 5, {1, 2, 18, 1, 14}},
+ { PTPBASE_SLAVE_OFM_STATS_MEAN_STRING, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveOfmStatsTable, 5, {1, 2, 18, 1, 15}},
+ { PTPBASE_SLAVE_OFM_STATS_STDDEV_STRING, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveOfmStatsTable, 5, {1, 2, 18, 1, 16}},
+ { PTPBASE_SLAVE_OFM_STATS_MEDIAN_STRING, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveOfmStatsTable, 5, {1, 2, 18, 1, 17}},
+ /* ptpBaseSlaveMpdStatistics */
+ { PTPBASE_SLAVE_MPD_STATS_CURRENT_VALUE, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveMpdStatsTable, 5, {1, 2, 19, 1, 4}},
+ { PTPBASE_SLAVE_MPD_STATS_CURRENT_VALUE_STRING, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveMpdStatsTable, 5, {1, 2, 19, 1, 5}},
+ { PTPBASE_SLAVE_MPD_STATS_PERIOD_SECONDS, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpSlaveMpdStatsTable, 5, {1, 2, 19, 1, 6}},
+ { PTPBASE_SLAVE_MPD_STATS_VALID, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpSlaveMpdStatsTable, 5, {1, 2, 19, 1, 7}},
+ { PTPBASE_SLAVE_MPD_STATS_MIN, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveMpdStatsTable, 5, {1, 2, 19, 1, 8}},
+ { PTPBASE_SLAVE_MPD_STATS_MAX, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveMpdStatsTable, 5, {1, 2, 19, 1, 9}},
+ { PTPBASE_SLAVE_MPD_STATS_MEAN, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveMpdStatsTable, 5, {1, 2, 19, 1, 10}},
+ { PTPBASE_SLAVE_MPD_STATS_STDDEV, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveMpdStatsTable, 5, {1, 2, 19, 1, 11}},
+ { PTPBASE_SLAVE_MPD_STATS_MEDIAN, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveMpdStatsTable, 5, {1, 2, 19, 1, 12}},
+ { PTPBASE_SLAVE_MPD_STATS_MIN_STRING, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveMpdStatsTable, 5, {1, 2, 19, 1, 13}},
+ { PTPBASE_SLAVE_MPD_STATS_MAX_STRING, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveMpdStatsTable, 5, {1, 2, 19, 1, 14}},
+ { PTPBASE_SLAVE_MPD_STATS_MEAN_STRING, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveMpdStatsTable, 5, {1, 2, 19, 1, 15}},
+ { PTPBASE_SLAVE_MPD_STATS_STDDEV_STRING, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveMpdStatsTable, 5, {1, 2, 19, 1, 16}},
+ { PTPBASE_SLAVE_MPD_STATS_MEDIAN_STRING, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpSlaveMpdStatsTable, 5, {1, 2, 19, 1, 17}},
+ /* ptpBaseSlaveFreqAdjStatistics */
+ { PTPBASE_SLAVE_FREQADJ_STATS_CURRENT_VALUE, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpSlaveFreqAdjStatsTable, 5, {1, 2, 20, 1, 4}},
+ { PTPBASE_SLAVE_FREQADJ_STATS_PERIOD_SECONDS, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpSlaveFreqAdjStatsTable, 5, {1, 2, 20, 1, 5}},
+ { PTPBASE_SLAVE_FREQADJ_STATS_VALID, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpSlaveFreqAdjStatsTable, 5, {1, 2, 20, 1, 6}},
+ { PTPBASE_SLAVE_FREQADJ_STATS_MIN, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpSlaveFreqAdjStatsTable, 5, {1, 2, 20, 1, 7}},
+ { PTPBASE_SLAVE_FREQADJ_STATS_MAX, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpSlaveFreqAdjStatsTable, 5, {1, 2, 20, 1, 8}},
+ { PTPBASE_SLAVE_FREQADJ_STATS_MEAN, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpSlaveFreqAdjStatsTable, 5, {1, 2, 20, 1, 9}},
+ { PTPBASE_SLAVE_FREQADJ_STATS_STDDEV, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpSlaveFreqAdjStatsTable, 5, {1, 2, 20, 1, 10}},
+ { PTPBASE_SLAVE_FREQADJ_STATS_MEDIAN, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpSlaveFreqAdjStatsTable, 5, {1, 2, 20, 1, 11}},
+ /* ptpbasePtpdSpecificCounters */
+ { PTPBASE_PTPD_SPECIFIC_COUNTERS_CLEAR, ASN_INTEGER, HANDLER_CAN_RWRITE,
+ snmpPtpdSpecificCountersTable, 5, {1, 2, 21, 1, 5}},
+ { PTPBASE_PTPD_SPECIFIC_COUNTERS_IGNORED_ANNOUNCE, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpdSpecificCountersTable, 5, {1, 2, 21, 1, 6}},
+ { PTPBASE_PTPD_SPECIFIC_COUNTERS_CONSECUTIVE_SEQUENCE_ERRORS, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpdSpecificCountersTable, 5, {1, 2, 21, 1, 7}},
+ { PTPBASE_PTPD_SPECIFIC_COUNTERS_DELAYMS_OUTLIERS_FOUND, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpdSpecificCountersTable, 5, {1, 2, 21, 1, 8}},
+ { PTPBASE_PTPD_SPECIFIC_COUNTERS_DELAYSM_OUTLIERS_FOUND, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpdSpecificCountersTable, 5, {1, 2, 21, 1, 9}},
+ { PTPBASE_PTPD_SPECIFIC_COUNTERS_MAX_DELAY_DROPS, ASN_INTEGER, HANDLER_CAN_RONLY,
+ snmpPtpdSpecificCountersTable, 5, {1, 2, 21, 1, 10}},
+ /* ptpBasePtpdSpecificData*/
+ { PTPBASE_PTPD_SPECIFIC_DATA_RAW_DELAYMS, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpPtpdSpecificDataTable, 5, {1, 2, 22, 1, 4}},
+ { PTPBASE_PTPD_SPECIFIC_DATA_RAW_DELAYMS_STRING, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpPtpdSpecificDataTable, 5, {1, 2, 22, 1, 5}},
+ { PTPBASE_PTPD_SPECIFIC_DATA_RAW_DELAYSM, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpPtpdSpecificDataTable, 5, {1, 2, 22, 1, 6}},
+ { PTPBASE_PTPD_SPECIFIC_DATA_RAW_DELAYSM_STRING, ASN_OCTET_STR, HANDLER_CAN_RONLY,
+ snmpPtpdSpecificDataTable, 5, {1, 2, 22, 1, 7}}
+};
+
+/**
+ * Log messages from NetSNMP subsystem.
+ */
+static int
+snmpLogCallback(int major, int minor,
+ void *serverarg, void *clientarg)
+{
+ struct snmp_log_message *slm = (struct snmp_log_message *)serverarg;
+ char *msg = strdup (slm->msg);
+ if (msg) msg[strlen(msg)-1] = '\0';
+
+ switch (slm->priority)
+ {
+ case LOG_EMERG: EMERGENCY("snmp[emerg]: %s\n", msg?msg:slm->msg); break;
+ case LOG_ALERT: ALERT ("snmp[alert]: %s\n", msg?msg:slm->msg); break;
+ case LOG_CRIT: CRITICAL ("snmp[crit]: %s\n", msg?msg:slm->msg); break;
+ case LOG_ERR: ERROR ("snmp[err]: %s\n", msg?msg:slm->msg); break;
+ case LOG_WARNING: WARNING ("snmp[warning]: %s\n", msg?msg:slm->msg); break;
+ case LOG_NOTICE: NOTICE ("snmp[notice]: %s\n", msg?msg:slm->msg); break;
+ case LOG_INFO: INFO ("snmp[info]: %s\n", msg?msg:slm->msg); break;
+ case LOG_DEBUG: DBGV ("snmp[debug]: %s\n", msg?msg:slm->msg); break;
+ }
+ free(msg);
+ return SNMP_ERR_NOERROR;
+}
+
+static int
+getNotifIndex(int eventType) {
+
+ switch (eventType) {
+ case PTPBASE_NOTIFS_UNEXPECTED_PORT_STATE:
+ return 1;
+ case PTPBASE_NOTIFS_EXPECTED_PORT_STATE:
+ return 2;
+ case PTPBASE_NOTIFS_SLAVE_OFFSET_THRESHOLD_EXCEEDED:
+ return 3;
+ case PTPBASE_NOTIFS_SLAVE_OFFSET_THRESHOLD_ACCEPTABLE:
+ return 4;
+ case PTPBASE_NOTIFS_SLAVE_CLOCK_STEP:
+ return 5;
+ case PTPBASE_NOTIFS_SLAVE_NO_SYNC:
+ return 6;
+ case PTPBASE_NOTIFS_SLAVE_RECEIVING_SYNC:
+ return 7;
+ case PTPBASE_NOTIFS_SLAVE_NO_DELAY:
+ return 8;
+ case PTPBASE_NOTIFS_SLAVE_RECEIVING_DELAY:
+ return 9;
+ case PTPBASE_NOTIFS_BEST_MASTER_CHANGE:
+ return 10;
+ case PTPBASE_NOTIFS_NETWORK_FAULT:
+ return 11;
+ case PTPBASE_NOTIFS_NETWORK_FAULT_CLEARED:
+ return 12;
+ case PTPBASE_NOTIFS_FREQADJ_FAST:
+ return 13;
+ case PTPBASE_NOTIFS_FREQADJ_NORMAL:
+ return 14;
+ case PTPBASE_NOTIFS_OFFSET_SECONDS:
+ return 15;
+ case PTPBASE_NOTIFS_OFFSET_SUB_SECONDS:
+ return 16;
+ case PTPBASE_NOTIFS_TIMEPROPERTIESDS_CHANGE:
+ return 17;
+ case PTPBASE_NOTIFS_DOMAIN_MISMATCH:
+ return 18;
+ case PTPBASE_NOTIFS_DOMAIN_MISMATCH_CLEARED:
+ return 19;
+ default:
+ return 0;
+ }
+}
+
+static void
+populateNotif (netsnmp_variable_list** varBinds, int eventType, PtpEventData *eventData) {
+
+ switch (eventType) {
+ case PTPBASE_NOTIFS_UNEXPECTED_PORT_STATE:
+ case PTPBASE_NOTIFS_EXPECTED_PORT_STATE:
+ {
+ oid portStateOid[] = { PTPBASE_MIB_OID, 1, 2, 9, 1, 6, PTPBASE_MIB_INDEX4 };
+
+ snmp_varlist_add_variable(varBinds, portStateOid, OID_LENGTH(portStateOid),
+ ASN_INTEGER, (u_char *) &eventData->portDS.portState, sizeof(eventData->portDS.portState));
+ }
+ return;
+ case PTPBASE_NOTIFS_SLAVE_OFFSET_THRESHOLD_EXCEEDED:
+ case PTPBASE_NOTIFS_SLAVE_OFFSET_THRESHOLD_ACCEPTABLE:
+ {
+ struct counter64 ofmNum;
+ Integer64 tmpi64;
+ internalTime_to_integer64(eventData->currentDS.offsetFromMaster, &tmpi64);
+ ofmNum.low = htonl(tmpi64.lsb);
+ ofmNum.high = htonl(tmpi64.msb);
+
+ tmpsnprintf(ofmStr, 64, "%.09f", timeInternalToDouble(&eventData->currentDS.offsetFromMaster));
+
+ oid ofmOid[] = { PTPBASE_MIB_OID, 1, 2, 1, 1, 5, PTPBASE_MIB_INDEX3 };
+ oid ofmStringOid[] = { PTPBASE_MIB_OID, 1, 2, 1, 1, 8, PTPBASE_MIB_INDEX3 };
+ oid thresholdOid[] = { PTPBASE_MIB_OID, 1, 2, 1, 1, 9, PTPBASE_MIB_INDEX3 };
+
+ snmp_varlist_add_variable(varBinds, ofmOid, OID_LENGTH(ofmOid),
+ ASN_OCTET_STR, (u_char *) &ofmNum, sizeof(ofmNum));
+ snmp_varlist_add_variable(varBinds, ofmStringOid, OID_LENGTH(ofmStringOid),
+ ASN_OCTET_STR, (u_char *) ofmStr, strlen(ofmStr));
+ snmp_varlist_add_variable(varBinds, thresholdOid, OID_LENGTH(thresholdOid),
+ ASN_INTEGER, (u_char *) &eventData->ofmAlarmThreshold, sizeof(snmpRtOpts->ofmAlarmThreshold));
+ }
+ return;
+ case PTPBASE_NOTIFS_SLAVE_NO_SYNC:
+ case PTPBASE_NOTIFS_SLAVE_RECEIVING_SYNC:
+ return;
+ case PTPBASE_NOTIFS_SLAVE_NO_DELAY:
+ case PTPBASE_NOTIFS_SLAVE_RECEIVING_DELAY:
+ return;
+ case PTPBASE_NOTIFS_BEST_MASTER_CHANGE:
+ {
+ oid portIdOid[] = { PTPBASE_MIB_OID, 1, 2, 2, 1, 4 , PTPBASE_MIB_INDEX3 };
+ oid portAddrTypeOid[] = { PTPBASE_MIB_OID, 1, 2, 2, 1, 14, PTPBASE_MIB_INDEX3 };
+ oid portAddrOid[] = { PTPBASE_MIB_OID, 1, 2, 2, 1, 15, PTPBASE_MIB_INDEX3 };
+ oid gmClockIdOid[] = { PTPBASE_MIB_OID, 1, 2, 2, 1, 8, PTPBASE_MIB_INDEX3 };
+ oid gmPriority1Oid[] = { PTPBASE_MIB_OID, 1, 2, 2, 1, 9, PTPBASE_MIB_INDEX3 };
+ oid gmPriority2Oid[] = { PTPBASE_MIB_OID, 1, 2, 2, 1, 10, PTPBASE_MIB_INDEX3 };
+ oid gmClockClassOid[] = { PTPBASE_MIB_OID, 1, 2, 2, 1, 11, PTPBASE_MIB_INDEX3 };
+ oid utcOffsetOid[] = { PTPBASE_MIB_OID, 1, 2, 5, 1 , 5, PTPBASE_MIB_INDEX3 };
+ oid utcOffsetValidOid[] = { PTPBASE_MIB_OID, 1, 2, 5, 1, 4, PTPBASE_MIB_INDEX3 };
+ oid portStateOid[] = { PTPBASE_MIB_OID, 1, 2, 9, 1, 6, PTPBASE_MIB_INDEX4 };
+
+ unsigned long addrType = SNMP_IPv4;
+ unsigned long priority1 = eventData->parentDS.grandmasterPriority1;
+ unsigned long priority2 = eventData->parentDS.grandmasterPriority2;
+ unsigned long clockClass = eventData->parentDS.grandmasterClockQuality.clockClass;
+ unsigned long utcOffset = eventData->timePropertiesDS.currentUtcOffset;
+ unsigned long utcOffsetValid = TO_TRUTHVALUE(eventData->timePropertiesDS.currentUtcOffsetValid);
+
+ snmp_varlist_add_variable(varBinds, portIdOid, OID_LENGTH(portIdOid),
+ ASN_OCTET_STR, (u_char *) &eventData->parentDS.parentPortIdentity, sizeof(PortIdentity));
+
+ snmp_varlist_add_variable(varBinds, portAddrTypeOid, OID_LENGTH(portAddrTypeOid),
+ ASN_INTEGER, (u_char *) &addrType, sizeof(addrType));
+
+ snmp_varlist_add_variable(varBinds, portAddrOid, OID_LENGTH(portAddrOid),
+ ASN_OCTET_STR, (u_char *) &eventData->bestMaster.sourceAddr, sizeof(eventData->bestMaster.sourceAddr));
+
+ snmp_varlist_add_variable(varBinds, gmClockIdOid, OID_LENGTH(gmClockIdOid),
+ ASN_OCTET_STR, (u_char *) &eventData->parentDS.grandmasterIdentity, sizeof(ClockIdentity));
+
+ snmp_varlist_add_variable(varBinds, gmPriority1Oid, OID_LENGTH(gmPriority1Oid),
+ ASN_UNSIGNED, (u_char *) &priority1, sizeof(priority1));
+
+ snmp_varlist_add_variable(varBinds, gmPriority2Oid, OID_LENGTH(gmPriority2Oid),
+ ASN_UNSIGNED, (u_char *) &priority2, sizeof(priority2));
+
+ snmp_varlist_add_variable(varBinds, gmClockClassOid, OID_LENGTH(gmClockClassOid),
+ ASN_UNSIGNED, (u_char *) &clockClass, sizeof(clockClass));
+
+ snmp_varlist_add_variable(varBinds, utcOffsetOid, OID_LENGTH(utcOffsetOid),
+ ASN_INTEGER, (u_char *) &utcOffset, sizeof(utcOffset));
+
+ snmp_varlist_add_variable(varBinds, utcOffsetValidOid, OID_LENGTH(utcOffsetValidOid),
+ ASN_INTEGER, (u_char *) &utcOffsetValid, sizeof(utcOffsetValid));
+
+ snmp_varlist_add_variable(varBinds, portStateOid, OID_LENGTH(portStateOid),
+ ASN_INTEGER, (u_char *) &eventData->portDS.portState, sizeof(eventData->portDS.portState));
+ }
+ return;
+ case PTPBASE_NOTIFS_NETWORK_FAULT:
+ case PTPBASE_NOTIFS_NETWORK_FAULT_CLEARED:
+ return;
+ case PTPBASE_NOTIFS_FREQADJ_FAST:
+ case PTPBASE_NOTIFS_FREQADJ_NORMAL:
+ return;
+ case PTPBASE_NOTIFS_SLAVE_CLOCK_STEP:
+ case PTPBASE_NOTIFS_OFFSET_SECONDS:
+ case PTPBASE_NOTIFS_OFFSET_SUB_SECONDS:
+ {
+ struct counter64 ofmNum;
+ Integer64 tmpi64;
+ internalTime_to_integer64(eventData->currentDS.offsetFromMaster, &tmpi64);
+ ofmNum.low = htonl(tmpi64.lsb);
+ ofmNum.high = htonl(tmpi64.msb);
+
+ tmpsnprintf(ofmStr, 64, "%.09f", timeInternalToDouble(&eventData->currentDS.offsetFromMaster));
+
+ oid ofmOid[] = { PTPBASE_MIB_OID, 1, 2, 1, 1, 5, PTPBASE_MIB_INDEX3 };
+ oid ofmStringOid[] = { PTPBASE_MIB_OID, 1, 2, 1, 1, 8, PTPBASE_MIB_INDEX3 };
+
+ snmp_varlist_add_variable(varBinds, ofmOid, OID_LENGTH(ofmOid),
+ ASN_OCTET_STR, (u_char *) &ofmNum, sizeof(ofmNum));
+ snmp_varlist_add_variable(varBinds, ofmStringOid, OID_LENGTH(ofmStringOid),
+ ASN_OCTET_STR, (u_char *) ofmStr, strlen(ofmStr));
+ }
+ return;
+
+ case PTPBASE_NOTIFS_TIMEPROPERTIESDS_CHANGE:
+ {
+ oid utcOffsetValidOid[] = { PTPBASE_MIB_OID, 1, 2, 5, 1, 4, PTPBASE_MIB_INDEX3 };
+ oid utcOffsetOid[] = { PTPBASE_MIB_OID, 1, 2, 5, 1 , 5, PTPBASE_MIB_INDEX3 };
+ oid leap59Oid[] = { PTPBASE_MIB_OID, 1, 2, 5, 1 , 6, PTPBASE_MIB_INDEX3 };
+ oid leap61Oid[] = { PTPBASE_MIB_OID, 1, 2, 5, 1 , 7, PTPBASE_MIB_INDEX3 };
+ oid timeTraceableOid[] = { PTPBASE_MIB_OID, 1, 2, 5, 1 , 8, PTPBASE_MIB_INDEX3 };
+ oid frequencyTraceableOid[] = { PTPBASE_MIB_OID, 1, 2, 5, 1 , 9, PTPBASE_MIB_INDEX3 };
+ oid ptpTimescaleOid[] = { PTPBASE_MIB_OID, 1, 2, 5, 1 , 10, PTPBASE_MIB_INDEX3 };
+ oid timeSourceOid[] = { PTPBASE_MIB_OID, 1, 2, 5, 1 , 11, PTPBASE_MIB_INDEX3 };
+
+ unsigned long utcOffset = eventData->timePropertiesDS.currentUtcOffset;
+ unsigned long utcOffsetValid = TO_TRUTHVALUE(eventData->timePropertiesDS.currentUtcOffsetValid);
+ unsigned long leap59 = TO_TRUTHVALUE(eventData->timePropertiesDS.leap61);
+ unsigned long leap61 = TO_TRUTHVALUE(eventData->timePropertiesDS.leap61);
+ unsigned long timeTraceable = TO_TRUTHVALUE(eventData->timePropertiesDS.timeTraceable);
+ unsigned long frequencyTraceable = TO_TRUTHVALUE(eventData->timePropertiesDS.frequencyTraceable);
+ unsigned long ptpTimescale = TO_TRUTHVALUE(eventData->timePropertiesDS.ptpTimescale);
+ unsigned long timeSource = eventData->timePropertiesDS.timeSource;
+
+ snmp_varlist_add_variable(varBinds, utcOffsetValidOid, OID_LENGTH(utcOffsetValidOid),
+ ASN_INTEGER, (u_char *) &utcOffsetValid, sizeof(utcOffsetValid));
+
+ snmp_varlist_add_variable(varBinds, utcOffsetOid, OID_LENGTH(utcOffsetOid),
+ ASN_INTEGER, (u_char *) &utcOffset, sizeof(utcOffset));
+
+ snmp_varlist_add_variable(varBinds, leap59Oid, OID_LENGTH(leap59Oid),
+ ASN_INTEGER, (u_char *) &leap59, sizeof(leap59));
+
+ snmp_varlist_add_variable(varBinds, leap61Oid, OID_LENGTH(leap61Oid),
+ ASN_INTEGER, (u_char *) &leap61, sizeof(leap61));
+
+ snmp_varlist_add_variable(varBinds, timeTraceableOid, OID_LENGTH(timeTraceableOid),
+ ASN_INTEGER, (u_char *) &timeTraceable, sizeof(timeTraceable));
+
+ snmp_varlist_add_variable(varBinds, frequencyTraceableOid, OID_LENGTH(frequencyTraceableOid),
+ ASN_INTEGER, (u_char *) &frequencyTraceable, sizeof(frequencyTraceable));
+
+ snmp_varlist_add_variable(varBinds, ptpTimescaleOid, OID_LENGTH(ptpTimescaleOid),
+ ASN_INTEGER, (u_char *) &ptpTimescale, sizeof(ptpTimescale));
+
+ snmp_varlist_add_variable(varBinds, ptpTimescaleOid, OID_LENGTH(ptpTimescaleOid),
+ ASN_INTEGER, (u_char *) &ptpTimescale, sizeof(ptpTimescale));
+
+ snmp_varlist_add_variable(varBinds, timeSourceOid, OID_LENGTH(timeSourceOid),
+ ASN_INTEGER, (u_char *) &timeSource, sizeof(timeSource));
+
+ }
+ return;
+ case PTPBASE_NOTIFS_DOMAIN_MISMATCH:
+ {
+ oid domainNumberOid[] = { PTPBASE_MIB_OID, 1, 2, 3, 1, 12, PTPBASE_MIB_INDEX3 };
+ oid lastMismatchedDomainOid[] = { PTPBASE_MIB_OID, 1, 2, 8, 1, 17, PTPBASE_MIB_INDEX4 };
+ unsigned long domainNumber = eventData->defaultDS.domainNumber;
+ unsigned long lastMismatchedDomain = eventData->portDS.lastMismatchedDomain;
+ snmp_varlist_add_variable(varBinds, domainNumberOid, OID_LENGTH(domainNumberOid),
+ ASN_INTEGER, (u_char *) &domainNumber, sizeof(domainNumber));
+ snmp_varlist_add_variable(varBinds, lastMismatchedDomainOid, OID_LENGTH(lastMismatchedDomainOid),
+ ASN_INTEGER, (u_char *) &lastMismatchedDomain, sizeof(lastMismatchedDomain));
+
+ }
+ return;
+ case PTPBASE_NOTIFS_DOMAIN_MISMATCH_CLEARED:
+ {
+ oid domainNumberOid[] = { PTPBASE_MIB_OID, 1, 2, 3, 1, 12, PTPBASE_MIB_INDEX3 };
+ unsigned long domainNumber = eventData->defaultDS.domainNumber;
+ snmp_varlist_add_variable(varBinds, domainNumberOid, OID_LENGTH(domainNumberOid),
+ ASN_INTEGER, (u_char *) &domainNumber, sizeof(domainNumber));
+ }
+ return;
+ default:
+ return;
+ }
+
+}
+
+static void
+sendNotif(int eventType, PtpEventData *eventData) {
+
+ /* snmpTrapOID.0 */
+ oid trapOid[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
+ netsnmp_variable_list *varBinds = NULL;
+
+ int notifIndex = getNotifIndex(eventType);
+
+ if(!notifIndex) {
+ DBG("SNMP trap request for unknown event type: %d\n", eventType);
+ return;
+ }
+
+ /* the notification oid*/
+ oid notifOid[] = { PTPBASE_MIB_OID, 0, (oid)notifIndex };
+
+ /* add notification oid */
+ snmp_varlist_add_variable(&varBinds, trapOid, OID_LENGTH(trapOid),
+ ASN_OBJECT_ID, (u_char *) notifOid, OID_LENGTH(notifOid) * sizeof(oid));
+
+ /* add the accompanying varbinds */
+ populateNotif(&varBinds, eventType, eventData);
+
+ send_v2trap(varBinds);
+
+ snmp_free_varbind(varBinds);
+
+}
+
+
+/**
+ * Initialisation of SNMP subsystem.
+ */
+void
+snmpInit(RunTimeOpts *rtOpts, PtpClock *ptpClock) {
+ netsnmp_enable_subagent();
+ snmp_disable_log();
+ snmp_enable_calllog();
+ snmp_register_callback(SNMP_CALLBACK_LIBRARY,
+ SNMP_CALLBACK_LOGGING,
+ snmpLogCallback,
+ NULL);
+ init_agent("ptpAgent");
+ REGISTER_MIB("ptpMib", snmpVariables, variable7, ptp_oid);
+ init_snmp("ptpAgent");
+
+ /* Currently, ptpd only handles one clock. We put it in a
+ * global variable for the need of our subsystem. */
+ snmpPtpClock = ptpClock;
+ snmpRtOpts = rtOpts;
+
+}
+
+/**
+ * Clean up and shut down the SNMP subagent
+ */
+
+void
+snmpShutdown() {
+ unregister_mib(ptp_oid, sizeof(ptp_oid) / sizeof(oid));
+ snmp_shutdown("ptpMib");
+ SOCK_CLEANUP;
+
+}
+
+void
+alarmHandler_snmp(AlarmEntry *alarm)
+{
+ int notifId = -1;
+
+ if(alarm->state == ALARM_SET) {
+ DBG("[snmp] Alarm %s set trap processed\n", alarm->name);
+ switch(alarm->id) {
+ case ALRM_PORT_STATE:
+ notifId = PTPBASE_NOTIFS_UNEXPECTED_PORT_STATE;
+ break;
+ case ALRM_OFM_THRESHOLD:
+ notifId = PTPBASE_NOTIFS_SLAVE_OFFSET_THRESHOLD_EXCEEDED;
+ break;
+ case ALRM_OFM_SECONDS:
+ notifId = PTPBASE_NOTIFS_OFFSET_SECONDS;
+ break;
+ case ALRM_NO_SYNC:
+ notifId = PTPBASE_NOTIFS_SLAVE_NO_SYNC;
+ break;
+ case ALRM_NO_DELAY:
+ notifId = PTPBASE_NOTIFS_SLAVE_NO_DELAY;
+ break;
+ case ALRM_NETWORK_FLT:
+ notifId = PTPBASE_NOTIFS_NETWORK_FAULT;
+ break;
+ case ALRM_FAST_ADJ:
+ notifId = PTPBASE_NOTIFS_FREQADJ_FAST;
+ break;
+ case ALRM_DOMAIN_MISMATCH:
+ notifId = PTPBASE_NOTIFS_DOMAIN_MISMATCH;
+ break;
+ }
+ }
+
+ if(alarm->state == ALARM_UNSET) {
+ DBG("[snmp] Alarm %s clear trap processed\n", alarm->name);
+ switch(alarm->id) {
+ case ALRM_PORT_STATE:
+ notifId = PTPBASE_NOTIFS_EXPECTED_PORT_STATE;
+ break;
+ case ALRM_OFM_THRESHOLD:
+ notifId = PTPBASE_NOTIFS_SLAVE_OFFSET_THRESHOLD_ACCEPTABLE;
+ break;
+ case ALRM_OFM_SECONDS:
+ notifId = PTPBASE_NOTIFS_OFFSET_SUB_SECONDS;
+ break;
+ case ALRM_NO_SYNC:
+ notifId = PTPBASE_NOTIFS_SLAVE_RECEIVING_SYNC;
+ break;
+ case ALRM_NO_DELAY:
+ notifId = PTPBASE_NOTIFS_SLAVE_RECEIVING_DELAY;
+ break;
+ case ALRM_NETWORK_FLT:
+ notifId = PTPBASE_NOTIFS_NETWORK_FAULT_CLEARED;
+ break;
+ case ALRM_FAST_ADJ:
+ notifId = PTPBASE_NOTIFS_FREQADJ_NORMAL;
+ break;
+ case ALRM_DOMAIN_MISMATCH:
+ notifId = PTPBASE_NOTIFS_DOMAIN_MISMATCH_CLEARED;
+ break;
+ }
+ }
+
+ if(alarm->eventOnly) {
+ DBG("[snmp] Event %s notification processed\n", alarm->name);
+ switch(alarm->id) {
+ case ALRM_CLOCK_STEP:
+ notifId = PTPBASE_NOTIFS_SLAVE_CLOCK_STEP;
+ break;
+ case ALRM_MASTER_CHANGE:
+ notifId = PTPBASE_NOTIFS_BEST_MASTER_CHANGE;
+ break;
+ case ALRM_TIMEPROP_CHANGE:
+ notifId = PTPBASE_NOTIFS_TIMEPROPERTIESDS_CHANGE;
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ if(notifId >= 0) {
+ sendNotif(notifId, &alarm->eventData);
+ return;
+ }
+
+ DBG("Unhandled SNMP event id 0x%x\n", alarm->id);
+
+}
diff --git a/rtemsbsd/ptpd/src/dep/startup.c b/rtemsbsd/ptpd/src/dep/startup.c
new file mode 100644
index 00000000..a9011736
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/startup.c
@@ -0,0 +1,1020 @@
+/*-
+ * Copyright (c) 2014-2015 Wojciech Owczarek,
+ * Copyright (c) 2012-2013 George V. Neville-Neil,
+ * Wojciech Owczarek
+ * Copyright (c) 2011-2012 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen,
+ * Inaqui Delgado,
+ * Rick Ratzel,
+ * National Instruments.
+ * Copyright (c) 2009-2010 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen
+ *
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file startup.c
+ * @date Wed Jun 23 09:33:27 2010
+ *
+ * @brief Code to handle daemon startup, including command line args
+ *
+ * The function in this file are called when the daemon starts up
+ * and include the getopt() command line argument parsing.
+ */
+
+#include "../ptpd.h"
+
+/*
+ * valgrind 3.5.0 currently reports no errors (last check: 20110512)
+ * valgrind 3.4.1 lacks an adjtimex handler
+ *
+ * to run: sudo valgrind --show-reachable=yes --leak-check=full --track-origins=yes -- ./ptpd2 -c ...
+ */
+
+/*
+ to test daemon locking and startup sequence itself, try:
+
+ function s() { set -o pipefail ; eval "$@" | sed 's/^/\t/' ; echo $?; }
+ sudo killall ptpd2
+ s ./ptpd2
+ s sudo ./ptpd2
+ s sudo ./ptpd2 -t -g
+ s sudo ./ptpd2 -t -g -b eth0
+ s sudo ./ptpd2 -t -g -b eth0
+ ps -ef | grep ptpd2
+*/
+
+/*
+ * Synchronous signal processing:
+ * original idea: http://www.openbsd.org/cgi-bin/cvsweb/src/usr.sbin/ntpd/ntpd.c?rev=1.68;content-type=text%2Fplain
+ */
+volatile sig_atomic_t sigint_received = 0;
+volatile sig_atomic_t sigterm_received = 0;
+volatile sig_atomic_t sighup_received = 0;
+volatile sig_atomic_t sigusr1_received = 0;
+volatile sig_atomic_t sigusr2_received = 0;
+
+/* Pointer to the current lock file */
+FILE* G_lockFilePointer;
+
+/*
+ * Function to catch signals asynchronously.
+ * Assuming that the daemon periodically calls checkSignals(), then all operations are safely done synchrously at a later opportunity.
+ *
+ * Please do NOT call any functions inside this handler - especially DBG() and its friends, or any glibc.
+ */
+void catchSignals(int sig)
+{
+ switch (sig) {
+ case SIGINT:
+ sigint_received = 1;
+ break;
+ case SIGTERM:
+ sigterm_received = 1;
+ break;
+ case SIGHUP:
+ sighup_received = 1;
+ break;
+ case SIGUSR1:
+ sigusr1_received = 1;
+ break;
+ case SIGUSR2:
+ sigusr2_received = 1;
+ break;
+ default:
+ /*
+ * TODO: should all other signals be catched, and handled as SIGINT?
+ *
+ * Reason: currently, all other signals are just uncatched, and the OS kills us.
+ * The difference is that we could then close the open files properly.
+ */
+ break;
+ }
+}
+
+/*
+ * exit the program cleanly
+ */
+void
+do_signal_close(PtpClock * ptpClock)
+{
+
+ timingDomain.shutdown(&timingDomain);
+
+ NOTIFY("Shutdown on close signal\n");
+ exit(0);
+}
+
+void
+applyConfig(dictionary *baseConfig, RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+
+ Boolean reloadSuccessful = TRUE;
+
+
+ /* Load default config to fill in the blanks in the config file */
+ RunTimeOpts tmpOpts;
+ loadDefaultSettings(&tmpOpts);
+
+ /* Check the new configuration for errors, fill in the blanks from defaults */
+ if( ( rtOpts->candidateConfig = parseConfig(CFGOP_PARSE, NULL, baseConfig, &tmpOpts)) == NULL ) {
+ WARNING("Configuration has errors, reload aborted\n");
+ return;
+ }
+
+ /* Check for changes between old and new configuration */
+ if(compareConfig(rtOpts->candidateConfig,rtOpts->currentConfig)) {
+ INFO("Configuration unchanged\n");
+ goto cleanup;
+ }
+
+ /*
+ * Mark which subsystems have to be restarted. Most of this will be picked up by doState()
+ * If there are errors past config correctness (such as non-existent NIC,
+ * or lock file clashes if automatic lock files used - abort the mission
+ */
+
+ rtOpts->restartSubsystems =
+ checkSubsystemRestart(rtOpts->candidateConfig, rtOpts->currentConfig, rtOpts);
+
+ /* If we're told to re-check lock files, do it: tmpOpts already has what rtOpts should */
+ if( (rtOpts->restartSubsystems & PTPD_CHECK_LOCKS) &&
+ tmpOpts.autoLockFile && !checkOtherLocks(&tmpOpts)) {
+ reloadSuccessful = FALSE;
+ }
+
+ /* If the network configuration has changed, check if the interface is OK */
+ if(rtOpts->restartSubsystems & PTPD_RESTART_NETWORK) {
+ INFO("Network configuration changed - checking interface(s)\n");
+ if(!testInterface(tmpOpts.primaryIfaceName, &tmpOpts)) {
+ reloadSuccessful = FALSE;
+ ERROR("Error: Cannot use %s interface\n",tmpOpts.primaryIfaceName);
+ }
+ if(rtOpts->backupIfaceEnabled && !testInterface(tmpOpts.backupIfaceName, &tmpOpts)) {
+ rtOpts->restartSubsystems = -1;
+ ERROR("Error: Cannot use %s interface as backup\n",tmpOpts.backupIfaceName);
+ }
+ }
+#if (defined(linux) && defined(HAVE_SCHED_H)) || defined(HAVE_SYS_CPUSET_H) || defined(__QNXNTO__)
+ /* Changing the CPU affinity mask */
+ if(rtOpts->restartSubsystems & PTPD_CHANGE_CPUAFFINITY) {
+ NOTIFY("Applying CPU binding configuration: changing selected CPU core\n");
+
+ if(setCpuAffinity(tmpOpts.cpuNumber) < 0) {
+ if(tmpOpts.cpuNumber == -1) {
+ ERROR("Could not unbind from CPU core %d\n", rtOpts->cpuNumber);
+ } else {
+ ERROR("Could bind to CPU core %d\n", tmpOpts.cpuNumber);
+ }
+ reloadSuccessful = FALSE;
+ } else {
+ if(tmpOpts.cpuNumber > -1)
+ INFO("Successfully bound "PTPD_PROGNAME" to CPU core %d\n", tmpOpts.cpuNumber);
+ else
+ INFO("Successfully unbound "PTPD_PROGNAME" from cpu core CPU core %d\n", rtOpts->cpuNumber);
+ }
+ }
+#endif
+
+ if(!reloadSuccessful) {
+ ERROR("New configuration cannot be applied - aborting reload\n");
+ rtOpts->restartSubsystems = 0;
+ goto cleanup;
+ }
+
+
+ /**
+ * Commit changes to rtOpts and currentConfig
+ * (this should never fail as the config has already been checked if we're here)
+ * However if this DOES fail, some default has been specified out of range -
+ * this is the only situation where parse will succeed but commit not:
+ * disable quiet mode to show what went wrong, then die.
+ */
+ if (rtOpts->currentConfig) {
+ dictionary_del(&rtOpts->currentConfig);
+ }
+ if ( (rtOpts->currentConfig = parseConfig(CFGOP_PARSE_QUIET, NULL, rtOpts->candidateConfig,rtOpts)) == NULL) {
+ CRITICAL("************ "PTPD_PROGNAME": parseConfig returned NULL during config commit"
+ " - this is a BUG - report the following: \n");
+
+ if ((rtOpts->currentConfig = parseConfig(CFGOP_PARSE, NULL, rtOpts->candidateConfig,rtOpts)) == NULL)
+ CRITICAL("*****************" PTPD_PROGNAME" shutting down **********************\n");
+ /*
+ * Could be assert(), but this should be done any time this happens regardless of
+ * compile options. Anyhow, if we're here, the daemon will no doubt segfault soon anyway
+ */
+ abort();
+ }
+
+ /* clean up */
+ cleanup:
+
+ dictionary_del(&rtOpts->candidateConfig);
+}
+
+
+/**
+ * Signal handler for HUP which tells us to swap the log file
+ * and reload configuration file if specified
+ *
+ * @param sig
+ */
+void
+do_signal_sighup(RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+
+
+
+ NOTIFY("SIGHUP received\n");
+
+#ifdef RUNTIME_DEBUG
+ if(rtOpts->transport == UDP_IPV4 && rtOpts->ipMode != IPMODE_UNICAST) {
+ DBG("SIGHUP - running an ipv4 multicast based mode, re-sending IGMP joins\n");
+ netRefreshIGMP(&ptpClock->netPath, rtOpts, ptpClock);
+ }
+#endif /* RUNTIME_DEBUG */
+
+
+ /* if we don't have a config file specified, we're done - just reopen log files*/
+ if(strlen(rtOpts->configFile) != 0) {
+
+ dictionary* tmpConfig = dictionary_new(0);
+
+ /* Try reloading the config file */
+ NOTIFY("Reloading configuration file: %s\n",rtOpts->configFile);
+
+ if(!loadConfigFile(&tmpConfig, rtOpts)) {
+
+ dictionary_del(&tmpConfig);
+
+ } else {
+ dictionary_merge(rtOpts->cliConfig, tmpConfig, 1, 1, "from command line");
+ applyConfig(tmpConfig, rtOpts, ptpClock);
+ dictionary_del(&tmpConfig);
+
+ }
+
+ }
+
+ /* tell the service it can perform any HUP-triggered actions */
+ ptpClock->timingService.reloadRequested = TRUE;
+
+ if(rtOpts->recordLog.logEnabled ||
+ rtOpts->eventLog.logEnabled ||
+ (rtOpts->statisticsLog.logEnabled))
+ INFO("Reopening log files\n");
+
+ restartLogging(rtOpts);
+
+ if(rtOpts->statisticsLog.logEnabled)
+ ptpClock->resetStatisticsLog = TRUE;
+
+
+}
+
+void
+restartSubsystems(RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ DBG("RestartSubsystems: %d\n",rtOpts->restartSubsystems);
+ /* So far, PTP_INITIALIZING is required for both network and protocol restart */
+ if((rtOpts->restartSubsystems & PTPD_RESTART_PROTOCOL) ||
+ (rtOpts->restartSubsystems & PTPD_RESTART_NETWORK)) {
+
+ if(rtOpts->restartSubsystems & PTPD_RESTART_NETWORK) {
+ NOTIFY("Applying network configuration: going into PTP_INITIALIZING\n");
+ }
+
+ /* These parameters have to be passed to ptpClock before re-init */
+ ptpClock->defaultDS.clockQuality.clockClass = rtOpts->clockQuality.clockClass;
+ ptpClock->defaultDS.slaveOnly = rtOpts->slaveOnly;
+ ptpClock->disabled = rtOpts->portDisabled;
+
+ if(rtOpts->restartSubsystems & PTPD_RESTART_PROTOCOL) {
+ INFO("Applying protocol configuration: going into %s\n",
+ ptpClock->disabled ? "PTP_DISABLED" : "PTP_INITIALIZING");
+ }
+
+ /* Move back to primary interface only during configuration changes. */
+ ptpClock->runningBackupInterface = FALSE;
+ toState(ptpClock->disabled ? PTP_DISABLED : PTP_INITIALIZING, rtOpts, ptpClock);
+
+ } else {
+ /* Nothing happens here for now - SIGHUP handler does this anyway */
+ if(rtOpts->restartSubsystems & PTPD_UPDATE_DATASETS) {
+ NOTIFY("Applying PTP engine configuration: updating datasets\n");
+ updateDatasets(ptpClock, rtOpts);
+ }}
+ /* Nothing happens here for now - SIGHUP handler does this anyway */
+ if(rtOpts->restartSubsystems & PTPD_RESTART_LOGGING) {
+ NOTIFY("Applying logging configuration: restarting logging\n");
+ }
+
+
+ if(rtOpts->restartSubsystems & PTPD_RESTART_ACLS) {
+ NOTIFY("Applying access control list configuration\n");
+ /* re-compile ACLs */
+ freeIpv4AccessList(&ptpClock->netPath.timingAcl);
+ freeIpv4AccessList(&ptpClock->netPath.managementAcl);
+ if(rtOpts->timingAclEnabled) {
+ ptpClock->netPath.timingAcl=createIpv4AccessList(rtOpts->timingAclPermitText,
+ rtOpts->timingAclDenyText, rtOpts->timingAclOrder);
+ }
+ if(rtOpts->managementAclEnabled) {
+ ptpClock->netPath.managementAcl=createIpv4AccessList(rtOpts->managementAclPermitText,
+ rtOpts->managementAclDenyText, rtOpts->managementAclOrder);
+ }
+ }
+
+ if(rtOpts->restartSubsystems & PTPD_RESTART_ALARMS) {
+ NOTIFY("Applying alarm configuration\n");
+ configureAlarms(ptpClock->alarms, ALRM_MAX, (void*)ptpClock);
+ }
+
+#ifdef PTPD_STATISTICS
+ /* Reinitialising the outlier filter containers */
+ if(rtOpts->restartSubsystems & PTPD_RESTART_FILTERS) {
+
+ NOTIFY("Applying filter configuration: re-initialising filters\n");
+
+ freeDoubleMovingStatFilter(&ptpClock->filterMS);
+ freeDoubleMovingStatFilter(&ptpClock->filterSM);
+
+ ptpClock->oFilterMS.shutdown(&ptpClock->oFilterMS);
+ ptpClock->oFilterSM.shutdown(&ptpClock->oFilterSM);
+
+ outlierFilterSetup(&ptpClock->oFilterMS);
+ outlierFilterSetup(&ptpClock->oFilterSM);
+
+ ptpClock->oFilterMS.init(&ptpClock->oFilterMS,&rtOpts->oFilterMSConfig, "delayMS");
+ ptpClock->oFilterSM.init(&ptpClock->oFilterSM,&rtOpts->oFilterSMConfig, "delaySM");
+
+
+ if(rtOpts->filterMSOpts.enabled) {
+ ptpClock->filterMS = createDoubleMovingStatFilter(&rtOpts->filterMSOpts,"delayMS");
+ }
+
+ if(rtOpts->filterSMOpts.enabled) {
+ ptpClock->filterSM = createDoubleMovingStatFilter(&rtOpts->filterSMOpts, "delaySM");
+ }
+
+ }
+#endif /* PTPD_STATISTICS */
+
+
+ ptpClock->timingService.reloadRequested = TRUE;
+
+ if(rtOpts->restartSubsystems & PTPD_RESTART_NTPENGINE && timingDomain.serviceCount > 1) {
+ ptpClock->ntpControl.timingService.shutdown(&ptpClock->ntpControl.timingService);
+ }
+
+ if((rtOpts->restartSubsystems & PTPD_RESTART_NTPENGINE) ||
+ (rtOpts->restartSubsystems & PTPD_RESTART_NTPCONFIG)) {
+ ntpSetup(rtOpts, ptpClock);
+ }
+ if((rtOpts->restartSubsystems & PTPD_RESTART_NTPENGINE) && rtOpts->ntpOptions.enableEngine) {
+ timingServiceSetup(&ptpClock->ntpControl.timingService);
+ ptpClock->ntpControl.timingService.init(&ptpClock->ntpControl.timingService);
+ }
+
+ ptpClock->timingService.dataSet.priority1 = rtOpts->preferNTP;
+
+ timingDomain.electionDelay = rtOpts->electionDelay;
+ if(timingDomain.electionLeft > timingDomain.electionDelay) {
+ timingDomain.electionLeft = timingDomain.electionDelay;
+ }
+
+ timingDomain.services[0]->holdTime = rtOpts->ntpOptions.failoverTimeout;
+
+ if(timingDomain.services[0]->holdTimeLeft >
+ timingDomain.services[0]->holdTime) {
+ timingDomain.services[0]->holdTimeLeft =
+ rtOpts->ntpOptions.failoverTimeout;
+ }
+
+ ptpClock->timingService.timeout = rtOpts->idleTimeout;
+
+ /* Update PI servo parameters */
+ setupPIservo(&ptpClock->servo, rtOpts);
+ /* Config changes don't require subsystem restarts - acknowledge it */
+ if(rtOpts->restartSubsystems == PTPD_RESTART_NONE) {
+ NOTIFY("Applying configuration\n");
+ }
+
+ if(rtOpts->restartSubsystems != -1)
+ rtOpts->restartSubsystems = 0;
+
+}
+
+
+/*
+ * Synchronous signal processing:
+ * This function should be called regularly from the main loop
+ */
+void
+checkSignals(RunTimeOpts * rtOpts, PtpClock * ptpClock)
+{
+ /*
+ * note:
+ * alarm signals are handled in a similar way in dep/timer.c
+ */
+
+ if(sigint_received || sigterm_received){
+ do_signal_close(ptpClock);
+ }
+
+ if(sighup_received){
+ do_signal_sighup(rtOpts, ptpClock);
+ sighup_received=0;
+ }
+
+ if(sigusr1_received){
+ if(ptpClock->portDS.portState == PTP_SLAVE){
+ WARNING("SIGUSR1 received, stepping clock to current known OFM\n");
+ stepClock(rtOpts, ptpClock);
+// ptpClock->clockControl.stepRequired = TRUE;
+ } else {
+ ERROR("SIGUSR1 received - will not step clock, not in PTP_SLAVE state\n");
+ }
+ sigusr1_received = 0;
+ }
+
+ if(sigusr2_received){
+
+/* testing only: testing step detection */
+#if 0
+ {
+ ptpClock->addOffset ^= 1;
+ INFO("a: %d\n", ptpClock->addOffset);
+ sigusr2_received = 0;
+ return;
+ }
+#endif
+ displayCounters(ptpClock);
+ displayAlarms(ptpClock->alarms, ALRM_MAX);
+ if(rtOpts->timingAclEnabled) {
+ INFO("\n\n");
+ INFO("** Timing message ACL:\n");
+ dumpIpv4AccessList(ptpClock->netPath.timingAcl);
+ }
+ if(rtOpts->managementAclEnabled) {
+ INFO("\n\n");
+ INFO("** Management message ACL:\n");
+ dumpIpv4AccessList(ptpClock->netPath.managementAcl);
+ }
+ if(rtOpts->clearCounters) {
+ clearCounters(ptpClock);
+ NOTIFY("PTP engine counters cleared\n");
+ }
+#ifdef PTPD_STATISTICS
+ if(rtOpts->oFilterSMConfig.enabled) {
+ ptpClock->oFilterSM.display(&ptpClock->oFilterSM);
+ }
+ if(rtOpts->oFilterMSConfig.enabled) {
+ ptpClock->oFilterMS.display(&ptpClock->oFilterMS);
+ }
+#endif /* PTPD_STATISTICS */
+ sigusr2_received = 0;
+ }
+
+}
+
+#ifdef RUNTIME_DEBUG
+/* These functions are useful to temporarily enable Debug around parts of code, similar to bash's "set -x" */
+void enable_runtime_debug(void )
+{
+ extern RunTimeOpts rtOpts;
+
+ rtOpts.debug_level = max(LOG_DEBUGV, rtOpts.debug_level);
+}
+
+void disable_runtime_debug(void )
+{
+ extern RunTimeOpts rtOpts;
+
+ rtOpts.debug_level = LOG_INFO;
+}
+#endif
+
+int
+writeLockFile(RunTimeOpts * rtOpts)
+{
+
+ int lockPid = 0;
+
+ DBGV("Checking lock file: %s\n", rtOpts->lockFile);
+
+ if ( (G_lockFilePointer=fopen(rtOpts->lockFile, "w+")) == NULL) {
+ PERROR("Could not open lock file %s for writing", rtOpts->lockFile);
+ return(0);
+ }
+ if (lockFile(fileno(G_lockFilePointer)) < 0) {
+ if ( checkLockStatus(fileno(G_lockFilePointer),
+ DEFAULT_LOCKMODE, &lockPid) == 0) {
+ ERROR("Another "PTPD_PROGNAME" instance is running: %s locked by PID %d\n",
+ rtOpts->lockFile, lockPid);
+ } else {
+ PERROR("Could not acquire lock on %s:", rtOpts->lockFile);
+ }
+ goto failure;
+ }
+ if(ftruncate(fileno(G_lockFilePointer), 0) == -1) {
+ PERROR("Could not truncate %s: %s",
+ rtOpts->lockFile, strerror(errno));
+ goto failure;
+ }
+ if ( fprintf(G_lockFilePointer, "%ld\n", (long)getpid()) == -1) {
+ PERROR("Could not write to lock file %s: %s",
+ rtOpts->lockFile, strerror(errno));
+ goto failure;
+ }
+ INFO("Successfully acquired lock on %s\n", rtOpts->lockFile);
+ fflush(G_lockFilePointer);
+ return(1);
+ failure:
+ fclose(G_lockFilePointer);
+ return(0);
+
+}
+
+void
+ptpdShutdown(PtpClock * ptpClock)
+{
+
+ extern RunTimeOpts rtOpts;
+
+ /*
+ * go into DISABLED state so the FSM can call any PTP-specific shutdown actions,
+ * such as canceling unicast transmission
+ */
+ toState(PTP_DISABLED, &rtOpts, ptpClock);
+ /* process any outstanding events before exit */
+ updateAlarms(ptpClock->alarms, ALRM_MAX);
+ netShutdown(&ptpClock->netPath);
+ free(ptpClock->foreign);
+
+ /* free management and signaling messages, they can have dynamic memory allocated */
+ if(ptpClock->msgTmpHeader.messageType == MANAGEMENT)
+ freeManagementTLV(&ptpClock->msgTmp.manage);
+ freeManagementTLV(&ptpClock->outgoingManageTmp);
+ if(ptpClock->msgTmpHeader.messageType == SIGNALING)
+ freeSignalingTLV(&ptpClock->msgTmp.signaling);
+ freeSignalingTLV(&ptpClock->outgoingSignalingTmp);
+
+#ifdef PTPD_SNMP
+ snmpShutdown();
+#endif /* PTPD_SNMP */
+
+#ifndef PTPD_STATISTICS
+ /* Not running statistics code - write observed drift to driftfile if enabled, inform user */
+ if(ptpClock->defaultDS.slaveOnly && !ptpClock->servo.runningMaxOutput)
+ saveDrift(ptpClock, &rtOpts, FALSE);
+#else
+ ptpClock->oFilterMS.shutdown(&ptpClock->oFilterMS);
+ ptpClock->oFilterSM.shutdown(&ptpClock->oFilterSM);
+ freeDoubleMovingStatFilter(&ptpClock->filterMS);
+ freeDoubleMovingStatFilter(&ptpClock->filterSM);
+
+ /* We are running statistics code - save drift on exit only if we're not monitoring servo stability */
+ if(!rtOpts.servoStabilityDetection && !ptpClock->servo.runningMaxOutput)
+ saveDrift(ptpClock, &rtOpts, FALSE);
+#endif /* PTPD_STATISTICS */
+
+ if (rtOpts.currentConfig != NULL)
+ dictionary_del(&rtOpts.currentConfig);
+ if(rtOpts.cliConfig != NULL)
+ dictionary_del(&rtOpts.cliConfig);
+
+ timerShutdown(ptpClock->timers);
+
+ free(ptpClock);
+ ptpClock = NULL;
+
+ extern PtpClock* G_ptpClock;
+ G_ptpClock = NULL;
+
+
+
+ /* properly clean lockfile (eventough new deaemons can acquire the lock after we die) */
+ if(!rtOpts.ignore_daemon_lock && G_lockFilePointer != NULL) {
+ fclose(G_lockFilePointer);
+ G_lockFilePointer = NULL;
+ }
+ unlink(rtOpts.lockFile);
+
+ if(rtOpts.statusLog.logEnabled) {
+ /* close and remove the status file */
+ if(rtOpts.statusLog.logFP != NULL) {
+ fclose(rtOpts.statusLog.logFP);
+ rtOpts.statusLog.logFP = NULL;
+ }
+ unlink(rtOpts.statusLog.logPath);
+ }
+
+ stopLogging(&rtOpts);
+
+}
+
+void dump_command_line_parameters(int argc, char **argv)
+{
+
+ int i = 0;
+ char sbuf[1000];
+ char *st = sbuf;
+ int len = 0;
+
+ *st = '\0';
+ for(i=0; i < argc; i++){
+ if(strcmp(argv[i],"") == 0)
+ continue;
+ len += snprintf(sbuf + len,
+ sizeof(sbuf) - len,
+ "%s ", argv[i]);
+ }
+ INFO("Starting %s daemon with parameters: %s\n", PTPD_PROGNAME, sbuf);
+}
+
+
+
+PtpClock *
+ptpdStartup(int argc, char **argv, Integer16 * ret, RunTimeOpts * rtOpts)
+{
+ PtpClock * ptpClock;
+ TimeInternal tmpTime;
+ int i = 0;
+
+ /*
+ * Set the default mode for all newly created files - previously
+ * this was not the case for log files. This adds consistency
+ * and allows to use FILE* vs. fds everywhere
+ */
+ umask(~DEFAULT_FILE_PERMS);
+
+ /* get some entropy in... */
+ getTime(&tmpTime);
+ srand(tmpTime.seconds ^ tmpTime.nanoseconds);
+
+ /**
+ * If a required setting, such as interface name, or a setting
+ * requiring a range check is to be set via getopts_long,
+ * the respective currentConfig dictionary entry should be set,
+ * instead of just setting the rtOpts field.
+ *
+ * Config parameter evaluation priority order:
+ * 1. Any dictionary keys set in the getopt_long loop
+ * 2. CLI long section:key type options
+ * 3. Any built-in config templates
+ * 4. Any templates loaded from template file
+ * 5. Config file (parsed last), merged with 2. and 3 - will be overwritten by CLI options
+ * 6. Defaults and any rtOpts fields set in the getopt_long loop
+ **/
+
+ /**
+ * Load defaults. Any options set here and further inside loadCommandLineOptions()
+ * by setting rtOpts fields, will be considered the defaults
+ * for config file and section:key long options.
+ */
+ loadDefaultSettings(rtOpts);
+ /* initialise the config dictionary */
+ rtOpts->candidateConfig = dictionary_new(0);
+ rtOpts->cliConfig = dictionary_new(0);
+
+ /* parse all long section:key options and clean up argv for getopt */
+ loadCommandLineKeys(rtOpts->cliConfig,argc,argv);
+ /* parse the normal short and long options, exit on error */
+ if (!loadCommandLineOptions(rtOpts, rtOpts->cliConfig, argc, argv, ret)) {
+ goto fail;
+ }
+
+ /* Display startup info and argv if not called with -? or -H */
+ NOTIFY("%s version %s starting\n",USER_DESCRIPTION, USER_VERSION);
+ dump_command_line_parameters(argc, argv);
+ /*
+ * we try to catch as many error conditions as possible, but before we call daemon().
+ * the exception is the lock file, as we get a new pid when we call daemon(),
+ * so this is checked twice: once to read, second to read/write
+ */
+ if(geteuid() != 0)
+ {
+ printf("Error: "PTPD_PROGNAME" daemon can only be run as root\n");
+ *ret = 1;
+ goto fail;
+ }
+
+ /* Have we got a config file? */
+ if(strlen(rtOpts->configFile) > 0) {
+ /* config file settings overwrite all others, except for empty strings */
+ INFO("Loading configuration file: %s\n",rtOpts->configFile);
+ if(loadConfigFile(&rtOpts->candidateConfig, rtOpts)) {
+ dictionary_merge(rtOpts->cliConfig, rtOpts->candidateConfig, 1, 1, "from command line");
+ } else {
+ *ret = 1;
+ dictionary_merge(rtOpts->cliConfig, rtOpts->candidateConfig, 1, 1, "from command line");
+ goto configcheck;
+ }
+ } else {
+ dictionary_merge(rtOpts->cliConfig, rtOpts->candidateConfig, 1, 1, "from command line");
+ }
+ /**
+ * This is where the final checking of the candidate settings container happens.
+ * A dictionary is returned with only the known options, explicitly set to defaults
+ * if not present. NULL is returned on any config error - parameters missing, out of range,
+ * etc. The getopt loop in loadCommandLineOptions() only sets keys verified here.
+ */
+ if( ( rtOpts->currentConfig = parseConfig(CFGOP_PARSE, NULL, rtOpts->candidateConfig,rtOpts)) == NULL ) {
+ *ret = 1;
+ dictionary_del(&rtOpts->candidateConfig);
+ goto configcheck;
+ }
+
+ /* we've been told to print the lock file and exit cleanly */
+ if(rtOpts->printLockFile) {
+ printf("%s\n", rtOpts->lockFile);
+ *ret = 0;
+ goto fail;
+ }
+
+ /* we don't need the candidate config any more */
+ dictionary_del(&rtOpts->candidateConfig);
+
+ /* Check network before going into background */
+ if(!testInterface(rtOpts->primaryIfaceName, rtOpts)) {
+ ERROR("Error: Cannot use %s interface\n",rtOpts->primaryIfaceName);
+ *ret = 1;
+ goto configcheck;
+ }
+ if(rtOpts->backupIfaceEnabled && !testInterface(rtOpts->backupIfaceName, rtOpts)) {
+ ERROR("Error: Cannot use %s interface as backup\n",rtOpts->backupIfaceName);
+ *ret = 1;
+ goto configcheck;
+ }
+
+
+configcheck:
+ /*
+ * We've been told to check config only - clean exit before checking locks
+ */
+ if(rtOpts->checkConfigOnly) {
+ if(*ret != 0) {
+ printf("Configuration has errors\n");
+ *ret = 1;
+ }
+ else
+ printf("Configuration OK\n");
+ goto fail;
+ }
+
+ /* Previous errors - exit */
+ if(*ret !=0)
+ goto fail;
+
+ /* First lock check, just to be user-friendly to the operator */
+ if(!rtOpts->ignore_daemon_lock) {
+ if(!writeLockFile(rtOpts)){
+ /* check and create Lock */
+ ERROR("Error: file lock failed (use -L or global:ignore_lock to ignore lock file)\n");
+ *ret = 3;
+ goto fail;
+ }
+ /* check for potential conflicts when automatic lock files are used */
+ if(!checkOtherLocks(rtOpts)) {
+ *ret = 3;
+ goto fail;
+ }
+ }
+
+ /* Manage log files: stats, log, status and quality file */
+ restartLogging(rtOpts);
+
+ /* Allocate memory after we're done with other checks but before going into daemon */
+ ptpClock = (PtpClock *) calloc(1, sizeof(PtpClock));
+ if (!ptpClock) {
+ PERROR("Error: Failed to allocate memory for protocol engine data");
+ *ret = 2;
+ goto fail;
+ } else {
+ DBG("allocated %d bytes for protocol engine data\n",
+ (int)sizeof(PtpClock));
+
+
+ ptpClock->foreign = (ForeignMasterRecord *)
+ calloc(rtOpts->max_foreign_records,
+ sizeof(ForeignMasterRecord));
+ if (!ptpClock->foreign) {
+ PERROR("failed to allocate memory for foreign "
+ "master data");
+ *ret = 2;
+ free(ptpClock);
+ goto fail;
+ } else {
+ DBG("allocated %d bytes for foreign master data\n",
+ (int)(rtOpts->max_foreign_records *
+ sizeof(ForeignMasterRecord)));
+ }
+ }
+
+ if(rtOpts->statisticsLog.logEnabled)
+ ptpClock->resetStatisticsLog = TRUE;
+
+ /* Init to 0 net buffer */
+ memset(ptpClock->msgIbuf, 0, PACKET_SIZE);
+ memset(ptpClock->msgObuf, 0, PACKET_SIZE);
+
+ /* Init outgoing management message */
+ ptpClock->outgoingManageTmp.tlv = NULL;
+
+
+ /* DAEMON */
+#ifdef PTPD_NO_DAEMON
+ if(!rtOpts->nonDaemon){
+ rtOpts->nonDaemon=TRUE;
+ }
+#endif
+
+ if(!rtOpts->nonDaemon){
+ /*
+ * fork to daemon - nochdir non-zero to preserve the working directory:
+ * allows relative paths to be used for log files, config files etc.
+ * Always redirect stdout/err to /dev/null
+ */
+ if (daemon(1,0) == -1) {
+ PERROR("Failed to start as daemon");
+ *ret = 3;
+ goto fail;
+ }
+ INFO(" Info: Now running as a daemon\n");
+ /*
+ * Wait for the parent process to terminate, but not forever.
+ * On some systems this happened after we tried re-acquiring
+ * the lock, so the lock would fail. Hence, we wait.
+ */
+ for (i = 0; i < 1000000; i++) {
+ /* Once we've been reaped by init, parent PID will be 1 */
+ if(getppid() == 1)
+ break;
+ usleep(1);
+ }
+ }
+
+ /* Second lock check, to replace the contents with our own new PID and re-acquire the advisory lock */
+ if(!rtOpts->nonDaemon && !rtOpts->ignore_daemon_lock){
+ /* check and create Lock */
+ if(!writeLockFile(rtOpts)){
+ ERROR("Error: file lock failed (use -L or global:ignore_lock to ignore lock file)\n");
+ *ret = 3;
+ goto fail;
+ }
+ }
+
+#if (defined(linux) && defined(HAVE_SCHED_H)) || defined(HAVE_SYS_CPUSET_H) || defined(__QNXNTO__)
+ /* Try binding to a single CPU core if configured to do so */
+ if(rtOpts->cpuNumber > -1) {
+ if(setCpuAffinity(rtOpts->cpuNumber) < 0) {
+ ERROR("Could not bind to CPU core %d\n", rtOpts->cpuNumber);
+ } else {
+ INFO("Successfully bound "PTPD_PROGNAME" to CPU core %d\n", rtOpts->cpuNumber);
+ }
+ }
+#endif
+
+ /* set up timers */
+ if(!timerSetup(ptpClock->timers)) {
+ PERROR("failed to set up event timers");
+ *ret = 2;
+ free(ptpClock);
+ goto fail;
+ }
+
+ ptpClock->rtOpts = rtOpts;
+
+ /* init alarms */
+ initAlarms(ptpClock->alarms, ALRM_MAX, (void*)ptpClock);
+ configureAlarms(ptpClock->alarms, ALRM_MAX, (void*)ptpClock);
+ ptpClock->alarmDelay = rtOpts->alarmInitialDelay;
+ /* we're delaying alarm processing - disable alarms for now */
+ if(ptpClock->alarmDelay) {
+ enableAlarms(ptpClock->alarms, ALRM_MAX, FALSE);
+ }
+
+
+ /* establish signal handlers */
+ signal(SIGINT, catchSignals);
+ signal(SIGTERM, catchSignals);
+ signal(SIGHUP, catchSignals);
+
+ signal(SIGUSR1, catchSignals);
+ signal(SIGUSR2, catchSignals);
+
+#if defined PTPD_SNMP
+ /* Start SNMP subsystem */
+ if (rtOpts->snmpEnabled)
+ snmpInit(rtOpts, ptpClock);
+#endif
+
+
+
+ NOTICE(USER_DESCRIPTION" started successfully on %s using \"%s\" preset (PID %d)\n",
+ rtOpts->ifaceName,
+ (getPtpPreset(rtOpts->selectedPreset,rtOpts)).presetName,
+ getpid());
+ ptpClock->resetStatisticsLog = TRUE;
+
+#ifdef PTPD_STATISTICS
+
+ outlierFilterSetup(&ptpClock->oFilterMS);
+ outlierFilterSetup(&ptpClock->oFilterSM);
+
+ ptpClock->oFilterMS.init(&ptpClock->oFilterMS,&rtOpts->oFilterMSConfig, "delayMS");
+ ptpClock->oFilterSM.init(&ptpClock->oFilterSM,&rtOpts->oFilterSMConfig, "delaySM");
+
+
+ if(rtOpts->filterMSOpts.enabled) {
+ ptpClock->filterMS = createDoubleMovingStatFilter(&rtOpts->filterMSOpts,"delayMS");
+ }
+
+ if(rtOpts->filterSMOpts.enabled) {
+ ptpClock->filterSM = createDoubleMovingStatFilter(&rtOpts->filterSMOpts, "delaySM");
+ }
+
+#endif
+
+#ifdef PTPD_PCAP
+ ptpClock->netPath.pcapEventSock = -1;
+ ptpClock->netPath.pcapGeneralSock = -1;
+#endif /* PTPD_PCAP */
+
+ ptpClock->netPath.generalSock = -1;
+ ptpClock->netPath.eventSock = -1;
+
+ *ret = 0;
+
+ return ptpClock;
+
+fail:
+ dictionary_del(&rtOpts->cliConfig);
+ dictionary_del(&rtOpts->candidateConfig);
+ dictionary_del(&rtOpts->currentConfig);
+ return 0;
+}
+
+void
+ntpSetup (RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ TimingService *ts = &ptpClock->ntpControl.timingService;
+
+
+
+ if (rtOpts->ntpOptions.enableEngine) {
+ timingDomain.services[1] = ts;
+ strncpy(ts->id, "NTP0", TIMINGSERVICE_MAX_DESC);
+ ts->dataSet.priority1 = 0;
+ ts->dataSet.type = TIMINGSERVICE_NTP;
+ ts->config = &rtOpts->ntpOptions;
+ ts->controller = &ptpClock->ntpControl;
+ /* for now, NTP is considered always active, so will never go idle */
+ ts->timeout = 60;
+ ts->updateInterval = rtOpts->ntpOptions.checkInterval;
+ timingDomain.serviceCount = 2;
+ } else {
+ timingDomain.serviceCount = 1;
+ timingDomain.services[1] = NULL;
+ if(timingDomain.best == ts || timingDomain.current == ts || timingDomain.preferred == ts) {
+ timingDomain.best = timingDomain.current = timingDomain.preferred = NULL;
+ }
+ }
+}
+
+
+
+
diff --git a/rtemsbsd/ptpd/src/dep/statistics.c b/rtemsbsd/ptpd/src/dep/statistics.c
new file mode 100644
index 00000000..c35629f8
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/statistics.c
@@ -0,0 +1,1047 @@
+/*-
+ * Copyright (c) 2013-2015 Wojciech Owczarek
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file statistics.c
+ * @authors Wojciech Owczarek
+ * @date Sun Jul 7 13:28:10 2013
+ * This souece file contains the implementations of functions maintaining and
+ * computing statistical information.
+ */
+
+#include "../ptpd.h"
+
+static int cmpInt32 (const void *vA, const void *vB) {
+
+ int32_t a = *(int32_t*)vA;
+ int32_t b = *(int32_t*)vB;
+
+ return ((a < b) ? -1 : (a > b) ? 1 : 0);
+}
+
+static int cmpDouble (const void *vA, const void *vB) {
+
+ double a = *(double*)vA;
+ double b = *(double*)vB;
+
+ return ((a < b) ? -1 : (a > b) ? 1 : 0);
+}
+
+static int cmpAbsInt32 (const void *vA, const void *vB) {
+
+ int32_t a = abs(*(int32_t*)vA);
+ int32_t b = abs(*(int32_t*)vB);
+
+ return ((a < b) ? -1 : (a > b) ? 1 : 0);
+}
+
+static int cmpAbsDouble (const void *vA, const void *vB) {
+
+ double a = fabs(*(double*)vA);
+ double b = fabs(*(double*)vB);
+
+ return ((a < b) ? -1 : (a > b) ? 1 : 0);
+}
+
+static int32_t median3Int(int32_t *bucket, int count)
+{
+
+ int32_t sortedSamples[count];
+ memcpy(sortedSamples, bucket, count * sizeof(int32_t));
+
+ switch (count) {
+
+ case 1:
+ return bucket[0];
+ case 2:
+ return (bucket[0] + bucket[1]) / 2;
+ case 3:
+ qsort(sortedSamples, count, sizeof(int32_t), cmpInt32);
+ return sortedSamples[1];
+ default:
+ return 0;
+
+ }
+
+}
+
+static double median3Double(double *bucket, int count)
+{
+
+ double sortedSamples[count];
+ memcpy(sortedSamples, bucket, count * sizeof(double));
+
+ switch (count) {
+
+ case 1:
+ return bucket[0];
+ case 2:
+ return (bucket[0] + bucket[1]) / 2.0;
+ case 3:
+ qsort(sortedSamples, count, sizeof(double), cmpInt32);
+ return sortedSamples[1];
+ default:
+ return 0;
+
+ }
+
+}
+
+void
+resetIntPermanentMean(IntPermanentMean* container) {
+
+ container->previous = container->mean;
+ container->mean = 0;
+ container->count = 0;
+
+}
+
+int32_t
+feedIntPermanentMean(IntPermanentMean* container, int32_t sample)
+{
+
+ container->mean += ( sample - container-> mean ) / ++container->count;
+
+ if(container->previous) {
+ container->bufferedMean = (container->previous + container->mean) / 2;
+ } else {
+ container->bufferedMean = container->mean;
+ }
+
+ return container->mean;
+
+}
+
+void
+resetIntPermanentStdDev(IntPermanentStdDev* container) {
+
+ resetIntPermanentMean(&container->meanContainer);
+ container->squareSum = 0;
+ container->stdDev = 0;
+
+}
+
+int32_t
+feedIntPermanentStdDev(IntPermanentStdDev* container, int32_t sample)
+{
+
+ container->squareSum += ( sample - container->meanContainer.mean) *
+ ( sample - feedIntPermanentMean(&container->meanContainer, sample ));
+
+ if(container->meanContainer.count > 1) {
+ container->stdDev = sqrt( container->squareSum /
+ (container->meanContainer.count - 1) );
+ } else {
+ container->stdDev = 0;
+ }
+
+ return container->stdDev;
+
+}
+
+void
+resetIntPermanentMedian(IntPermanentMedian* container) {
+
+ memset(container, 0, sizeof(IntPermanentMedian));
+
+}
+
+int32_t
+feedIntPermanentMedian(IntPermanentMedian* container, int32_t sample)
+{
+
+ container->bucket[container->count] = sample;
+ container->count++;
+
+ container->median = median3Int(container->bucket, container->count);
+
+ if(container->count == 3) {
+ container->count=1;
+ container->bucket[1] = container->bucket[2] = 0;
+ container->bucket[0] = container->median;
+ }
+
+ return container->median;
+
+}
+
+
+void
+resetDoublePermanentMean(DoublePermanentMean* container)
+{
+ if(container == NULL)
+ return;
+ container->previous = container->mean;
+ container->mean = 0.0;
+ container->count = 0;
+
+}
+
+double
+feedDoublePermanentMean(DoublePermanentMean* container, double sample)
+{
+
+ container->mean += ( sample - container-> mean ) / ++container->count;
+
+ if(container->previous) {
+ container->bufferedMean = (container->previous + container->mean) / 2;
+ } else {
+ container->bufferedMean = container->mean;
+ }
+
+ return container->mean;
+
+}
+
+void
+resetDoublePermanentStdDev(DoublePermanentStdDev* container)
+{
+
+ if(container == NULL)
+ return;
+ resetDoublePermanentMean(&container->meanContainer);
+ container->squareSum = 0.0;
+ container->stdDev = 0.0;
+
+}
+
+double
+feedDoublePermanentStdDev(DoublePermanentStdDev* container, double sample)
+{
+
+ container->squareSum += ( sample - container->meanContainer.mean) *
+ ( sample - feedDoublePermanentMean(&container->meanContainer, sample )) ;
+
+ if(container->meanContainer.count > 1) {
+ container->stdDev = sqrt(container->squareSum /
+ ((container->meanContainer.count+0.0) - 1.0)) ;
+ } else {
+ container->stdDev = 0;
+ }
+
+ return container->stdDev;
+
+}
+
+void
+resetDoublePermanentMedian(DoublePermanentMedian* container)
+{
+
+ memset(container, 0, sizeof(DoublePermanentMedian));
+
+}
+
+double
+feedDoublePermanentMedian(DoublePermanentMedian* container, double sample)
+{
+
+ container->bucket[container->count] = sample;
+ container->count++;
+
+ container->median = median3Double(container->bucket, container->count);
+
+ if(container->count == 3) {
+ container->count=1;
+ container->bucket[1] = container->bucket[2] = 0;
+ container->bucket[0] = container->median;
+ }
+
+ return container->median;
+
+}
+
+
+/* Moving statistics - up to last n samples */
+
+IntMovingMean*
+createIntMovingMean(int capacity)
+{
+
+ IntMovingMean* container;
+ if ( !(container = calloc (1, sizeof(IntMovingMean))) ) {
+ return NULL;
+ }
+
+ container->capacity = (capacity > STATCONTAINER_MAX_SAMPLES ) ?
+ STATCONTAINER_MAX_SAMPLES : capacity;
+
+ if ( !(container->samples = calloc (container->capacity, sizeof(int32_t))) ) {
+ free(container);
+ return NULL;
+ }
+
+ return container;
+
+}
+
+void
+freeIntMovingMean(IntMovingMean** container)
+{
+
+ free((*container)->samples);
+ free(*container);
+ *container = NULL;
+
+}
+
+void
+resetIntMovingMean(IntMovingMean* container)
+{
+
+ if(container == NULL)
+ return;
+ container->sum = 0;
+ container->mean = 0;
+ container->count = 0;
+ memset(container->samples, 0, sizeof(&container->samples));
+
+}
+
+int32_t
+feedIntMovingMean(IntMovingMean* container, int32_t sample)
+{
+
+ if(container == NULL) return 0;
+
+ /* sample buffer is full */
+ if ( container->count == container->capacity ) {
+ /* keep the sum current - drop the oldest value */
+ container->sum -= container->samples[0];
+ /* shift the sample buffer left, dropping the oldest sample */
+ memmove(container->samples, container->samples + 1,
+ sizeof(sample) * (container->capacity - 1) );
+ /* counter will be incremented further, so we decrement it here */
+ container->count--;
+ container->full = TRUE;
+ }
+
+ container->samples[container->count++] = sample;
+ container->sum += sample;
+ container->mean = container->sum / container->count;
+
+ return container->mean;
+
+}
+
+IntMovingStdDev* createIntMovingStdDev(int capacity)
+{
+
+ IntMovingStdDev* container;
+ if ( !(container = calloc (1, sizeof(IntMovingStdDev))) ) {
+ return NULL;
+ }
+
+ if((container->meanContainer = createIntMovingMean(capacity))
+ == NULL) {
+ free(container);
+ return NULL;
+ }
+
+ return container;
+
+}
+
+void
+freeIntMovingStdDev(IntMovingStdDev** container)
+{
+
+ freeIntMovingMean(&((*container)->meanContainer));
+ free(*container);
+ *container = NULL;
+
+}
+
+void
+resetIntMovingStdDev(IntMovingStdDev* container)
+{
+
+ if(container == NULL)
+ return;
+ resetIntMovingMean(container->meanContainer);
+ container->squareSum = 0;
+ container->stdDev = 0;
+
+}
+
+int32_t
+feedIntMovingStdDev(IntMovingStdDev* container, int32_t sample)
+{
+
+ int i = 0;
+
+ if(container == NULL)
+ return 0;
+
+ feedIntMovingMean(container->meanContainer, sample);
+
+ if (container->meanContainer->count < 2) {
+ container->stdDev = 0;
+ } else {
+
+ container->squareSum = 0;
+ for(i = 0; i < container->meanContainer->count; i++) {
+ container->squareSum += ( container->meanContainer->samples[i] - container->meanContainer->mean ) *
+ ( container->meanContainer->samples[i] - container->meanContainer->mean );
+ }
+
+ container->stdDev = sqrt ( container->squareSum /
+ (container->meanContainer->count - 1 ));
+
+ }
+
+ return container->stdDev;
+
+}
+
+DoubleMovingMean*
+createDoubleMovingMean(int capacity)
+{
+
+ DoubleMovingMean* container;
+ if ( !(container = calloc (1, sizeof(DoubleMovingMean))) ) {
+ return NULL;
+ }
+
+ container->capacity = (capacity > STATCONTAINER_MAX_SAMPLES ) ?
+ STATCONTAINER_MAX_SAMPLES : capacity;
+ if ( !(container->samples = calloc(container->capacity, sizeof(double))) ) {
+ free(container);
+ return NULL;
+ }
+ return container;
+
+}
+
+void
+freeDoubleMovingMean(DoubleMovingMean** container)
+{
+
+ if(*container == NULL) {
+ return;
+ }
+ free((*container)->samples);
+ free(*container);
+ *container = NULL;
+
+}
+
+void
+resetDoubleMovingMean(DoubleMovingMean* container)
+{
+
+ if(container == NULL)
+ return;
+ container->sum = 0;
+ container->mean = 0;
+ container->count = 0;
+ container->counter = 0;
+ memset(container->samples, 0, sizeof(&container->samples));
+
+}
+
+
+double
+feedDoubleMovingMean(DoubleMovingMean* container, double sample)
+{
+
+ if(container == NULL)
+ return 0;
+ /* sample buffer is full */
+ if ( container->count == container->capacity ) {
+ /* keep the sum current - drop the oldest value */
+ container->sum -= container->samples[0];
+ /* shift the sample buffer left, dropping the oldest sample */
+ memmove(container->samples, container->samples + 1,
+ sizeof(sample) * (container->capacity - 1) );
+ /* counter will be incremented further, so we decrement it here */
+ container->count--;
+ container->full = TRUE;
+ }
+ container->samples[container->count++] = sample;
+ container->sum += sample;
+ container->mean = container->sum / container->count;
+
+ container->counter++;
+ container->counter = container->counter % container->capacity;
+// INFO("c: %d\n", container->counter);
+
+ return container->mean;
+
+}
+
+DoubleMovingStdDev* createDoubleMovingStdDev(int capacity)
+{
+
+ DoubleMovingStdDev* container;
+ if ( !(container = calloc (1, sizeof(DoubleMovingStdDev))) ) {
+ return NULL;
+ }
+
+ if((container->meanContainer = createDoubleMovingMean(capacity))
+ == NULL) {
+ free(container);
+ return NULL;
+ }
+
+ return container;
+
+}
+
+void
+freeDoubleMovingStdDev(DoubleMovingStdDev** container)
+{
+
+ freeDoubleMovingMean(&((*container)->meanContainer));
+ free(*container);
+ *container = NULL;
+
+}
+
+void
+resetDoubleMovingStdDev(DoubleMovingStdDev* container)
+{
+
+ if(container == NULL)
+ return;
+ resetDoubleMovingMean(container->meanContainer);
+ container->squareSum = 0.0;
+ container->stdDev = 0.0;
+
+}
+double
+feedDoubleMovingStdDev(DoubleMovingStdDev* container, double sample)
+{
+
+ int i = 0;
+
+ if(container == NULL)
+ return 0.0;
+
+ feedDoubleMovingMean(container->meanContainer, sample);
+
+ if (container->meanContainer->count < 2) {
+ container->stdDev = 0.0;
+ } else {
+
+ container->squareSum = 0.0;
+ for(i = 0; i < container->meanContainer->count; i++) {
+ container->squareSum += ( container->meanContainer->samples[i] - container->meanContainer->mean ) *
+ ( container->meanContainer->samples[i] - container->meanContainer->mean );
+ }
+
+ container->stdDev = sqrt ( container->squareSum /
+ (container->meanContainer->count - 1));
+
+ }
+
+ if(container->meanContainer->counter == 0) {
+ container->periodicStdDev = container->stdDev;
+ }
+
+ return container->stdDev;
+
+}
+
+IntMovingStatFilter* createIntMovingStatFilter(StatFilterOptions *config, const char* id)
+{
+
+ IntMovingStatFilter* container;
+
+ if(config->filterType > FILTER_MAXVALUE) {
+ return NULL;
+ }
+
+ if ( !(container = calloc (1, sizeof(IntMovingStatFilter))) ) {
+ return NULL;
+ }
+
+ if((container->meanContainer = createIntMovingMean(config->windowSize))
+ == NULL) {
+ free(container);
+ return NULL;
+ }
+
+ container->filterType = config->filterType;
+ container->windowType = config->windowType;
+
+ if(config->windowSize < 2) container->windowType = WINDOW_SLIDING;
+
+ strncpy(container->identifier, id, 10);
+
+ return container;
+
+}
+
+void
+freeIntMovingStatFilter(IntMovingStatFilter** container)
+{
+
+ freeIntMovingMean(&((*container)->meanContainer));
+ free(*container);
+ *container = NULL;
+
+}
+
+void
+resetIntMovingStatFilter(IntMovingStatFilter* container)
+{
+
+ if(container == NULL)
+ return;
+ resetIntMovingMean(container->meanContainer);
+ container->output = 0;
+
+}
+
+Boolean
+feedIntMovingStatFilter(IntMovingStatFilter* container, int32_t sample)
+{
+ if(container == NULL)
+ return 0;
+
+ if(container->filterType == FILTER_NONE) {
+
+ container->output = sample;
+ return TRUE;
+
+ } else {
+ /* cheat - the mean container is used as a general purpose sliding window */
+ feedIntMovingMean(container->meanContainer, sample);
+
+ }
+
+ container->counter++;
+ container->counter = container->counter % container->meanContainer->capacity;
+
+ switch(container->filterType) {
+
+ case FILTER_MEAN:
+
+ container->output = container->meanContainer->mean;
+ break;
+
+ case FILTER_MEDIAN:
+ {
+ int count = container->meanContainer->count;
+ int32_t sortedSamples[count];
+
+ Boolean odd = ((count %2 ) == 1);
+
+ memcpy(sortedSamples, container->meanContainer->samples, count * sizeof(sample));
+
+ qsort(sortedSamples, count, sizeof(sample), cmpInt32);
+
+ if(odd) {
+
+ container->output = sortedSamples[(count / 2)];
+
+ } else {
+
+ container->output = (sortedSamples[(count / 2) -1] + sortedSamples[(count / 2)]) / 2;
+
+ }
+
+ }
+ break;
+
+ case FILTER_MIN:
+ {
+ int count = container->meanContainer->count;
+ int32_t sortedSamples[count];
+ memcpy(sortedSamples, container->meanContainer->samples, count * sizeof(sample));
+ qsort(sortedSamples, count, sizeof(sample), cmpInt32);
+ container->output = sortedSamples[0];
+
+ }
+ break;
+
+ case FILTER_MAX:
+ {
+ int count = container->meanContainer->count;
+ int32_t sortedSamples[count];
+ memcpy(sortedSamples, container->meanContainer->samples, count * sizeof(sample));
+ qsort(sortedSamples, count, sizeof(sample), cmpInt32);
+ container->output = sortedSamples[count - 1];
+
+ }
+ break;
+
+ case FILTER_ABSMIN:
+ {
+ int count = container->meanContainer->count;
+ int32_t sortedSamples[count];
+ memcpy(sortedSamples, container->meanContainer->samples, count * sizeof(sample));
+ qsort(sortedSamples, count, sizeof(sample), cmpAbsInt32);
+ container->output = sortedSamples[0];
+
+ }
+ break;
+
+ case FILTER_ABSMAX:
+ {
+ int count = container->meanContainer->count;
+ int32_t sortedSamples[count];
+ memcpy(sortedSamples, container->meanContainer->samples, count * sizeof(sample));
+ qsort(sortedSamples, count, sizeof(sample), cmpAbsInt32);
+ container->output = sortedSamples[count - 1];
+
+ }
+ break;
+ default:
+ container->output = sample;
+ return TRUE;
+ }
+
+
+ DBGV("filter %s, Sample %d output %d\n", container->identifier, sample, container->output);
+
+ if((container->windowType == WINDOW_INTERVAL) && (container->counter != 0)) {
+ return FALSE;
+ }
+ return TRUE;
+
+}
+
+DoubleMovingStatFilter* createDoubleMovingStatFilter(StatFilterOptions *config, const char* id)
+{
+ DoubleMovingStatFilter* container;
+
+ if(config->filterType > FILTER_MAXVALUE) {
+ return NULL;
+ }
+
+ if ( !(container = calloc (1, sizeof(DoubleMovingStatFilter))) ) {
+ return NULL;
+ }
+
+ if((container->meanContainer = createDoubleMovingMean(config->windowSize))
+ == NULL) {
+ free(container);
+ return NULL;
+ }
+
+ container->filterType = config->filterType;
+ container->windowType = config->windowType;
+
+ if(config->windowSize < 2) container->windowType = WINDOW_SLIDING;
+
+ strncpy(container->identifier, id, 10);
+
+ return container;
+
+}
+
+void
+freeDoubleMovingStatFilter(DoubleMovingStatFilter** container)
+{
+
+ if((container==NULL) || (*container==NULL)) {
+ return;
+ }
+ freeDoubleMovingMean(&((*container)->meanContainer));
+ free(*container);
+ *container = NULL;
+
+}
+
+void
+resetDoubleMovingStatFilter(DoubleMovingStatFilter* container)
+{
+
+ if(container == NULL)
+ return;
+ resetDoubleMovingMean(container->meanContainer);
+ container->output = 0;
+
+}
+
+Boolean
+feedDoubleMovingStatFilter(DoubleMovingStatFilter* container, double sample)
+{
+
+ if(container == NULL)
+ return 0;
+
+ if(container->filterType == FILTER_NONE) {
+
+ container->output = sample;
+ return TRUE;
+
+ } else {
+ /* cheat - the mean container is used as a general purpose sliding window */
+ feedDoubleMovingMean(container->meanContainer, sample);
+ }
+
+ container->counter++;
+ container->counter = container->counter % container->meanContainer->capacity;
+
+ switch(container->filterType) {
+
+ case FILTER_MEAN:
+
+ container->output = container->meanContainer->mean;
+ break;
+
+ case FILTER_MEDIAN:
+ {
+ int count = container->meanContainer->count;
+ double sortedSamples[count];
+
+ Boolean odd = ((count %2 ) == 1);
+
+ memcpy(sortedSamples, container->meanContainer->samples, count * sizeof(sample));
+
+ qsort(sortedSamples, count, sizeof(sample), cmpDouble);
+
+ if(odd) {
+
+ container->output = sortedSamples[(count / 2)];
+
+ } else {
+
+ container->output = (sortedSamples[(count / 2) -1] + sortedSamples[(count / 2)]) / 2;
+
+ }
+
+ }
+ break;
+
+ case FILTER_MIN:
+ {
+ int count = container->meanContainer->count;
+ double sortedSamples[count];
+ memcpy(sortedSamples, container->meanContainer->samples, count * sizeof(sample));
+ qsort(sortedSamples, count, sizeof(sample), cmpDouble);
+ container->output = sortedSamples[0];
+
+ }
+ break;
+
+ case FILTER_MAX:
+ {
+ int count = container->meanContainer->count;
+ double sortedSamples[count];
+ memcpy(sortedSamples, container->meanContainer->samples, count * sizeof(sample));
+ qsort(sortedSamples, count, sizeof(sample), cmpDouble);
+ container->output = sortedSamples[count - 1];
+
+ }
+ break;
+
+ case FILTER_ABSMIN:
+ {
+ int count = container->meanContainer->count;
+ double sortedSamples[count];
+ memcpy(sortedSamples, container->meanContainer->samples, count * sizeof(sample));
+ qsort(sortedSamples, count, sizeof(sample), cmpAbsDouble);
+ container->output = sortedSamples[0];
+
+ }
+ break;
+
+ case FILTER_ABSMAX:
+ {
+ int count = container->meanContainer->count;
+ double sortedSamples[count];
+ memcpy(sortedSamples, container->meanContainer->samples, count * sizeof(sample));
+ qsort(sortedSamples, count, sizeof(sample), cmpAbsDouble);
+ container->output = sortedSamples[count - 1];
+
+ }
+ break;
+ default:
+ container->output = sample;
+ return TRUE;
+ }
+
+ DBGV("Filter %s, Sample %.09f output %.09f\n", container->identifier, sample, container->output);
+
+ if((container->windowType == WINDOW_INTERVAL) && (container->counter != 0)) {
+ return FALSE;
+ }
+ return TRUE;
+
+}
+
+double getpeircesCriterion(int numObservations, int numDoubtful) {
+
+ static const double peircesTable[60][9] = {
+/* 1 - 10 samples */
+ {-1, -1, -1, -1, -1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1, -1, -1, -1, -1},
+ {1.196, -1, -1, -1, -1, -1, -1, -1, -1},
+ {1.383, 1.078, -1, -1, -1, -1, -1, -1, -1},
+ {1.509, 1.2, -1, -1, -1, -1, -1, -1, -1},
+ {1.61, 1.299, 1.099, -1, -1, -1, -1, -1, -1},
+ {1.693, 1.382, 1.187, 1.022, -1, -1, -1, -1, -1},
+ {1.763, 1.453, 1.261, 1.109, -1, -1, -1, -1, -1},
+ {1.824, 1.515, 1.324, 1.178, 1.045, -1, -1, -1, -1},
+ {1.878, 1.57, 1.38, 1.237, 1.114, -1, -1, -1, -1},
+/* 11 - 20 samples */
+ {1.925, 1.619, 1.43, 1.289, 1.172, 1.059, -1, -1, -1},
+ {1.969, 1.663, 1.475, 1.336, 1.221, 1.118, 1.009, -1, -1},
+ {2.007, 1.704, 1.516, 1.379, 1.266, 1.167, 1.07, -1, -1},
+ {2.043, 1.741, 1.554, 1.417, 1.307, 1.21, 1.12, 1.026, -1},
+ {2.076, 1.775, 1.589, 1.453, 1.344, 1.249, 1.164, 1.078, -1},
+ {2.106, 1.807, 1.622, 1.486, 1.378, 1.285, 1.202, 1.122, 1.039},
+ {2.134, 1.836, 1.652, 1.517, 1.409, 1.318, 1.237, 1.161, 1.084},
+ {2.161, 1.864, 1.68, 1.546, 1.438, 1.348, 1.268, 1.195, 1.123},
+ {2.185, 1.89, 1.707, 1.573, 1.466, 1.377, 1.298, 1.226, 1.158},
+ {2.209, 1.914, 1.732, 1.599, 1.492, 1.404, 1.326, 1.255, 1.19},
+/* 21 - 30 samples */
+ {2.23, 1.938, 1.756, 1.623, 1.517, 1.429, 1.352, 1.282, 1.218},
+ {2.251, 1.96, 1.779, 1.646, 1.54, 1.452, 1.376, 1.308, 1.245},
+ {2.271, 1.981, 1.8, 1.668, 1.563, 1.475, 1.399, 1.332, 1.27},
+ {2.29, 2, 1.821, 1.689, 1.584, 1.497, 1.421, 1.354, 1.293},
+ {2.307, 2.019, 1.84, 1.709, 1.604, 1.517, 1.442, 1.375, 1.315},
+ {2.324, 2.037, 1.859, 1.728, 1.624, 1.537, 1.462, 1.396, 1.336},
+ {2.341, 2.055, 1.877, 1.746, 1.642, 1.556, 1.481, 1.415, 1.356},
+ {2.356, 2.071, 1.894, 1.764, 1.66, 1.574, 1.5, 1.434, 1.375},
+ {2.371, 2.088, 1.911, 1.781, 1.677, 1.591, 1.517, 1.452, 1.393},
+/* 31 - 40 samples */
+ {2.385, 2.103, 1.927, 1.797, 1.694, 1.608, 1.534, 1.469, 1.411},
+ {2.399, 2.118, 1.942, 1.812, 1.71, 1.624, 1.55, 1.486, 1.428},
+ {2.412, 2.132, 1.957, 1.828, 1.725, 1.64, 1.567, 1.502, 1.444},
+ {2.425, 2.146, 1.971, 1.842, 1.74, 1.655, 1.582, 1.517, 1.459},
+ {2.438, 2.159, 1.985, 1.856, 1.754, 1.669, 1.597, 1.532, 1.475},
+ {2.45, 2.172, 1.998, 1.87, 1.768, 1.683, 1.611, 1.547, 1.489},
+ {2.461, 2.184, 2.011, 1.883, 1.782, 1.697, 1.624, 1.561, 1.504},
+ {2.472, 2.196, 2.024, 1.896, 1.795, 1.711, 1.638, 1.574, 1.517},
+ {2.483, 2.208, 2.036, 1.909, 1.807, 1.723, 1.651, 1.587, 1.531},
+/* 41 - 50 samples */
+ {2.494, 2.219, 2.047, 1.921, 1.82, 1.736, 1.664, 1.6, 1.544},
+ {2.504, 2.23, 2.059, 1.932, 1.832, 1.748, 1.676, 1.613, 1.556},
+ {2.514, 2.241, 2.07, 1.944, 1.843, 1.76, 1.688, 1.625, 1.568},
+ {2.524, 2.251, 2.081, 1.955, 1.855, 1.771, 1.699, 1.636, 1.58},
+ {2.533, 2.261, 2.092, 1.966, 1.866, 1.783, 1.711, 1.648, 1.592},
+ {2.542, 2.271, 2.102, 1.976, 1.876, 1.794, 1.722, 1.659, 1.603},
+ {2.551, 2.281, 2.112, 1.987, 1.887, 1.804, 1.733, 1.67, 1.614},
+ {2.56, 2.29, 2.122, 1.997, 1.897, 1.815, 1.743, 1.681, 1.625},
+ {2.568, 2.299, 2.131, 2.006, 1.907, 1.825, 1.754, 1.691, 1.636},
+ {2.577, 2.308, 2.14, 2.016, 1.917, 1.835, 1.764, 1.701, 1.646},
+/* 51 - 60 * samples */
+ {2.585, 2.317, 2.149, 2.026, 1.927, 1.844, 1.773, 1.711, 1.656},
+ {2.592, 2.326, 2.158, 2.035, 1.936, 1.854, 1.783, 1.721, 1.666},
+ {2.6, 2.334, 2.167, 2.044, 1.945, 1.863, 1.792, 1.73, 1.675},
+ {2.608, 2.342, 2.175, 2.052, 1.954, 1.872, 1.802, 1.74, 1.685},
+ {2.615, 2.35, 2.184, 2.061, 1.963, 1.881, 1.811, 1.749, 1.694},
+ {2.622, 2.358, 2.192, 2.069, 1.972, 1.89, 1.82, 1.758, 1.703},
+ {2.629, 2.365, 2.2, 2.077, 1.98, 1.898, 1.828, 1.767, 1.711},
+ {2.636, 2.373, 2.207, 2.085, 1.988, 1.907, 1.837, 1.775, 1.72},
+ {2.643, 2.38, 2.215, 2.093, 1.996, 1.915, 1.845, 1.784, 1.729},
+ {2.65, 2.387, 2.223, 2.101, 2.004, 1.923, 1.853, 1.792, 1.737},
+ {2.656, 2.394, 2.23, 2.109, 2.012, 1.931, 1.861, 1.8, 1.745},
+ {2.663, 2.401, 2.237, 2.116, 2.019, 1.939, 1.869, 1.808, 1.753},
+ };
+
+ if ( numObservations < 1 || numObservations > 60)
+ return -1.0;
+ if ( numDoubtful < 1 || numDoubtful > 9)
+ return -1.0;
+
+ return(peircesTable[numObservations - 1][numDoubtful - 1]);
+
+}
+
+Boolean isIntPeircesOutlier(IntMovingStdDev *container, int32_t sample, double threshold) {
+
+ double maxDev;
+
+ /* Sanity check - race condition was seen when enabling and disabling filters repeatedly */
+ if(container == NULL || container->meanContainer == NULL)
+ return FALSE;
+
+ maxDev = container->stdDev * getpeircesCriterion(container->meanContainer->count, 1) * threshold;
+
+ /*
+ * Two cases:
+ * - Too early - we got a -1 from Peirce's table,
+ * - safeguard: std dev is zero and filter is blocking
+ * everything, hus, we let the sample through
+ */
+ if (maxDev <= 0.0 ) return FALSE;
+
+ if(fabs((double)(sample - container->meanContainer->mean)) > maxDev) {
+ DBGV("Peirce %s outlier: val: %d, cnt: %d, mxd: %.09f (%.03f * dev * %.03f), dev: %.09f, mea: %.09f, dif: %.09f\n", container->identifier,
+ sample, container->meanContainer->count, maxDev, getpeircesCriterion(container->meanContainer->count, 1), threshold,
+ container->stdDev,
+ container->meanContainer->mean, fabs(sample - container->meanContainer->mean));
+ return TRUE;
+ }
+
+ return FALSE;
+
+}
+
+Boolean isDoublePeircesOutlier(DoubleMovingStdDev *container, double sample, double threshold) {
+
+ double maxDev;
+
+ /* Sanity check - race condition was seen when enabling and disabling filters repeatedly */
+ if(container == NULL || container->meanContainer == NULL)
+ return FALSE;
+
+ maxDev = container->stdDev * getpeircesCriterion(container->meanContainer->count, 1) * threshold;
+
+ /*
+ * Two cases:
+ * - Too early - we got a -1 from Peirce's table,
+ * - safeguard: std dev is zero and filter is blocking
+ * everything, thus, we let the sample through
+ */
+ if (maxDev <= 0.0 ) {
+ return FALSE;
+ }
+
+ if(fabs((double)(sample - container->meanContainer->mean)) > maxDev) {
+ DBG("Peirce %s outlier: val: %.09f, cnt: %d, mxd: %.09f (%.03f * dev * %.03f), dev: %.09f, mea: %.09f, dif: %.09f\n", container->identifier,
+ sample, container->meanContainer->count, maxDev, getpeircesCriterion(container->meanContainer->count, 1), threshold,
+ container->stdDev,
+ container->meanContainer->mean, fabs(sample - container->meanContainer->mean));
+ return TRUE;
+ }
+
+ return FALSE;
+
+}
+
+void
+clearPtpEngineSlaveStats(PtpEngineSlaveStats* stats)
+{
+ memset(stats, 0, sizeof(*stats));
+}
+
+void
+resetPtpEngineSlaveStats(PtpEngineSlaveStats* stats) {
+
+ resetDoublePermanentStdDev(&stats->ofmStats);
+ resetDoublePermanentStdDev(&stats->mpdStats);
+ resetDoublePermanentMedian(&stats->ofmMedianContainer);
+ resetDoublePermanentMedian(&stats->mpdMedianContainer);
+ stats->ofmStatsUpdated = FALSE;
+ stats->mpdStatsUpdated = FALSE;
+}
diff --git a/rtemsbsd/ptpd/src/dep/statistics.h b/rtemsbsd/ptpd/src/dep/statistics.h
new file mode 100644
index 00000000..8383db9b
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/statistics.h
@@ -0,0 +1,230 @@
+/**
+ * @file statistics.h
+ * @authors Wojciech Owczarek
+ * @date Sun Jul 7 13:28:10 2013
+ *
+ * This header file contains the definitions of structures and functions
+ * implementing standard statistical measures - means and standard deviations,
+ * both complete (from t0 to tnow) and moving (from t0+n*windowsize to t0+(n+1)*windowsize)
+ */
+
+#ifndef STATISTICS_H_
+#define STATISTICS_H_
+
+#define STATCONTAINER_MAX_SAMPLES 60
+
+/* "Permanent" i.e. non-moving statistics containers - useful for long term measurement */
+
+typedef struct {
+
+ int32_t mean;
+ int32_t previous;
+ int32_t bufferedMean;
+ int32_t count;
+
+} IntPermanentMean;
+
+typedef struct {
+
+ double mean;
+ double previous;
+ double bufferedMean;
+ double count;
+
+} DoublePermanentMean;
+
+typedef struct {
+
+ IntPermanentMean meanContainer;
+ int32_t squareSum;
+ int32_t stdDev;
+
+} IntPermanentStdDev;
+
+typedef struct {
+
+ DoublePermanentMean meanContainer;
+ double squareSum;
+ double stdDev;
+
+} DoublePermanentStdDev;
+
+typedef struct {
+ int32_t median;
+ int32_t bucket[3];
+ uint8_t count;
+} IntPermanentMedian;
+
+typedef struct {
+ double median;
+ double bucket[3];
+ uint8_t count;
+} DoublePermanentMedian;
+
+void resetIntPermanentMean(IntPermanentMean* container);
+int32_t feedIntPermanentMean(IntPermanentMean* container, int32_t sample);
+void resetIntPermanentStdDev(IntPermanentStdDev* container);
+int32_t feedIntPermanentStdDev(IntPermanentStdDev* container, int32_t sample);
+void resetIntPermanentMedian(IntPermanentMedian* container);
+int32_t feedIntPermanentMedian(IntPermanentMedian* container, int32_t sample);
+
+void resetDoublePermanentMean(DoublePermanentMean* container);
+double feedDoublePermanentMean(DoublePermanentMean* container, double sample);
+void resetDoublePermanentStdDev(DoublePermanentStdDev* container);
+double feedDoublePermanentStdDev(DoublePermanentStdDev* container, double sample);
+void resetDoublePermanentMedian(DoublePermanentMedian* container);
+double feedDoublePermanentMedian(DoublePermanentMedian* container, double sample);
+
+/* Moving statistics - up to last n samples */
+
+typedef struct {
+
+ int32_t mean;
+ int32_t sum;
+ int32_t* samples;
+ Boolean full;
+ int count;
+ int capacity;
+
+} IntMovingMean;
+
+typedef struct {
+
+ double mean;
+ double sum;
+ double* samples;
+ Boolean full;
+ int count;
+ int counter;
+ int capacity;
+
+} DoubleMovingMean;
+
+typedef struct {
+
+ IntMovingMean* meanContainer;
+ int32_t squareSum;
+ int32_t stdDev;
+ char* identifier[10];
+
+} IntMovingStdDev;
+
+typedef struct {
+
+ DoubleMovingMean* meanContainer;
+ double squareSum;
+ double stdDev;
+ double periodicStdDev;
+ char identifier[10];
+
+} DoubleMovingStdDev;
+
+typedef struct {
+
+ IntMovingMean* meanContainer;
+ int32_t output;
+ int32_t* sortedSamples;
+ char identifier[10];
+ int counter;
+ uint8_t filterType;
+ uint8_t windowType;
+
+} IntMovingStatFilter;
+
+typedef struct {
+
+ DoubleMovingMean* meanContainer;
+ double output;
+ double* sortedSamples;
+ char identifier[10];
+ int counter;
+ uint8_t filterType;
+ uint8_t windowType;
+
+} DoubleMovingStatFilter;
+
+typedef struct {
+
+ Boolean enabled;
+ uint8_t filterType;
+ int windowSize;
+ uint8_t windowType;
+
+} StatFilterOptions;
+
+IntMovingMean* createIntMovingMean(int capacity);
+void freeIntMovingMean(IntMovingMean** container);
+void resetIntMovingMean(IntMovingMean* container);
+int32_t feedIntMovingMean(IntMovingMean* container, int32_t sample);
+
+IntMovingStdDev* createIntMovingStdDev(int capacity);
+void freeIntMovingStdDev(IntMovingStdDev** container);
+void resetIntMovingStdDev(IntMovingStdDev* container);
+int32_t feedIntMovingStdDev(IntMovingStdDev* container, int32_t sample);
+
+DoubleMovingMean* createDoubleMovingMean(int capacity);
+void freeDoubleMovingMean(DoubleMovingMean** container);
+void resetDoubleMovingMean(DoubleMovingMean* container);
+double feedDoubleMovingMean(DoubleMovingMean* container, double sample);
+
+DoubleMovingStdDev* createDoubleMovingStdDev(int capacity);
+void freeDoubleMovingStdDev(DoubleMovingStdDev** container);
+void resetDoubleMovingStdDev(DoubleMovingStdDev* container);
+double feedDoubleMovingStdDev(DoubleMovingStdDev* container, double sample);
+
+IntMovingStatFilter* createIntMovingStatFilter(StatFilterOptions* config, const char* id);
+void freeIntMovingStatFilter(IntMovingStatFilter** container);
+void resetIntMovingStatFilter(IntMovingStatFilter* container);
+Boolean feedIntMovingStatFilter(IntMovingStatFilter* container, int32_t sample);
+
+DoubleMovingStatFilter* createDoubleMovingStatFilter(StatFilterOptions* config, const char* id);
+void freeDoubleMovingStatFilter(DoubleMovingStatFilter** container);
+void resetDoubleMovingStatFilter(DoubleMovingStatFilter* container);
+Boolean feedDoubleMovingStatFilter(DoubleMovingStatFilter* container, double sample);
+
+void intStatsTest(int32_t sample);
+void doubleStatsTest(double sample);
+
+double getPeircesCriterion(int numObservations, int numDoubtful);
+
+Boolean isIntPeircesOutlier(IntMovingStdDev *container, int32_t sample, double threshold);
+Boolean isDoublePeircesOutlier(DoubleMovingStdDev *container, double sample, double threshold);
+
+/**
+ * \struct PtpEngineSlaveStats
+ * \brief Ptpd clock statistics per port
+ */
+typedef struct
+{
+ Boolean statsCalculated;
+ Boolean ofmStatsUpdated;
+ Boolean mpdStatsUpdated;
+ double ofmMean;
+ double ofmStdDev;
+ double ofmMedian;
+ double ofmMin;
+ double ofmMinFinal;
+ double ofmMax;
+ double ofmMaxFinal;
+ double mpdMean;
+ double mpdStdDev;
+ double mpdMedian;
+ double mpdMin;
+ double mpdMinFinal;
+ double mpdMax;
+ double mpdMaxFinal;
+ Boolean mpdIsStable;
+ double mpdStabilityThreshold;
+ int mpdStabilityPeriod;
+ DoublePermanentStdDev ofmStats;
+ DoublePermanentStdDev mpdStats;
+ DoublePermanentMedian ofmMedianContainer;
+ DoublePermanentMedian mpdMedianContainer;
+} PtpEngineSlaveStats;
+
+void clearPtpEngineSlaveStats(PtpEngineSlaveStats* stats);
+void resetPtpEngineSlaveStats(PtpEngineSlaveStats* stats);
+
+#endif /*STATISTICS_H_*/
+
+
diff --git a/rtemsbsd/ptpd/src/dep/sys.c b/rtemsbsd/ptpd/src/dep/sys.c
new file mode 100644
index 00000000..31553b84
--- /dev/null
+++ b/rtemsbsd/ptpd/src/dep/sys.c
@@ -0,0 +1,2661 @@
+/*-
+ * Copyright (c) 2012-2015 Wojciech Owczarek,
+ * Copyright (c) 2011-2012 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen,
+ * Inaqui Delgado,
+ * Rick Ratzel,
+ * National Instruments.
+ * Copyright (c) 2009-2010 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen
+ *
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file sys.c
+ * @date Tue Jul 20 16:19:46 2010
+ *
+ * @brief Code to call kernel time routines and also display server statistics.
+ *
+ *
+ */
+
+#include "../ptpd.h"
+
+#ifdef HAVE_NETINET_ETHER_H
+# include <netinet/ether.h>
+#endif
+
+#ifdef HAVE_NET_ETHERNET_H
+# include <net/ethernet.h>
+#endif
+
+#ifdef HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+
+#ifdef HAVE_NET_IF_ETHER_H
+# include <net/if_ether.h>
+#endif
+
+/* only C99 has the round function built-in */
+double round (double __x);
+
+static int closeLog(LogFileHandler* handler);
+
+#ifdef __QNXNTO__
+typedef struct {
+ _uint64 counter; /* iteration counter */
+ _uint64 prev_tsc; /* previous clock cycles */
+ _uint64 last_clock; /* clock reading at last timer interrupt */
+ _uint64 cps; /* cycles per second */
+ _uint64 prev_delta; /* previous clock cycle delta */
+ _uint64 cur_delta; /* last clock cycle delta */
+ _uint64 filtered_delta; /* filtered delta */
+ double ns_per_tick; /* nanoseconds per cycle */
+} TimerIntData;
+
+/* do not access directly! tied to clock interrupt! */
+static TimerIntData tData;
+static Boolean tDataUpdated = FALSE;
+
+#endif /* __QNXNTO__ */
+
+/*
+ returns a static char * for the representation of time, for debug purposes
+ DO NOT call this twice in the same printf!
+*/
+char *dump_TimeInternal(const TimeInternal * p)
+{
+ static char buf[100];
+
+ snprint_TimeInternal(buf, 100, p);
+ return buf;
+}
+
+
+/*
+ displays 2 timestamps and their strings in sequence, and the difference between then
+ DO NOT call this twice in the same printf!
+*/
+char *dump_TimeInternal2(const char *st1, const TimeInternal * p1, const char *st2, const TimeInternal * p2)
+{
+ static char buf[BUF_SIZE];
+ int n = 0;
+
+ /* display Timestamps */
+ if (st1) {
+ n += snprintf(buf + n, BUF_SIZE - n, "%s ", st1);
+ }
+ n += snprint_TimeInternal(buf + n, BUF_SIZE - n, p1);
+ n += snprintf(buf + n, BUF_SIZE - n, " ");
+
+ if (st2) {
+ n += snprintf(buf + n, BUF_SIZE - n, "%s ", st2);
+ }
+ n += snprint_TimeInternal(buf + n, BUF_SIZE - n, p2);
+ n += snprintf(buf + n, BUF_SIZE - n, " ");
+
+ /* display difference */
+ TimeInternal r;
+ subTime(&r, p1, p2);
+ n += snprintf(buf + n, BUF_SIZE - n, " (diff: ");
+ n += snprint_TimeInternal(buf + n, BUF_SIZE - n, &r);
+ n += snprintf(buf + n, BUF_SIZE - n, ") ");
+
+ return buf;
+}
+
+
+
+int
+snprint_TimeInternal(char *s, int max_len, const TimeInternal * p)
+{
+ int len = 0;
+
+ /* always print either a space, or the leading "-". This makes the stat files columns-aligned */
+ len += snprintf(&s[len], max_len - len, "%c",
+ isTimeInternalNegative(p)? '-':' ');
+
+ len += snprintf(&s[len], max_len - len, "%d.%09d",
+ abs(p->seconds), abs(p->nanoseconds));
+
+ return len;
+}
+
+
+/* debug aid: convert a time variable into a static char */
+char *time2st(const TimeInternal * p)
+{
+ static char buf[1000];
+
+ snprint_TimeInternal(buf, sizeof(buf), p);
+ return buf;
+}
+
+
+
+void DBG_time(const char *name, const TimeInternal p)
+{
+ DBG(" %s: %s\n", name, time2st(&p));
+
+}
+
+
+char *
+translatePortState(PtpClock *ptpClock)
+{
+ char *s;
+ switch(ptpClock->portDS.portState) {
+ case PTP_INITIALIZING: s = "init"; break;
+ case PTP_FAULTY: s = "flt"; break;
+ case PTP_LISTENING:
+ /* seperate init-reset from real resets */
+ if(ptpClock->resetCount == 1){
+ s = "lstn_init";
+ } else {
+ s = "lstn_reset";
+ }
+ break;
+ case PTP_PASSIVE: s = "pass"; break;
+ case PTP_UNCALIBRATED: s = "uncl"; break;
+ case PTP_SLAVE: s = "slv"; break;
+ case PTP_PRE_MASTER: s = "pmst"; break;
+ case PTP_MASTER: s = "mst"; break;
+ case PTP_DISABLED: s = "dsbl"; break;
+ default: s = "?"; break;
+ }
+ return s;
+}
+
+
+int
+snprint_ClockIdentity(char *s, int max_len, const ClockIdentity id)
+{
+ int len = 0;
+ int i;
+
+ for (i = 0; ;) {
+ len += snprintf(&s[len], max_len - len, "%02x", (unsigned char) id[i]);
+
+ if (++i >= CLOCK_IDENTITY_LENGTH)
+ break;
+ }
+
+ return len;
+}
+
+
+/* show the mac address in an easy way */
+int
+snprint_ClockIdentity_mac(char *s, int max_len, const ClockIdentity id)
+{
+ int len = 0;
+ int i;
+
+ for (i = 0; ;) {
+ /* skip bytes 3 and 4 */
+ if(!((i==3) || (i==4))){
+ len += snprintf(&s[len], max_len - len, "%02x", (unsigned char) id[i]);
+
+ if (++i >= CLOCK_IDENTITY_LENGTH)
+ break;
+
+ /* print a separator after each byte except the last one */
+ len += snprintf(&s[len], max_len - len, "%s", ":");
+ } else {
+
+ i++;
+ }
+ }
+
+ return len;
+}
+
+
+/*
+ * wrapper that caches the latest value of ether_ntohost
+ * this function will NOT check the last accces time of /etc/ethers,
+ * so it only have different output on a failover or at restart
+ *
+ */
+int ether_ntohost_cache(char *hostname, struct ether_addr *addr)
+{
+ static int valid = 0;
+ static struct ether_addr prev_addr;
+ static char buf[BUF_SIZE];
+
+#ifdef HAVE_STRUCT_ETHER_ADDR_OCTET
+ if (memcmp(addr->octet, &prev_addr,
+ sizeof(struct ether_addr )) != 0) {
+ valid = 0;
+ }
+#else
+ if (memcmp(addr->ether_addr_octet, &prev_addr,
+ sizeof(struct ether_addr )) != 0) {
+ valid = 0;
+ }
+#endif
+ if (!valid) {
+ if(ether_ntohost(buf, addr)){
+ snprintf(buf, BUF_SIZE,"%s", "unknown");
+ }
+
+ /* clean possible commas from the string */
+ while (strchr(buf, ',') != NULL) {
+ *(strchr(buf, ',')) = '_';
+ }
+
+ prev_addr = *addr;
+ }
+
+ valid = 1;
+ strncpy(hostname, buf, 100);
+ return 0;
+}
+
+
+/* Show the hostname configured in /etc/ethers */
+int
+snprint_ClockIdentity_ntohost(char *s, int max_len, const ClockIdentity id)
+{
+ int len = 0;
+ int i,j;
+ char buf[100];
+ struct ether_addr e;
+
+ /* extract mac address */
+ for (i = 0, j = 0; i< CLOCK_IDENTITY_LENGTH ; i++ ){
+ /* skip bytes 3 and 4 */
+ if(!((i==3) || (i==4))){
+#ifdef HAVE_STRUCT_ETHER_ADDR_OCTET
+ e.octet[j] = (uint8_t) id[i];
+#else
+ e.ether_addr_octet[j] = (uint8_t) id[i];
+#endif
+ j++;
+ }
+ }
+
+ /* convert and print hostname */
+ ether_ntohost_cache(buf, &e);
+ len += snprintf(&s[len], max_len - len, "(%s)", buf);
+
+ return len;
+}
+
+
+int
+snprint_PortIdentity(char *s, int max_len, const PortIdentity *id)
+{
+ int len = 0;
+
+#ifdef PRINT_MAC_ADDRESSES
+ len += snprint_ClockIdentity_mac(&s[len], max_len - len, id->clockIdentity);
+#else
+ len += snprint_ClockIdentity(&s[len], max_len - len, id->clockIdentity);
+#endif
+
+ len += snprint_ClockIdentity_ntohost(&s[len], max_len - len, id->clockIdentity);
+
+ len += snprintf(&s[len], max_len - len, "/%d", (unsigned) id->portNumber);
+ return len;
+}
+
+/* Write a formatted string to file pointer */
+int writeMessage(FILE* destination, uint32_t *lastHash, int priority, const char * format, va_list ap) {
+
+
+ extern RunTimeOpts rtOpts;
+ extern Boolean startupInProgress;
+
+ int written;
+ char time_str[MAXTIMESTR];
+ struct timeval now;
+#ifndef RUNTIME_DEBUG
+ char buf[PATH_MAX +1];
+ uint32_t hash;
+ va_list ap1;
+#endif /* RUNTIME_DEBUG */
+
+ extern char *translatePortState(PtpClock *ptpClock);
+ extern PtpClock *G_ptpClock;
+
+ if(destination == NULL)
+ return -1;
+
+ /* If we're starting up as daemon, only print <= WARN */
+ if ((destination == stderr) &&
+ !rtOpts.nonDaemon && startupInProgress &&
+ (priority > LOG_WARNING)){
+ return 1;
+ }
+
+#ifndef RUNTIME_DEBUG
+ /* check if this message produces the same hash as last */
+ memset(buf, 0, sizeof(buf));
+ va_copy(ap1, ap);
+ vsnprintf(buf, PATH_MAX, format, ap1);
+ hash = fnvHash(buf, sizeof(buf), 0);
+ if(lastHash != NULL) {
+ if(format[0] != '\n' && lastHash != NULL) {
+ /* last message was the same - don't print the next one */
+ if( (lastHash != 0) && (hash == *lastHash)) {
+ return 0;
+ }
+ }
+ *lastHash = hash;
+ }
+#endif /* RUNTIME_DEBUG */
+
+ /* Print timestamps and prefixes only if we're running in foreground or logging to file*/
+ if( rtOpts.nonDaemon || destination != stderr) {
+
+ /*
+ * select debug tagged with timestamps. This will slow down PTP itself if you send a lot of messages!
+ * it also can cause problems in nested debug statements (which are solved by turning the signal
+ * handling synchronous, and not calling this function inside asycnhronous signal processing)
+ */
+ gettimeofday(&now, 0);
+ strftime(time_str, MAXTIMESTR, "%F %X", localtime((time_t*)&now.tv_sec));
+ fprintf(destination, "%s.%06d ", time_str, (int)now.tv_usec );
+ fprintf(destination,PTPD_PROGNAME"[%d].%s (%-9s ",
+ (int)getpid(), startupInProgress ? "startup" : rtOpts.ifaceName,
+ priority == LOG_EMERG ? "emergency)" :
+ priority == LOG_ALERT ? "alert)" :
+ priority == LOG_CRIT ? "critical)" :
+ priority == LOG_ERR ? "error)" :
+ priority == LOG_WARNING ? "warning)" :
+ priority == LOG_NOTICE ? "notice)" :
+ priority == LOG_INFO ? "info)" :
+ priority == LOG_DEBUG ? "debug1)" :
+ priority == LOG_DEBUG2 ? "debug2)" :
+ priority == LOG_DEBUGV ? "debug3)" :
+ "unk)");
+
+
+ fprintf(destination, " (%s) ", G_ptpClock ?
+ translatePortState(G_ptpClock) : "___");
+ }
+ written = vfprintf(destination, format, ap);
+ return written;
+
+}
+
+void
+updateLogSize(LogFileHandler* handler)
+{
+
+ struct stat st;
+ if(handler->logFP == NULL)
+ return;
+ if (fstat(fileno(handler->logFP), &st) != -1) {
+ handler->fileSize = st.st_size;
+ } else {
+#ifdef RUNTIME_DEBUG
+/* 2.3.1: do not print to stderr or log file */
+// fprintf(stderr, "fstat on %s file failed!\n", handler->logID);
+#endif /* RUNTIME_DEBUG */
+ }
+
+}
+
+
+/*
+ * Prints a message, randing from critical to debug.
+ * This either prints the message to syslog, or with timestamp+state to stderr
+ */
+void
+logMessage(int priority, const char * format, ...)
+{
+ extern RunTimeOpts rtOpts;
+ extern Boolean startupInProgress;
+ va_list ap , ap1;
+
+ va_copy(ap1, ap);
+ va_start(ap1, format);
+ va_start(ap, format);
+
+#ifdef RUNTIME_DEBUG
+ if ((priority >= LOG_DEBUG) && (priority > rtOpts.debug_level)) {
+ goto end;
+ }
+#endif
+
+ /* log level filter */
+ if(priority > rtOpts.logLevel) {
+ goto end;
+ }
+ /* If we're using a log file and the message has been written OK, we're done*/
+ if(rtOpts.eventLog.logEnabled && rtOpts.eventLog.logFP != NULL) {
+ if(writeMessage(rtOpts.eventLog.logFP, &rtOpts.eventLog.lastHash, priority, format, ap) > 0) {
+ maintainLogSize(&rtOpts.eventLog);
+ if(!startupInProgress)
+ goto end;
+ else {
+ rtOpts.eventLog.lastHash = 0;
+ goto std_err;
+ }
+ }
+ }
+
+ /*
+ * Otherwise we try syslog - if we're here, we didn't write to log file.
+ * If we're running in background and we're starting up, also log first
+ * messages to syslog to at least leave a trace.
+ */
+ if (rtOpts.useSysLog ||
+ (!rtOpts.nonDaemon && startupInProgress)) {
+ static Boolean syslogOpened;
+#ifdef RUNTIME_DEBUG
+ /*
+ * Syslog only has 8 message levels (3 bits)
+ * important: messages will only appear if "*.debug /var/log/debug" is on /etc/rsyslog.conf
+ */
+ if(priority > LOG_DEBUG){
+ priority = LOG_DEBUG;
+ }
+#endif
+
+ if (!syslogOpened) {
+ openlog(PTPD_PROGNAME, LOG_PID, LOG_DAEMON);
+ syslogOpened = TRUE;
+ }
+ vsyslog(priority, format, ap);
+ if (!startupInProgress) {
+ goto end;
+ }
+ else {
+ rtOpts.eventLog.lastHash = 0;
+ goto std_err;
+ }
+ }
+std_err:
+
+ /* Either all else failed or we're running in foreground - or we also log to stderr */
+ writeMessage(stderr, &rtOpts.eventLog.lastHash, priority, format, ap1);
+
+end:
+ va_end(ap1);
+ va_end(ap);
+ return;
+}
+
+
+/* Restart a file log target based on its settings */
+int
+restartLog(LogFileHandler* handler, Boolean quiet)
+{
+
+ /* The FP is open - close it */
+ if(handler->logFP != NULL) {
+ handler->lastHash=0;
+ fclose(handler->logFP);
+ /*
+ * fclose doesn't do this at least on Linux - changes the underlying FD to -1,
+ * but not the FP to NULL - with this we can tell if the FP is closed
+ */
+ handler->logFP=NULL;
+ /* If we're not logging to file (any more), call it quits */
+ if (!handler->logEnabled) {
+ if(!quiet) INFO("Logging to %s file disabled. Closing file.\n", handler->logID);
+ if(handler->unlinkOnClose)
+ unlink(handler->logPath);
+ return 1;
+ }
+ }
+ /* FP is not open and we're not logging */
+ if (!handler->logEnabled)
+ return 1;
+
+ /* Open the file */
+ if ( (handler->logFP = fopen(handler->logPath, handler->openMode)) == NULL) {
+ if(!quiet) PERROR("Could not open %s file", handler->logID);
+ } else {
+ if(handler->truncateOnReopen) {
+ if(!ftruncate(fileno(handler->logFP), 0)) {
+ if(!quiet) INFO("Truncated %s file\n", handler->logID);
+ } else
+ DBG("Could not truncate % file: %s\n", handler->logID, handler->logPath);
+ }
+ /* \n flushes output for us, no need for fflush() - if you want something different, set it later */
+ setlinebuf(handler->logFP);
+
+ }
+ return (handler->logFP != NULL);
+}
+
+/* Close a file log target */
+static int
+closeLog(LogFileHandler* handler)
+{
+ if(handler->logFP != NULL) {
+ fclose(handler->logFP);
+ handler->logFP=NULL;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* Return TRUE only if the log file had to be rotated / truncated - FALSE does not mean error */
+/* Mini-logrotate: truncate file if exceeds preset size, also rotate up to n number of files if configured */
+Boolean
+maintainLogSize(LogFileHandler* handler)
+{
+
+ if(handler->maxSize) {
+ if(handler->logFP == NULL)
+ return FALSE;
+ updateLogSize(handler);
+#ifdef RUNTIME_DEBUG
+/* 2.3.1: do not print to stderr or log file */
+// fprintf(stderr, "%s logsize: %d\n", handler->logID, handler->fileSize);
+#endif /* RUNTIME_DEBUG */
+ if(handler->fileSize > (handler->maxSize * 1024)) {
+
+ /* Rotate the log file */
+ if (handler->maxFiles) {
+ int i = 0;
+ int logFileNumber = 0;
+ time_t maxMtime = 0;
+ struct stat st;
+ char fname[PATH_MAX];
+ /* Find the last modified file of the series */
+ while(++i <= handler->maxFiles) {
+ memset(fname, 0, PATH_MAX);
+ snprintf(fname, PATH_MAX,"%s.%d", handler->logPath, i);
+ if((stat(fname,&st) != -1) && S_ISREG(st.st_mode)) {
+ if(st.st_mtime > maxMtime) {
+ maxMtime = st.st_mtime;
+ logFileNumber = i;
+ }
+ }
+ }
+ /* Use next file in line or first one if rolled over */
+ if(++logFileNumber > handler->maxFiles)
+ logFileNumber = 1;
+ memset(fname, 0, PATH_MAX);
+ snprintf(fname, PATH_MAX,"%s.%d", handler->logPath, logFileNumber);
+ /* Move current file to new location */
+ rename(handler->logPath, fname);
+ /* Reopen to reactivate the original path */
+ if(restartLog(handler,TRUE)) {
+ INFO("Rotating %s file - size above %dkB\n",
+ handler->logID, handler->maxSize);
+ } else {
+ DBG("Could not rotate %s file\n", handler->logPath);
+ }
+ return TRUE;
+ /* Just truncate - maxSize given but no maxFiles */
+ } else {
+
+ if(!ftruncate(fileno(handler->logFP),0)) {
+ INFO("Truncating %s file - size above %dkB\n",
+ handler->logID, handler->maxSize);
+ } else {
+#ifdef RUNTIME_DEBUG
+/* 2.3.1: do not print to stderr or log file */
+// fprintf(stderr, "Could not truncate %s file\n", handler->logPath);
+#endif
+ }
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+
+}
+
+void
+restartLogging(RunTimeOpts* rtOpts)
+{
+
+ if(!restartLog(&rtOpts->statisticsLog, TRUE))
+ NOTIFY("Failed logging to %s file\n", rtOpts->statisticsLog.logID);
+
+ if(!restartLog(&rtOpts->recordLog, TRUE))
+ NOTIFY("Failed logging to %s file\n", rtOpts->recordLog.logID);
+
+ if(!restartLog(&rtOpts->eventLog, TRUE))
+ NOTIFY("Failed logging to %s file\n", rtOpts->eventLog.logID);
+
+ if(!restartLog(&rtOpts->statusLog, TRUE))
+ NOTIFY("Failed logging to %s file\n", rtOpts->statusLog.logID);
+
+}
+
+void
+stopLogging(RunTimeOpts* rtOpts)
+{
+ closeLog(&rtOpts->statisticsLog);
+ closeLog(&rtOpts->recordLog);
+ closeLog(&rtOpts->eventLog);
+ closeLog(&rtOpts->statusLog);
+}
+
+void
+logStatistics(PtpClock * ptpClock)
+{
+ extern RunTimeOpts rtOpts;
+ static int errorMsg = 0;
+ static char sbuf[SCREEN_BUFSZ * 2];
+ int len = 0;
+ TimeInternal now;
+ time_t time_s;
+ FILE* destination;
+ static TimeInternal prev_now_sync, prev_now_delay;
+ char time_str[MAXTIMESTR];
+
+ if (!rtOpts.logStatistics) {
+ return;
+ }
+
+ if(rtOpts.statisticsLog.logEnabled && rtOpts.statisticsLog.logFP != NULL)
+ destination = rtOpts.statisticsLog.logFP;
+ else
+ destination = stdout;
+
+ if (ptpClock->resetStatisticsLog) {
+ ptpClock->resetStatisticsLog = FALSE;
+ fprintf(destination,"# %s, State, Clock ID, One Way Delay, "
+ "Offset From Master, Slave to Master, "
+ "Master to Slave, Observed Drift, Last packet Received, Sequence ID"
+#ifdef PTPD_STATISTICS
+ ", One Way Delay Mean, One Way Delay Std Dev, Offset From Master Mean, Offset From Master Std Dev, Observed Drift Mean, Observed Drift Std Dev, raw delayMS, raw delaySM"
+#endif
+ "\n", (rtOpts.statisticsTimestamp == TIMESTAMP_BOTH) ? "Timestamp, Unix timestamp" : "Timestamp");
+ }
+
+ memset(sbuf, 0, sizeof(sbuf));
+
+ getTime(&now);
+
+ /*
+ * print one log entry per X seconds for Sync and DelayResp messages, to reduce disk usage.
+ */
+
+ if ((ptpClock->portDS.portState == PTP_SLAVE) && (rtOpts.statisticsLogInterval)) {
+
+ switch(ptpClock->char_last_msg) {
+ case 'S':
+ if((now.seconds - prev_now_sync.seconds) < rtOpts.statisticsLogInterval){
+ DBGV("Suppressed Sync statistics log entry - statisticsLogInterval configured\n");
+ return;
+ }
+ prev_now_sync = now;
+ break;
+ case 'D':
+ case 'P':
+ if((now.seconds - prev_now_delay.seconds) < rtOpts.statisticsLogInterval){
+ DBGV("Suppressed Sync statistics log entry - statisticsLogInterval configured\n");
+ return;
+ }
+ prev_now_delay = now;
+ default:
+ break;
+ }
+ }
+
+ time_s = now.seconds;
+
+ /* output date-time timestamp if configured */
+ if (rtOpts.statisticsTimestamp == TIMESTAMP_DATETIME ||
+ rtOpts.statisticsTimestamp == TIMESTAMP_BOTH) {
+ strftime(time_str, MAXTIMESTR, "%Y-%m-%d %X", localtime(&time_s));
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, "%s.%06d, %s, ",
+ time_str, (int)now.nanoseconds/1000, /* Timestamp */
+ translatePortState(ptpClock)); /* State */
+ }
+
+ /* output unix timestamp s.ns if configured */
+ if (rtOpts.statisticsTimestamp == TIMESTAMP_UNIX ||
+ rtOpts.statisticsTimestamp == TIMESTAMP_BOTH) {
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, "%d.%06d, %s,",
+ now.seconds, now.nanoseconds, /* Timestamp */
+ translatePortState(ptpClock)); /* State */
+ }
+
+ if (ptpClock->portDS.portState == PTP_SLAVE) {
+ len += snprint_PortIdentity(sbuf + len, sizeof(sbuf) - len,
+ &ptpClock->parentDS.parentPortIdentity); /* Clock ID */
+
+ /*
+ * if grandmaster ID differs from parent port ID then
+ * also print GM ID
+ */
+ if (memcmp(ptpClock->parentDS.grandmasterIdentity,
+ ptpClock->parentDS.parentPortIdentity.clockIdentity,
+ CLOCK_IDENTITY_LENGTH)) {
+ len += snprint_ClockIdentity(sbuf + len,
+ sizeof(sbuf) - len,
+ ptpClock->parentDS.grandmasterIdentity);
+ }
+
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, ", ");
+
+ if(rtOpts.delayMechanism == E2E) {
+ len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len,
+ &ptpClock->currentDS.meanPathDelay);
+ } else {
+ len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len,
+ &ptpClock->portDS.peerMeanPathDelay);
+ }
+
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, ", ");
+
+ len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len,
+ &ptpClock->currentDS.offsetFromMaster);
+
+ /* print MS and SM with sign */
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, ", ");
+
+ if(rtOpts.delayMechanism == E2E) {
+ len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len,
+ &(ptpClock->delaySM));
+ } else {
+ len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len,
+ &(ptpClock->pdelaySM));
+ }
+
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, ", ");
+
+ len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len,
+ &(ptpClock->delayMS));
+
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, ", %.09f, %c, %05d",
+ ptpClock->servo.observedDrift,
+ ptpClock->char_last_msg,
+ ptpClock->msgTmpHeader.sequenceId);
+
+#ifdef PTPD_STATISTICS
+
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, ", %.09f, %.00f, %.09f, %.00f",
+ ptpClock->slaveStats.mpdMean,
+ ptpClock->slaveStats.mpdStdDev * 1E9,
+ ptpClock->slaveStats.ofmMean,
+ ptpClock->slaveStats.ofmStdDev * 1E9);
+
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, ", %.0f, %.0f, ",
+ ptpClock->servo.driftMean,
+ ptpClock->servo.driftStdDev);
+
+ len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len,
+ &(ptpClock->rawDelayMS));
+
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, ", ");
+
+ len += snprint_TimeInternal(sbuf + len, sizeof(sbuf) - len,
+ &(ptpClock->rawDelaySM));
+
+#endif /* PTPD_STATISTICS */
+
+ } else {
+ if ((ptpClock->portDS.portState == PTP_MASTER) || (ptpClock->portDS.portState == PTP_PASSIVE)) {
+
+ len += snprint_PortIdentity(sbuf + len, sizeof(sbuf) - len,
+ &ptpClock->parentDS.parentPortIdentity);
+
+ //len += snprintf(sbuf + len, sizeof(sbuf) - len, ")");
+ }
+
+ /* show the current reset number on the log */
+ if (ptpClock->portDS.portState == PTP_LISTENING) {
+ len += snprintf(sbuf + len,
+ sizeof(sbuf) - len,
+ " %d ", ptpClock->resetCount);
+ }
+ }
+
+ /* add final \n in normal status lines */
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, "\n");
+
+#if 0 /* NOTE: Do we want this? */
+ if (rtOpts.nonDaemon) {
+ /* in -C mode, adding an extra \n makes stats more clear intermixed with debug comments */
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, "\n");
+ }
+#endif
+
+ /* fprintf may get interrupted by a signal - silently retry once */
+ if (fprintf(destination, "%s", sbuf) < len) {
+ if (fprintf(destination, "%s", sbuf) < len) {
+ if(!errorMsg) {
+ PERROR("Error while writing statistics");
+ }
+ errorMsg = TRUE;
+ }
+ }
+
+ if(destination == rtOpts.statisticsLog.logFP) {
+ if (maintainLogSize(&rtOpts.statisticsLog))
+ ptpClock->resetStatisticsLog = TRUE;
+ }
+
+}
+
+/* periodic status update */
+void
+periodicUpdate(const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+
+ char tmpBuf[200];
+ char masterIdBuf[150];
+ int len = 0;
+
+ memset(tmpBuf, 0, sizeof(tmpBuf));
+ memset(masterIdBuf, 0, sizeof(masterIdBuf));
+
+ TimeInternal *mpd = &ptpClock->currentDS.meanPathDelay;
+
+ len += snprint_PortIdentity(masterIdBuf + len, sizeof(masterIdBuf) - len,
+ &ptpClock->parentDS.parentPortIdentity);
+ if(ptpClock->bestMaster && ptpClock->bestMaster->sourceAddr) {
+ char strAddr[MAXHOSTNAMELEN];
+ struct in_addr tmpAddr;
+ tmpAddr.s_addr = ptpClock->bestMaster->sourceAddr;
+ inet_ntop(AF_INET, (struct sockaddr_in *)(&tmpAddr), strAddr, MAXHOSTNAMELEN);
+ len += snprintf(masterIdBuf + len, sizeof(masterIdBuf) - len, " (IPv4:%s)",strAddr);
+ }
+
+ if(ptpClock->portDS.delayMechanism == P2P) {
+ mpd = &ptpClock->portDS.peerMeanPathDelay;
+ }
+
+ if(ptpClock->portDS.portState == PTP_SLAVE) {
+ snprint_TimeInternal(tmpBuf, sizeof(tmpBuf), &ptpClock->currentDS.offsetFromMaster);
+#ifdef PTPD_STATISTICS
+ if(ptpClock->slaveStats.statsCalculated) {
+ INFO("Status update: state %s, best master %s, ofm %s s, ofm_mean % .09f s, ofm_dev % .09f s\n",
+ portState_getName(ptpClock->portDS.portState),
+ masterIdBuf,
+ tmpBuf,
+ ptpClock->slaveStats.ofmMean,
+ ptpClock->slaveStats.ofmStdDev);
+ snprint_TimeInternal(tmpBuf, sizeof(tmpBuf), mpd);
+ if (ptpClock->portDS.delayMechanism == E2E) {
+ INFO("Status update: state %s, best master %s, mpd %s s, mpd_mean % .09f s, mpd_dev % .09f s\n",
+ portState_getName(ptpClock->portDS.portState),
+ masterIdBuf,
+ tmpBuf,
+ ptpClock->slaveStats.mpdMean,
+ ptpClock->slaveStats.mpdStdDev);
+ } else if(ptpClock->portDS.delayMechanism == P2P) {
+ INFO("Status update: state %s, best master %s, mpd %s s\n", portState_getName(ptpClock->portDS.portState), masterIdBuf, tmpBuf);
+ }
+ } else {
+ INFO("Status update: state %s, best master %s, ofm %s s\n", portState_getName(ptpClock->portDS.portState), masterIdBuf, tmpBuf);
+ snprint_TimeInternal(tmpBuf, sizeof(tmpBuf), mpd);
+ if(ptpClock->portDS.delayMechanism != DELAY_DISABLED) {
+ INFO("Status update: state %s, best master %s, mpd %s s\n", portState_getName(ptpClock->portDS.portState), masterIdBuf, tmpBuf);
+ }
+ }
+#else
+ INFO("Status update: state %s, best master %s, ofm %s s\n", portState_getName(ptpClock->portDS.portState), masterIdBuf, tmpBuf);
+ snprint_TimeInternal(tmpBuf, sizeof(tmpBuf), mpd);
+ if(ptpClock->portDS.delayMechanism != DELAY_DISABLED) {
+ INFO("Status update: state %s, best master %s, mpd %s s\n", portState_getName(ptpClock->portDS.portState), masterIdBuf, tmpBuf);
+ }
+#endif /* PTPD_STATISTICS */
+ } else if(ptpClock->portDS.portState == PTP_MASTER) {
+ if(rtOpts->unicastNegotiation || ptpClock->unicastDestinationCount) {
+ INFO("Status update: state %s, %d slaves\n", portState_getName(ptpClock->portDS.portState),
+ ptpClock->unicastDestinationCount + ptpClock->slaveCount);
+ } else {
+ INFO("Status update: state %s\n", portState_getName(ptpClock->portDS.portState));
+ }
+ } else {
+ INFO("Status update: state %s\n", portState_getName(ptpClock->portDS.portState));
+ }
+}
+
+
+void
+displayStatus(PtpClock *ptpClock, const char *prefixMessage)
+{
+
+ static char sbuf[SCREEN_BUFSZ];
+ char strAddr[MAXHOSTNAMELEN];
+ int len = 0;
+
+ memset(strAddr, 0, sizeof(strAddr));
+ memset(sbuf, ' ', sizeof(sbuf));
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, "%s", prefixMessage);
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, "%s",
+ portState_getName(ptpClock->portDS.portState));
+
+ if (ptpClock->portDS.portState >= PTP_MASTER) {
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, ", Best master: ");
+ len += snprint_PortIdentity(sbuf + len, sizeof(sbuf) - len,
+ &ptpClock->parentDS.parentPortIdentity);
+ if(ptpClock->bestMaster && ptpClock->bestMaster->sourceAddr) {
+ struct in_addr tmpAddr;
+ tmpAddr.s_addr = ptpClock->bestMaster->sourceAddr;
+ inet_ntop(AF_INET, (struct sockaddr_in *)(&tmpAddr), strAddr, MAXHOSTNAMELEN);
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, " (IPv4:%s)",strAddr);
+ }
+ }
+ if(ptpClock->portDS.portState == PTP_MASTER)
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, " (self)");
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, "\n");
+ NOTICE("%s",sbuf);
+}
+
+#define STATUSPREFIX "%-19s:"
+void
+writeStatusFile(PtpClock *ptpClock,const RunTimeOpts *rtOpts, Boolean quiet)
+{
+
+ char outBuf[2048];
+ char tmpBuf[200];
+
+ int n = getAlarmSummary(NULL, 0, ptpClock->alarms, ALRM_MAX);
+ char alarmBuf[n];
+
+ getAlarmSummary(alarmBuf, n, ptpClock->alarms, ALRM_MAX);
+
+ if(rtOpts->statusLog.logFP == NULL)
+ return;
+
+ char timeStr[MAXTIMESTR];
+ char hostName[MAXHOSTNAMELEN];
+
+ struct timeval now;
+ memset(hostName, 0, MAXHOSTNAMELEN);
+
+ gethostname(hostName, MAXHOSTNAMELEN);
+ gettimeofday(&now, 0);
+ strftime(timeStr, MAXTIMESTR, "%a %b %d %X %Z %Y", localtime((time_t*)&now.tv_sec));
+
+ FILE* out = rtOpts->statusLog.logFP;
+ memset(outBuf, 0, sizeof(outBuf));
+
+ setbuf(out, outBuf);
+ if(ftruncate(fileno(out), 0) < 0) {
+ DBG("writeStatusFile: ftruncate() failed\n");
+ }
+ rewind(out);
+
+ fprintf(out, STATUSPREFIX" %s, PID %d\n","Host info", hostName, (int)getpid());
+ fprintf(out, STATUSPREFIX" %s\n","Local time", timeStr);
+ strftime(timeStr, MAXTIMESTR, "%a %b %d %X %Z %Y", gmtime((time_t*)&now.tv_sec));
+ fprintf(out, STATUSPREFIX" %s\n","Kernel time", timeStr);
+ fprintf(out, STATUSPREFIX" %s%s\n","Interface", rtOpts->ifaceName,
+ (rtOpts->backupIfaceEnabled && ptpClock->runningBackupInterface) ? " (backup)" : (rtOpts->backupIfaceEnabled)?
+ " (primary)" : "");
+ fprintf(out, STATUSPREFIX" %s\n","Preset", dictionary_get(rtOpts->currentConfig, "ptpengine:preset", ""));
+ fprintf(out, STATUSPREFIX" %s%s","Transport", dictionary_get(rtOpts->currentConfig, "ptpengine:transport", ""),
+ (rtOpts->transport==UDP_IPV4 && rtOpts->pcap == TRUE)?" + libpcap":"");
+
+ if(rtOpts->transport != IEEE_802_3) {
+ fprintf(out,", %s", dictionary_get(rtOpts->currentConfig, "ptpengine:ip_mode", ""));
+ fprintf(out,"%s", rtOpts->unicastNegotiation ? " negotiation":"");
+ }
+
+ fprintf(out,"\n");
+
+ fprintf(out, STATUSPREFIX" %s\n","Delay mechanism", dictionary_get(rtOpts->currentConfig, "ptpengine:delay_mechanism", ""));
+ if(ptpClock->portDS.portState >= PTP_MASTER) {
+ fprintf(out, STATUSPREFIX" %s\n","Sync mode", ptpClock->defaultDS.twoStepFlag ? "TWO_STEP" : "ONE_STEP");
+ }
+ if(ptpClock->defaultDS.slaveOnly && rtOpts->anyDomain) {
+ fprintf(out, STATUSPREFIX" %d, preferred %d\n","PTP domain",
+ ptpClock->defaultDS.domainNumber, rtOpts->domainNumber);
+ } else if(ptpClock->defaultDS.slaveOnly && rtOpts->unicastNegotiation) {
+ fprintf(out, STATUSPREFIX" %d, default %d\n","PTP domain", ptpClock->defaultDS.domainNumber, rtOpts->domainNumber);
+ } else {
+ fprintf(out, STATUSPREFIX" %d\n","PTP domain", ptpClock->defaultDS.domainNumber);
+ }
+ fprintf(out, STATUSPREFIX" %s\n","Port state", portState_getName(ptpClock->portDS.portState));
+ if(strlen(alarmBuf) > 0) {
+ fprintf(out, STATUSPREFIX" %s\n","Alarms", alarmBuf);
+ }
+ memset(tmpBuf, 0, sizeof(tmpBuf));
+ snprint_PortIdentity(tmpBuf, sizeof(tmpBuf),
+ &ptpClock->portDS.portIdentity);
+ fprintf(out, STATUSPREFIX" %s\n","Local port ID", tmpBuf);
+
+
+ if(ptpClock->portDS.portState >= PTP_MASTER) {
+ memset(tmpBuf, 0, sizeof(tmpBuf));
+ snprint_PortIdentity(tmpBuf, sizeof(tmpBuf),
+ &ptpClock->parentDS.parentPortIdentity);
+ fprintf(out, STATUSPREFIX" %s","Best master ID", tmpBuf);
+ if(ptpClock->portDS.portState == PTP_MASTER)
+ fprintf(out," (self)");
+ fprintf(out,"\n");
+ }
+ if(rtOpts->transport == UDP_IPV4 &&
+ ptpClock->portDS.portState > PTP_MASTER &&
+ ptpClock->bestMaster && ptpClock->bestMaster->sourceAddr) {
+ {
+ struct in_addr tmpAddr;
+ tmpAddr.s_addr = ptpClock->bestMaster->sourceAddr;
+ fprintf(out, STATUSPREFIX" %s\n","Best master IP", inet_ntoa(tmpAddr));
+ }
+ }
+ if(ptpClock->portDS.portState == PTP_SLAVE) {
+ fprintf(out, STATUSPREFIX" Priority1 %d, Priority2 %d, clockClass %d","GM priority",
+ ptpClock->parentDS.grandmasterPriority1, ptpClock->parentDS.grandmasterPriority2, ptpClock->parentDS.grandmasterClockQuality.clockClass);
+ if(rtOpts->unicastNegotiation && ptpClock->parentGrants != NULL ) {
+ fprintf(out, ", localPref %d", ptpClock->parentGrants->localPreference);
+ }
+ fprintf(out, "%s\n", (ptpClock->bestMaster != NULL && ptpClock->bestMaster->disqualified) ? " (timeout)" : "");
+ }
+
+ if(ptpClock->defaultDS.clockQuality.clockClass < 128 ||
+ ptpClock->portDS.portState == PTP_SLAVE ||
+ ptpClock->portDS.portState == PTP_PASSIVE){
+ fprintf(out, STATUSPREFIX" ","Time properties");
+ fprintf(out, "%s timescale, ",ptpClock->timePropertiesDS.ptpTimescale ? "PTP":"ARB");
+ fprintf(out, "tracbl: time %s, freq %s, src: %s(0x%02x)\n", ptpClock->timePropertiesDS.timeTraceable ? "Y" : "N",
+ ptpClock->timePropertiesDS.frequencyTraceable ? "Y" : "N",
+ getTimeSourceName(ptpClock->timePropertiesDS.timeSource),
+ ptpClock->timePropertiesDS.timeSource);
+ fprintf(out, STATUSPREFIX" ","UTC properties");
+ fprintf(out, "UTC valid: %s", ptpClock->timePropertiesDS.currentUtcOffsetValid ? "Y" : "N");
+ fprintf(out, ", UTC offset: %d",ptpClock->timePropertiesDS.currentUtcOffset);
+ fprintf(out, "%s",ptpClock->timePropertiesDS.leap61 ?
+ ", LEAP61 pending" : ptpClock->timePropertiesDS.leap59 ? ", LEAP59 pending" : "");
+ if (ptpClock->portDS.portState == PTP_SLAVE) {
+ fprintf(out, "%s", rtOpts->preferUtcValid ? ", prefer UTC" : "");
+ fprintf(out, "%s", rtOpts->requireUtcValid ? ", require UTC" : "");
+ }
+ fprintf(out,"\n");
+ }
+
+ if(ptpClock->portDS.portState == PTP_SLAVE) {
+ memset(tmpBuf, 0, sizeof(tmpBuf));
+ snprint_TimeInternal(tmpBuf, sizeof(tmpBuf),
+ &ptpClock->currentDS.offsetFromMaster);
+ fprintf(out, STATUSPREFIX" %s s","Offset from Master", tmpBuf);
+#ifdef PTPD_STATISTICS
+ if(ptpClock->slaveStats.statsCalculated)
+ fprintf(out, ", mean % .09f s, dev % .09f s",
+ ptpClock->slaveStats.ofmMean,
+ ptpClock->slaveStats.ofmStdDev
+ );
+#endif /* PTPD_STATISTICS */
+ fprintf(out,"\n");
+
+ if(ptpClock->portDS.delayMechanism == E2E) {
+ memset(tmpBuf, 0, sizeof(tmpBuf));
+ snprint_TimeInternal(tmpBuf, sizeof(tmpBuf),
+ &ptpClock->currentDS.meanPathDelay);
+ fprintf(out, STATUSPREFIX" %s s","Mean Path Delay", tmpBuf);
+#ifdef PTPD_STATISTICS
+ if(ptpClock->slaveStats.statsCalculated)
+ fprintf(out, ", mean % .09f s, dev % .09f s",
+ ptpClock->slaveStats.mpdMean,
+ ptpClock->slaveStats.mpdStdDev
+ );
+#endif /* PTPD_STATISTICS */
+ fprintf(out,"\n");
+ }
+ if(ptpClock->portDS.delayMechanism == P2P) {
+ memset(tmpBuf, 0, sizeof(tmpBuf));
+ snprint_TimeInternal(tmpBuf, sizeof(tmpBuf),
+ &ptpClock->portDS.peerMeanPathDelay);
+ fprintf(out, STATUSPREFIX" %s s","Mean Path (p)Delay", tmpBuf);
+ fprintf(out,"\n");
+ }
+
+ fprintf(out, STATUSPREFIX" ","Clock status");
+ if(rtOpts->enablePanicMode) {
+ if(ptpClock->panicMode) {
+ fprintf(out,"panic mode,");
+
+ }
+ }
+ if(rtOpts->calibrationDelay) {
+ fprintf(out, "%s, ",
+ ptpClock->isCalibrated ? "calibrated" : "not calibrated");
+ }
+ fprintf(out, "%s",
+ ptpClock->clockControl.granted ? "in control" : "no control");
+ if(rtOpts->noAdjust) {
+ fprintf(out, ", read-only");
+ }
+#ifdef PTPD_STATISTICS
+ else {
+ if (rtOpts->servoStabilityDetection) {
+ fprintf(out, ", %s",
+ ptpClock->servo.isStable ? "stabilised" : "not stabilised");
+ }
+ }
+#endif /* PTPD_STATISTICS */
+ fprintf(out,"\n");
+
+
+ fprintf(out, STATUSPREFIX" % .03f ppm","Clock correction",
+ ptpClock->servo.observedDrift / 1000.0);
+if(ptpClock->servo.runningMaxOutput)
+ fprintf(out, " (slewing at maximum rate)");
+else {
+#ifdef PTPD_STATISTICS
+ if(ptpClock->slaveStats.statsCalculated)
+ fprintf(out, ", mean % .03f ppm, dev % .03f ppm",
+ ptpClock->servo.driftMean / 1000.0,
+ ptpClock->servo.driftStdDev / 1000.0
+ );
+ if(rtOpts->servoStabilityDetection) {
+ fprintf(out, ", dev thr % .03f ppm",
+ ptpClock->servo.stabilityThreshold / 1000.0
+ );
+ }
+#endif /* PTPD_STATISTICS */
+}
+ fprintf(out,"\n");
+
+
+ }
+
+
+
+ if(ptpClock->portDS.portState == PTP_MASTER || ptpClock->portDS.portState == PTP_PASSIVE) {
+
+ fprintf(out, STATUSPREFIX" %d","Priority1 ", ptpClock->defaultDS.priority1);
+ if(ptpClock->portDS.portState == PTP_PASSIVE)
+ fprintf(out, " (best master: %d)", ptpClock->parentDS.grandmasterPriority1);
+ fprintf(out,"\n");
+ fprintf(out, STATUSPREFIX" %d","Priority2 ", ptpClock->defaultDS.priority2);
+ if(ptpClock->portDS.portState == PTP_PASSIVE)
+ fprintf(out, " (best master: %d)", ptpClock->parentDS.grandmasterPriority2);
+ fprintf(out,"\n");
+ fprintf(out, STATUSPREFIX" %d","ClockClass ", ptpClock->defaultDS.clockQuality.clockClass);
+ if(ptpClock->portDS.portState == PTP_PASSIVE)
+ fprintf(out, " (best master: %d)", ptpClock->parentDS.grandmasterClockQuality.clockClass);
+ fprintf(out,"\n");
+ if(ptpClock->portDS.delayMechanism == P2P) {
+ memset(tmpBuf, 0, sizeof(tmpBuf));
+ snprint_TimeInternal(tmpBuf, sizeof(tmpBuf),
+ &ptpClock->portDS.peerMeanPathDelay);
+ fprintf(out, STATUSPREFIX" %s s","Mean Path (p)Delay", tmpBuf);
+ fprintf(out,"\n");
+ }
+
+ }
+
+ if(ptpClock->portDS.portState == PTP_MASTER || ptpClock->portDS.portState == PTP_PASSIVE ||
+ ptpClock->portDS.portState == PTP_SLAVE) {
+
+ fprintf(out, STATUSPREFIX" ","Message rates");
+
+ if (ptpClock->portDS.logSyncInterval == UNICAST_MESSAGEINTERVAL)
+ fprintf(out,"[UC-unknown]");
+ else if (ptpClock->portDS.logSyncInterval <= 0)
+ fprintf(out,"%.0f/s",pow(2,-ptpClock->portDS.logSyncInterval));
+ else
+ fprintf(out,"1/%.0fs",pow(2,ptpClock->portDS.logSyncInterval));
+ fprintf(out, " sync");
+
+
+ if(ptpClock->portDS.delayMechanism == E2E) {
+ if (ptpClock->portDS.logMinDelayReqInterval == UNICAST_MESSAGEINTERVAL)
+ fprintf(out,", [UC-unknown]");
+ else if (ptpClock->portDS.logMinDelayReqInterval <= 0)
+ fprintf(out,", %.0f/s",pow(2,-ptpClock->portDS.logMinDelayReqInterval));
+ else
+ fprintf(out,", 1/%.0fs",pow(2,ptpClock->portDS.logMinDelayReqInterval));
+ fprintf(out, " delay");
+ }
+
+ if(ptpClock->portDS.delayMechanism == P2P) {
+ if (ptpClock->portDS.logMinPdelayReqInterval == UNICAST_MESSAGEINTERVAL)
+ fprintf(out,", [UC-unknown]");
+ else if (ptpClock->portDS.logMinPdelayReqInterval <= 0)
+ fprintf(out,", %.0f/s",pow(2,-ptpClock->portDS.logMinPdelayReqInterval));
+ else
+ fprintf(out,", 1/%.0fs",pow(2,ptpClock->portDS.logMinPdelayReqInterval));
+ fprintf(out, " pdelay");
+ }
+
+ if (ptpClock->portDS.logAnnounceInterval == UNICAST_MESSAGEINTERVAL)
+ fprintf(out,", [UC-unknown]");
+ else if (ptpClock->portDS.logAnnounceInterval <= 0)
+ fprintf(out,", %.0f/s",pow(2,-ptpClock->portDS.logAnnounceInterval));
+ else
+ fprintf(out,", 1/%.0fs",pow(2,ptpClock->portDS.logAnnounceInterval));
+ fprintf(out, " announce");
+
+ fprintf(out,"\n");
+
+ }
+
+ fprintf(out, STATUSPREFIX" ","TimingService");
+
+ fprintf(out, "current %s, best %s, pref %s", (timingDomain.current != NULL) ? timingDomain.current->id : "none",
+ (timingDomain.best != NULL) ? timingDomain.best->id : "none",
+ (timingDomain.preferred != NULL) ? timingDomain.preferred->id : "none");
+
+ if((timingDomain.current != NULL) &&
+ (timingDomain.current->holdTimeLeft > 0)) {
+ fprintf(out, ", hold %d sec", timingDomain.current->holdTimeLeft);
+ } else if(timingDomain.electionLeft) {
+ fprintf(out, ", elec %d sec", timingDomain.electionLeft);
+ }
+
+ fprintf(out, "\n");
+
+ fprintf(out, STATUSPREFIX" ","TimingServices");
+
+ fprintf(out, "total %d, avail %d, oper %d, idle %d, in_ctrl %d%s\n",
+ timingDomain.serviceCount,
+ timingDomain.availableCount,
+ timingDomain.operationalCount,
+ timingDomain.idleCount,
+ timingDomain.controlCount,
+ timingDomain.controlCount > 1 ? " (!)":"");
+
+ fprintf(out, STATUSPREFIX" ","Performance");
+ fprintf(out,"Message RX %d/s, TX %d/s", ptpClock->counters.messageReceiveRate,
+ ptpClock->counters.messageSendRate);
+ if(ptpClock->portDS.portState == PTP_MASTER) {
+ if(rtOpts->unicastNegotiation) {
+ fprintf(out,", slaves %d", ptpClock->slaveCount);
+ } else if (rtOpts->ipMode == IPMODE_UNICAST) {
+ fprintf(out,", slaves %d", ptpClock->unicastDestinationCount);
+ }
+ }
+
+ fprintf(out,"\n");
+
+ if ( ptpClock->portDS.portState == PTP_SLAVE ||
+ ptpClock->defaultDS.clockQuality.clockClass == 255 ) {
+
+ fprintf(out, STATUSPREFIX" %lu\n","Announce received",
+ (unsigned long)ptpClock->counters.announceMessagesReceived);
+ fprintf(out, STATUSPREFIX" %lu\n","Sync received",
+ (unsigned long)ptpClock->counters.syncMessagesReceived);
+ if(ptpClock->defaultDS.twoStepFlag)
+ fprintf(out, STATUSPREFIX" %lu\n","Follow-up received",
+ (unsigned long)ptpClock->counters.followUpMessagesReceived);
+ if(ptpClock->portDS.delayMechanism == E2E) {
+ fprintf(out, STATUSPREFIX" %lu\n","DelayReq sent",
+ (unsigned long)ptpClock->counters.delayReqMessagesSent);
+ fprintf(out, STATUSPREFIX" %lu\n","DelayResp received",
+ (unsigned long)ptpClock->counters.delayRespMessagesReceived);
+ }
+ }
+
+ if( ptpClock->portDS.portState == PTP_MASTER ||
+ ptpClock->defaultDS.clockQuality.clockClass < 128 ) {
+ fprintf(out, STATUSPREFIX" %lu received, %lu sent \n","Announce",
+ (unsigned long)ptpClock->counters.announceMessagesReceived,
+ (unsigned long)ptpClock->counters.announceMessagesSent);
+ fprintf(out, STATUSPREFIX" %lu\n","Sync sent",
+ (unsigned long)ptpClock->counters.syncMessagesSent);
+ if(ptpClock->defaultDS.twoStepFlag)
+ fprintf(out, STATUSPREFIX" %lu\n","Follow-up sent",
+ (unsigned long)ptpClock->counters.followUpMessagesSent);
+
+ if(ptpClock->portDS.delayMechanism == E2E) {
+ fprintf(out, STATUSPREFIX" %lu\n","DelayReq received",
+ (unsigned long)ptpClock->counters.delayReqMessagesReceived);
+ fprintf(out, STATUSPREFIX" %lu\n","DelayResp sent",
+ (unsigned long)ptpClock->counters.delayRespMessagesSent);
+ }
+
+ }
+
+ if(ptpClock->portDS.delayMechanism == P2P) {
+
+ fprintf(out, STATUSPREFIX" %lu received, %lu sent\n","PdelayReq",
+ (unsigned long)ptpClock->counters.pdelayReqMessagesReceived,
+ (unsigned long)ptpClock->counters.pdelayReqMessagesSent);
+ fprintf(out, STATUSPREFIX" %lu received, %lu sent\n","PdelayResp",
+ (unsigned long)ptpClock->counters.pdelayRespMessagesReceived,
+ (unsigned long)ptpClock->counters.pdelayRespMessagesSent);
+ fprintf(out, STATUSPREFIX" %lu received, %lu sent\n","PdelayRespFollowUp",
+ (unsigned long)ptpClock->counters.pdelayRespFollowUpMessagesReceived,
+ (unsigned long)ptpClock->counters.pdelayRespFollowUpMessagesSent);
+
+ }
+
+ if(ptpClock->counters.domainMismatchErrors)
+ fprintf(out, STATUSPREFIX" %lu\n","Domain Mismatches",
+ (unsigned long)ptpClock->counters.domainMismatchErrors);
+
+ if(ptpClock->counters.ignoredAnnounce)
+ fprintf(out, STATUSPREFIX" %lu\n","Ignored Announce",
+ (unsigned long)ptpClock->counters.ignoredAnnounce);
+
+ if(ptpClock->counters.unicastGrantsDenied)
+ fprintf(out, STATUSPREFIX" %lu\n","Denied Unicast",
+ (unsigned long)ptpClock->counters.unicastGrantsDenied);
+
+ fprintf(out, STATUSPREFIX" %lu\n","State transitions",
+ (unsigned long)ptpClock->counters.stateTransitions);
+ fprintf(out, STATUSPREFIX" %lu\n","PTP Engine resets",
+ (unsigned long)ptpClock->resetCount);
+
+
+ fflush(out);
+}
+
+void
+displayPortIdentity(PortIdentity *port, const char *prefixMessage)
+{
+ static char sbuf[SCREEN_BUFSZ];
+ int len = 0;
+
+ memset(sbuf, ' ', sizeof(sbuf));
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, "%s ", prefixMessage);
+ len += snprint_PortIdentity(sbuf + len, sizeof(sbuf) - len, port);
+ len += snprintf(sbuf + len, sizeof(sbuf) - len, "\n");
+ INFO("%s",sbuf);
+}
+
+
+void
+recordSync(UInteger16 sequenceId, TimeInternal * time)
+{
+ extern RunTimeOpts rtOpts;
+ if (rtOpts.recordLog.logEnabled && rtOpts.recordLog.logFP != NULL) {
+ fprintf(rtOpts.recordLog.logFP, "%d %llu\n", sequenceId,
+ ((time->seconds * 1000000000ULL) + time->nanoseconds)
+ );
+ maintainLogSize(&rtOpts.recordLog);
+ }
+}
+
+Boolean
+nanoSleep(TimeInternal * t)
+{
+ struct timespec ts, tr;
+
+ ts.tv_sec = t->seconds;
+ ts.tv_nsec = t->nanoseconds;
+
+ if (nanosleep(&ts, &tr) < 0) {
+ t->seconds = tr.tv_sec;
+ t->nanoseconds = tr.tv_nsec;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#ifdef __QNXNTO__
+
+static const struct sigevent* timerIntHandler(void* data, int id) {
+ struct timespec tp;
+ TimerIntData* myData = (TimerIntData*)data;
+ uint64_t new_tsc = ClockCycles();
+
+ clock_gettime(CLOCK_REALTIME, &tp);
+
+ if(new_tsc > myData->prev_tsc) {
+ myData->cur_delta = new_tsc - myData->prev_tsc;
+ /* when hell freezeth over, thy TSC shall roll over */
+ } else {
+ myData->cur_delta = myData->prev_delta;
+ }
+ /* 4/6 weighted average */
+ myData->filtered_delta = (40 * myData->cur_delta + 60 * myData->prev_delta) / 100;
+ myData->prev_delta = myData->cur_delta;
+ myData->prev_tsc = new_tsc;
+
+ if(myData->counter < 2) {
+ myData->counter++;
+ }
+
+ myData->last_clock = timespec2nsec(&tp);
+ return NULL;
+
+}
+#endif
+
+ void getTime(TimeInternal *time)
+ {
+#ifdef __QNXNTO__
+ static TimerIntData tmpData;
+ int ret;
+ uint64_t delta;
+ double tick_delay;
+ uint64_t clock_offset;
+ struct timespec tp;
+ if(!tDataUpdated) {
+ memset(&tData, 0, sizeof(TimerIntData));
+ if(ThreadCtl(_NTO_TCTL_IO, 0) == -1) {
+ ERROR("QNX: could not give process I/O privileges");
+ return;
+ }
+
+ tData.cps = SYSPAGE_ENTRY(qtime)->cycles_per_sec;
+ tData.ns_per_tick = 1000000000.0 / tData.cps;
+ tData.prev_tsc = ClockCycles();
+ clock_gettime(CLOCK_REALTIME, &tp);
+ tData.last_clock = timespec2nsec(&tp);
+ ret = InterruptAttach(0, timerIntHandler, &tData, sizeof(TimerIntData), _NTO_INTR_FLAGS_END | _NTO_INTR_FLAGS_TRK_MSK);
+
+ if(ret == -1) {
+ ERROR("QNX: could not attach to timer interrupt");
+ return ;
+ }
+ tDataUpdated = TRUE;
+ time->seconds = tp.tv_sec;
+ time->nanoseconds = tp.tv_nsec;
+ return;
+ }
+
+ memcpy(&tmpData, &tData, sizeof(TimerIntData));
+
+ delta = ClockCycles() - tmpData.prev_tsc;
+
+ /* compute time since last clock update */
+ tick_delay = (double)delta / (double)tmpData.filtered_delta;
+ clock_offset = (uint64_t)(tick_delay * tmpData.ns_per_tick * (double)tmpData.filtered_delta);
+
+ /* not filtered yet */
+ if(tData.counter < 2) {
+ clock_offset = 0;
+ }
+
+ DBGV("QNX getTime cps: %lld tick interval: %.09f, time since last tick: %lld\n",
+ tmpData.cps, tmpData.filtered_delta * tmpData.ns_per_tick, clock_offset);
+
+ nsec2timespec(&tp, tmpData.last_clock + clock_offset);
+
+ time->seconds = tp.tv_sec;
+ time->nanoseconds = tp.tv_nsec;
+ return;
+#else
+
+#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
+
+ struct timespec tp;
+ if (clock_gettime(CLOCK_REALTIME, &tp) < 0) {
+ PERROR("clock_gettime() failed, exiting.");
+ exit(0);
+ }
+ time->seconds = tp.tv_sec;
+ time->nanoseconds = tp.tv_nsec;
+
+#else
+
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ time->seconds = tv.tv_sec;
+ time->nanoseconds = tv.tv_usec * 1000;
+
+#endif /* _POSIX_TIMERS */
+#endif /* __QNXNTO__ */
+}
+
+void
+getTimeMonotonic(TimeInternal * time)
+{
+#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
+
+ struct timespec tp;
+#ifndef CLOCK_MONOTINIC
+ if (clock_gettime(CLOCK_REALTIME, &tp) < 0) {
+#else
+ if (clock_gettime(CLOCK_MONOTONIC, &tp) < 0) {
+#endif /* CLOCK_MONOTONIC */
+ PERROR("clock_gettime() failed, exiting.");
+ exit(0);
+ }
+ time->seconds = tp.tv_sec;
+ time->nanoseconds = tp.tv_nsec;
+#else
+
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ time->seconds = tv.tv_sec;
+ time->nanoseconds = tv.tv_usec * 1000;
+
+#endif /* _POSIX_TIMERS */
+}
+
+
+void
+setTime(TimeInternal * time)
+{
+
+#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
+
+ struct timespec tp;
+ tp.tv_sec = time->seconds;
+ tp.tv_nsec = time->nanoseconds;
+
+#else
+
+ struct timeval tv;
+ tv.tv_sec = time->seconds;
+ tv.tv_usec = time->nanoseconds / 1000;
+
+#endif /* _POSIX_TIMERS */
+
+#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
+
+ if (clock_settime(CLOCK_REALTIME, &tp) < 0) {
+ PERROR("Could not set system time");
+ return;
+ }
+
+#else
+
+ settimeofday(&tv, 0);
+
+#endif /* _POSIX_TIMERS */
+
+ struct timespec tmpTs = { time->seconds,0 };
+
+ char timeStr[MAXTIMESTR];
+ strftime(timeStr, MAXTIMESTR, "%x %X", localtime(&tmpTs.tv_sec));
+ WARNING("Stepped the system clock to: %s.%d\n",
+ timeStr, time->nanoseconds);
+
+}
+
+#ifdef HAVE_LINUX_RTC_H
+
+/* Set the RTC to the desired time time */
+void setRtc(TimeInternal *timeToSet)
+{
+
+ static Boolean deviceFound = FALSE;
+ static char* rtcDev;
+ struct tm* tmTime;
+ time_t seconds;
+ int rtcFd;
+ struct stat statBuf;
+
+ if (!deviceFound) {
+ if(stat("/dev/misc/rtc", &statBuf) == 0) {
+ rtcDev="/dev/misc/rtc\0";
+ deviceFound = TRUE;
+ } else if(stat("/dev/rtc", &statBuf) == 0) {
+ rtcDev="/dev/rtc\0";
+ deviceFound = TRUE;
+ } else if(stat("/dev/rtc0", &statBuf) == 0) {
+ rtcDev="/dev/rtc0\0";
+ deviceFound = TRUE;
+ } else {
+
+ ERROR("Could not set RTC time - no suitable rtc device found\n");
+ return;
+ }
+
+ if(!S_ISCHR(statBuf.st_mode)) {
+ ERROR("Could not set RTC time - device %s is not a character device\n",
+ rtcDev);
+ deviceFound = FALSE;
+ return;
+ }
+
+ }
+
+ DBGV("Usable RTC device: %s\n",rtcDev);
+
+ if(timeToSet->seconds == 0 && timeToSet->nanoseconds==0) {
+ getTime(timeToSet);
+ }
+
+
+
+ if((rtcFd = open(rtcDev, O_RDONLY)) < 0) {
+ PERROR("Could not set RTC time: error opening %s", rtcDev);
+ return;
+ }
+
+ seconds = (time_t)timeToSet->seconds;
+ if(timeToSet->nanoseconds >= 500000) seconds++;
+ tmTime = gmtime(&seconds);
+
+ DBGV("Set RTC from %d seconds to y: %d m: %d d: %d \n",timeToSet->seconds,tmTime->tm_year,tmTime->tm_mon,tmTime->tm_mday);
+
+ if(ioctl(rtcFd, RTC_SET_TIME, tmTime) < 0) {
+ PERROR("Could not set RTC time on %s - ioctl failed", rtcDev);
+ goto cleanup;
+ }
+
+ NOTIFY("Succesfully set RTC time using %s\n", rtcDev);
+
+cleanup:
+
+ close(rtcFd);
+
+}
+
+#endif /* HAVE_LINUX_RTC_H */
+
+/* returns a double beween 0.0 and 1.0 */
+double
+getRand(void)
+{
+ return((rand() * 1.0) / RAND_MAX);
+}
+
+/* Attempt setting advisory write lock on a file descriptor*/
+int
+lockFile(int fd)
+{
+ struct flock fl;
+
+ fl.l_type = DEFAULT_LOCKMODE;
+ fl.l_start = 0;
+ fl.l_whence = SEEK_SET;
+ fl.l_len = 0;
+ return(fcntl(fd, F_SETLK, &fl));
+}
+
+/*
+ * Check if a file descriptor's lock flags match specified flags,
+ * do not acquire lock - just query. If the flags match, populate
+ * lockPid with the PID of the process already holding the lock(s)
+ * Return values: -1 = error, 0 = locked, 1 = not locked
+ */
+int checkLockStatus(int fd, short lockType, int *lockPid) {
+
+ struct flock fl;
+
+ memset(&fl, 0, sizeof(fl));
+
+ if(fcntl(fd, F_GETLK, &fl) == -1)
+ {
+ return -1;
+ }
+ /* Return 0 if file is already locked, but not by us */
+ if((lockType & fl.l_type) && fl.l_pid != getpid()) {
+ *lockPid = fl.l_pid;
+ return 0;
+ }
+
+ return 1;
+
+}
+
+/*
+ * Check if it's possible to acquire lock on a file - if already locked,
+ * populate lockPid with the PID currently holding the write lock.
+ */
+int
+checkFileLockable(const char *fileName, int *lockPid) {
+
+ FILE* fileHandle;
+ int ret;
+
+ if((fileHandle = fopen(fileName,"w+")) == NULL) {
+ PERROR("Could not open %s for writing\n");
+ return -1;
+ }
+
+ ret = checkLockStatus(fileno(fileHandle), DEFAULT_LOCKMODE, lockPid);
+ if (ret == -1) {
+ PERROR("Could not check lock status of %s",fileName);
+ }
+
+ fclose(fileHandle);
+ return ret;
+}
+
+/*
+ * If automatic lock files are used, check for potential conflicts
+ * based on already existing lock files containing our interface name
+ * or clock driver name
+ */
+Boolean
+checkOtherLocks(RunTimeOpts* rtOpts)
+{
+
+
+char searchPattern[PATH_MAX];
+char * lockPath = 0;
+int lockPid = 0;
+glob_t matchedFiles;
+Boolean ret = TRUE;
+int matches = 0, counter = 0;
+
+ /* no need to check locks */
+ if(rtOpts->ignore_daemon_lock ||
+ !rtOpts->autoLockFile)
+ return TRUE;
+
+ /*
+ * Try to discover if we can run in desired mode
+ * based on the presence of other lock files
+ * and them being lockable
+ */
+
+ /* Check for other ptpd running on the same interface - same for all modes */
+ snprintf(searchPattern, PATH_MAX,"%s/%s_*_%s.lock",
+ rtOpts->lockDirectory, PTPD_PROGNAME,rtOpts->ifaceName);
+
+ DBGV("SearchPattern: %s\n",searchPattern);
+ switch(glob(searchPattern, 0, NULL, &matchedFiles)) {
+
+ case GLOB_NOSPACE:
+ case GLOB_ABORTED:
+ PERROR("Could not scan %s directory\n", rtOpts->lockDirectory);;
+ ret = FALSE;
+ goto end;
+ default:
+ break;
+ }
+
+ counter = matchedFiles.gl_pathc;
+ matches = counter;
+ while (matches--) {
+ lockPath=matchedFiles.gl_pathv[matches];
+ DBG("matched: %s\n",lockPath);
+ /* check if there is a lock file with our NIC in the name */
+ switch(checkFileLockable(lockPath, &lockPid)) {
+ /* Could not check lock status */
+ case -1:
+ ERROR("Looks like "USER_DESCRIPTION" may be already running on %s: %s found, but could not check lock\n",
+ rtOpts->ifaceName, lockPath);
+ ret = FALSE;
+ goto end;
+ /* It was possible to acquire lock - file looks abandoned */
+ case 1:
+ DBG("Lock file %s found, but is not locked for writing.\n", lockPath);
+ ret = TRUE;
+ break;
+ /* file is locked */
+ case 0:
+ ERROR("Looks like "USER_DESCRIPTION" is already running on %s: %s found and is locked by pid %d\n",
+ rtOpts->ifaceName, lockPath, lockPid);
+ ret = FALSE;
+ goto end;
+ }
+ }
+
+ if(matches > 0)
+ globfree(&matchedFiles);
+ /* Any mode that can control the clock - also check the clock driver */
+ if(rtOpts->clockQuality.clockClass > 127 ) {
+ snprintf(searchPattern, PATH_MAX,"%s/%s_%s_*.lock",
+ rtOpts->lockDirectory,PTPD_PROGNAME,DEFAULT_CLOCKDRIVER);
+ DBGV("SearchPattern: %s\n",searchPattern);
+
+ switch(glob(searchPattern, 0, NULL, &matchedFiles)) {
+
+ case GLOB_NOSPACE:
+ case GLOB_ABORTED:
+ PERROR("Could not scan %s directory\n", rtOpts->lockDirectory);;
+ ret = FALSE;
+ goto end;
+ default:
+ break;
+ }
+ counter = matchedFiles.gl_pathc;
+ matches = counter;
+ while (counter--) {
+ lockPath=matchedFiles.gl_pathv[counter];
+ DBG("matched: %s\n",lockPath);
+ /* Check if there is a lock file with our clock driver in the name */
+ switch(checkFileLockable(lockPath, &lockPid)) {
+ /* could not check lock status */
+ case -1:
+ ERROR("Looks like "USER_DESCRIPTION" may already be controlling the \""DEFAULT_CLOCKDRIVER
+ "\" clock: %s found, but could not check lock status.\n", lockPath);
+ ret = FALSE;
+ goto end;
+ /* it was possible to acquire lock - looks abandoned */
+ case 1:
+ DBG("Lock file %s found, but is not locked for writing.\n", lockPath);
+ ret = TRUE;
+ break;
+ /* file is locked */
+ case 0:
+ ERROR("Looks like "USER_DESCRIPTION" is already controlling the \""DEFAULT_CLOCKDRIVER
+ "\" clock: %s found and is locked by pid %d\n", lockPath, lockPid);
+ default:
+ ret = FALSE;
+ goto end;
+ }
+ }
+ }
+
+ ret = TRUE;
+
+end:
+ if(matches>0)
+ globfree(&matchedFiles);
+ return ret;
+
+}
+
+
+/* Whole block of adjtimex() functions starts here - only for systems with sys/timex.h */
+
+#ifdef HAVE_SYS_TIMEX_H
+
+/*
+ * Apply a tick / frequency shift to the kernel clock
+ */
+
+Boolean
+adjFreq(double adj)
+{
+
+ extern RunTimeOpts rtOpts;
+ struct timex t;
+
+#ifdef HAVE_STRUCT_TIMEX_TICK
+ Integer32 tickAdj = 0;
+
+#ifdef PTPD_DBG2
+ double oldAdj = adj;
+#endif
+
+#endif /* HAVE_STRUCT_TIMEX_TICK */
+
+ memset(&t, 0, sizeof(t));
+
+ /* Clamp to max PPM */
+ if (adj > rtOpts.servoMaxPpb){
+ adj = rtOpts.servoMaxPpb;
+ } else if (adj < -rtOpts.servoMaxPpb){
+ adj = -rtOpts.servoMaxPpb;
+ }
+
+/* Y U NO HAVE TICK? */
+#ifdef HAVE_STRUCT_TIMEX_TICK
+
+ /* Get the USER_HZ value */
+ Integer32 userHZ = sysconf(_SC_CLK_TCK);
+
+ /*
+ * Get the tick resolution (ppb) - offset caused by changing the tick value by 1.
+ * The ticks value is the duration of one tick in us. So with userHz = 100 ticks per second,
+ * change of ticks by 1 (us) means a 100 us frequency shift = 100 ppm = 100000 ppb.
+ * For userHZ = 1000, change by 1 is a 1ms offset (10 times more ticks per second)
+ */
+ Integer32 tickRes = userHZ * 1000;
+
+ /*
+ * If we are outside the standard +/-512ppm, switch to a tick + freq combination:
+ * Keep moving ticks from adj to tickAdj until we get back to the normal range.
+ * The offset change will not be super smooth as we flip between tick and frequency,
+ * but this in general should only be happening under extreme conditions when dragging the
+ * offset down from very large values. When maxPPM is left at the default value, behaviour
+ * is the same as previously, clamped to 512ppm, but we keep tick at the base value,
+ * preventing long stabilisation times say when we had a non-default tick value left over
+ * from a previous NTP run.
+ */
+ if (adj > ADJ_FREQ_MAX){
+ while (adj > ADJ_FREQ_MAX) {
+ tickAdj++;
+ adj -= tickRes;
+ }
+
+ } else if (adj < -ADJ_FREQ_MAX){
+ while (adj < -ADJ_FREQ_MAX) {
+ tickAdj--;
+ adj += tickRes;
+ }
+ }
+ /* Base tick duration - 10000 when userHZ = 100 */
+ t.tick = 1E6 / userHZ;
+ /* Tick adjustment if necessary */
+ t.tick += tickAdj;
+
+
+ t.modes = ADJ_TICK;
+
+#endif /* HAVE_STRUCT_TIMEX_TICK */
+
+ t.modes |= MOD_FREQUENCY;
+
+ double dFreq = adj * ((1 << 16) / 1000.0);
+ t.freq = (int) round(dFreq);
+#ifdef HAVE_STRUCT_TIMEX_TICK
+ DBG2("adjFreq: oldadj: %.09f, newadj: %.09f, tick: %d, tickadj: %d\n", oldAdj, adj,t.tick,tickAdj);
+#endif /* HAVE_STRUCT_TIMEX_TICK */
+ DBG2(" adj is %.09f; t freq is %d (float: %.09f)\n", adj, t.freq, dFreq);
+
+ return !adjtimex(&t);
+}
+
+
+double
+getAdjFreq(void)
+{
+ struct timex t;
+ double dFreq;
+
+ DBGV("getAdjFreq called\n");
+
+ memset(&t, 0, sizeof(t));
+ t.modes = 0;
+ adjtimex(&t);
+
+ dFreq = (t.freq + 0.0) / ((1<<16) / 1000.0);
+
+ DBGV(" kernel adj is: %f, kernel freq is: %d\n",
+ dFreq, t.freq);
+
+ return(dFreq);
+}
+
+
+/* First cut on informing the clock */
+void
+informClockSource(PtpClock* ptpClock)
+{
+ struct timex tmx;
+
+ memset(&tmx, 0, sizeof(tmx));
+
+ tmx.modes = MOD_MAXERROR | MOD_ESTERROR;
+
+ tmx.maxerror = (ptpClock->currentDS.offsetFromMaster.seconds * 1E9 +
+ ptpClock->currentDS.offsetFromMaster.nanoseconds) / 1000;
+ tmx.esterror = tmx.maxerror;
+
+ if (adjtimex(&tmx) < 0)
+ DBG("informClockSource: could not set adjtimex flags: %s", strerror(errno));
+}
+
+
+void
+unsetTimexFlags(int flags, Boolean quiet)
+{
+ struct timex tmx;
+ int ret;
+
+ memset(&tmx, 0, sizeof(tmx));
+
+ tmx.modes = MOD_STATUS;
+
+ tmx.status = getTimexFlags();
+ if(tmx.status == -1)
+ return;
+ /* unset all read-only flags */
+ tmx.status &= ~STA_RONLY;
+ tmx.status &= ~flags;
+
+ ret = adjtimex(&tmx);
+
+ if (ret < 0)
+ PERROR("Could not unset adjtimex flags: %s", strerror(errno));
+
+ if(!quiet && ret > 2) {
+ switch (ret) {
+ case TIME_OOP:
+ WARNING("Adjtimex: leap second already in progress\n");
+ break;
+ case TIME_WAIT:
+ WARNING("Adjtimex: leap second already occurred\n");
+ break;
+#if !defined(TIME_BAD)
+ case TIME_ERROR:
+#else
+ case TIME_BAD:
+#endif /* TIME_BAD */
+ default:
+ DBGV("unsetTimexFlags: adjtimex() returned TIME_BAD\n");
+ break;
+ }
+ }
+}
+
+int getTimexFlags(void)
+{
+ struct timex tmx;
+ int ret;
+
+ memset(&tmx, 0, sizeof(tmx));
+
+ tmx.modes = 0;
+ ret = adjtimex(&tmx);
+ if (ret < 0) {
+ PERROR("Could not read adjtimex flags: %s", strerror(errno));
+ return(-1);
+
+ }
+ return( tmx.status );
+}
+
+Boolean
+checkTimexFlags(int flags) {
+ int tflags = getTimexFlags();
+
+ if (tflags == -1)
+ return FALSE;
+ return ((tflags & flags) == flags);
+}
+
+/*
+ * TODO: track NTP API changes - NTP API version check
+ * is required - the method of setting the TAI offset
+ * may change with next API versions
+ */
+
+#if defined(MOD_TAI) && NTP_API == 4
+void
+setKernelUtcOffset(int utc_offset) {
+
+ struct timex tmx;
+ int ret;
+
+ memset(&tmx, 0, sizeof(tmx));
+
+ tmx.modes = MOD_TAI;
+ tmx.constant = utc_offset;
+
+ DBG2("Kernel NTP API supports TAI offset. "
+ "Setting TAI offset to %d", utc_offset);
+
+ ret = adjtimex(&tmx);
+
+ if (ret < 0) {
+ PERROR("Could not set kernel TAI offset: %s", strerror(errno));
+ }
+}
+Boolean
+getKernelUtcOffset(int *utc_offset) {
+
+ static Boolean warned = FALSE;
+ int ret;
+
+#if defined(HAVE_NTP_GETTIME)
+ struct ntptimeval ntpv;
+ memset(&ntpv, 0, sizeof(ntpv));
+ ret = ntp_gettime(&ntpv);
+#else
+ struct timex tmx;
+ memset(&tmx, 0, sizeof(tmx));
+ tmx.modes = 0;
+ ret = adjtimex(&tmx);
+#endif /* HAVE_NTP_GETTIME */
+
+ if (ret < 0) {
+ if(!warned) {
+ PERROR("Could not read adjtimex/ntp_gettime flags: %s", strerror(errno));
+ }
+ warned = TRUE;
+ return FALSE;
+
+ }
+#if !defined(HAVE_NTP_GETTIME) && defined(HAVE_STRUCT_TIMEX_TAI)
+ *utc_offset = ( tmx.tai );
+ return TRUE;
+#elif defined(HAVE_NTP_GETTIME) && defined(HAVE_STRUCT_NTPTIMEVAL_TAI)
+ *utc_offset = (int)(ntpv.tai);
+ return TRUE;
+#endif /* HAVE_STRUCT_TIMEX_TAI */
+ if(!warned) {
+ WARNING("No OS support for kernel TAI/UTC offset information. Cannot read UTC offset.\n");
+ }
+ warned = TRUE;
+ return FALSE;
+}
+#endif /* MOD_TAI */
+
+void
+setTimexFlags(int flags, Boolean quiet)
+{
+ struct timex tmx;
+ int ret;
+
+ memset(&tmx, 0, sizeof(tmx));
+
+ tmx.modes = MOD_STATUS;
+
+ tmx.status = getTimexFlags();
+ if(tmx.status == -1)
+ return;
+ /* unset all read-only flags */
+ tmx.status &= ~STA_RONLY;
+ tmx.status |= flags;
+
+ ret = adjtimex(&tmx);
+
+ if (ret < 0)
+ PERROR("Could not set adjtimex flags: %s", strerror(errno));
+
+ if(!quiet && ret > 2) {
+ switch (ret) {
+ case TIME_OOP:
+ WARNING("Adjtimex: leap second already in progress\n");
+ break;
+ case TIME_WAIT:
+ WARNING("Adjtimex: leap second already occurred\n");
+ break;
+#if !defined(TIME_BAD)
+ case TIME_ERROR:
+#else
+ case TIME_BAD:
+#endif /* TIME_BAD */
+ default:
+ DBGV("unsetTimexFlags: adjtimex() returned TIME_BAD\n");
+ break;
+ }
+ }
+}
+
+#endif /* SYS_TIMEX_H */
+
+#define DRIFTFORMAT "%.0f"
+
+void
+restoreDrift(PtpClock * ptpClock, const RunTimeOpts * rtOpts, Boolean quiet)
+{
+
+ FILE *driftFP;
+ Boolean reset_offset = FALSE;
+ double recovered_drift;
+
+ DBGV("restoreDrift called\n");
+
+ if (ptpClock->drift_saved && rtOpts->drift_recovery_method > 0 ) {
+ ptpClock->servo.observedDrift = ptpClock->last_saved_drift;
+ if (!rtOpts->noAdjust && ptpClock->clockControl.granted) {
+ adjFreq_wrapper(rtOpts, ptpClock, -ptpClock->last_saved_drift);
+ }
+ DBG("loaded cached drift\n");
+ return;
+ }
+
+ switch (rtOpts->drift_recovery_method) {
+
+ case DRIFT_FILE:
+
+ if( (driftFP = fopen(rtOpts->driftFile,"r")) == NULL) {
+ if(errno!=ENOENT) {
+ PERROR("Could not open drift file: %s - using current kernel frequency offset. Ignore this error if ",
+ rtOpts->driftFile);
+ } else {
+ NOTICE("Drift file %s not found - will be initialised on write\n",rtOpts->driftFile);
+ }
+ } else if (fscanf(driftFP, "%lf", &recovered_drift) != 1) {
+ PERROR("Could not load saved offset from drift file - using current kernel frequency offset");
+ fclose(driftFP);
+ } else {
+
+ if(recovered_drift == 0)
+ recovered_drift = 0;
+
+ fclose(driftFP);
+ if(quiet)
+ DBGV("Observed drift loaded from %s: "DRIFTFORMAT" ppb\n",
+ rtOpts->driftFile,
+ recovered_drift);
+ else
+ INFO("Observed drift loaded from %s: "DRIFTFORMAT" ppb\n",
+ rtOpts->driftFile,
+ recovered_drift);
+ break;
+ }
+
+ case DRIFT_KERNEL:
+#ifdef HAVE_SYS_TIMEX_H
+ recovered_drift = -getAdjFreq();
+#else
+ recovered_drift = 0;
+#endif /* HAVE_SYS_TIMEX_H */
+ if(recovered_drift == 0)
+ recovered_drift = 0;
+
+ if (quiet)
+ DBGV("Observed_drift loaded from kernel: "DRIFTFORMAT" ppb\n",
+ recovered_drift);
+ else
+ INFO("Observed_drift loaded from kernel: "DRIFTFORMAT" ppb\n",
+ recovered_drift);
+
+ break;
+
+
+ default:
+
+ reset_offset = TRUE;
+
+ }
+
+ if (reset_offset) {
+ if (!rtOpts->noAdjust && ptpClock->clockControl.granted)
+ adjFreq_wrapper(rtOpts, ptpClock, 0);
+ ptpClock->servo.observedDrift = 0;
+ return;
+ }
+
+ ptpClock->servo.observedDrift = recovered_drift;
+
+ ptpClock->drift_saved = TRUE;
+ ptpClock->last_saved_drift = recovered_drift;
+
+ if (!rtOpts->noAdjust)
+ adjFreq_wrapper(rtOpts, ptpClock, -recovered_drift);
+
+}
+
+
+
+void
+saveDrift(PtpClock * ptpClock, const RunTimeOpts * rtOpts, Boolean quiet)
+{
+ FILE *driftFP;
+
+ DBGV("saveDrift called\n");
+
+ if(ptpClock->portDS.portState == PTP_PASSIVE ||
+ ptpClock->portDS.portState == PTP_MASTER ||
+ ptpClock->defaultDS.clockQuality.clockClass < 128) {
+ DBGV("We're not slave - not saving drift\n");
+ return;
+ }
+
+ if(ptpClock->servo.observedDrift == 0.0 &&
+ ptpClock->portDS.portState == PTP_LISTENING )
+ return;
+
+ if (rtOpts->drift_recovery_method > 0) {
+ ptpClock->last_saved_drift = ptpClock->servo.observedDrift;
+ ptpClock->drift_saved = TRUE;
+ }
+
+ if (rtOpts->drift_recovery_method != DRIFT_FILE)
+ return;
+
+ if(ptpClock->servo.runningMaxOutput) {
+ DBG("Servo running at maximum shift - not saving drift file");
+ return;
+ }
+
+ if( (driftFP = fopen(rtOpts->driftFile,"w")) == NULL) {
+ PERROR("Could not open drift file %s for writing", rtOpts->driftFile);
+ return;
+ }
+
+ /* The fractional part really won't make a difference here */
+ fprintf(driftFP, "%d\n", (int)round(ptpClock->servo.observedDrift));
+
+ if (quiet) {
+ DBGV("Wrote observed drift ("DRIFTFORMAT" ppb) to %s\n",
+ ptpClock->servo.observedDrift, rtOpts->driftFile);
+ } else {
+ INFO("Wrote observed drift ("DRIFTFORMAT" ppb) to %s\n",
+ ptpClock->servo.observedDrift, rtOpts->driftFile);
+ }
+ fclose(driftFP);
+}
+
+#undef DRIFTFORMAT
+
+int parseLeapFile(char *path, LeapSecondInfo *info)
+{
+ FILE *leapFP;
+ TimeInternal now;
+ char lineBuf[PATH_MAX];
+
+ unsigned long ntpSeconds = 0;
+ Integer32 utcSeconds = 0;
+ Integer32 utcExpiry = 0;
+ int ntpOffset = 0;
+ int res;
+
+ getTime(&now);
+
+ info->valid = FALSE;
+
+ if( (leapFP = fopen(path,"r")) == NULL) {
+ PERROR("Could not open leap second list file %s", path);
+ return 0;
+ } else
+
+ memset(info, 0, sizeof(LeapSecondInfo));
+
+ while (fgets(lineBuf, PATH_MAX - 1, leapFP) != NULL) {
+
+ /* capture file expiry time */
+ res = sscanf(lineBuf, "#@ %lu", &ntpSeconds);
+ if(res == 1) {
+ utcExpiry = ntpSeconds - NTP_EPOCH;
+ DBG("leapfile expiry %d\n", utcExpiry);
+ }
+ /* capture leap seconds information */
+ res = sscanf(lineBuf, "%lu %d", &ntpSeconds, &ntpOffset);
+ if(res ==2) {
+ utcSeconds = ntpSeconds - NTP_EPOCH;
+ DBG("leapfile date %d offset %d\n", utcSeconds, ntpOffset);
+
+ /* next leap second date found */
+
+ if((now.seconds ) < utcSeconds) {
+ info->nextOffset = ntpOffset;
+ info->endTime = utcSeconds;
+ info->startTime = utcSeconds - 86400;
+ break;
+ } else
+ /* current leap second value found */
+ if(now.seconds >= utcSeconds) {
+ info->currentOffset = ntpOffset;
+ }
+
+ }
+
+ }
+
+ fclose(leapFP);
+
+ /* leap file past expiry date */
+ if(utcExpiry && utcExpiry < now.seconds) {
+ WARNING("Leap seconds file is expired. Please download the current version\n");
+ return 0;
+ }
+
+ /* we have the current offset - the rest can be invalid but at least we have this */
+ if(info->currentOffset != 0) {
+ info->offsetValid = TRUE;
+ }
+
+ /* if anything failed, return 0 so we know we cannot use leap file information */
+ if((info->startTime == 0) || (info->endTime == 0) ||
+ (info->currentOffset == 0) || (info->nextOffset == 0)) {
+ return 0;
+ INFO("Leap seconds file %s loaded (incomplete): now %d, current %d next %d from %d to %d, type %s\n", path,
+ now.seconds,
+ info->currentOffset, info->nextOffset,
+ info->startTime, info->endTime, info->leapType > 0 ? "positive" : info->leapType < 0 ? "negative" : "unknown");
+ }
+
+ if(info->nextOffset > info->currentOffset) {
+ info->leapType = 1;
+ }
+
+ if(info->nextOffset < info->currentOffset) {
+ info->leapType = -1;
+ }
+
+ INFO("Leap seconds file %s loaded: now %d, current %d next %d from %d to %d, type %s\n", path,
+ now.seconds,
+ info->currentOffset, info->nextOffset,
+ info->startTime, info->endTime, info->leapType > 0 ? "positive" : info->leapType < 0 ? "negative" : "unknown");
+ info->valid = TRUE;
+ return 1;
+
+}
+
+void
+updateXtmp (TimeInternal oldTime, TimeInternal newTime)
+{
+
+/* Add the old time entry to utmp/wtmp */
+
+/* About as long as the ntpd implementation, but not any less ugly */
+
+#ifdef HAVE_UTMPX_H
+ struct utmpx utx;
+ memset(&utx, 0, sizeof(utx));
+ strncpy(utx.ut_user, "date", sizeof(utx.ut_user));
+#ifndef OTIME_MSG
+ strncpy(utx.ut_line, "|", sizeof(utx.ut_line));
+#else
+ strncpy(utx.ut_line, OTIME_MSG, sizeof(utx.ut_line));
+#endif /* OTIME_MSG */
+#ifdef OLD_TIME
+ utx.ut_tv.tv_sec = oldTime.seconds;
+ utx.ut_tv.tv_usec = oldTime.nanoseconds / 1000;
+ utx.ut_type = OLD_TIME;
+#else /* no ut_type */
+ utx.ut_time = oldTime.seconds;
+#endif /* OLD_TIME */
+
+/* ======== BEGIN OLD TIME EVENT - UTMPX / WTMPX =========== */
+#ifdef HAVE_UTMPXNAME
+ utmpxname("/var/log/utmp");
+#endif /* HAVE_UTMPXNAME */
+ setutxent();
+ pututxline(&utx);
+ endutxent();
+#ifdef HAVE_UPDWTMPX
+ updwtmpx("/var/log/wtmp", &utx);
+#endif /* HAVE_IPDWTMPX */
+/* ======== END OLD TIME EVENT - UTMPX / WTMPX =========== */
+
+#else /* NO UTMPX_H */
+
+#ifdef HAVE_UTMP_H
+ struct utmp ut;
+ memset(&ut, 0, sizeof(ut));
+ strncpy(ut.ut_name, "date", sizeof(ut.ut_name));
+#ifndef OTIME_MSG
+ strncpy(ut.ut_line, "|", sizeof(ut.ut_line));
+#else
+ strncpy(ut.ut_line, OTIME_MSG, sizeof(ut.ut_line));
+#endif /* OTIME_MSG */
+
+#ifdef OLD_TIME
+
+#ifdef HAVE_STRUCT_UTMP_UT_TIME
+ ut.ut_time = oldTime.seconds;
+#else
+ ut.ut_tv.tv_sec = oldTime.seconds;
+ ut.ut_tv.tv_usec = oldTime.nanoseconds / 1000;
+#endif /* HAVE_STRUCT_UTMP_UT_TIME */
+
+ ut.ut_type = OLD_TIME;
+#else /* no ut_type */
+ ut.ut_time = oldTime.seconds;
+#endif /* OLD_TIME */
+
+/* ======== BEGIN OLD TIME EVENT - UTMP / WTMP =========== */
+#ifdef HAVE_UTMPNAME
+ utmpname(UTMP_FILE);
+#endif /* HAVE_UTMPNAME */
+#ifdef HAVE_SETUTENT
+ setutent();
+#endif /* HAVE_SETUTENT */
+#ifdef HAVE_PUTUTLINE
+ pututline(&ut);
+#endif /* HAVE_PUTUTLINE */
+#ifdef HAVE_ENDUTENT
+ endutent();
+#endif /* HAVE_ENDUTENT */
+#ifdef HAVE_UTMPNAME
+ utmpname(WTMP_FILE);
+#endif /* HAVE_UTMPNAME */
+#ifdef HAVE_SETUTENT
+ setutent();
+#endif /* HAVE_SETUTENT */
+#ifdef HAVE_PUTUTLINE
+ pututline(&ut);
+#endif /* HAVE_PUTUTLINE */
+#ifdef HAVE_ENDUTENT
+ endutent();
+#endif /* HAVE_ENDUTENT */
+/* ======== END OLD TIME EVENT - UTMP / WTMP =========== */
+
+#endif /* HAVE_UTMP_H */
+#endif /* HAVE_UTMPX_H */
+
+/* Add the new time entry to utmp/wtmp */
+
+#ifdef HAVE_UTMPX_H
+ memset(&utx, 0, sizeof(utx));
+ strncpy(utx.ut_user, "date", sizeof(utx.ut_user));
+#ifndef NTIME_MSG
+ strncpy(utx.ut_line, "{", sizeof(utx.ut_line));
+#else
+ strncpy(utx.ut_line, NTIME_MSG, sizeof(utx.ut_line));
+#endif /* NTIME_MSG */
+#ifdef NEW_TIME
+ utx.ut_tv.tv_sec = newTime.seconds;
+ utx.ut_tv.tv_usec = newTime.nanoseconds / 1000;
+ utx.ut_type = NEW_TIME;
+#else /* no ut_type */
+ utx.ut_time = newTime.seconds;
+#endif /* NEW_TIME */
+
+/* ======== BEGIN NEW TIME EVENT - UTMPX / WTMPX =========== */
+#ifdef HAVE_UTMPXNAME
+ utmpxname("/var/log/utmp");
+#endif /* HAVE_UTMPXNAME */
+ setutxent();
+ pututxline(&utx);
+ endutxent();
+#ifdef HAVE_UPDWTMPX
+ updwtmpx("/var/log/wtmp", &utx);
+#endif /* HAVE_UPDWTMPX */
+/* ======== END NEW TIME EVENT - UTMPX / WTMPX =========== */
+
+#else /* NO UTMPX_H */
+
+#ifdef HAVE_UTMP_H
+ memset(&ut, 0, sizeof(ut));
+ strncpy(ut.ut_name, "date", sizeof(ut.ut_name));
+#ifndef NTIME_MSG
+ strncpy(ut.ut_line, "{", sizeof(ut.ut_line));
+#else
+ strncpy(ut.ut_line, NTIME_MSG, sizeof(ut.ut_line));
+#endif /* NTIME_MSG */
+#ifdef NEW_TIME
+
+#ifdef HAVE_STRUCT_UTMP_UT_TIME
+ ut.ut_time = newTime.seconds;
+#else
+ ut.ut_tv.tv_sec = newTime.seconds;
+ ut.ut_tv.tv_usec = newTime.nanoseconds / 1000;
+#endif /* HAVE_STRUCT_UTMP_UT_TIME */
+ ut.ut_type = NEW_TIME;
+#else /* no ut_type */
+ ut.ut_time = newTime.seconds;
+#endif /* NEW_TIME */
+
+/* ======== BEGIN NEW TIME EVENT - UTMP / WTMP =========== */
+#ifdef HAVE_UTMPNAME
+ utmpname(UTMP_FILE);
+#endif /* HAVE_UTMPNAME */
+#ifdef HAVE_SETUTENT
+ setutent();
+#endif /* HAVE_SETUTENT */
+#ifdef HAVE_PUTUTLINE
+ pututline(&ut);
+#endif /* HAVE_PUTUTLINE */
+#ifdef HAVE_ENDUTENT
+ endutent();
+#endif /* HAVE_ENDUTENT */
+#ifdef HAVE_UTMPNAME
+ utmpname(WTMP_FILE);
+#endif /* HAVE_UTMPNAME */
+#ifdef HAVE_SETUTENT
+ setutent();
+#endif /* HAVE_SETUTENT */
+#ifdef HAVE_PUTUTLINE
+ pututline(&ut);
+#endif /* HAVE_PUTUTLINE */
+#ifdef HAVE_ENDUTENT
+ endutent();
+#endif /* HAVE_ENDUTENT */
+/* ======== END NEW TIME EVENT - UTMP / WTMP =========== */
+
+#endif /* HAVE_UTMP_H */
+#endif /* HAVE_UTMPX_H */
+
+}
+
+int setCpuAffinity(int cpu) {
+
+#ifdef __QNXNTO__
+ unsigned num_elements = 0;
+ int *rsizep, masksize_bytes, size;
+ int *rmaskp, *imaskp;
+ void *my_data;
+ uint32_t cpun;
+ num_elements = RMSK_SIZE(_syspage_ptr->num_cpu);
+
+ masksize_bytes = num_elements * sizeof(unsigned);
+
+ size = sizeof(int) + 2 * masksize_bytes;
+ if ((my_data = malloc(size)) == NULL) {
+ return -1;
+ } else {
+ memset(my_data, 0x00, size);
+
+ rsizep = (int *)my_data;
+ rmaskp = rsizep + 1;
+ imaskp = rmaskp + num_elements;
+
+ *rsizep = num_elements;
+
+ if(cpu > _syspage_ptr->num_cpu) {
+ return -1;
+ }
+
+ if(cpu >= 0) {
+ cpun = (uint32_t)cpu;
+ RMSK_SET(cpun, rmaskp);
+ RMSK_SET(cpun, imaskp);
+ } else {
+ for(cpun = 0; cpun < num_elements; cpun++) {
+ RMSK_SET(cpun, rmaskp);
+ RMSK_SET(cpun, imaskp);
+ }
+ }
+ int ret = ThreadCtl( _NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT, my_data);
+ free(my_data);
+ return ret;
+ }
+
+#endif
+
+#ifdef HAVE_SYS_CPUSET_H
+ cpuset_t mask;
+ CPU_ZERO(&mask);
+ if(cpu >= 0) {
+ CPU_SET(cpu,&mask);
+ } else {
+ int i;
+ for(i = 0; i < CPU_SETSIZE; i++) {
+ CPU_SET(i, &mask);
+ }
+ }
+ return(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
+ -1, sizeof(mask), &mask));
+#endif /* HAVE_SYS_CPUSET_H */
+
+#if defined(linux) && defined(HAVE_SCHED_H)
+ cpu_set_t mask;
+ CPU_ZERO(&mask);
+ if(cpu >= 0) {
+ CPU_SET(cpu,&mask);
+ } else {
+ int i;
+ for(i = 0; i < CPU_SETSIZE; i++) {
+ CPU_SET(i, &mask);
+ }
+ }
+ return sched_setaffinity(0, sizeof(mask), &mask);
+#endif /* linux && HAVE_SCHED_H */
+
+return -1;
+
+}
diff --git a/rtemsbsd/ptpd/src/display.c b/rtemsbsd/ptpd/src/display.c
new file mode 100644
index 00000000..839b69b0
--- /dev/null
+++ b/rtemsbsd/ptpd/src/display.c
@@ -0,0 +1,1152 @@
+/*-
+ * Copyright (c) 2012-2013 George V. Neville-Neil,
+ * Wojciech Owczarek
+ * Copyright (c) 2011-2012 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen,
+ * Inaqui Delgado,
+ * Rick Ratzel,
+ * National Instruments.
+ * Copyright (c) 2009-2010 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen
+ *
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file display.c
+ * @date Thu Aug 12 09:06:21 2010
+ *
+ * @brief General routines for displaying internal data.
+ *
+ *
+ */
+
+#include "ptpd.h"
+
+/**\brief Display an Integer64 type*/
+void
+integer64_display(const Integer64 * bigint)
+{
+ DBGV("Integer 64 : \n");
+ DBGV("LSB : %u\n", bigint->lsb);
+ DBGV("MSB : %d\n", bigint->msb);
+}
+
+/**\brief Display an UInteger48 type*/
+void
+uInteger48_display(const UInteger48 * bigint)
+{
+ DBGV("Integer 48 : \n");
+ DBGV("LSB : %u\n", bigint->lsb);
+ DBGV("MSB : %u\n", bigint->msb);
+}
+
+/** \brief Display a TimeInternal Structure*/
+void
+timeInternal_display(const TimeInternal * timeInternal)
+{
+ DBGV("seconds : %d \n", timeInternal->seconds);
+ DBGV("nanoseconds %d \n", timeInternal->nanoseconds);
+}
+
+/** \brief Display a Timestamp Structure*/
+void
+timestamp_display(const Timestamp * timestamp)
+{
+ uInteger48_display(×tamp->secondsField);
+ DBGV("nanoseconds %u \n", timestamp->nanosecondsField);
+}
+
+/**\brief Display a Clockidentity Structure*/
+void
+clockIdentity_display(const ClockIdentity clockIdentity)
+{
+ DBGV(
+ "ClockIdentity : %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ clockIdentity[0], clockIdentity[1], clockIdentity[2],
+ clockIdentity[3], clockIdentity[4], clockIdentity[5],
+ clockIdentity[6], clockIdentity[7]
+ );
+}
+
+/**\brief Display MAC address*/
+void
+clockUUID_display(const Octet * sourceUuid)
+{
+ DBGV(
+ "sourceUuid %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ sourceUuid[0], sourceUuid[1], sourceUuid[2], sourceUuid[3],
+ sourceUuid[4], sourceUuid[5]
+ );
+}
+
+/**\brief Display Network info*/
+void
+netPath_display(const NetPath * net)
+{
+#ifdef RUNTIME_DEBUG
+ struct in_addr tmpAddr;
+ DBGV("eventSock : %d \n", net->eventSock);
+ DBGV("generalSock : %d \n", net->generalSock);
+ tmpAddr.s_addr = net->multicastAddr;
+ DBGV("multicastAdress : %s \n", inet_ntoa(tmpAddr));
+ tmpAddr.s_addr = net->peerMulticastAddr;
+ DBGV("peerMulticastAddress : %s \n", inet_ntoa(tmpAddr));
+#endif /* RUNTIME_DEBUG */
+}
+
+/**\brief Display a IntervalTimer Structure*/
+void
+intervalTimer_display(const IntervalTimer * ptimer)
+{
+ DBGV("interval : %.06f \n", ptimer->interval);
+ DBGV("expire : %d \n", ptimer->expired);
+}
+
+/**\brief Display a TimeInterval Structure*/
+void
+timeInterval_display(const TimeInterval * timeInterval)
+{
+ integer64_display(&timeInterval->scaledNanoseconds);
+}
+
+/**\brief Display a Portidentity Structure*/
+void
+portIdentity_display(const PortIdentity * portIdentity)
+{
+ clockIdentity_display(portIdentity->clockIdentity);
+ DBGV("port number : %d \n", portIdentity->portNumber);
+}
+
+/**\brief Display a Clockquality Structure*/
+void
+clockQuality_display(const ClockQuality * clockQuality)
+{
+ DBGV("clockClass : %d \n", clockQuality->clockClass);
+ DBGV("clockAccuracy : %d \n", clockQuality->clockAccuracy);
+ DBGV("offsetScaledLogVariance : %d \n", clockQuality->offsetScaledLogVariance);
+}
+
+/**\brief Display PTPText Structure*/
+void
+PTPText_display(const PTPText *p, const PtpClock *ptpClock)
+{
+ DBGV(" lengthField : %d \n", p->lengthField);
+ DBGV(" textField : %.*s \n", (int)p->lengthField, p->textField);
+}
+
+/**\brief Display the Network Interface Name*/
+void
+iFaceName_display(const Octet * iFaceName)
+{
+
+ int i;
+
+ DBGV("iFaceName : ");
+
+ for (i = 0; i < IFACE_NAME_LENGTH; i++) {
+ DBGV("%c", iFaceName[i]);
+ }
+ DBGV("\n");
+
+}
+
+/**\brief Display an Unicast Adress*/
+void
+unicast_display(const Octet * unicast)
+{
+
+ int i;
+
+ DBGV("Unicast adress : ");
+
+ for (i = 0; i < NET_ADDRESS_LENGTH; i++) {
+ DBGV("%c", unicast[i]);
+ }
+ DBGV("\n");
+
+}
+
+
+/**\brief Display Sync message*/
+void
+msgSync_display(const MsgSync * sync)
+{
+ DBGV("Message Sync : \n");
+ timestamp_display(&sync->originTimestamp);
+ DBGV("\n");
+}
+
+/**\brief Display Header message*/
+void
+msgHeader_display(const MsgHeader * header)
+{
+ DBGV("Message header : \n");
+ DBGV("\n");
+ DBGV("transportSpecific : %d\n", header->transportSpecific);
+ DBGV("messageType : %d\n", header->messageType);
+ DBGV("versionPTP : %d\n", header->versionPTP);
+ DBGV("messageLength : %d\n", header->messageLength);
+ DBGV("domainNumber : %d\n", header->domainNumber);
+ DBGV("FlagField %02hhx:%02hhx\n", header->flagField0, header->flagField1);
+ DBGV("CorrectionField : \n");
+ integer64_display(&header->correctionField);
+ DBGV("SourcePortIdentity : \n");
+ portIdentity_display(&header->sourcePortIdentity);
+ DBGV("sequenceId : %d\n", header->sequenceId);
+ DBGV("controlField : %d\n", header->controlField);
+ DBGV("logMessageInterval : %d\n", header->logMessageInterval);
+ DBGV("\n");
+}
+
+/**\brief Display Announce message*/
+void
+msgAnnounce_display(const MsgAnnounce * announce)
+{
+ DBGV("Announce Message : \n");
+ DBGV("\n");
+ DBGV("originTimestamp : \n");
+ DBGV("secondField : \n");
+ timestamp_display(&announce->originTimestamp);
+ DBGV("currentUtcOffset : %d \n", announce->currentUtcOffset);
+ DBGV("grandMasterPriority1 : %d \n", announce->grandmasterPriority1);
+ DBGV("grandMasterClockQuality : \n");
+ clockQuality_display(&announce->grandmasterClockQuality);
+ DBGV("grandMasterPriority2 : %d \n", announce->grandmasterPriority2);
+ DBGV("grandMasterIdentity : \n");
+ clockIdentity_display(announce->grandmasterIdentity);
+ DBGV("stepsRemoved : %d \n", announce->stepsRemoved);
+ DBGV("timeSource : %d \n", announce->timeSource);
+ DBGV("\n");
+}
+
+/**\brief Display Follow_UP message*/
+void
+msgFollowUp_display(const MsgFollowUp * follow)
+{
+ timestamp_display(&follow->preciseOriginTimestamp);
+}
+
+/**\brief Display DelayReq message*/
+void
+msgDelayReq_display(const MsgDelayReq * req)
+{
+ timestamp_display(&req->originTimestamp);
+}
+
+/**\brief Display DelayResp message*/
+void
+msgDelayResp_display(const MsgDelayResp * resp)
+{
+ timestamp_display(&resp->receiveTimestamp);
+ portIdentity_display(&resp->requestingPortIdentity);
+}
+
+/**\brief Display Pdelay_Req message*/
+void
+msgPdelayReq_display(const MsgPdelayReq * preq)
+{
+ timestamp_display(&preq->originTimestamp);
+}
+
+/**\brief Display Pdelay_Resp message*/
+void
+msgPdelayResp_display(const MsgPdelayResp * presp)
+{
+
+ timestamp_display(&presp->requestReceiptTimestamp);
+ portIdentity_display(&presp->requestingPortIdentity);
+}
+
+/**\brief Display Pdelay_Resp Follow Up message*/
+void
+msgPdelayRespFollowUp_display(const MsgPdelayRespFollowUp * prespfollow)
+{
+
+ timestamp_display(&prespfollow->responseOriginTimestamp);
+ portIdentity_display(&prespfollow->requestingPortIdentity);
+}
+
+/**\brief Display Management message*/
+void
+msgManagement_display(const MsgManagement * manage)
+{
+ DBGV("Management Message : \n");
+ DBGV("\n");
+ DBGV("targetPortIdentity : \n");
+ portIdentity_display(&manage->targetPortIdentity);
+ DBGV("startingBoundaryHops : %d \n", manage->startingBoundaryHops);
+ DBGV("boundaryHops : %d \n", manage->boundaryHops);
+ DBGV("actionField : %d\n", manage->actionField);
+}
+
+/**\brief Display ManagementTLV Slave Only message*/
+void
+mMSlaveOnly_display(const MMSlaveOnly *slaveOnly, const PtpClock *ptpClock)
+{
+ DBGV("Slave Only ManagementTLV message \n");
+ DBGV("SO : %d \n", slaveOnly->so);
+}
+
+/**\brief Display ManagementTLV Clock Description message*/
+void
+mMClockDescription_display(const MMClockDescription *clockDescription, const PtpClock *ptpClock)
+{
+ DBGV("Clock Description ManagementTLV message \n");
+ DBGV("clockType0 : %d \n", clockDescription->clockType0);
+ DBGV("clockType1 : %d \n", clockDescription->clockType1);
+ DBGV("physicalLayerProtocol : \n");
+ PTPText_display(&clockDescription->physicalLayerProtocol, ptpClock);
+ DBGV("physicalAddressLength : %d \n", clockDescription->physicalAddress.addressLength);
+ if(clockDescription->physicalAddress.addressField) {
+ DBGV("physicalAddressField : \n");
+ clockUUID_display(clockDescription->physicalAddress.addressField);
+ }
+ DBGV("protocolAddressNetworkProtocol : %d \n", clockDescription->protocolAddress.networkProtocol);
+ DBGV("protocolAddressLength : %d \n", clockDescription->protocolAddress.addressLength);
+ if(clockDescription->protocolAddress.addressField) {
+ DBGV("protocolAddressField : %d.%d.%d.%d \n",
+ (UInteger8)clockDescription->protocolAddress.addressField[0],
+ (UInteger8)clockDescription->protocolAddress.addressField[1],
+ (UInteger8)clockDescription->protocolAddress.addressField[2],
+ (UInteger8)clockDescription->protocolAddress.addressField[3]);
+ }
+ DBGV("manufacturerIdentity0 : %d \n", clockDescription->manufacturerIdentity0);
+ DBGV("manufacturerIdentity1 : %d \n", clockDescription->manufacturerIdentity1);
+ DBGV("manufacturerIdentity2 : %d \n", clockDescription->manufacturerIdentity2);
+ DBGV("productDescription : \n");
+ PTPText_display(&clockDescription->productDescription, ptpClock);
+ DBGV("revisionData : \n");
+ PTPText_display(&clockDescription->revisionData, ptpClock);
+ DBGV("userDescription : \n");
+ PTPText_display(&clockDescription->userDescription, ptpClock);
+ DBGV("profileIdentity0 : %d \n", clockDescription->profileIdentity0);
+ DBGV("profileIdentity1 : %d \n", clockDescription->profileIdentity1);
+ DBGV("profileIdentity2 : %d \n", clockDescription->profileIdentity2);
+ DBGV("profileIdentity3 : %d \n", clockDescription->profileIdentity3);
+ DBGV("profileIdentity4 : %d \n", clockDescription->profileIdentity4);
+ DBGV("profileIdentity5 : %d \n", clockDescription->profileIdentity5);
+}
+
+void
+mMUserDescription_display(const MMUserDescription* userDescription, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMInitialize_display(const MMInitialize* initialize, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMDefaultDataSet_display(const MMDefaultDataSet* defaultDataSet, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMCurrentDataSet_display(const MMCurrentDataSet* currentDataSet, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMParentDataSet_display(const MMParentDataSet* parentDataSet, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMTimePropertiesDataSet_display(const MMTimePropertiesDataSet* timePropertiesDataSet, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMPortDataSet_display(const MMPortDataSet* portDataSet, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMPriority1_display(const MMPriority1* priority1, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMPriority2_display(const MMPriority2* priority2, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMDomain_display(const MMDomain* domain, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMLogAnnounceInterval_display(const MMLogAnnounceInterval* logAnnounceInterval, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMAnnounceReceiptTimeout_display(const MMAnnounceReceiptTimeout* announceReceiptTimeout, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMLogSyncInterval_display(const MMLogSyncInterval* logSyncInterval, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMVersionNumber_display(const MMVersionNumber* versionNumber, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMTime_display(const MMTime* time, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMClockAccuracy_display(const MMClockAccuracy* clockAccuracy, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMUtcProperties_display(const MMUtcProperties* utcProperties, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMTraceabilityProperties_display(const MMTraceabilityProperties* traceabilityProperties, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMTimescaleProperties_display(const MMTimescaleProperties* TimescaleProperties, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMUnicastNegotiationEnable_display(const MMUnicastNegotiationEnable* unicastNegotiationEnable, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+
+void
+mMDelayMechanism_display(const MMDelayMechanism* delayMechanism, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMLogMinPdelayReqInterval_display(const MMLogMinPdelayReqInterval* logMinPdelayReqInterval, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+void
+mMErrorStatus_display(const MMErrorStatus* errorStatus, const PtpClock *ptpClock)
+{
+ /* TODO: implement me */
+}
+
+/**\brief Display Signaling message*/
+void
+msgSignaling_display(const MsgSignaling * signaling)
+{
+ DBGV("Signaling Message : \n");
+ DBGV("\n");
+ DBGV("targetPortIdentity : \n");
+ portIdentity_display(&signaling->targetPortIdentity);
+}
+
+void
+sMRequestUnicastTransmission_display(const SMRequestUnicastTransmission* request, const PtpClock *ptpClock)
+{
+ DBGV("Request Unicast Transmission SignalingTLV message \n");
+ DBGV("messageType: %d\n", request->messageType);
+ DBGV("logInterMessagePeriod: %d\n", request->logInterMessagePeriod);
+ DBGV("durationField: %d\n", request->durationField);
+}
+
+void
+sMGrantUnicastTransmission_display(const SMGrantUnicastTransmission* grant, const PtpClock *ptpClock)
+{
+ DBGV("Grant Unicast Transmission SignalingTLV message \n");
+ DBGV("messageType: %d\n", grant->messageType);
+ DBGV("logInterMessagePeriod: %d\n", grant->logInterMessagePeriod);
+ DBGV("durationField: %d\n", grant->durationField);
+ DBGV("R (renewal invited): %d\n", grant->renewal_invited);
+}
+
+void
+sMCancelUnicastTransmission_display(const SMCancelUnicastTransmission* tlv, const PtpClock *ptpClock)
+{
+ DBGV("Cancel Unicast Transmission SignalingTLV message \n");
+ DBGV("messageType: %d\n", tlv->messageType);
+}
+
+void
+sMAcknowledgeCancelUnicastTransmission_display(const SMAcknowledgeCancelUnicastTransmission* tlv, const PtpClock *ptpClock)
+{
+ DBGV("Acknowledge Cancel Unicast Transmission SignalingTLV message \n");
+ DBGV("messageType: %d\n", tlv->messageType);
+}
+
+#define FORMAT_SERVO "%f"
+
+/**\brief Display runTimeOptions structure*/
+void
+displayRunTimeOpts(const RunTimeOpts * rtOpts)
+{
+
+ DBGV("---Run time Options Display-- \n");
+ DBGV("\n");
+ DBGV("announceInterval : %d \n", rtOpts->logAnnounceInterval);
+ DBGV("syncInterval : %d \n", rtOpts->logSyncInterval);
+ clockQuality_display(&(rtOpts->clockQuality));
+ DBGV("priority1 : %d \n", rtOpts->priority1);
+ DBGV("priority2 : %d \n", rtOpts->priority2);
+ DBGV("domainNumber : %d \n", rtOpts->domainNumber);
+ DBGV("slaveOnly : %d \n", rtOpts->slaveOnly);
+ DBGV("currentUtcOffset : %d \n", rtOpts->timeProperties.currentUtcOffset);
+ DBGV("noAdjust : %d \n", rtOpts->noAdjust);
+ DBGV("logStatistics : %d \n", rtOpts->logStatistics);
+ iFaceName_display(rtOpts->ifaceName);
+ DBGV("kP : %d \n", rtOpts->servoKP);
+ DBGV("kI : %d \n", rtOpts->servoKI);
+ DBGV("s : %d \n", rtOpts->s);
+ DBGV("inbound latency : \n");
+ timeInternal_display(&(rtOpts->inboundLatency));
+ DBGV("outbound latency : \n");
+ timeInternal_display(&(rtOpts->outboundLatency));
+ DBGV("max_foreign_records : %d \n", rtOpts->max_foreign_records);
+ DBGV("transport : %d \n", rtOpts->transport);
+ DBGV("\n");
+}
+
+
+/**\brief Display Default data set of a PtpClock*/
+void
+displayDefault(const PtpClock * ptpClock)
+{
+ DBGV("---Ptp Clock Default Data Set-- \n");
+ DBGV("\n");
+ DBGV("twoStepFlag : %d \n", ptpClock->defaultDS.twoStepFlag);
+ clockIdentity_display(ptpClock->defaultDS.clockIdentity);
+ DBGV("numberPorts : %d \n", ptpClock->defaultDS.numberPorts);
+ clockQuality_display(&(ptpClock->defaultDS.clockQuality));
+ DBGV("priority1 : %d \n", ptpClock->defaultDS.priority1);
+ DBGV("priority2 : %d \n", ptpClock->defaultDS.priority2);
+ DBGV("domainNumber : %d \n", ptpClock->defaultDS.domainNumber);
+ DBGV("slaveOnly : %d \n", ptpClock->defaultDS.slaveOnly);
+ DBGV("\n");
+}
+
+
+/**\brief Display Current data set of a PtpClock*/
+void
+displayCurrent(const PtpClock * ptpClock)
+{
+ DBGV("---Ptp Clock Current Data Set-- \n");
+ DBGV("\n");
+
+ DBGV("stepsremoved : %d \n", ptpClock->currentDS.stepsRemoved);
+ DBGV("Offset from master : \n");
+ timeInternal_display(&ptpClock->currentDS.offsetFromMaster);
+ DBGV("Mean path delay : \n");
+ timeInternal_display(&ptpClock->currentDS.meanPathDelay);
+ DBGV("\n");
+}
+
+
+
+/**\brief Display Parent data set of a PtpClock*/
+void
+displayParent(const PtpClock * ptpClock)
+{
+ DBGV("---Ptp Clock Parent Data Set-- \n");
+ DBGV("\n");
+ portIdentity_display(&(ptpClock->parentDS.parentPortIdentity));
+ DBGV("parentStats : %d \n", ptpClock->parentDS.parentStats);
+ DBGV("observedParentOffsetScaledLogVariance : %d \n", ptpClock->parentDS.observedParentOffsetScaledLogVariance);
+ DBGV("observedParentClockPhaseChangeRate : %d \n", ptpClock->parentDS.observedParentClockPhaseChangeRate);
+ DBGV("--GrandMaster--\n");
+ clockIdentity_display(ptpClock->parentDS.grandmasterIdentity);
+ clockQuality_display(&ptpClock->parentDS.grandmasterClockQuality);
+ DBGV("grandmasterpriority1 : %d \n", ptpClock->parentDS.grandmasterPriority1);
+ DBGV("grandmasterpriority2 : %d \n", ptpClock->parentDS.grandmasterPriority2);
+ DBGV("\n");
+}
+
+/**\brief Display Global data set of a PtpClock*/
+void
+displayGlobal(const PtpClock * ptpClock)
+{
+ DBGV("---Ptp Clock Global Time Data Set-- \n");
+ DBGV("\n");
+
+ DBGV("currentUtcOffset : %d \n", ptpClock->timePropertiesDS.currentUtcOffset);
+ DBGV("currentUtcOffsetValid : %d \n", ptpClock->timePropertiesDS.currentUtcOffsetValid);
+ DBGV("leap59 : %d \n", ptpClock->timePropertiesDS.leap59);
+ DBGV("leap61 : %d \n", ptpClock->timePropertiesDS.leap61);
+ DBGV("timeTraceable : %d \n", ptpClock->timePropertiesDS.timeTraceable);
+ DBGV("frequencyTraceable : %d \n", ptpClock->timePropertiesDS.frequencyTraceable);
+ DBGV("ptpTimescale : %d \n", ptpClock->timePropertiesDS.ptpTimescale);
+ DBGV("timeSource : %d \n", ptpClock->timePropertiesDS.timeSource);
+ DBGV("\n");
+}
+
+/**\brief Display Port data set of a PtpClock*/
+void
+displayPort(const PtpClock * ptpClock)
+{
+ DBGV("---Ptp Clock Port Data Set-- \n");
+ DBGV("\n");
+
+ portIdentity_display(&ptpClock->portDS.portIdentity);
+ DBGV("port state : %d \n", ptpClock->portDS.portState);
+ DBGV("logMinDelayReqInterval : %d \n", ptpClock->portDS.logMinDelayReqInterval);
+ DBGV("peerMeanPathDelay : \n");
+ timeInternal_display(&ptpClock->portDS.peerMeanPathDelay);
+ DBGV("logAnnounceInterval : %d \n", ptpClock->portDS.logAnnounceInterval);
+ DBGV("announceReceiptTimeout : %d \n", ptpClock->portDS.announceReceiptTimeout);
+ DBGV("logSyncInterval : %d \n", ptpClock->portDS.logSyncInterval);
+ DBGV("delayMechanism : %d \n", ptpClock->portDS.delayMechanism);
+ DBGV("logMinPdelayReqInterval : %d \n", ptpClock->portDS.logMinPdelayReqInterval);
+ DBGV("versionNumber : %d \n", ptpClock->portDS.versionNumber);
+ DBGV("\n");
+}
+
+/**\brief Display ForeignMaster data set of a PtpClock*/
+void
+displayForeignMaster(const PtpClock * ptpClock)
+{
+
+ ForeignMasterRecord *foreign;
+ int i;
+
+ if (ptpClock->number_foreign_records > 0) {
+
+ DBGV("---Ptp Clock Foreign Data Set-- \n");
+ DBGV("\n");
+ DBGV("There is %d Foreign master Recorded \n", ptpClock->number_foreign_records);
+ foreign = ptpClock->foreign;
+
+ for (i = 0; i < ptpClock->number_foreign_records; i++) {
+
+ portIdentity_display(&foreign->foreignMasterPortIdentity);
+ DBGV("number of Announce message received : %d \n", foreign->foreignMasterAnnounceMessages);
+ msgHeader_display(&foreign->header);
+ msgAnnounce_display(&foreign->announce);
+
+ foreign++;
+ }
+
+ } else {
+ DBGV("No Foreign masters recorded \n");
+ }
+
+ DBGV("\n");
+
+
+}
+
+/**\brief Display other data set of a PtpClock*/
+
+void
+displayOthers(const PtpClock * ptpClock)
+{
+
+ int i;
+
+ /* Usefull to display name of timers */
+#ifdef PTPD_DBGV
+ char timer[][26] = {
+ "PDELAYREQ_INTERVAL_TIMER",
+ "SYNC_INTERVAL_TIMER",
+ "ANNOUNCE_RECEIPT_TIMER",
+ "ANNOUNCE_INTERVAL_TIMER"
+ };
+#endif
+ DBGV("---Ptp Others Data Set-- \n");
+ DBGV("\n");
+
+ /*DBGV("master_to_slave_delay : \n");
+ timeInternal_display(&ptpClock->master_to_slave_delay);
+ DBGV("\n");
+ DBGV("slave_to_master_delay : \n");
+ timeInternal_display(&ptpClock->slave_to_master_delay);
+ */
+
+ DBGV("\n");
+ DBGV("delay_req_receive_time : \n");
+ timeInternal_display(&ptpClock->pdelay_req_receive_time);
+ DBGV("\n");
+ DBGV("delay_req_send_time : \n");
+ timeInternal_display(&ptpClock->pdelay_req_send_time);
+ DBGV("\n");
+ DBGV("delay_resp_receive_time : \n");
+ timeInternal_display(&ptpClock->pdelay_resp_receive_time);
+ DBGV("\n");
+ DBGV("delay_resp_send_time : \n");
+ timeInternal_display(&ptpClock->pdelay_resp_send_time);
+ DBGV("\n");
+ DBGV("sync_receive_time : \n");
+ timeInternal_display(&ptpClock->sync_receive_time);
+ DBGV("\n");
+ //DBGV("R : %f \n", ptpClock->R);
+ DBGV("sentPdelayReq : %d \n", ptpClock->sentPdelayReq);
+ DBGV("sentPdelayReqSequenceId : %d \n", ptpClock->sentPdelayReqSequenceId);
+ DBGV("waitingForFollow : %d \n", ptpClock->waitingForFollow);
+ DBGV("\n");
+ DBGV("Offset from master filter : \n");
+ DBGV("nsec_prev : %d \n", ptpClock->ofm_filt.nsec_prev);
+ DBGV("y : %d \n", ptpClock->ofm_filt.y);
+ DBGV("\n");
+ DBGV("One way delay filter : \n");
+ DBGV("nsec_prev : %d \n", ptpClock->mpd_filt.nsec_prev);
+ DBGV("y : %d \n", ptpClock->mpd_filt.y);
+ DBGV("s_exp : %d \n", ptpClock->mpd_filt.s_exp);
+ DBGV("\n");
+ DBGV("observed drift : "FORMAT_SERVO" \n", ptpClock->servo.observedDrift);
+ DBGV("message activity %d \n", ptpClock->message_activity);
+ DBGV("\n");
+
+ for (i = 0; i < PTP_MAX_TIMER; i++) {
+ DBGV("%s : \n", timer[i]);
+ intervalTimer_display(&ptpClock->timers[i]);
+ DBGV("\n");
+ }
+
+ netPath_display(&ptpClock->netPath);
+ DBGV("mCommunication technology %d \n", ptpClock->port_communication_technology);
+ clockUUID_display(ptpClock->netPath.interfaceID);
+ DBGV("\n");
+}
+
+
+/**\brief Display Buffer in & out of a PtpClock*/
+void
+displayBuffer(const PtpClock * ptpClock)
+{
+
+ int i;
+ int j;
+
+ j = 0;
+
+ DBGV("PtpClock Buffer Out \n");
+ DBGV("\n");
+
+ for (i = 0; i < PACKET_SIZE; i++) {
+ DBGV(":%02hhx", ptpClock->msgObuf[i]);
+ j++;
+
+ if (j == 8) {
+ DBGV(" ");
+
+ }
+ if (j == 16) {
+ DBGV("\n");
+ j = 0;
+ }
+ }
+ DBGV("\n");
+ j = 0;
+ DBGV("\n");
+
+ DBGV("PtpClock Buffer In \n");
+ DBGV("\n");
+ for (i = 0; i < PACKET_SIZE; i++) {
+ DBGV(":%02hhx", ptpClock->msgIbuf[i]);
+ j++;
+
+ if (j == 8) {
+ DBGV(" ");
+
+ }
+ if (j == 16) {
+ DBGV("\n");
+ j = 0;
+ }
+ }
+ DBGV("\n");
+ DBGV("\n");
+}
+
+/**\convert port state to string*/
+const char
+*portState_getName(Enumeration8 portState)
+{
+ static const char *ptpStates[] = {
+ [PTP_INITIALIZING] = "PTP_INITIALIZING",
+ [PTP_FAULTY] = "PTP_FAULTY",
+ [PTP_DISABLED] = "PTP_DISABLED",
+ [PTP_LISTENING] = "PTP_LISTENING",
+ [PTP_PRE_MASTER] = "PTP_PRE_MASTER",
+ [PTP_MASTER] = "PTP_MASTER",
+ [PTP_PASSIVE] = "PTP_PASSIVE",
+ [PTP_UNCALIBRATED] = "PTP_UNCALIBRATED",
+ [PTP_SLAVE] = "PTP_SLAVE"
+ };
+
+ /* converting to int to avoid compiler warnings when comparing enum*/
+ static const int max = PTP_SLAVE;
+ int intstate = portState;
+
+ if( intstate < 0 || intstate > max ) {
+ return("PTP_UNKNOWN");
+ }
+
+ return(ptpStates[portState]);
+
+}
+
+/**\brief Display all PTP clock (port) counters*/
+void
+displayCounters(const PtpClock * ptpClock)
+{
+
+ /* TODO: print port identity */
+ INFO("\n============= PTP port counters =============\n");
+
+ INFO("Message counters:\n");
+ INFO(" announceMessagesSent : %lu\n",
+ (unsigned long)ptpClock->counters.announceMessagesSent);
+ INFO(" announceMessagesReceived : %lu\n",
+ (unsigned long)ptpClock->counters.announceMessagesReceived);
+ INFO(" syncMessagesSent : %lu\n",
+ (unsigned long)ptpClock->counters.syncMessagesSent);
+ INFO(" syncMessagesReceived : %lu\n",
+ (unsigned long)ptpClock->counters.syncMessagesReceived);
+ INFO(" followUpMessagesSent : %lu\n",
+ (unsigned long)ptpClock->counters.followUpMessagesSent);
+ INFO(" followUpMessagesReceived : %lu\n",
+ (unsigned long)ptpClock->counters.followUpMessagesReceived);
+ INFO(" delayReqMessagesSent : %lu\n",
+ (unsigned long)ptpClock->counters.delayReqMessagesSent);
+ INFO(" delayReqMessagesReceived : %lu\n",
+ (unsigned long)ptpClock->counters.delayReqMessagesReceived);
+ INFO(" delayRespMessagesSent : %lu\n",
+ (unsigned long)ptpClock->counters.delayRespMessagesSent);
+ INFO(" delayRespMessagesReceived : %lu\n",
+ (unsigned long)ptpClock->counters.delayRespMessagesReceived);
+ INFO(" pdelayReqMessagesSent : %lu\n",
+ (unsigned long)ptpClock->counters.pdelayReqMessagesSent);
+ INFO(" pdelayReqMessagesReceived : %lu\n",
+ (unsigned long)ptpClock->counters.pdelayReqMessagesReceived);
+ INFO(" pdelayRespMessagesSent : %lu\n",
+ (unsigned long)ptpClock->counters.pdelayRespMessagesSent);
+ INFO(" pdelayRespMessagesReceived : %lu\n",
+ (unsigned long)ptpClock->counters.pdelayRespMessagesReceived);
+ INFO(" pdelayRespFollowUpMessagesSent : %lu\n",
+ (unsigned long)ptpClock->counters.pdelayRespFollowUpMessagesSent);
+ INFO("pdelayRespFollowUpMessagesReceived : %lu\n",
+ (unsigned long)ptpClock->counters.pdelayRespFollowUpMessagesReceived);
+ INFO(" signalingMessagesSent : %lu\n",
+ (unsigned long)ptpClock->counters.signalingMessagesSent);
+ INFO(" signalingMessagesReceived : %lu\n",
+ (unsigned long)ptpClock->counters.signalingMessagesReceived);
+ INFO(" managementMessagesSent : %lu\n",
+ (unsigned long)ptpClock->counters.managementMessagesSent);
+ INFO(" managementMessagesReceived : %lu\n",
+ (unsigned long)ptpClock->counters.managementMessagesReceived);
+
+ if(ptpClock->counters.signalingMessagesReceived ||
+ ptpClock->counters.signalingMessagesSent) {
+ INFO("Unicast negotiation counters:\n");
+ INFO(" unicastGrantsRequested : %lu\n",
+ (unsigned long)ptpClock->counters.unicastGrantsRequested);
+ INFO(" unicastGrantsGranted : %lu\n",
+ (unsigned long)ptpClock->counters.unicastGrantsGranted);
+ INFO(" unicastGrantsDenied : %lu\n",
+ (unsigned long)ptpClock->counters.unicastGrantsDenied);
+ INFO(" unicastGrantsCancelSent : %lu\n",
+ (unsigned long)ptpClock->counters.unicastGrantsCancelSent);
+ INFO(" unicastGrantsCancelReceived : %lu\n",
+ (unsigned long)ptpClock->counters.unicastGrantsCancelReceived);
+ INFO(" unicastGrantsCancelAckReceived : %lu\n",
+ (unsigned long)ptpClock->counters.unicastGrantsCancelAckReceived);
+ INFO(" unicastGrantsCancelAckSent : %lu\n",
+ (unsigned long)ptpClock->counters.unicastGrantsCancelAckSent);
+
+ }
+/* not implemented yet */
+#if 0
+ INFO("FMR counters:\n");
+ INFO(" foreignAdded : %lu\n",
+ (unsigned long)ptpClock->counters.foreignAdded);
+ INFO(" foreignMax : %lu\n",
+ (unsigned long)ptpClock->counters.foreignMax);
+ INFO(" foreignRemoved : %lu\n",
+ (unsigned long)ptpClock->counters.foreignRemoved);
+ INFO(" foreignOverflow : %lu\n",
+ (unsigned long)ptpClock->counters.foreignOverflow);
+#endif /* 0 */
+
+ INFO("Protocol engine counters:\n");
+ INFO(" stateTransitions : %lu\n",
+ (unsigned long)ptpClock->counters.stateTransitions);
+ INFO(" bestMasterChanges : %lu\n",
+ (unsigned long)ptpClock->counters.bestMasterChanges);
+ INFO(" announceTimeouts : %lu\n",
+ (unsigned long)ptpClock->counters.announceTimeouts);
+
+ INFO("Discarded / unknown message counters:\n");
+ INFO(" discardedMessages : %lu\n",
+ (unsigned long)ptpClock->counters.discardedMessages);
+ INFO(" unknownMessages : %lu\n",
+ (unsigned long)ptpClock->counters.unknownMessages);
+ INFO(" ignoredAnnounce : %lu\n",
+ (unsigned long)ptpClock->counters.ignoredAnnounce);
+ INFO(" aclManagementMessagesDiscarded : %lu\n",
+ (unsigned long)ptpClock->counters.aclManagementMessagesDiscarded);
+ INFO(" aclTimingMessagesDiscarded : %lu\n",
+ (unsigned long)ptpClock->counters.aclTimingMessagesDiscarded);
+
+ INFO("Error counters:\n");
+ INFO(" messageSendErrors : %lu\n",
+ (unsigned long)ptpClock->counters.messageSendErrors);
+ INFO(" messageRecvErrors : %lu\n",
+ (unsigned long)ptpClock->counters.messageRecvErrors);
+ INFO(" messageFormatErrors : %lu\n",
+ (unsigned long)ptpClock->counters.messageFormatErrors);
+ INFO(" protocolErrors : %lu\n",
+ (unsigned long)ptpClock->counters.protocolErrors);
+ INFO(" versionMismatchErrors : %lu\n",
+ (unsigned long)ptpClock->counters.versionMismatchErrors);
+ INFO(" domainMismatchErrors : %lu\n",
+ (unsigned long)ptpClock->counters.domainMismatchErrors);
+ INFO(" sequenceMismatchErrors : %lu\n",
+ (unsigned long)ptpClock->counters.sequenceMismatchErrors);
+ INFO(" consecutiveSequenceErrors : %lu\n",
+ (unsigned long)ptpClock->counters.consecutiveSequenceErrors);
+ INFO(" delayMechanismMismatchErrors : %lu\n",
+ (unsigned long)ptpClock->counters.delayMechanismMismatchErrors);
+ INFO(" maxDelayDrops : %lu\n",
+ (unsigned long)ptpClock->counters.maxDelayDrops);
+
+
+#ifdef PTPD_STATISTICS
+ INFO("Outlier filter hits:\n");
+ INFO(" delayMSOutliersFound : %lu\n",
+ (unsigned long)ptpClock->counters.delayMSOutliersFound);
+ INFO(" delaySMOutliersFound : %lu\n",
+ (unsigned long)ptpClock->counters.delaySMOutliersFound);
+#endif /* PTPD_STATISTICS */
+
+}
+
+const char *
+getTimeSourceName(Enumeration8 timeSource)
+{
+
+ switch(timeSource) {
+
+ case ATOMIC_CLOCK:
+ return "ATOMIC_CLOCK";
+ case GPS:
+ return "GPS";
+ case TERRESTRIAL_RADIO:
+ return "TERRERSTRIAL_RADIO";
+ case PTP:
+ return "PTP";
+ case NTP:
+ return "NTP";
+ case HAND_SET:
+ return "HAND_SET";
+ case OTHER:
+ return "OTHER";
+ case INTERNAL_OSCILLATOR:
+ return "INTERNAL_OSCILLATOR";
+ default:
+ return "UNKNOWN";
+ }
+
+}
+
+const char *
+getMessageTypeName(Enumeration8 messageType)
+{
+ switch(messageType)
+ {
+ case ANNOUNCE:
+ return("Announce");
+ break;
+ case SYNC:
+ return("Sync");
+ break;
+ case FOLLOW_UP:
+ return("FollowUp");
+ break;
+ case DELAY_REQ:
+ return("DelayReq");
+ break;
+ case DELAY_RESP:
+ return("DelayResp");
+ break;
+ case PDELAY_REQ:
+ return("PdelayReq");
+ break;
+ case PDELAY_RESP:
+ return("PdelayResp");
+ break;
+ case PDELAY_RESP_FOLLOW_UP:
+ return("PdelayRespFollowUp");
+ break;
+ case MANAGEMENT:
+ return("Management");
+ break;
+ case SIGNALING:
+ return("Signaling");
+ break;
+ default:
+ return("Unknown");
+ break;
+ }
+
+}
+
+const char* accToString(uint8_t acc) {
+
+ switch(acc) {
+
+ case ACC_25NS:
+ return "ACC_25NS";
+ case ACC_100NS:
+ return "ACC_100NS";
+ case ACC_250NS:
+ return "ACC_250NS";
+ case ACC_1US:
+ return "ACC_1US";
+ case ACC_2_5US:
+ return "ACC_2_5US";
+ case ACC_10US:
+ return "ACC_10US";
+ case ACC_25US:
+ return "ACC_25US";
+ case ACC_100US:
+ return "ACC_100US";
+ case ACC_250US:
+ return "ACC_250US";
+ case ACC_1MS:
+ return "ACC_1MS";
+ case ACC_2_5MS:
+ return "ACC_2_5MS";
+ case ACC_10MS:
+ return "ACC_10MS";
+ case ACC_25MS:
+ return "ACC_25MS";
+ case ACC_100MS:
+ return "ACC_100MS";
+ case ACC_250MS:
+ return "ACC_250MS";
+ case ACC_1S:
+ return "ACC_1S";
+ case ACC_10S:
+ return "ACC_10S";
+ case ACC_10SPLUS:
+ return "ACC_10SPLUS";
+ case ACC_UNKNOWN:
+ return "ACC_UNKNOWN";
+ default:
+ return NULL;
+
+ }
+}
+
+const char* delayMechToString(uint8_t mech) {
+
+ switch(mech) {
+ case E2E:
+ return "E2E";
+ case P2P:
+ return "P2P";
+ case DELAY_DISABLED:
+ return "DELAY_DISABLED";
+ default:
+ return NULL;
+ }
+
+}
+
+/**\brief Display all PTP clock (port) statistics*/
+void
+displayStatistics(const PtpClock* ptpClock)
+{
+
+/*
+ INFO("Clock stats: ofm mean: %lu, ofm median: %d,"
+ "ofm std dev: %lu, observed drift std dev: %d\n",
+ ptpClock->stats.ofmMean, ptpClock->stats.ofmMedian,
+ ptpClock->stats.ofmStdDev, ptpClock->stats.driftStdDev);
+*/
+
+}
+
+/**\brief Display All data sets and counters of a PtpClock*/
+void
+displayPtpClock(const PtpClock * ptpClock)
+{
+
+ displayDefault(ptpClock);
+ displayCurrent(ptpClock);
+ displayParent(ptpClock);
+ displayGlobal(ptpClock);
+ displayPort(ptpClock);
+ displayForeignMaster(ptpClock);
+ displayBuffer(ptpClock);
+ displayOthers(ptpClock);
+ displayCounters(ptpClock);
+ displayStatistics(ptpClock);
+
+}
diff --git a/rtemsbsd/ptpd/src/leap-seconds.list b/rtemsbsd/ptpd/src/leap-seconds.list
new file mode 100644
index 00000000..22fa7857
--- /dev/null
+++ b/rtemsbsd/ptpd/src/leap-seconds.list
@@ -0,0 +1,250 @@
+#
+# In the following text, the symbol '#' introduces
+# a comment, which continues from that symbol until
+# the end of the line. A plain comment line has a
+# whitespace character following the comment indicator.
+# There are also special comment lines defined below.
+# A special comment will always have a non-whitespace
+# character in column 2.
+#
+# A blank line should be ignored.
+#
+# The following table shows the corrections that must
+# be applied to compute International Atomic Time (TAI)
+# from the Coordinated Universal Time (UTC) values that
+# are transmitted by almost all time services.
+#
+# The first column shows an epoch as a number of seconds
+# since 1 January 1900, 00:00:00 (1900.0 is also used to
+# indicate the same epoch.) Both of these time stamp formats
+# ignore the complexities of the time scales that were
+# used before the current definition of UTC at the start
+# of 1972. (See note 3 below.)
+# The second column shows the number of seconds that
+# must be added to UTC to compute TAI for any timestamp
+# at or after that epoch. The value on each line is
+# valid from the indicated initial instant until the
+# epoch given on the next one or indefinitely into the
+# future if there is no next line.
+# (The comment on each line shows the representation of
+# the corresponding initial epoch in the usual
+# day-month-year format. The epoch always begins at
+# 00:00:00 UTC on the indicated day. See Note 5 below.)
+#
+# Important notes:
+#
+# 1. Coordinated Universal Time (UTC) is often referred to
+# as Greenwich Mean Time (GMT). The GMT time scale is no
+# longer used, and the use of GMT to designate UTC is
+# discouraged.
+#
+# 2. The UTC time scale is realized by many national
+# laboratories and timing centers. Each laboratory
+# identifies its realization with its name: Thus
+# UTC(NIST), UTC(USNO), etc. The differences among
+# these different realizations are typically on the
+# order of a few nanoseconds (i.e., 0.000 000 00x s)
+# and can be ignored for many purposes. These differences
+# are tabulated in Circular T, which is published monthly
+# by the International Bureau of Weights and Measures
+# (BIPM). See www.bipm.org for more information.
+#
+# 3. The current definition of the relationship between UTC
+# and TAI dates from 1 January 1972. A number of different
+# time scales were in use before that epoch, and it can be
+# quite difficult to compute precise timestamps and time
+# intervals in those "prehistoric" days. For more information,
+# consult:
+#
+# The Explanatory Supplement to the Astronomical
+# Ephemeris.
+# or
+# Terry Quinn, "The BIPM and the Accurate Measurement
+# of Time," Proc. of the IEEE, Vol. 79, pp. 894-905,
+# July, 1991.
+#
+# 4. The decision to insert a leap second into UTC is currently
+# the responsibility of the International Earth Rotation and
+# Reference Systems Service. (The name was changed from the
+# International Earth Rotation Service, but the acronym IERS
+# is still used.)
+#
+# Leap seconds are announced by the IERS in its Bulletin C.
+#
+# See www.iers.org for more details.
+#
+# Every national laboratory and timing center uses the
+# data from the BIPM and the IERS to construct UTC(lab),
+# their local realization of UTC.
+#
+# Although the definition also includes the possibility
+# of dropping seconds ("negative" leap seconds), this has
+# never been done and is unlikely to be necessary in the
+# foreseeable future.
+#
+# 5. If your system keeps time as the number of seconds since
+# some epoch (e.g., NTP timestamps), then the algorithm for
+# assigning a UTC time stamp to an event that happens during a positive
+# leap second is not well defined. The official name of that leap
+# second is 23:59:60, but there is no way of representing that time
+# in these systems.
+# Many systems of this type effectively stop the system clock for
+# one second during the leap second and use a time that is equivalent
+# to 23:59:59 UTC twice. For these systems, the corresponding TAI
+# timestamp would be obtained by advancing to the next entry in the
+# following table when the time equivalent to 23:59:59 UTC
+# is used for the second time. Thus the leap second which
+# occurred on 30 June 1972 at 23:59:59 UTC would have TAI
+# timestamps computed as follows:
+#
+# ...
+# 30 June 1972 23:59:59 (2287785599, first time): TAI= UTC + 10 seconds
+# 30 June 1972 23:59:60 (2287785599,second time): TAI= UTC + 11 seconds
+# 1 July 1972 00:00:00 (2287785600) TAI= UTC + 11 seconds
+# ...
+#
+# If your system realizes the leap second by repeating 00:00:00 UTC twice
+# (this is possible but not usual), then the advance to the next entry
+# in the table must occur the second time that a time equivalent to
+# 00:00:00 UTC is used. Thus, using the same example as above:
+#
+# ...
+# 30 June 1972 23:59:59 (2287785599): TAI= UTC + 10 seconds
+# 30 June 1972 23:59:60 (2287785600, first time): TAI= UTC + 10 seconds
+# 1 July 1972 00:00:00 (2287785600,second time): TAI= UTC + 11 seconds
+# ...
+#
+# in both cases the use of timestamps based on TAI produces a smooth
+# time scale with no discontinuity in the time interval. However,
+# although the long-term behavior of the time scale is correct in both
+# methods, the second method is technically not correct because it adds
+# the extra second to the wrong day.
+#
+# This complexity would not be needed for negative leap seconds (if they
+# are ever used). The UTC time would skip 23:59:59 and advance from
+# 23:59:58 to 00:00:00 in that case. The TAI offset would decrease by
+# 1 second at the same instant. This is a much easier situation to deal
+# with, since the difficulty of unambiguously representing the epoch
+# during the leap second does not arise.
+#
+# Some systems implement leap seconds by amortizing the leap second
+# over the last few minutes of the day. The frequency of the local
+# clock is decreased (or increased) to realize the positive (or
+# negative) leap second. This method removes the time step described
+# above. Although the long-term behavior of the time scale is correct
+# in this case, this method introduces an error during the adjustment
+# period both in time and in frequency with respect to the official
+# definition of UTC.
+#
+# Questions or comments to:
+# Judah Levine
+# Time and Frequency Division
+# NIST
+# Boulder, Colorado
+# Judah.Levine at nist.gov
+#
+# Last Update of leap second values: 8 July 2016
+#
+# The following line shows this last update date in NTP timestamp
+# format. This is the date on which the most recent change to
+# the leap second data was added to the file. This line can
+# be identified by the unique pair of characters in the first two
+# columns as shown below.
+#
+#$ 3676924800
+#
+# The NTP timestamps are in units of seconds since the NTP epoch,
+# which is 1 January 1900, 00:00:00. The Modified Julian Day number
+# corresponding to the NTP time stamp, X, can be computed as
+#
+# X/86400 + 15020
+#
+# where the first term converts seconds to days and the second
+# term adds the MJD corresponding to the time origin defined above.
+# The integer portion of the result is the integer MJD for that
+# day, and any remainder is the time of day, expressed as the
+# fraction of the day since 0 hours UTC. The conversion from day
+# fraction to seconds or to hours, minutes, and seconds may involve
+# rounding or truncation, depending on the method used in the
+# computation.
+#
+# The data in this file will be updated periodically as new leap
+# seconds are announced. In addition to being entered on the line
+# above, the update time (in NTP format) will be added to the basic
+# file name leap-seconds to form the name leap-seconds.<NTP TIME>.
+# In addition, the generic name leap-seconds.list will always point to
+# the most recent version of the file.
+#
+# This update procedure will be performed only when a new leap second
+# is announced.
+#
+# The following entry specifies the expiration date of the data
+# in this file in units of seconds since the origin at the instant
+# 1 January 1900, 00:00:00. This expiration date will be changed
+# at least twice per year whether or not a new leap second is
+# announced. These semi-annual changes will be made no later
+# than 1 June and 1 December of each year to indicate what
+# action (if any) is to be taken on 30 June and 31 December,
+# respectively. (These are the customary effective dates for new
+# leap seconds.) This expiration date will be identified by a
+# unique pair of characters in columns 1 and 2 as shown below.
+# In the unlikely event that a leap second is announced with an
+# effective date other than 30 June or 31 December, then this
+# file will be edited to include that leap second as soon as it is
+# announced or at least one month before the effective date
+# (whichever is later).
+# If an announcement by the IERS specifies that no leap second is
+# scheduled, then only the expiration date of the file will
+# be advanced to show that the information in the file is still
+# current -- the update time stamp, the data and the name of the file
+# will not change.
+#
+# Updated through IERS Bulletin C52
+# File expires on: 28 June 2017
+#
+#@ 3707596800
+#
+2272060800 10 # 1 Jan 1972
+2287785600 11 # 1 Jul 1972
+2303683200 12 # 1 Jan 1973
+2335219200 13 # 1 Jan 1974
+2366755200 14 # 1 Jan 1975
+2398291200 15 # 1 Jan 1976
+2429913600 16 # 1 Jan 1977
+2461449600 17 # 1 Jan 1978
+2492985600 18 # 1 Jan 1979
+2524521600 19 # 1 Jan 1980
+2571782400 20 # 1 Jul 1981
+2603318400 21 # 1 Jul 1982
+2634854400 22 # 1 Jul 1983
+2698012800 23 # 1 Jul 1985
+2776982400 24 # 1 Jan 1988
+2840140800 25 # 1 Jan 1990
+2871676800 26 # 1 Jan 1991
+2918937600 27 # 1 Jul 1992
+2950473600 28 # 1 Jul 1993
+2982009600 29 # 1 Jul 1994
+3029443200 30 # 1 Jan 1996
+3076704000 31 # 1 Jul 1997
+3124137600 32 # 1 Jan 1999
+3345062400 33 # 1 Jan 2006
+3439756800 34 # 1 Jan 2009
+3550089600 35 # 1 Jul 2012
+3644697600 36 # 1 Jul 2015
+3692217600 37 # 1 Jan 2017
+#
+# the following special comment contains the
+# hash value of the data in this file computed
+# use the secure hash algorithm as specified
+# by FIPS 180-1. See the files in ~/pub/sha for
+# the details of how this hash value is
+# computed. Note that the hash computation
+# ignores comments and whitespace characters
+# in data lines. It includes the NTP values
+# of both the last modification time and the
+# expiration time of the file, but not the
+# white space on those lines.
+# the hash line is also ignored in the
+# computation.
+#
+#h dacf2c42 2c4765d6 3c797af8 2cf630eb 699c8c67
diff --git a/rtemsbsd/ptpd/src/management.c b/rtemsbsd/ptpd/src/management.c
new file mode 100644
index 00000000..66cdda98
--- /dev/null
+++ b/rtemsbsd/ptpd/src/management.c
@@ -0,0 +1,1896 @@
+/*-
+ * Copyright (c) 2012-2015 Wojciech Owczarek,
+ * Copyright (c) 2011-2012 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen,
+ * Inaqui Delgado,
+ * Rick Ratzel,
+ * National Instruments.
+ * Copyright (c) 2009-2010 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen
+ *
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file management.c
+ * @date Wed Jul 27 13:07:30 CDT 2011
+ *
+ * @brief Routines to handle incoming management messages
+ *
+ *
+ */
+
+#include "ptpd.h"
+
+static void handleMMNullManagement(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMClockDescription(MsgManagement*, MsgManagement*, RunTimeOpts*, PtpClock*);
+static void handleMMSlaveOnly(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMUserDescription(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMSaveInNonVolatileStorage(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMResetNonVolatileStorage(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMInitialize(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMDefaultDataSet(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMCurrentDataSet(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMParentDataSet(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMTimePropertiesDataSet(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMPortDataSet(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMPriority1(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMPriority2(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMDomain(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMLogAnnounceInterval(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMAnnounceReceiptTimeout(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMLogSyncInterval(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMVersionNumber(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMEnablePort(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMDisablePort(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMTime(MsgManagement*, MsgManagement*, PtpClock*, const RunTimeOpts*);
+static void handleMMClockAccuracy(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMUtcProperties(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMTraceabilityProperties(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMTimescaleProperties(MsgManagement*, MsgManagement*, PtpClock*);
+
+static void handleMMUnicastNegotiationEnable(MsgManagement*, MsgManagement*, PtpClock*, RunTimeOpts*);
+static void handleMMDelayMechanism(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMLogMinPdelayReqInterval(MsgManagement*, MsgManagement*, PtpClock*);
+static void handleMMErrorStatus(MsgManagement*);
+static void handleErrorManagementMessage(MsgManagement *incoming, MsgManagement *outgoing,
+ PtpClock *ptpClock, Enumeration16 mgmtId,
+ Enumeration16 errorId);
+#if 0
+static void issueManagement(MsgHeader*,MsgManagement*,const RunTimeOpts*,PtpClock*);
+#endif
+static void issueManagementRespOrAck(MsgManagement*, Integer32, const RunTimeOpts*,PtpClock*);
+static void issueManagementErrorStatus(MsgManagement*, Integer32, const RunTimeOpts*,PtpClock*);
+
+void
+handleManagement(MsgHeader *header,
+ Boolean isFromSelf, Integer32 sourceAddress, RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ DBGV("Management message received : \n");
+ Integer32 dst;
+
+ int tlvOffset = 0;
+ int tlvFound = 0;
+
+ MsgManagement *mgmtMsg = &ptpClock->msgTmp.manage;
+
+ /*
+ * If request was unicast, reply to source, always, even if we ourselves are running multicast.
+ * This is not in line with the standard, but allows us to always reply to unicast management messages.
+ * This is a good thing - this allows us to always be able to monitor the node using unicast management messages.
+ */
+
+ if ( (header->flagField0 & PTP_UNICAST) == PTP_UNICAST) {
+ dst = sourceAddress;
+ } else {
+ dst = 0;
+ }
+
+ if (isFromSelf) {
+ DBGV("handleManagement: Ignore message from self \n");
+ return;
+ }
+
+ if(!rtOpts->managementEnabled) {
+ DBGV("Dropping management message - management message support disabled");
+ ptpClock->counters.discardedMessages++;
+ return;
+ }
+
+ /* loop over all supported TLVs as if they came in separate messages */
+ while(msgUnpackManagement(ptpClock->msgIbuf,mgmtMsg, header, ptpClock, tlvOffset)) {
+
+ if(mgmtMsg->tlv == NULL) {
+
+ if(tlvFound==0) {
+ DBGV("handleManagement: No TLVs in message\n");
+ ptpClock->counters.messageFormatErrors++;
+ } else {
+ DBGV("handleManagement: No more TLVs\n");
+ }
+ return;
+ }
+
+ tlvFound++;
+
+ /* accept the message if directed either to us or to all-ones */
+ if(!acceptPortIdentity(ptpClock->portDS.portIdentity, mgmtMsg->targetPortIdentity))
+ {
+ DBGV("handleManagement: The management message was not accepted");
+ ptpClock->counters.discardedMessages++;
+ goto end;
+ }
+
+ if(!rtOpts->managementSetEnable &&
+ (mgmtMsg->actionField == SET ||
+ mgmtMsg->actionField == COMMAND)) {
+ DBGV("Dropping SET/COMMAND management message - read-only mode enabled");
+ ptpClock->counters.discardedMessages++;
+ goto end;
+ }
+
+ /* is this an error status management TLV? */
+ if(mgmtMsg->tlv->tlvType == TLV_MANAGEMENT_ERROR_STATUS) {
+ DBGV("handleManagement: Error Status TLV\n");
+ unpackMMErrorStatus(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock);
+ handleMMErrorStatus(mgmtMsg);
+ ptpClock->counters.managementMessagesReceived++;
+ goto end;
+ } else if (mgmtMsg->tlv->tlvType != TLV_MANAGEMENT) {
+ /* do nothing, implemention specific handling */
+ DBGV("handleManagement: Currently unsupported management TLV type\n");
+ ptpClock->counters.discardedMessages++;
+ goto end;
+ }
+
+ /* if this is a SET, there is potential for applying new config */
+ if (mgmtMsg->actionField & (SET | COMMAND)) {
+ ptpClock->managementConfig = dictionary_new(0);
+ dictionary_merge(rtOpts->currentConfig, ptpClock->managementConfig, 1, 0, NULL);
+ }
+
+ switch(mgmtMsg->tlv->managementId)
+ {
+ case MM_NULL_MANAGEMENT:
+ DBGV("handleManagement: Null Management\n");
+ handleMMNullManagement(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_CLOCK_DESCRIPTION:
+ DBGV("handleManagement: Clock Description\n");
+ if(mgmtMsg->actionField != GET && !unpackMMClockDescription(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMClockDescription(mgmtMsg, &ptpClock->outgoingManageTmp, rtOpts, ptpClock);
+ break;
+ case MM_USER_DESCRIPTION:
+ DBGV("handleManagement: User Description\n");
+ if(mgmtMsg->actionField != GET && !unpackMMUserDescription(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMUserDescription(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_SAVE_IN_NON_VOLATILE_STORAGE:
+ DBGV("handleManagement: Save In Non-Volatile Storage\n");
+ handleMMSaveInNonVolatileStorage(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_RESET_NON_VOLATILE_STORAGE:
+ DBGV("handleManagement: Reset Non-Volatile Storage\n");
+ handleMMResetNonVolatileStorage(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_INITIALIZE:
+ DBGV("handleManagement: Initialize\n");
+ if(mgmtMsg->actionField != GET && !unpackMMInitialize(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMInitialize(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_DEFAULT_DATA_SET:
+ DBGV("handleManagement: Default Data Set\n");
+ if(mgmtMsg->actionField != GET && !unpackMMDefaultDataSet(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMDefaultDataSet(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_CURRENT_DATA_SET:
+ DBGV("handleManagement: Current Data Set\n");
+ if(mgmtMsg->actionField != GET && !unpackMMCurrentDataSet(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMCurrentDataSet(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_PARENT_DATA_SET:
+ DBGV("handleManagement: Parent Data Set\n");
+ if(mgmtMsg->actionField != GET && !unpackMMParentDataSet(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMParentDataSet(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_TIME_PROPERTIES_DATA_SET:
+ DBGV("handleManagement: TimeProperties Data Set\n");
+ if(mgmtMsg->actionField != GET && !unpackMMTimePropertiesDataSet(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMTimePropertiesDataSet(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_PORT_DATA_SET:
+ DBGV("handleManagement: Port Data Set\n");
+ if(mgmtMsg->actionField != GET && !unpackMMPortDataSet(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMPortDataSet(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_PRIORITY1:
+ DBGV("handleManagement: Priority1\n");
+ if(mgmtMsg->actionField != GET && !unpackMMPriority1(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMPriority1(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_PRIORITY2:
+ DBGV("handleManagement: Priority2\n");
+ if(mgmtMsg->actionField != GET && !unpackMMPriority2(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMPriority2(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_DOMAIN:
+ DBGV("handleManagement: Domain\n");
+ if(mgmtMsg->actionField != GET && !unpackMMDomain(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMDomain(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_SLAVE_ONLY:
+ DBGV("handleManagement: Slave Only\n");
+ if(mgmtMsg->actionField != GET && !unpackMMSlaveOnly(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMSlaveOnly(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_LOG_ANNOUNCE_INTERVAL:
+ DBGV("handleManagement: Log Announce Interval\n");
+ if(mgmtMsg->actionField != GET && !unpackMMLogAnnounceInterval(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMLogAnnounceInterval(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_ANNOUNCE_RECEIPT_TIMEOUT:
+ DBGV("handleManagement: Announce Receipt Timeout\n");
+ if(mgmtMsg->actionField != GET && !unpackMMAnnounceReceiptTimeout(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMAnnounceReceiptTimeout(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_LOG_SYNC_INTERVAL:
+ DBGV("handleManagement: Log Sync Interval\n");
+ if(mgmtMsg->actionField != GET && !unpackMMLogSyncInterval(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMLogSyncInterval(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_VERSION_NUMBER:
+ DBGV("handleManagement: Version Number\n");
+ if(mgmtMsg->actionField != GET && !unpackMMVersionNumber(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMVersionNumber(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_ENABLE_PORT:
+ DBGV("handleManagement: Enable Port\n");
+ handleMMEnablePort(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_DISABLE_PORT:
+ DBGV("handleManagement: Disable Port\n");
+ handleMMDisablePort(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_TIME:
+ DBGV("handleManagement: Time\n");
+ if(mgmtMsg->actionField != GET && !unpackMMTime(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMTime(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock, rtOpts);
+ break;
+ case MM_CLOCK_ACCURACY:
+ DBGV("handleManagement: Clock Accuracy\n");
+ if(mgmtMsg->actionField != GET && !unpackMMClockAccuracy(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMClockAccuracy(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_UTC_PROPERTIES:
+ DBGV("handleManagement: Utc Properties\n");
+ if(mgmtMsg->actionField != GET && !unpackMMUtcProperties(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMUtcProperties(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_TRACEABILITY_PROPERTIES:
+ DBGV("handleManagement: Traceability Properties\n");
+ if(mgmtMsg->actionField != GET && !unpackMMTraceabilityProperties(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMTraceabilityProperties(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_TIMESCALE_PROPERTIES:
+ DBGV("handleManagement: Timescale Properties\n");
+ if(mgmtMsg->actionField != GET && !unpackMMTimescaleProperties(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMTimescaleProperties(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_UNICAST_NEGOTIATION_ENABLE:
+ DBGV("handleManagement: Unicast Negotiation Enable\n");
+ if(mgmtMsg->actionField != GET && !unpackMMUnicastNegotiationEnable(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMUnicastNegotiationEnable(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock, rtOpts);
+ break;
+ case MM_DELAY_MECHANISM:
+ DBGV("handleManagement: Delay Mechanism\n");
+ if(mgmtMsg->actionField != GET && !unpackMMDelayMechanism(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMDelayMechanism(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_LOG_MIN_PDELAY_REQ_INTERVAL:
+ DBGV("handleManagement: Log Min Pdelay Req Interval\n");
+ if(mgmtMsg->actionField != GET && !unpackMMLogMinPdelayReqInterval(ptpClock->msgIbuf, tlvOffset, mgmtMsg, ptpClock)) {
+ DBG("handleManagement: (bufGuard) error while unpacking management %02x\n", mgmtMsg->tlv->managementId);
+ ptpClock->counters.messageFormatErrors++;
+ goto end;
+ }
+ handleMMLogMinPdelayReqInterval(mgmtMsg, &ptpClock->outgoingManageTmp, ptpClock);
+ break;
+ case MM_FAULT_LOG:
+ case MM_FAULT_LOG_RESET:
+ case MM_PATH_TRACE_LIST:
+ case MM_PATH_TRACE_ENABLE:
+ case MM_GRANDMASTER_CLUSTER_TABLE:
+ case MM_UNICAST_MASTER_TABLE:
+ case MM_UNICAST_MASTER_MAX_TABLE_SIZE:
+ case MM_ACCEPTABLE_MASTER_TABLE:
+ case MM_ACCEPTABLE_MASTER_TABLE_ENABLED:
+ case MM_ACCEPTABLE_MASTER_MAX_TABLE_SIZE:
+ case MM_ALTERNATE_MASTER:
+ case MM_ALTERNATE_TIME_OFFSET_ENABLE:
+ case MM_ALTERNATE_TIME_OFFSET_NAME:
+ case MM_ALTERNATE_TIME_OFFSET_MAX_KEY:
+ case MM_ALTERNATE_TIME_OFFSET_PROPERTIES:
+ case MM_TRANSPARENT_CLOCK_DEFAULT_DATA_SET:
+ case MM_TRANSPARENT_CLOCK_PORT_DATA_SET:
+ case MM_PRIMARY_DOMAIN:
+ DBGV("handleManagement: Currently unsupported managementTLV %d\n",
+ mgmtMsg->tlv->managementId);
+ handleErrorManagementMessage(mgmtMsg, &ptpClock->outgoingManageTmp,
+ ptpClock, mgmtMsg->tlv->managementId,
+ NOT_SUPPORTED);
+ ptpClock->counters.discardedMessages++;
+ break;
+ default:
+ DBGV("handleManagement: Unknown managementTLV %d\n",
+ mgmtMsg->tlv->managementId);
+ handleErrorManagementMessage(mgmtMsg, &ptpClock->outgoingManageTmp,
+ ptpClock, mgmtMsg->tlv->managementId,
+ NO_SUCH_ID);
+ ptpClock->counters.discardedMessages++;
+ }
+ /* send management message response or acknowledge */
+ if(ptpClock->outgoingManageTmp.tlv->tlvType == TLV_MANAGEMENT) {
+ if(ptpClock->outgoingManageTmp.actionField == RESPONSE ||
+ ptpClock->outgoingManageTmp.actionField == ACKNOWLEDGE) {
+ issueManagementRespOrAck(&ptpClock->outgoingManageTmp, dst, rtOpts, ptpClock);
+ }
+ } else if(ptpClock->outgoingManageTmp.tlv->tlvType == TLV_MANAGEMENT_ERROR_STATUS) {
+ issueManagementErrorStatus(&ptpClock->outgoingManageTmp, dst, rtOpts, ptpClock);
+ }
+
+ end:
+ /* Movin' on up! */
+ tlvOffset += TL_LENGTH + mgmtMsg->tlv->lengthField;
+ /* cleanup msgTmp managementTLV */
+ freeManagementTLV(mgmtMsg);
+ /* cleanup outgoing managementTLV */
+ freeManagementTLV(&ptpClock->outgoingManageTmp);
+
+ }
+
+ if(ptpClock->managementConfig != NULL) {
+ NOTICE("SET / COMMAND management message received - looking for configuration changes\n");
+ applyConfig(ptpClock->managementConfig, rtOpts, ptpClock);
+ dictionary_del(&ptpClock->managementConfig);
+ }
+
+ if(!tlvFound) {
+ DBGV("handleManagement: No TLVs in message\n");
+ ptpClock->counters.messageFormatErrors++;
+ } else {
+ ptpClock->counters.managementMessagesReceived++;
+ DBGV("handleManagement: No more TLVs\n");
+ }
+
+
+}
+
+
+/**\brief Initialize outgoing management message fields*/
+void initOutgoingMsgManagement(MsgManagement* incoming, MsgManagement* outgoing, PtpClock *ptpClock)
+{
+ /* set header fields */
+ outgoing->header.transportSpecific = ptpClock->portDS.transportSpecific;
+ outgoing->header.messageType = MANAGEMENT;
+ outgoing->header.versionPTP = ptpClock->portDS.versionNumber;
+ outgoing->header.domainNumber = ptpClock->defaultDS.domainNumber;
+ /* set header flagField to zero for management messages, Spec 13.3.2.6 */
+ outgoing->header.flagField0 = 0x00;
+ outgoing->header.flagField1 = 0x00;
+ outgoing->header.correctionField.msb = 0;
+ outgoing->header.correctionField.lsb = 0;
+ copyPortIdentity(&outgoing->header.sourcePortIdentity, &ptpClock->portDS.portIdentity);
+ outgoing->header.sequenceId = incoming->header.sequenceId;
+ outgoing->header.controlField = 0x4; /* deprecrated for ptp version 2 */
+ outgoing->header.logMessageInterval = 0x7F;
+
+ /* set management message fields */
+ copyPortIdentity( &outgoing->targetPortIdentity, &incoming->header.sourcePortIdentity );
+ outgoing->startingBoundaryHops = incoming->startingBoundaryHops - incoming->boundaryHops;
+ outgoing->boundaryHops = outgoing->startingBoundaryHops;
+ outgoing->actionField = 0; /* set default action, avoid uninitialized value */
+
+ /* init managementTLV */
+ XMALLOC(outgoing->tlv, sizeof(ManagementTLV));
+ outgoing->tlv->dataField = NULL;
+ outgoing->tlv->lengthField = 0;
+}
+
+/**\brief Handle incoming NULL_MANAGEMENT message*/
+void handleMMNullManagement(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received NULL_MANAGEMENT message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_NULL_MANAGEMENT;
+
+ switch(incoming->actionField)
+ {
+ case GET:
+ outgoing->actionField = RESPONSE;
+ DBGV(" GET mgmt msg\n");
+ break;
+ case SET:
+ outgoing->actionField = RESPONSE;
+ DBGV(" SET mgmt msg\n");
+ break;
+ case COMMAND:
+ outgoing->actionField = ACKNOWLEDGE;
+ DBGV(" COMMAND mgmt msg\n");
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_NULL_MANAGEMENT,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming CLOCK_DESCRIPTION management message*/
+void handleMMClockDescription(MsgManagement* incoming, MsgManagement* outgoing, RunTimeOpts *rtOpts, PtpClock* ptpClock)
+{
+ DBGV("received CLOCK_DESCRIPTION management message \n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_CLOCK_DESCRIPTION;
+
+ MMClockDescription *data = NULL;
+ switch(incoming->actionField)
+ {
+ case GET:
+ DBGV(" GET action \n");
+ /* Table 38 */
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof( MMClockDescription));
+ data = (MMClockDescription*)outgoing->tlv->dataField;
+ memset(data, 0, sizeof( MMClockDescription));
+ /* GET actions */
+ /* this is an ordnary node, clockType field bit 0 is one */
+ data->clockType0 = 0x80;
+ data->clockType1 = 0x00;
+ /* physical layer protocol */
+ data->physicalLayerProtocol.lengthField = sizeof(PROTOCOL) - 1;
+ XMALLOC(data->physicalLayerProtocol.textField,
+ data->physicalLayerProtocol.lengthField);
+ memcpy(data->physicalLayerProtocol.textField,
+ &PROTOCOL,
+ data->physicalLayerProtocol.lengthField);
+ /* physical address */
+ data->physicalAddress.addressLength = PTP_UUID_LENGTH;
+ XMALLOC(data->physicalAddress.addressField, PTP_UUID_LENGTH);
+ memcpy(data->physicalAddress.addressField,
+ ptpClock->netPath.interfaceID,
+ PTP_UUID_LENGTH);
+ /* protocol address */
+ data->protocolAddress.addressLength = 4;
+ data->protocolAddress.networkProtocol = 1;
+ XMALLOC(data->protocolAddress.addressField,
+ data->protocolAddress.addressLength);
+ memcpy(data->protocolAddress.addressField,
+ &ptpClock->netPath.interfaceAddr.s_addr,
+ data->protocolAddress.addressLength);
+ /* manufacturerIdentity OUI */
+ data->manufacturerIdentity0 = MANUFACTURER_ID_OUI0;
+ data->manufacturerIdentity1 = MANUFACTURER_ID_OUI1;
+ data->manufacturerIdentity2 = MANUFACTURER_ID_OUI2;
+ /* reserved */
+ data->reserved = 0;
+ /* product description */
+ tmpsnprintf(tmpStr, 64, PRODUCT_DESCRIPTION, rtOpts->productDescription);
+ data->productDescription.lengthField = strlen(tmpStr);
+ XMALLOC(data->productDescription.textField,
+ data->productDescription.lengthField);
+ memcpy(data->productDescription.textField,
+ tmpStr,
+ data->productDescription.lengthField);
+ /* revision data */
+ data->revisionData.lengthField = sizeof(REVISION) - 1;
+ XMALLOC(data->revisionData.textField,
+ data->revisionData.lengthField);
+ memcpy(data->revisionData.textField,
+ &REVISION,
+ data->revisionData.lengthField);
+ /* user description */
+ data->userDescription.lengthField = strlen(ptpClock->userDescription);
+ XMALLOC(data->userDescription.textField,
+ data->userDescription.lengthField);
+ memcpy(data->userDescription.textField,
+ ptpClock->userDescription,
+ data->userDescription.lengthField);
+ /* profile identity is zero unless specific profile is implemented */
+ memcpy(&data->profileIdentity0, &ptpClock->profileIdentity, 6);
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action \n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_CLOCK_DESCRIPTION,
+ NOT_SUPPORTED);
+ }
+}
+
+/**\brief Handle incoming SLAVE_ONLY management message type*/
+void handleMMSlaveOnly(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received SLAVE_ONLY management message \n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_SLAVE_ONLY;
+
+ MMSlaveOnly* data = NULL;
+ switch (incoming->actionField)
+ {
+ case SET:
+ DBGV(" SET action \n");
+ data = (MMSlaveOnly*)incoming->tlv->dataField;
+ /* SET actions */
+ ptpClock->defaultDS.slaveOnly = data->so;
+ ptpClock->record_update = TRUE;
+ setConfig(ptpClock->managementConfig, "ptpengine:slave_only", ptpClock->defaultDS.slaveOnly ? "Y" : "N");
+ /* intentionally fall through to GET case */
+ case GET:
+ DBGV(" GET action \n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMSlaveOnly));
+ data = (MMSlaveOnly*)outgoing->tlv->dataField;
+ /* GET actions */
+ data->so = ptpClock->defaultDS.slaveOnly;
+ data->reserved = 0x0;
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_SLAVE_ONLY,
+ NOT_SUPPORTED);
+ }
+}
+
+/**\brief Handle incoming USER_DESCRIPTION management message type*/
+void handleMMUserDescription(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received USER_DESCRIPTION message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_USER_DESCRIPTION;
+
+ MMUserDescription* data = NULL;
+ switch(incoming->actionField)
+ {
+ case SET:
+ DBGV(" SET action \n");
+ data = (MMUserDescription*)incoming->tlv->dataField;
+ UInteger8 userDescriptionLength = data->userDescription.lengthField;
+ if(userDescriptionLength <= USER_DESCRIPTION_MAX) {
+ memset(ptpClock->userDescription, 0, sizeof(ptpClock->userDescription));
+ memcpy(ptpClock->userDescription, data->userDescription.textField,
+ userDescriptionLength);
+ /* add null-terminator to make use of C string function strlen later */
+ ptpClock->userDescription[userDescriptionLength] = '\0';
+ tmpsnprintf(ud, data->userDescription.lengthField+1, "%s", (char*)data->userDescription.textField);
+ setConfig(ptpClock->managementConfig, "ptpengine:port_description", ud);
+ } else {
+ WARNING("management user description exceeds specification length \n");
+ }
+
+ /* intentionally fall through to GET case */
+ case GET:
+ DBGV(" GET action \n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof( MMUserDescription));
+ data = (MMUserDescription*)outgoing->tlv->dataField;
+ memset(data, 0, sizeof(MMUserDescription));
+ /* GET actions */
+ data->userDescription.lengthField = strlen(ptpClock->userDescription);
+ XMALLOC(data->userDescription.textField,
+ data->userDescription.lengthField);
+ memcpy(data->userDescription.textField,
+ ptpClock->userDescription,
+ data->userDescription.lengthField);
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_USER_DESCRIPTION,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming SAVE_IN_NON_VOLATILE_STORAGE management message type*/
+void handleMMSaveInNonVolatileStorage(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received SAVE_IN_NON_VOLATILE_STORAGE message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_SAVE_IN_NON_VOLATILE_STORAGE;
+
+ switch( incoming->actionField )
+ {
+ case COMMAND:
+ /* issue a NOT_SUPPORTED error management message, intentionally fall through */
+ case ACKNOWLEDGE:
+ /* issue a NOT_SUPPORTED error management message, intentionally fall through */
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_SAVE_IN_NON_VOLATILE_STORAGE,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming RESET_NON_VOLATILE_STORAGE management message type*/
+void handleMMResetNonVolatileStorage(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received RESET_NON_VOLATILE_STORAGE message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_RESET_NON_VOLATILE_STORAGE;
+
+ switch( incoming->actionField )
+ {
+ case COMMAND:
+ /* issue a NOT_SUPPORTED error management message, intentionally fall through */
+ case ACKNOWLEDGE:
+ /* issue a NOT_SUPPORTED error management message, intentionally fall through */
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_RESET_NON_VOLATILE_STORAGE,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming INITIALIZE management message type*/
+void handleMMInitialize(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received INITIALIZE message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_INITIALIZE;
+
+ MMInitialize* incomingData = NULL;
+ MMInitialize* outgoingData = NULL;
+ switch( incoming->actionField )
+ {
+ case COMMAND:
+ DBGV(" COMMAND action\n");
+ outgoing->actionField = ACKNOWLEDGE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMInitialize));
+ incomingData = (MMInitialize*)incoming->tlv->dataField;
+ outgoingData = (MMInitialize*)outgoing->tlv->dataField;
+ /* Table 45 - INITIALIZATION_KEY enumeration */
+ switch( incomingData->initializeKey )
+ {
+ case INITIALIZE_EVENT:
+ /* cause INITIALIZE event */
+ setPortState(ptpClock, PTP_INITIALIZING);
+ break;
+ default:
+ /* do nothing, implementation specific */
+ DBGV("initializeKey != 0, do nothing\n");
+ }
+ outgoingData->initializeKey = incomingData->initializeKey;
+ break;
+ case ACKNOWLEDGE:
+ DBGV(" ACKNOWLEDGE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_INITIALIZE,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming DEFAULT_DATA_SET management message type*/
+void handleMMDefaultDataSet(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received DEFAULT_DATA_SET message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_DEFAULT_DATA_SET;
+
+ MMDefaultDataSet* data = NULL;
+ switch( incoming->actionField )
+ {
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMDefaultDataSet));
+ data = (MMDefaultDataSet*)outgoing->tlv->dataField;
+ /* GET actions */
+ /* get bit and align for slave only */
+ Octet so = ptpClock->defaultDS.slaveOnly << 1;
+ /* get bit and align by shifting right 1 since TWO_STEP_FLAG is either 0b00 or 0b10 */
+ Octet tsc = ptpClock->defaultDS.twoStepFlag >> 1;
+ data->so_tsc = so | tsc;
+ data->reserved0 = 0x0;
+ data->numberPorts = ptpClock->defaultDS.numberPorts;
+ data->priority1 = ptpClock->defaultDS.priority1;
+ data->clockQuality.clockAccuracy = ptpClock->defaultDS.clockQuality.clockAccuracy;
+ data->clockQuality.clockClass = ptpClock->defaultDS.clockQuality.clockClass;
+ data->clockQuality.offsetScaledLogVariance =
+ ptpClock->defaultDS.clockQuality.offsetScaledLogVariance;
+ data->priority2 = ptpClock->defaultDS.priority2;
+ /* copy clockIdentity */
+ copyClockIdentity(data->clockIdentity, ptpClock->defaultDS.clockIdentity);
+ data->domainNumber = ptpClock->defaultDS.domainNumber;
+ data->reserved1 = 0x0;
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_DEFAULT_DATA_SET,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming CURRENT_DATA_SET management message type*/
+void handleMMCurrentDataSet(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received CURRENT_DATA_SET message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_CURRENT_DATA_SET;
+
+ MMCurrentDataSet *data = NULL;
+ switch( incoming->actionField )
+ {
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof( MMCurrentDataSet));
+ data = (MMCurrentDataSet*)outgoing->tlv->dataField;
+ /* GET actions */
+ data->stepsRemoved = ptpClock->currentDS.stepsRemoved;
+ TimeInterval oFM;
+ oFM.scaledNanoseconds.lsb = 0;
+ oFM.scaledNanoseconds.msb = 0;
+ internalTime_to_integer64(ptpClock->currentDS.offsetFromMaster, &oFM.scaledNanoseconds);
+ data->offsetFromMaster.scaledNanoseconds.lsb = oFM.scaledNanoseconds.lsb;
+ data->offsetFromMaster.scaledNanoseconds.msb = oFM.scaledNanoseconds.msb;
+ TimeInterval mPD;
+ mPD.scaledNanoseconds.lsb = 0;
+ mPD.scaledNanoseconds.msb = 0;
+ internalTime_to_integer64(ptpClock->currentDS.meanPathDelay, &mPD.scaledNanoseconds);
+ data->meanPathDelay.scaledNanoseconds.lsb = mPD.scaledNanoseconds.lsb;
+ data->meanPathDelay.scaledNanoseconds.msb = mPD.scaledNanoseconds.msb;
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_CURRENT_DATA_SET,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming PARENT_DATA_SET management message type*/
+void handleMMParentDataSet(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received PARENT_DATA_SET message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_PARENT_DATA_SET;
+
+ MMParentDataSet *data = NULL;
+ switch( incoming->actionField )
+ {
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMParentDataSet));
+ data = (MMParentDataSet*)outgoing->tlv->dataField;
+ /* GET actions */
+ copyPortIdentity(&data->parentPortIdentity, &ptpClock->parentDS.parentPortIdentity);
+ data->PS = ptpClock->parentDS.parentStats;
+ data->reserved = 0;
+ data->observedParentOffsetScaledLogVariance =
+ ptpClock->parentDS.observedParentOffsetScaledLogVariance;
+ data->observedParentClockPhaseChangeRate =
+ ptpClock->parentDS.observedParentClockPhaseChangeRate;
+ data->grandmasterPriority1 = ptpClock->parentDS.grandmasterPriority1;
+ data->grandmasterClockQuality.clockAccuracy =
+ ptpClock->parentDS.grandmasterClockQuality.clockAccuracy;
+ data->grandmasterClockQuality.clockClass =
+ ptpClock->parentDS.grandmasterClockQuality.clockClass;
+ data->grandmasterClockQuality.offsetScaledLogVariance =
+ ptpClock->parentDS.grandmasterClockQuality.offsetScaledLogVariance;
+ data->grandmasterPriority2 = ptpClock->parentDS.grandmasterPriority2;
+ copyClockIdentity(data->grandmasterIdentity, ptpClock->parentDS.grandmasterIdentity);
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_PARENT_DATA_SET,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming PROPERTIES_DATA_SET management message type*/
+void handleMMTimePropertiesDataSet(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received TIME_PROPERTIES message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_TIME_PROPERTIES_DATA_SET;
+
+ MMTimePropertiesDataSet *data = NULL;
+ switch( incoming->actionField )
+ {
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMTimePropertiesDataSet));
+ data = (MMTimePropertiesDataSet*)outgoing->tlv->dataField;
+ /* GET actions */
+ data->currentUtcOffset = ptpClock->timePropertiesDS.currentUtcOffset;
+ Octet ftra = SET_FIELD(ptpClock->timePropertiesDS.frequencyTraceable, FTRA);
+ Octet ttra = SET_FIELD(ptpClock->timePropertiesDS.timeTraceable, TTRA);
+ Octet ptp = SET_FIELD(ptpClock->timePropertiesDS.ptpTimescale, PTPT);
+ Octet utcv = SET_FIELD(ptpClock->timePropertiesDS.currentUtcOffsetValid, UTCV);
+ Octet li59 = SET_FIELD(ptpClock->timePropertiesDS.leap59, LI59);
+ Octet li61 = SET_FIELD(ptpClock->timePropertiesDS.leap61, LI61);
+ data->ftra_ttra_ptp_utcv_li59_li61 = ftra | ttra | ptp | utcv | li59 | li61;
+ data->timeSource = ptpClock->timePropertiesDS.timeSource;
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_TIME_PROPERTIES_DATA_SET,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming PORT_DATA_SET management message type*/
+void handleMMPortDataSet(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received PORT_DATA_SET message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_PORT_DATA_SET;
+
+ MMPortDataSet *data = NULL;
+ switch( incoming->actionField )
+ {
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMPortDataSet));
+ data = (MMPortDataSet*)outgoing->tlv->dataField;
+ copyPortIdentity(&data->portIdentity, &ptpClock->portDS.portIdentity);
+ data->portState = ptpClock->portDS.portState;
+ data->logMinDelayReqInterval = ptpClock->portDS.logMinDelayReqInterval;
+ TimeInterval pMPD;
+ pMPD.scaledNanoseconds.lsb = 0;
+ pMPD.scaledNanoseconds.msb = 0;
+ internalTime_to_integer64(ptpClock->portDS.peerMeanPathDelay, &pMPD.scaledNanoseconds);
+ data->peerMeanPathDelay.scaledNanoseconds.lsb = pMPD.scaledNanoseconds.lsb;
+ data->peerMeanPathDelay.scaledNanoseconds.msb = pMPD.scaledNanoseconds.msb;
+ data->logAnnounceInterval = ptpClock->portDS.logAnnounceInterval;
+ data->announceReceiptTimeout = ptpClock->portDS.announceReceiptTimeout;
+ data->logSyncInterval = ptpClock->portDS.logSyncInterval;
+ data->delayMechanism = ptpClock->portDS.delayMechanism;
+ data->logMinPdelayReqInterval = ptpClock->portDS.logMinPdelayReqInterval;
+ data->reserved = 0;
+ data->versionNumber = ptpClock->portDS.versionNumber;
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action \n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_PORT_DATA_SET,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming PRIORITY1 management message type*/
+void handleMMPriority1(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received PRIORITY1 message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_PRIORITY1;
+
+ MMPriority1* data = NULL;
+ switch( incoming->actionField )
+ {
+ case SET:
+ DBGV(" SET action\n");
+ data = (MMPriority1*)incoming->tlv->dataField;
+ /* SET actions */
+ ptpClock->defaultDS.priority1 = data->priority1;
+ tmpsnprintf(tmpStr, 4, "%d", data->priority1);
+ setConfig(ptpClock->managementConfig, "ptpengine:priority1", tmpStr);
+ ptpClock->record_update = TRUE;
+ /* intentionally fall through to GET case */
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMPriority1));
+ data = (MMPriority1*)outgoing->tlv->dataField;
+ /* GET actions */
+ data->priority1 = ptpClock->defaultDS.priority1;
+ data->reserved = 0x0;
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_PRIORITY1,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming PRIORITY2 management message type*/
+void handleMMPriority2(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received PRIORITY2 message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_PRIORITY2;
+
+ MMPriority2* data = NULL;
+ switch( incoming->actionField )
+ {
+ case SET:
+ DBGV(" SET action\n");
+ data = (MMPriority2*)incoming->tlv->dataField;
+ /* SET actions */
+ ptpClock->defaultDS.priority2 = data->priority2;
+ tmpsnprintf(tmpStr, 4, "%d", data->priority2);
+ setConfig(ptpClock->managementConfig, "ptpengine:priority2", tmpStr);
+ ptpClock->record_update = TRUE;
+ /* intentionally fall through to GET case */
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMPriority2));
+ data = (MMPriority2*)outgoing->tlv->dataField;
+ /* GET actions */
+ data->priority2 = ptpClock->defaultDS.priority2;
+ data->reserved = 0x0;
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_PRIORITY2,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming DOMAIN management message type*/
+void handleMMDomain(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received DOMAIN message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_DOMAIN;
+
+ MMDomain* data = NULL;
+ switch( incoming->actionField )
+ {
+ case SET:
+ DBGV(" SET action\n");
+ data = (MMDomain*)incoming->tlv->dataField;
+ /* SET actions */
+ ptpClock->defaultDS.domainNumber = data->domainNumber;
+ tmpsnprintf(tmpStr, 4, "%d", data->domainNumber);
+ setConfig(ptpClock->managementConfig, "ptpengine:domain", tmpStr);
+ ptpClock->record_update = TRUE;
+ /* intentionally fall through to GET case */
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMDomain));
+ data = (MMDomain*)outgoing->tlv->dataField;
+ /* GET actions */
+ data->domainNumber = ptpClock->defaultDS.domainNumber;
+ data->reserved = 0x0;
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_DOMAIN,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming LOG_ANNOUNCE_INTERVAL management message type*/
+void handleMMLogAnnounceInterval(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received LOG_ANNOUNCE_INTERVAL message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_LOG_ANNOUNCE_INTERVAL;
+
+ MMLogAnnounceInterval* data = NULL;
+ switch( incoming->actionField )
+ {
+ case SET:
+ DBGV(" SET action\n");
+ data = (MMLogAnnounceInterval*)incoming->tlv->dataField;
+ /* SET actions */
+ ptpClock->portDS.logAnnounceInterval = data->logAnnounceInterval;
+ tmpsnprintf(tmpStr, 4, "%d", data->logAnnounceInterval);
+ setConfig(ptpClock->managementConfig, "ptpengine:log_announce_interval", tmpStr);
+ ptpClock->record_update = TRUE;
+ /* intentionally fall through to GET case */
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMLogAnnounceInterval));
+ data = (MMLogAnnounceInterval*)outgoing->tlv->dataField;
+ /* GET actions */
+ data->logAnnounceInterval = ptpClock->portDS.logAnnounceInterval;
+ data->reserved = 0x0;
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_LOG_ANNOUNCE_INTERVAL,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming ANNOUNCE_RECEIPT_TIMEOUT management message type*/
+void handleMMAnnounceReceiptTimeout(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received ANNOUNCE_RECEIPT_TIMEOUT message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_ANNOUNCE_RECEIPT_TIMEOUT;
+
+ MMAnnounceReceiptTimeout* data = NULL;
+ switch( incoming->actionField )
+ {
+ case SET:
+ DBGV(" SET action\n");
+ data = (MMAnnounceReceiptTimeout*)incoming->tlv->dataField;
+ /* SET actions */
+ ptpClock->portDS.announceReceiptTimeout = data->announceReceiptTimeout;
+ tmpsnprintf(tmpStr, 4, "%d", data->announceReceiptTimeout);
+ setConfig(ptpClock->managementConfig, "ptpengine:announce_receipt_timeout", tmpStr);
+ ptpClock->record_update = TRUE;
+ /* intentionally fall through to GET case */
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMAnnounceReceiptTimeout));
+ data = (MMAnnounceReceiptTimeout*)outgoing->tlv->dataField;
+ /* GET actions */
+ data->announceReceiptTimeout = ptpClock->portDS.announceReceiptTimeout;
+ data->reserved = 0x0;
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_ANNOUNCE_RECEIPT_TIMEOUT,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming LOG_SYNC_INTERVAL management message type*/
+void handleMMLogSyncInterval(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received LOG_SYNC_INTERVAL message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_LOG_SYNC_INTERVAL;
+
+ MMLogSyncInterval* data = NULL;
+ switch( incoming->actionField )
+ {
+ case SET:
+ DBGV(" SET action\n");
+ data = (MMLogSyncInterval*)incoming->tlv->dataField;
+ /* SET actions */
+ ptpClock->portDS.logSyncInterval = data->logSyncInterval;
+ tmpsnprintf(tmpStr, 4, "%d", data->logSyncInterval);
+ setConfig(ptpClock->managementConfig, "ptpengine:log_sync_interval", tmpStr);
+ ptpClock->record_update = TRUE;
+ /* intentionally fall through to GET case */
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMLogSyncInterval));
+ data = (MMLogSyncInterval*)outgoing->tlv->dataField;
+ /* GET actions */
+ data->logSyncInterval = ptpClock->portDS.logSyncInterval;
+ data->reserved = 0x0;
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_LOG_SYNC_INTERVAL,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming VERSION_NUMBER management message type*/
+void handleMMVersionNumber(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received VERSION_NUMBER message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_VERSION_NUMBER;
+
+ MMVersionNumber* data = NULL;
+ switch( incoming->actionField )
+ {
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMVersionNumber));
+ data = (MMVersionNumber*)outgoing->tlv->dataField;
+ /* GET actions */
+ data->reserved0 = 0x0;
+ data->versionNumber = ptpClock->portDS.versionNumber;
+ data->reserved1 = 0x0;
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ /* only version 2. go away. */
+ case SET:
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_VERSION_NUMBER,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming ENABLE_PORT management message type*/
+void handleMMEnablePort(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received ENABLE_PORT message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_ENABLE_PORT;
+
+ switch( incoming->actionField )
+ {
+ case COMMAND:
+ DBGV(" COMMAND action\n");
+ outgoing->actionField = ACKNOWLEDGE;
+ ptpClock->disabled = FALSE;
+ setConfig(ptpClock->managementConfig, "ptpengine:disabled", "N");
+ break;
+ case ACKNOWLEDGE:
+ DBGV(" ACKNOWLEDGE action\n");
+ /* TODO: implementation specific */
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_ENABLE_PORT,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming DISABLE_PORT management message type*/
+void handleMMDisablePort(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received DISABLE_PORT message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_DISABLE_PORT;
+
+ switch( incoming->actionField )
+ {
+ case COMMAND:
+ DBGV(" COMMAND action\n");
+ outgoing->actionField = ACKNOWLEDGE;
+ /* the state machine needs to know that we are not yet disabled but want to be */
+ /* ptpClock->disabled = TRUE; */
+ setConfig(ptpClock->managementConfig, "ptpengine:disabled", "Y");
+ break;
+ case ACKNOWLEDGE:
+ DBGV(" ACKNOWLEDGE action\n");
+ /* TODO: implementation specific */
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_DISABLE_PORT,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming TIME management message type*/
+void handleMMTime(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock, const RunTimeOpts* rtOpts)
+{
+ DBGV("received TIME message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_TIME;
+
+ MMTime* data = NULL;
+ switch( incoming->actionField )
+ {
+ case SET:
+ DBGV(" SET action\n");
+ data = (MMTime*)incoming->tlv->dataField;
+ /* SET actions */
+ /* TODO: add currentTime */
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMTime));
+ data = (MMTime*)outgoing->tlv->dataField;
+ /* GET actions */
+ TimeInternal internalTime;
+ getTime(&internalTime);
+ if (respectUtcOffset(rtOpts, ptpClock) == TRUE) {
+ internalTime.seconds += ptpClock->timePropertiesDS.currentUtcOffset;
+ }
+ fromInternalTime(&internalTime, &data->currentTime);
+ timestamp_display(&data->currentTime);
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_TIME,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming CLOCK_ACCURACY management message type*/
+void handleMMClockAccuracy(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received CLOCK_ACCURACY message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_CLOCK_ACCURACY;
+
+ MMClockAccuracy* data = NULL;
+ switch( incoming->actionField )
+ {
+ case SET:
+ DBGV(" SET action\n");
+ data = (MMClockAccuracy*)incoming->tlv->dataField;
+ /* SET actions */
+ ptpClock->defaultDS.clockQuality.clockAccuracy = data->clockAccuracy;
+ const char * acc = accToString(data->clockAccuracy);
+ if(acc != NULL) {
+ setConfig(ptpClock->managementConfig, "ptpengine:clock_accuracy", acc);
+ }
+ ptpClock->record_update = TRUE;
+ /* intentionally fall through to GET case */
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMClockAccuracy));
+ data = (MMClockAccuracy*)outgoing->tlv->dataField;
+ /* GET actions */
+ data->clockAccuracy = ptpClock->defaultDS.clockQuality.clockAccuracy;
+ data->reserved = 0x0;
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_CLOCK_ACCURACY,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming UTC_PROPERTIES management message type*/
+void handleMMUtcProperties(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received UTC_PROPERTIES message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_UTC_PROPERTIES;
+
+ MMUtcProperties* data = NULL;
+ switch( incoming->actionField )
+ {
+ case SET:
+ DBGV(" SET action\n");
+ data = (MMUtcProperties*)incoming->tlv->dataField;
+ /* SET actions */
+ ptpClock->timePropertiesDS.currentUtcOffset = data->currentUtcOffset;
+ /* set bit */
+ ptpClock->timePropertiesDS.currentUtcOffsetValid = IS_SET(data->utcv_li59_li61, UTCV);
+ ptpClock->timePropertiesDS.leap59 = IS_SET(data->utcv_li59_li61, LI59);
+ ptpClock->timePropertiesDS.leap61 = IS_SET(data->utcv_li59_li61, LI61);
+ tmpsnprintf(tmpStr, 20, "%d", data->currentUtcOffset);
+ /* todo: setting leap flags can be handy when we remotely controll PTPd GMs */
+ setConfig(ptpClock->managementConfig, "ptpengine:utc_offset", tmpStr);
+ setConfig(ptpClock->managementConfig, "ptpengine:utc_offset_valid", IS_SET(data->utcv_li59_li61, UTCV) ? "Y" : "N");
+ ptpClock->record_update = TRUE;
+ /* intentionally fall through to GET case */
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMUtcProperties));
+ data = (MMUtcProperties*)outgoing->tlv->dataField;
+ /* GET actions */
+ data->currentUtcOffset = ptpClock->timePropertiesDS.currentUtcOffset;
+ Octet utcv = SET_FIELD(ptpClock->timePropertiesDS.currentUtcOffsetValid, UTCV);
+ Octet li59 = SET_FIELD(ptpClock->timePropertiesDS.leap59, LI59);
+ Octet li61 = SET_FIELD(ptpClock->timePropertiesDS.leap61, LI61);
+ data->utcv_li59_li61 = utcv | li59 | li61;
+ data->reserved = 0x0;
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_UTC_PROPERTIES,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming TRACEABILITY_PROPERTIES management message type*/
+void handleMMTraceabilityProperties(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received TRACEABILITY_PROPERTIES message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_TRACEABILITY_PROPERTIES;
+
+ MMTraceabilityProperties* data = NULL;
+ switch( incoming->actionField )
+ {
+ case SET:
+ DBGV(" SET action\n");
+ data = (MMTraceabilityProperties*)incoming->tlv->dataField;
+ /* SET actions */
+ ptpClock->timePropertiesDS.frequencyTraceable = IS_SET(data->ftra_ttra, FTRA);
+ ptpClock->timePropertiesDS.timeTraceable = IS_SET(data->ftra_ttra, TTRA);
+ setConfig(ptpClock->managementConfig, "ptpengine:frequency_traceable", IS_SET(data->ftra_ttra, FTRA) ? "Y" : "N");
+ setConfig(ptpClock->managementConfig, "ptpengine:time_traceable", IS_SET(data->ftra_ttra, TTRA) ? "Y" : "N");
+ ptpClock->record_update = TRUE;
+ /* intentionally fall through to GET case */
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMTraceabilityProperties));
+ data = (MMTraceabilityProperties*)outgoing->tlv->dataField;
+ /* GET actions */
+ Octet ftra = SET_FIELD(ptpClock->timePropertiesDS.frequencyTraceable, FTRA);
+ Octet ttra = SET_FIELD(ptpClock->timePropertiesDS.timeTraceable, TTRA);
+ data->ftra_ttra = ftra | ttra;
+ data->reserved = 0x0;
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_TRACEABILITY_PROPERTIES,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming TIMESCALE_PROPERTIES management message type*/
+void handleMMTimescaleProperties(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received TIMESCALE_PROPERTIES message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_TRACEABILITY_PROPERTIES;
+
+ MMTimescaleProperties* data = NULL;
+ switch( incoming->actionField )
+ {
+ case SET:
+ DBGV(" SET action\n");
+ data = (MMTimescaleProperties*)incoming->tlv->dataField;
+ /* SET actions */
+ setConfig(ptpClock->managementConfig, "ptpengine:ptp_timesource", getTimeSourceName(data->timeSource));
+ setConfig(ptpClock->managementConfig, "ptpengine:ptp_timescale", data->ptp ? "PTP" : "ARB");
+ /* intentionally fall through to GET case */
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMTimescaleProperties));
+ data = (MMTimescaleProperties*)outgoing->tlv->dataField;
+ /* GET actions */
+ data->ptp = ptpClock->timePropertiesDS.ptpTimescale;
+ data->timeSource = ptpClock->timePropertiesDS.timeSource;
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_TRACEABILITY_PROPERTIES,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming UNICAST_NEGOTIATION_ENABLE management message type*/
+void handleMMUnicastNegotiationEnable(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock, RunTimeOpts *rtOpts)
+{
+ DBGV("received UNICAST_NEGOTIATION_ENABLE message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_UNICAST_NEGOTIATION_ENABLE;
+
+ MMUnicastNegotiationEnable* data = NULL;
+ switch( incoming->actionField )
+ {
+ case SET:
+ DBGV(" SET action\n");
+ data = (MMUnicastNegotiationEnable*)incoming->tlv->dataField;
+ /* SET actions */
+ setConfig(ptpClock->managementConfig, "ptpengine:unicast_negotiation", data->en ? "Y" : "N");
+ ptpClock->record_update = TRUE;
+ /* intentionally fall through to GET case */
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMUnicastNegotiationEnable));
+ data = (MMUnicastNegotiationEnable*)outgoing->tlv->dataField;
+ /* GET actions */
+ data->en = rtOpts->unicastNegotiation;
+ data->reserved = 0x0;
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_UNICAST_NEGOTIATION_ENABLE,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming DELAY_MECHANISM management message type*/
+void handleMMDelayMechanism(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received DELAY_MECHANISM message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_DELAY_MECHANISM;
+
+ MMDelayMechanism *data = NULL;
+ switch( incoming->actionField )
+ {
+ case SET:
+ DBGV(" SET action\n");
+ data = (MMDelayMechanism*)incoming->tlv->dataField;
+ /* SET actions */
+ ptpClock->portDS.delayMechanism = data->delayMechanism;
+ const char * mech = delayMechToString(data->delayMechanism);
+ if(mech != NULL) {
+ setConfig(ptpClock->managementConfig, "ptpengine:delay_mechanism", mech);
+ }
+ ptpClock->record_update = TRUE;
+ /* intentionally fall through to GET case */
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMDelayMechanism));
+ data = (MMDelayMechanism*)outgoing->tlv->dataField;
+ /* GET actions */
+ data->delayMechanism = ptpClock->portDS.delayMechanism;
+ data->reserved = 0x0;
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_DELAY_MECHANISM,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming LOG_MIN_PDELAY_REQ_INTERVAL management message type*/
+void handleMMLogMinPdelayReqInterval(MsgManagement* incoming, MsgManagement* outgoing, PtpClock* ptpClock)
+{
+ DBGV("received LOG_MIN_PDELAY_REQ_INTERVAL message\n");
+
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ outgoing->tlv->tlvType = TLV_MANAGEMENT;
+ outgoing->tlv->managementId = MM_LOG_MIN_PDELAY_REQ_INTERVAL;
+
+ MMLogMinPdelayReqInterval* data = NULL;
+ switch( incoming->actionField )
+ {
+ case SET:
+ DBGV(" SET action\n");
+ data = (MMLogMinPdelayReqInterval*)incoming->tlv->dataField;
+ /* SET actions */
+ ptpClock->portDS.logMinPdelayReqInterval = data->logMinPdelayReqInterval;
+ tmpsnprintf(tmpStr, 4, "%d", data->logMinPdelayReqInterval);
+ setConfig(ptpClock->managementConfig, "ptpengine:log_peer_delayreq_interval", tmpStr);
+ ptpClock->record_update = TRUE;
+ /* intentionally fall through to GET case */
+ case GET:
+ DBGV(" GET action\n");
+ outgoing->actionField = RESPONSE;
+ XMALLOC(outgoing->tlv->dataField, sizeof(MMLogMinPdelayReqInterval));
+ data = (MMLogMinPdelayReqInterval*)outgoing->tlv->dataField;
+ /* GET actions */
+ data->logMinPdelayReqInterval = ptpClock->portDS.logMinPdelayReqInterval;
+ data->reserved = 0x0;
+ break;
+ case RESPONSE:
+ DBGV(" RESPONSE action\n");
+ /* TODO: implementation specific */
+ break;
+ default:
+ DBGV(" unknown actionType \n");
+ free(outgoing->tlv);
+ handleErrorManagementMessage(incoming, outgoing,
+ ptpClock, MM_LOG_MIN_PDELAY_REQ_INTERVAL,
+ NOT_SUPPORTED);
+ }
+
+}
+
+/**\brief Handle incoming ERROR_STATUS management message type*/
+void handleMMErrorStatus(MsgManagement *incoming)
+{
+ (void)incoming;
+ DBGV("received MANAGEMENT_ERROR_STATUS message \n");
+ /* implementation specific */
+}
+
+/**\brief Handle issuing ERROR_STATUS management message type*/
+void handleErrorManagementMessage(MsgManagement *incoming, MsgManagement *outgoing, PtpClock *ptpClock, Enumeration16 mgmtId, Enumeration16 errorId)
+{
+ /* init management header fields */
+ initOutgoingMsgManagement(incoming, outgoing, ptpClock);
+ /* init management error status tlv fields */
+ outgoing->tlv->tlvType = TLV_MANAGEMENT_ERROR_STATUS;
+ /* management error status managementId field is the errorId */
+ outgoing->tlv->managementId = errorId;
+ switch(incoming->actionField)
+ {
+ case GET:
+ case SET:
+ outgoing->actionField = RESPONSE;
+ break;
+ case RESPONSE:
+ outgoing->actionField = ACKNOWLEDGE;
+ break;
+ default:
+ outgoing->actionField = 0;
+ }
+
+ XMALLOC(outgoing->tlv->dataField, sizeof( MMErrorStatus));
+ MMErrorStatus *data = (MMErrorStatus*)outgoing->tlv->dataField;
+ /* set managementId */
+ data->managementId = mgmtId;
+ data->reserved = 0x00;
+ data->displayData.lengthField = 0;
+ data->displayData.textField = NULL;
+
+}
+
+#if 0
+static void
+issueManagement(MsgHeader *header,MsgManagement *manage,const RunTimeOpts *rtOpts,
+ PtpClock *ptpClock)
+{
+
+ ptpClock->counters.managementMessagesSent++;
+
+}
+#endif
+
+static void
+issueManagementRespOrAck(MsgManagement *outgoing, Integer32 dst, const RunTimeOpts *rtOpts,
+ PtpClock *ptpClock)
+{
+
+ /* pack ManagementTLV */
+ msgPackManagementTLV( ptpClock->msgObuf, outgoing, ptpClock);
+
+ /* set header messageLength, the outgoing->tlv->lengthField is now valid */
+ outgoing->header.messageLength = MANAGEMENT_LENGTH +
+ TL_LENGTH +
+ outgoing->tlv->lengthField;
+
+ msgPackManagement( ptpClock->msgObuf, outgoing, ptpClock);
+
+
+ if(!netSendGeneral(ptpClock->msgObuf, outgoing->header.messageLength,
+ &ptpClock->netPath, rtOpts, dst)) {
+ DBGV("Management response/acknowledge can't be sent -> FAULTY state \n");
+ ptpClock->counters.messageSendErrors++;
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ } else {
+ DBGV("Management response/acknowledge msg sent \n");
+ ptpClock->counters.managementMessagesSent++;
+ }
+}
+
+static void
+issueManagementErrorStatus(MsgManagement *outgoing, Integer32 dst, const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+
+ /* pack ManagementErrorStatusTLV */
+ msgPackManagementErrorStatusTLV( ptpClock->msgObuf, outgoing, ptpClock);
+
+ /* set header messageLength, the outgoing->tlv->lengthField is now valid */
+ outgoing->header.messageLength = MANAGEMENT_LENGTH +
+ TL_LENGTH +
+ outgoing->tlv->lengthField;
+
+ msgPackManagement( ptpClock->msgObuf, outgoing, ptpClock);
+
+ if(!netSendGeneral(ptpClock->msgObuf, outgoing->header.messageLength,
+ &ptpClock->netPath, rtOpts, dst)) {
+ DBGV("Management error status can't be sent -> FAULTY state \n");
+ ptpClock->counters.messageSendErrors++;
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ } else {
+ DBGV("Management error status msg sent \n");
+ ptpClock->counters.managementMessagesSent++;
+ }
+
+}
diff --git a/rtemsbsd/ptpd/src/protocol.c b/rtemsbsd/ptpd/src/protocol.c
new file mode 100644
index 00000000..1514add2
--- /dev/null
+++ b/rtemsbsd/ptpd/src/protocol.c
@@ -0,0 +1,3534 @@
+/*-
+ * Copyright (c) 2012-2016 Wojciech Owczarek,
+ * Copyright (c) 2011-2012 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen,
+ * Inaqui Delgado,
+ * Rick Ratzel,
+ * National Instruments.
+ * Copyright (c) 2009-2010 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen
+ *
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file protocol.c
+ * @date Wed Jun 23 09:40:39 2010
+ *
+ * @brief The code that handles the IEEE-1588 protocol and state machine
+ *
+ *
+ */
+
+#include "ptpd.h"
+
+Boolean doInit(RunTimeOpts*,PtpClock*);
+static void doState(RunTimeOpts*,PtpClock*);
+
+void handle(RunTimeOpts*,PtpClock*);
+
+static void handleAnnounce(MsgHeader*, ssize_t,Boolean, const RunTimeOpts*,PtpClock*);
+static void handleSync(const MsgHeader*, ssize_t,TimeInternal*,Boolean,Integer32, Integer32, const RunTimeOpts*,PtpClock*);
+static void handleFollowUp(const MsgHeader*, ssize_t,Boolean,const RunTimeOpts*,PtpClock*);
+static void handlePdelayReq(MsgHeader*, ssize_t,const TimeInternal*, Integer32, Boolean,const RunTimeOpts*,PtpClock*);
+static void handleDelayReq(const MsgHeader*, ssize_t, const TimeInternal*,Integer32, Boolean,const RunTimeOpts*,PtpClock*);
+static void handlePdelayResp(const MsgHeader*, TimeInternal* ,ssize_t,Boolean, Integer32, Integer32, const RunTimeOpts*,PtpClock*);
+static void handleDelayResp(const MsgHeader*, ssize_t, const RunTimeOpts*,PtpClock*);
+static void handlePdelayRespFollowUp(const MsgHeader*, ssize_t, Boolean, const RunTimeOpts*,PtpClock*);
+
+/* handleManagement in management.c */
+/* handleSignaling in signaling.c */
+
+#ifndef PTPD_SLAVE_ONLY /* does not get compiled when building slave only */
+static void issueAnnounce(const RunTimeOpts*,PtpClock*);
+static void issueAnnounceSingle(Integer32, UInteger16*, const RunTimeOpts*,PtpClock*);
+static void issueSync(const RunTimeOpts*,PtpClock*);
+static TimeInternal issueSyncSingle(Integer32, UInteger16*, const RunTimeOpts*,PtpClock*);
+static void issueFollowup(const TimeInternal*,const RunTimeOpts*,PtpClock*, Integer32, const UInteger16);
+#endif /* PTPD_SLAVE_ONLY */
+static void issuePdelayReq(const RunTimeOpts*,PtpClock*);
+static void issueDelayReq(const RunTimeOpts*,PtpClock*);
+static void issuePdelayResp(const TimeInternal*,MsgHeader*,Integer32,const RunTimeOpts*,PtpClock*);
+static void issueDelayResp(const TimeInternal*,MsgHeader*,Integer32,const RunTimeOpts*,PtpClock*);
+static void issuePdelayRespFollowUp(const TimeInternal*,MsgHeader*, Integer32, const RunTimeOpts*,PtpClock*, const UInteger16);
+
+static void processMessage(RunTimeOpts* rtOpts, PtpClock* ptpClock, TimeInternal* timeStamp, ssize_t length);
+
+#ifndef PTPD_SLAVE_ONLY /* does not get compiled when building slave only */
+static void processSyncFromSelf(const TimeInternal * tint, const RunTimeOpts * rtOpts, PtpClock * ptpClock, Integer32 dst, const UInteger16 sequenceId);
+static void indexSync(TimeInternal *timeStamp, UInteger16 sequenceId, Integer32 transportAddress, SyncDestEntry *index);
+#endif /* PTPD_SLAVE_ONLY */
+
+static void processDelayReqFromSelf(const TimeInternal * tint, const RunTimeOpts * rtOpts, PtpClock * ptpClock);
+static void processPdelayReqFromSelf(const TimeInternal * tint, const RunTimeOpts * rtOpts, PtpClock * ptpClock);
+static void processPdelayRespFromSelf(const TimeInternal * tint, const RunTimeOpts * rtOpts, PtpClock * ptpClock, Integer32 dst, const UInteger16 sequenceId);
+
+/* this shouldn't really be in protocol.c, it will be moved later */
+static void timestampCorrection(const RunTimeOpts * rtOpts, PtpClock *ptpClock, TimeInternal *timeStamp);
+
+static Integer32 lookupSyncIndex(TimeInternal *timeStamp, UInteger16 sequenceId, SyncDestEntry *index);
+static Integer32 findSyncDestination(TimeInternal *timeStamp, const RunTimeOpts *rtOpts, PtpClock *ptpClock);
+
+
+#ifndef PTPD_SLAVE_ONLY
+
+/* store transportAddress in an index table */
+static void
+indexSync(TimeInternal *timeStamp, UInteger16 sequenceId, Integer32 transportAddress, SyncDestEntry *index)
+{
+
+ uint32_t hash = 0;
+
+#if defined(RUNTIME_DEBUG) || defined (PTPD_DBGV)
+ struct in_addr tmpAddr;
+ tmpAddr.s_addr = transportAddress;
+#endif /* RUNTIME_DEBUG */
+
+ if(timeStamp == NULL || index == NULL) {
+ return;
+ }
+
+ hash = fnvHash(timeStamp, sizeof(TimeInternal), UNICAST_MAX_DESTINATIONS);
+
+ if(index[hash].transportAddress) {
+ DBG("indexSync: hash collision - clearing entry %s:%04x\n", inet_ntoa(tmpAddr), hash);
+ index[hash].transportAddress = 0;
+ } else {
+ DBG("indexSync: indexed successfully %s:%04x\n", inet_ntoa(tmpAddr), hash);
+ index[hash].transportAddress = transportAddress;
+ }
+
+}
+
+#endif /* PTPD_SLAVE_ONLY */
+
+/* sync destination index lookup */
+static Integer32
+lookupSyncIndex(TimeInternal *timeStamp, UInteger16 sequenceId, SyncDestEntry *index)
+{
+
+ uint32_t hash = 0;
+ Integer32 previousAddress;
+
+ if(timeStamp == NULL || index == NULL) {
+ return 0;
+ }
+
+ hash = fnvHash(timeStamp, sizeof(TimeInternal), UNICAST_MAX_DESTINATIONS);
+
+ if(index[hash].transportAddress == 0) {
+ DBG("lookupSyncIndex: cache miss\n");
+ return 0;
+ } else {
+ DBG("lookupSyncIndex: cache hit - clearing old entry\n");
+ previousAddress = index[hash].transportAddress;
+ index[hash].transportAddress = 0;
+ return previousAddress;
+ }
+
+}
+
+/* iterative search for Sync destination for the given cached timestamp */
+static Integer32
+findSyncDestination(TimeInternal *timeStamp, const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+
+ int i = 0;
+
+ for(i = 0; i < UNICAST_MAX_DESTINATIONS; i++) {
+
+ if(rtOpts->unicastNegotiation) {
+ if( (timeStamp->seconds == ptpClock->unicastGrants[i].lastSyncTimestamp.seconds) &&
+ (timeStamp->nanoseconds == ptpClock->unicastGrants[i].lastSyncTimestamp.nanoseconds)) {
+ clearTime(&ptpClock->unicastGrants[i].lastSyncTimestamp);
+ return ptpClock->unicastGrants[i].transportAddress;
+ }
+ } else {
+ if( (timeStamp->seconds == ptpClock->unicastDestinations[i].lastSyncTimestamp.seconds) &&
+ (timeStamp->nanoseconds == ptpClock->unicastDestinations[i].lastSyncTimestamp.nanoseconds)) {
+ clearTime(&ptpClock->unicastDestinations[i].lastSyncTimestamp);
+ return ptpClock->unicastDestinations[i].transportAddress;
+ }
+ }
+ }
+
+ return 0;
+
+}
+
+void addForeign(Octet*,MsgHeader*,PtpClock*, UInteger8, UInteger32);
+
+/* loop forever. doState() has a switch for the actions and events to be
+ checked for 'port_state'. the actions and events may or may not change
+ 'port_state' by calling toState(), but once they are done we loop around
+ again and perform the actions required for the new 'port_state'. */
+void
+protocol(RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ DBG("event POWERUP\n");
+
+ timerStart(&ptpClock->timers[TIMINGDOMAIN_UPDATE_TIMER],timingDomain.updateInterval);
+ timerStart(&ptpClock->timers[ALARM_UPDATE_TIMER],ALARM_UPDATE_INTERVAL);
+
+ ptpClock->disabled = rtOpts->portDisabled;
+
+ if(ptpClock->disabled) {
+ toState(PTP_DISABLED, rtOpts, ptpClock);
+ WARNING("PTP port starting in DISABLED state. Awaiting config change or management message\n");
+ /* initialize networking so we can be remotely enabled */
+ netShutdown(&ptpClock->netPath);
+ if (!netInit(&ptpClock->netPath, rtOpts, ptpClock)) {
+ ERROR("Failed to initialize network in disabled state, will not be able to re-enable!\n");
+ }
+ /* populate the basics required to receive management messages in DISABLED state */
+ initData(rtOpts, ptpClock);
+ updateDatasets(ptpClock, rtOpts);
+ } else {
+ toState(PTP_INITIALIZING, rtOpts, ptpClock);
+ }
+ if(rtOpts->statusLog.logEnabled)
+ writeStatusFile(ptpClock, rtOpts, TRUE);
+
+ /* run the status file update every 1 .. 1.2 seconds */
+ timerStart(&ptpClock->timers[STATUSFILE_UPDATE_TIMER],rtOpts->statusFileUpdateInterval * (1.0 + 0.2 * getRand()));
+ timerStart(&ptpClock->timers[PERIODIC_INFO_TIMER],rtOpts->statsUpdateInterval);
+
+ DBG("Debug Initializing...\n");
+
+ if(rtOpts->statusLog.logEnabled)
+ writeStatusFile(ptpClock, rtOpts, TRUE);
+
+ for (;;)
+ {
+ /* 20110701: this main loop was rewritten to be more clear */
+ if(ptpClock->disabled && ptpClock->portDS.portState != PTP_DISABLED) {
+ toState(PTP_DISABLED, rtOpts, ptpClock);
+ }
+
+ if(!ptpClock->disabled && ptpClock->portDS.portState == PTP_DISABLED) {
+ toState(PTP_INITIALIZING, rtOpts, ptpClock);
+ }
+
+ if (ptpClock->portDS.portState == PTP_INITIALIZING) {
+
+ /*
+ * DO NOT shut down once started. We have to "wait intelligently",
+ * that is keep processing signals. If init failed, wait for n seconds
+ * until next retry, do not exit. Wait in chunks so SIGALRM can interrupt.
+ */
+ if(ptpClock->initFailure) {
+ usleep(10000);
+ ptpClock->initFailureTimeout--;
+ }
+
+ if(!ptpClock->initFailure || ptpClock->initFailureTimeout <= 0) {
+ if(!doInit(rtOpts, ptpClock)) {
+ ERROR("PTPd init failed - will retry in %d seconds\n", DEFAULT_FAILURE_WAITTIME);
+ writeStatusFile(ptpClock, rtOpts, TRUE);
+ ptpClock->initFailure = TRUE;
+ ptpClock->initFailureTimeout = 100 * DEFAULT_FAILURE_WAITTIME;
+ SET_ALARM(ALRM_NETWORK_FLT, TRUE);
+ } else {
+ ptpClock->initFailure = FALSE;
+ ptpClock->initFailureTimeout = 0;
+ SET_ALARM(ALRM_NETWORK_FLT, FALSE);
+ }
+
+ }
+
+ } else {
+ doState(rtOpts, ptpClock);
+ }
+
+ if(ptpClock->disabled && ptpClock->portDS.portState != PTP_DISABLED) {
+ toState(PTP_DISABLED, rtOpts, ptpClock);
+ }
+
+ if (ptpClock->message_activity)
+ DBGV("activity\n");
+
+ /* Configuration has changed */
+ if(rtOpts->restartSubsystems > 0) {
+ restartSubsystems(rtOpts, ptpClock);
+ }
+
+ if (timerExpired(&ptpClock->timers[TIMINGDOMAIN_UPDATE_TIMER])) {
+ timingDomain.update(&timingDomain);
+ }
+
+ if(ptpClock->defaultDS.slaveOnly) {
+ SET_ALARM(ALRM_PORT_STATE, ptpClock->portDS.portState != PTP_SLAVE);
+ }
+
+ if(ptpClock->defaultDS.clockQuality.clockClass < 128) {
+ SET_ALARM(ALRM_PORT_STATE, ptpClock->portDS.portState != PTP_MASTER && ptpClock->portDS.portState != PTP_PASSIVE );
+ }
+
+ if (timerExpired(&ptpClock->timers[ALARM_UPDATE_TIMER])) {
+ if(rtOpts->alarmInitialDelay && (ptpClock->alarmDelay > 0)) {
+ ptpClock->alarmDelay -= ALARM_UPDATE_INTERVAL;
+ if(ptpClock->alarmDelay <= 0 && rtOpts->alarmsEnabled) {
+ INFO("Alarm delay expired - starting alarm processing\n");
+ enableAlarms(ptpClock->alarms, ALRM_MAX, TRUE);
+ }
+ }
+ updateAlarms(ptpClock->alarms, ALRM_MAX);
+ }
+
+
+ if (timerExpired(&ptpClock->timers[UNICAST_GRANT_TIMER])) {
+ if(rtOpts->unicastDestinationsSet) {
+ refreshUnicastGrants(ptpClock->unicastGrants,
+ ptpClock->unicastDestinationCount, rtOpts, ptpClock);
+ } else {
+ refreshUnicastGrants(ptpClock->unicastGrants,
+ UNICAST_MAX_DESTINATIONS, rtOpts, ptpClock);
+ }
+ if(ptpClock->unicastPeerDestination.transportAddress) {
+ refreshUnicastGrants(&ptpClock->peerGrants,
+ 1, rtOpts, ptpClock);
+
+ }
+ }
+
+ /* Perform the heavy signal processing synchronously */
+ checkSignals(rtOpts, ptpClock);
+ }
+}
+
+/* state change wrapper to allow for event generation / control when changing state */
+void setPortState(PtpClock *ptpClock, Enumeration8 state)
+{
+
+ if(ptpClock == NULL) {
+ return;
+ }
+ if(ptpClock->portDS.portState != state) {
+ ptpClock->portDS.lastPortState = ptpClock->portDS.portState;
+ DBG("State change from %s to %s\n", portState_getName(ptpClock->portDS.lastPortState), portState_getName(state));
+ }
+
+ /* "expected state" checks */
+
+ ptpClock->portDS.portState = state;
+
+ if(ptpClock->defaultDS.slaveOnly) {
+ SET_ALARM(ALRM_PORT_STATE, state != PTP_SLAVE);
+ } else if(ptpClock->defaultDS.clockQuality.clockClass < 128) {
+ SET_ALARM(ALRM_PORT_STATE, state != PTP_MASTER && state != PTP_PASSIVE );
+ }
+
+
+}
+
+/* perform actions required when leaving 'port_state' and entering 'state' */
+void
+toState(UInteger8 state, const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ ptpClock->message_activity = TRUE;
+
+ /* leaving state tasks */
+ switch (ptpClock->portDS.portState)
+ {
+ case PTP_MASTER:
+
+ timerStop(&ptpClock->timers[SYNC_INTERVAL_TIMER]);
+ timerStop(&ptpClock->timers[ANNOUNCE_INTERVAL_TIMER]);
+ timerStop(&ptpClock->timers[PDELAYREQ_INTERVAL_TIMER]);
+ timerStop(&ptpClock->timers[DELAY_RECEIPT_TIMER]);
+ timerStop(&ptpClock->timers[MASTER_NETREFRESH_TIMER]);
+
+ if(rtOpts->unicastNegotiation && rtOpts->ipMode==IPMODE_UNICAST) {
+ cancelAllGrants(ptpClock->unicastGrants, UNICAST_MAX_DESTINATIONS,
+ rtOpts, ptpClock);
+ if(ptpClock->portDS.delayMechanism == P2P) {
+ cancelAllGrants(&ptpClock->peerGrants, 1,
+ rtOpts, ptpClock);
+ }
+ }
+
+ break;
+ case PTP_SLAVE:
+ timerStop(&ptpClock->timers[ANNOUNCE_RECEIPT_TIMER]);
+ timerStop(&ptpClock->timers[SYNC_RECEIPT_TIMER]);
+ timerStop(&ptpClock->timers[DELAY_RECEIPT_TIMER]);
+
+ if(rtOpts->unicastNegotiation && rtOpts->ipMode==IPMODE_UNICAST && ptpClock->parentGrants != NULL) {
+ /* do not cancel, just start re-requesting so we can still send a cancel on exit */
+ ptpClock->parentGrants->grantData[ANNOUNCE_INDEXED].timeLeft = 0;
+ ptpClock->parentGrants->grantData[SYNC_INDEXED].timeLeft = 0;
+ if(ptpClock->portDS.delayMechanism == E2E) {
+ ptpClock->parentGrants->grantData[DELAY_RESP_INDEXED].timeLeft = 0;
+ }
+
+ ptpClock->parentGrants = NULL;
+
+ if(ptpClock->portDS.delayMechanism == P2P) {
+ cancelUnicastTransmission(&ptpClock->peerGrants.grantData[PDELAY_RESP_INDEXED], rtOpts, ptpClock);
+ cancelAllGrants(&ptpClock->peerGrants, 1, rtOpts, ptpClock);
+ }
+ }
+
+ if (ptpClock->portDS.delayMechanism == E2E)
+ timerStop(&ptpClock->timers[DELAYREQ_INTERVAL_TIMER]);
+ else if (ptpClock->portDS.delayMechanism == P2P)
+ timerStop(&ptpClock->timers[PDELAYREQ_INTERVAL_TIMER]);
+/* If statistics are enabled, drift should have been saved already - otherwise save it*/
+#ifndef PTPD_STATISTICS
+ /* save observed drift value, don't inform user */
+ saveDrift(ptpClock, rtOpts, TRUE);
+#endif /* PTPD_STATISTICS */
+
+#ifdef PTPD_STATISTICS
+ resetPtpEngineSlaveStats(&ptpClock->slaveStats);
+ timerStop(&ptpClock->timers[STATISTICS_UPDATE_TIMER]);
+#endif /* PTPD_STATISTICS */
+ ptpClock->panicMode = FALSE;
+ ptpClock->panicOver = FALSE;
+ timerStop(&ptpClock->timers[PANIC_MODE_TIMER]);
+ initClock(rtOpts, ptpClock);
+
+ case PTP_PASSIVE:
+ timerStop(&ptpClock->timers[PDELAYREQ_INTERVAL_TIMER]);
+ timerStop(&ptpClock->timers[DELAY_RECEIPT_TIMER]);
+ timerStop(&ptpClock->timers[ANNOUNCE_RECEIPT_TIMER]);
+ break;
+
+ case PTP_LISTENING:
+ timerStop(&ptpClock->timers[ANNOUNCE_RECEIPT_TIMER]);
+ timerStop(&ptpClock->timers[SYNC_RECEIPT_TIMER]);
+ timerStop(&ptpClock->timers[DELAY_RECEIPT_TIMER]);
+
+ /* we're leaving LISTENING - reset counter */
+ if(state != PTP_LISTENING) {
+ ptpClock->listenCount = 0;
+ }
+ break;
+ case PTP_INITIALIZING:
+ if(rtOpts->unicastNegotiation) {
+ if(!ptpClock->defaultDS.slaveOnly) {
+
+ initUnicastGrantTable(ptpClock->unicastGrants,
+ ptpClock->portDS.delayMechanism,
+ UNICAST_MAX_DESTINATIONS, NULL,
+ rtOpts, ptpClock);
+
+ if(rtOpts->unicastDestinationsSet) {
+ initUnicastGrantTable(ptpClock->unicastGrants,
+ ptpClock->portDS.delayMechanism,
+ ptpClock->unicastDestinationCount, ptpClock->unicastDestinations,
+ rtOpts, ptpClock);
+ }
+
+ } else {
+ initUnicastGrantTable(ptpClock->unicastGrants,
+ ptpClock->portDS.delayMechanism,
+ ptpClock->unicastDestinationCount, ptpClock->unicastDestinations,
+ rtOpts, ptpClock);
+ }
+ if(ptpClock->unicastPeerDestination.transportAddress) {
+ initUnicastGrantTable(&ptpClock->peerGrants,
+ ptpClock->portDS.delayMechanism,
+ 1, &ptpClock->unicastPeerDestination,
+ rtOpts, ptpClock);
+ }
+ /* this must be set regardless of delay mechanism,
+ * so functions can see this is a peer table
+ */
+ ptpClock->peerGrants.isPeer = TRUE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* entering state tasks */
+
+ ptpClock->counters.stateTransitions++;
+
+ DBG("state %s\n",portState_getName(state));
+
+ /*
+ * No need of PRE_MASTER state because of only ordinary clock
+ * implementation.
+ */
+
+ switch (state)
+ {
+ case PTP_INITIALIZING:
+ setPortState(ptpClock, PTP_INITIALIZING);
+ break;
+
+ case PTP_FAULTY:
+ setPortState(ptpClock, PTP_FAULTY);
+ break;
+
+ case PTP_DISABLED:
+ /* well, theoretically we're still in the previous state, so we're not in breach of standard */
+ if(rtOpts->unicastNegotiation && rtOpts->ipMode==IPMODE_UNICAST) {
+ cancelAllGrants(ptpClock->unicastGrants, ptpClock->unicastDestinationCount,
+ rtOpts, ptpClock);
+ }
+ ptpClock->bestMaster = NULL;
+ /* see? NOW we're in disabled state */
+ setPortState(ptpClock, PTP_DISABLED);
+ break;
+
+ case PTP_LISTENING:
+
+ if(rtOpts->unicastNegotiation) {
+ timerStart(&ptpClock->timers[UNICAST_GRANT_TIMER], UNICAST_GRANT_REFRESH_INTERVAL);
+ }
+
+ /* in Listening state, make sure we don't send anything. Instead we just expect/wait for announces (started below) */
+ timerStop(&ptpClock->timers[SYNC_INTERVAL_TIMER]);
+ timerStop(&ptpClock->timers[ANNOUNCE_INTERVAL_TIMER]);
+ timerStop(&ptpClock->timers[SYNC_RECEIPT_TIMER]);
+ timerStop(&ptpClock->timers[DELAY_RECEIPT_TIMER]);
+ timerStop(&ptpClock->timers[PDELAYREQ_INTERVAL_TIMER]);
+ timerStop(&ptpClock->timers[DELAYREQ_INTERVAL_TIMER]);
+
+ /* This is (re) started on clock updates only */
+ timerStop(&ptpClock->timers[CLOCK_UPDATE_TIMER]);
+
+ /* if we're ignoring announces (disable_bmca), go straight to master */
+ if(ptpClock->defaultDS.clockQuality.clockClass <= 127 && rtOpts->disableBMCA) {
+ DBG("unicast master only and ignoreAnnounce: going into MASTER state\n");
+ ptpClock->number_foreign_records = 0;
+ ptpClock->foreign_record_i = 0;
+ m1(rtOpts,ptpClock);
+ toState(PTP_MASTER, rtOpts, ptpClock);
+ break;
+ }
+
+ /*
+ * Count how many _unique_ timeouts happen to us.
+ * If we were already in Listen mode, then do not count this as a seperate reset, but stil do a new IGMP refresh
+ */
+ if (ptpClock->portDS.portState != PTP_LISTENING) {
+ ptpClock->resetCount++;
+ } else {
+ ptpClock->listenCount++;
+ if( ptpClock->listenCount >= rtOpts->maxListen ) {
+ WARNING("Still in LISTENING after %d restarts - restarting transports\n",
+ rtOpts->maxListen);
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ ptpClock->listenCount = 0;
+ break;
+ }
+ }
+
+ /* Revert to the original DelayReq interval, and ignore the one for the last master */
+ ptpClock->portDS.logMinDelayReqInterval = rtOpts->initial_delayreq;
+
+ /* force a IGMP refresh per reset */
+ if (rtOpts->ipMode != IPMODE_UNICAST && rtOpts->do_IGMP_refresh && rtOpts->transport != IEEE_802_3) {
+ /* if multicast refresh failed, restart network - helps recover after driver reloads and such */
+ if(!netRefreshIGMP(&ptpClock->netPath, rtOpts, ptpClock)) {
+ WARNING("Error while refreshing multicast - restarting transports\n");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ break;
+ }
+ }
+
+ timerStart(&ptpClock->timers[ANNOUNCE_RECEIPT_TIMER],
+ (ptpClock->portDS.announceReceiptTimeout) *
+ (pow(2,ptpClock->portDS.logAnnounceInterval)));
+
+ ptpClock->announceTimeouts = 0;
+
+ ptpClock->bestMaster = NULL;
+
+ /*
+ * Log status update only once - condition must be checked before we write the new state,
+ * but the new state must be eritten before the log message...
+ */
+ if (ptpClock->portDS.portState != state) {
+ setPortState(ptpClock, PTP_LISTENING);
+ displayStatus(ptpClock, "Now in state: ");
+ } else {
+ setPortState(ptpClock, PTP_LISTENING);
+ }
+
+ break;
+#ifndef PTPD_SLAVE_ONLY
+ case PTP_MASTER:
+ if(rtOpts->unicastNegotiation) {
+ timerStart(&ptpClock->timers[UNICAST_GRANT_TIMER], 1);
+ }
+ timerStart(&ptpClock->timers[SYNC_INTERVAL_TIMER],
+ pow(2,ptpClock->portDS.logSyncInterval));
+ DBG("SYNC INTERVAL TIMER : %f \n",
+ pow(2,ptpClock->portDS.logSyncInterval));
+ timerStart(&ptpClock->timers[ANNOUNCE_INTERVAL_TIMER],
+ pow(2,ptpClock->portDS.logAnnounceInterval));
+ timerStart(&ptpClock->timers[PDELAYREQ_INTERVAL_TIMER],
+ pow(2,ptpClock->portDS.logMinPdelayReqInterval));
+ if(ptpClock->portDS.delayMechanism == P2P) {
+ timerStart(&ptpClock->timers[DELAY_RECEIPT_TIMER], max(
+ (ptpClock->portDS.announceReceiptTimeout) * (pow(2,ptpClock->portDS.logAnnounceInterval)),
+ MISSED_MESSAGES_MAX * (pow(2,ptpClock->portDS.logMinPdelayReqInterval))));
+ }
+ if( rtOpts->do_IGMP_refresh &&
+ rtOpts->transport == UDP_IPV4 &&
+ rtOpts->ipMode != IPMODE_UNICAST &&
+ rtOpts->masterRefreshInterval > 9 )
+ timerStart(&ptpClock->timers[MASTER_NETREFRESH_TIMER],
+ rtOpts->masterRefreshInterval);
+ setPortState(ptpClock, PTP_MASTER);
+ displayStatus(ptpClock, "Now in state: ");
+
+ ptpClock->bestMaster = NULL;
+
+ break;
+#endif /* PTPD_SLAVE_ONLY */
+ case PTP_PASSIVE:
+ timerStart(&ptpClock->timers[PDELAYREQ_INTERVAL_TIMER],
+ pow(2,ptpClock->portDS.logMinPdelayReqInterval));
+ timerStart(&ptpClock->timers[ANNOUNCE_RECEIPT_TIMER],
+ (ptpClock->portDS.announceReceiptTimeout) *
+ (pow(2,ptpClock->portDS.logAnnounceInterval)));
+ if(ptpClock->portDS.delayMechanism == P2P) {
+ timerStart(&ptpClock->timers[DELAY_RECEIPT_TIMER], max(
+ (ptpClock->portDS.announceReceiptTimeout) * (pow(2,ptpClock->portDS.logAnnounceInterval)),
+ MISSED_MESSAGES_MAX * (pow(2,ptpClock->portDS.logMinPdelayReqInterval))));
+ }
+ setPortState(ptpClock, PTP_PASSIVE);
+ p1(ptpClock, rtOpts);
+ displayStatus(ptpClock, "Now in state: ");
+ break;
+
+ case PTP_UNCALIBRATED:
+ setPortState(ptpClock, PTP_UNCALIBRATED);
+ break;
+
+ case PTP_SLAVE:
+ if(rtOpts->unicastNegotiation) {
+ timerStart(&ptpClock->timers[UNICAST_GRANT_TIMER], 1);
+ }
+ if(rtOpts->clockUpdateTimeout > 0) {
+ timerStart(&ptpClock->timers[CLOCK_UPDATE_TIMER], rtOpts->clockUpdateTimeout);
+ }
+ initClock(rtOpts, ptpClock);
+ /*
+ * restore the observed drift value using the selected method,
+ * reset on failure or when -F 0 (default) is used, don't inform user
+ */
+ restoreDrift(ptpClock, rtOpts, TRUE);
+
+ ptpClock->waitingForFollow = FALSE;
+ ptpClock->waitingForDelayResp = FALSE;
+
+ if(rtOpts->calibrationDelay) {
+ ptpClock->isCalibrated = FALSE;
+ }
+
+ // FIXME: clear these vars inside initclock
+ clearTime(&ptpClock->delay_req_send_time);
+ clearTime(&ptpClock->delay_req_receive_time);
+ clearTime(&ptpClock->pdelay_req_send_time);
+ clearTime(&ptpClock->pdelay_req_receive_time);
+ clearTime(&ptpClock->pdelay_resp_send_time);
+ clearTime(&ptpClock->pdelay_resp_receive_time);
+
+ timerStart(&ptpClock->timers[OPERATOR_MESSAGES_TIMER],
+ OPERATOR_MESSAGES_INTERVAL);
+
+ timerStart(&ptpClock->timers[ANNOUNCE_RECEIPT_TIMER],
+ (ptpClock->portDS.announceReceiptTimeout) *
+ (pow(2,ptpClock->portDS.logAnnounceInterval)));
+
+ timerStart(&ptpClock->timers[SYNC_RECEIPT_TIMER], max(
+ (ptpClock->portDS.announceReceiptTimeout) * (pow(2,ptpClock->portDS.logAnnounceInterval)),
+ MISSED_MESSAGES_MAX * (pow(2,ptpClock->portDS.logSyncInterval))
+ ));
+
+ if(ptpClock->portDS.delayMechanism == E2E) {
+ timerStart(&ptpClock->timers[DELAY_RECEIPT_TIMER], max(
+ (ptpClock->portDS.announceReceiptTimeout) * (pow(2,ptpClock->portDS.logAnnounceInterval)),
+ MISSED_MESSAGES_MAX * (pow(2,ptpClock->portDS.logMinDelayReqInterval))));
+ }
+ if(ptpClock->portDS.delayMechanism == P2P) {
+ timerStart(&ptpClock->timers[DELAY_RECEIPT_TIMER], max(
+ (ptpClock->portDS.announceReceiptTimeout) * (pow(2,ptpClock->portDS.logAnnounceInterval)),
+ MISSED_MESSAGES_MAX * (pow(2,ptpClock->portDS.logMinPdelayReqInterval))));
+ }
+
+ /*
+ * Previously, this state transition would start the
+ * delayreq timer immediately. However, if this was
+ * faster than the first received sync, then the servo
+ * would drop the delayResp Now, we only start the
+ * timer after we receive the first sync (in
+ * handle_sync())
+ */
+ ptpClock->syncWaiting = TRUE;
+ ptpClock->delayRespWaiting = TRUE;
+ ptpClock->announceTimeouts = 0;
+ setPortState(ptpClock, PTP_SLAVE);
+ displayStatus(ptpClock, "Now in state: ");
+ ptpClock->followUpGap = 0;
+
+#ifdef PTPD_STATISTICS
+ if(rtOpts->oFilterMSConfig.enabled) {
+ ptpClock->oFilterMS.reset(&ptpClock->oFilterMS);
+ }
+ if(rtOpts->oFilterSMConfig.enabled) {
+ ptpClock->oFilterSM.reset(&ptpClock->oFilterSM);
+ }
+ if(rtOpts->filterMSOpts.enabled) {
+ resetDoubleMovingStatFilter(ptpClock->filterMS);
+ }
+ if(rtOpts->filterSMOpts.enabled) {
+ resetDoubleMovingStatFilter(ptpClock->filterSM);
+ }
+ clearPtpEngineSlaveStats(&ptpClock->slaveStats);
+ ptpClock->servo.driftMean = 0;
+ ptpClock->servo.driftStdDev = 0;
+ ptpClock->servo.isStable = FALSE;
+ ptpClock->servo.stableCount = 0;
+ ptpClock->servo.updateCount = 0;
+ ptpClock->servo.statsCalculated = FALSE;
+ ptpClock->servo.statsUpdated = FALSE;
+ resetDoublePermanentMedian(&ptpClock->servo.driftMedianContainer);
+ resetDoublePermanentStdDev(&ptpClock->servo.driftStats);
+ timerStart(&ptpClock->timers[STATISTICS_UPDATE_TIMER], rtOpts->statsUpdateInterval);
+#endif /* PTPD_STATISTICS */
+ break;
+ default:
+ DBG("to unrecognized state\n");
+ break;
+ }
+
+ if (rtOpts->logStatistics)
+ logStatistics(ptpClock);
+}
+
+
+Boolean
+doInit(RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ char filterMask[200];
+
+ DBG("manufacturerOUI: %02hhx:%02hhx:%02hhx \n",
+ MANUFACTURER_ID_OUI0,
+ MANUFACTURER_ID_OUI1,
+ MANUFACTURER_ID_OUI2);
+ /* initialize networking */
+ netShutdown(&ptpClock->netPath);
+
+ if(rtOpts->backupIfaceEnabled &&
+ ptpClock->runningBackupInterface) {
+ rtOpts->ifaceName = rtOpts->backupIfaceName;
+ } else {
+ rtOpts->ifaceName = rtOpts->primaryIfaceName;
+ }
+
+ if (!netInit(&ptpClock->netPath, rtOpts, ptpClock)) {
+ ERROR("Failed to initialize network\n");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ return FALSE;
+ }
+
+ strncpy(filterMask,FILTER_MASK,199);
+
+ /* initialize other stuff */
+ initData(rtOpts, ptpClock);
+ initClock(rtOpts, ptpClock);
+ setupPIservo(&ptpClock->servo, rtOpts);
+ /* restore observed drift and inform user */
+ if(ptpClock->defaultDS.clockQuality.clockClass > 127)
+ restoreDrift(ptpClock, rtOpts, FALSE);
+ m1(rtOpts, ptpClock );
+ msgPackHeader(ptpClock->msgObuf, ptpClock);
+
+ toState(PTP_LISTENING, rtOpts, ptpClock);
+
+ if(rtOpts->statusLog.logEnabled)
+ writeStatusFile(ptpClock, rtOpts, TRUE);
+
+ return TRUE;
+}
+
+/* handle actions and events for 'port_state' */
+static void
+doState(RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ UInteger8 state;
+
+ ptpClock->message_activity = FALSE;
+
+ /* Process record_update (BMC algorithm) before everything else */
+ switch (ptpClock->portDS.portState)
+ {
+ case PTP_LISTENING:
+ case PTP_PASSIVE:
+ case PTP_SLAVE:
+ case PTP_MASTER:
+ /*State decision Event*/
+
+ /* If we received a valid Announce message
+ * and can use it (record_update),
+ * or we received a SET management message that
+ * changed an attribute in ptpClock,
+ * then run the BMC algorithm
+ */
+ if(ptpClock->record_update)
+ {
+ DBG2("event STATE_DECISION_EVENT\n");
+ ptpClock->record_update = FALSE;
+ state = bmc(ptpClock->foreign, rtOpts, ptpClock);
+ if(state != ptpClock->portDS.portState)
+ toState(state, rtOpts, ptpClock);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ switch (ptpClock->portDS.portState)
+ {
+ case PTP_FAULTY:
+ /* imaginary troubleshooting */
+ DBG("event FAULT_CLEARED\n");
+ toState(PTP_INITIALIZING, rtOpts, ptpClock);
+ return;
+
+ case PTP_LISTENING:
+ case PTP_UNCALIBRATED:
+ // passive mode behaves like the SLAVE state, in order to wait for the announce timeout of the current active master
+ case PTP_PASSIVE:
+ case PTP_SLAVE:
+ handle(rtOpts, ptpClock);
+
+ /*
+ * handle SLAVE timers:
+ * - No Announce message was received
+ * - Time to send new delayReq (miss of delayResp is not monitored explicitelly)
+ */
+ if (timerExpired(&ptpClock->timers[ANNOUNCE_RECEIPT_TIMER]))
+ {
+ DBG("event ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES\n");
+
+ if(!ptpClock->defaultDS.slaveOnly &&
+ ptpClock->defaultDS.clockQuality.clockClass != SLAVE_ONLY_CLOCK_CLASS) {
+ ptpClock->number_foreign_records = 0;
+ ptpClock->foreign_record_i = 0;
+ ptpClock->bestMaster = NULL;
+ m1(rtOpts,ptpClock);
+ toState(PTP_MASTER, rtOpts, ptpClock);
+
+ } else if(ptpClock->portDS.portState != PTP_LISTENING) {
+#ifdef PTPD_STATISTICS
+ /* stop statistics updates */
+ timerStop(&ptpClock->timers[STATISTICS_UPDATE_TIMER]);
+#endif /* PTPD_STATISTICS */
+
+ if(ptpClock->announceTimeouts < rtOpts->announceTimeoutGracePeriod) {
+ /*
+ * Don't reset yet - just disqualify current GM.
+ * If another live master exists, it will be selected,
+ * otherwise, timer will cycle and we will reset.
+ * Also don't clear the FMR just yet.
+ */
+ if (!ptpClock->bestMaster->disqualified) {
+ ptpClock->bestMaster->disqualified = TRUE;
+ WARNING("GM announce timeout, disqualified current best GM\n");
+ ptpClock->counters.announceTimeouts++;
+ }
+
+ if (rtOpts->ipMode != IPMODE_UNICAST && rtOpts->do_IGMP_refresh && rtOpts->transport != IEEE_802_3) {
+ /* if multicast refresh failed, restart network - helps recover after driver reloads and such */
+ if(!netRefreshIGMP(&ptpClock->netPath, rtOpts, ptpClock)) {
+ WARNING("Error while refreshing multicast - restarting transports\n");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ break;
+ }
+ }
+
+ if (rtOpts->announceTimeoutGracePeriod > 0) ptpClock->announceTimeouts++;
+
+ INFO("Waiting for new master, %d of %d attempts\n",ptpClock->announceTimeouts,rtOpts->announceTimeoutGracePeriod);
+ } else {
+ WARNING("No active masters present. Resetting port.\n");
+ ptpClock->number_foreign_records = 0;
+ ptpClock->foreign_record_i = 0;
+ ptpClock->bestMaster = NULL;
+ /* if flipping between primary and backup interface, a full nework re-init is required */
+ if(rtOpts->backupIfaceEnabled) {
+ ptpClock->runningBackupInterface = !ptpClock->runningBackupInterface;
+ toState(PTP_INITIALIZING, rtOpts, ptpClock);
+ NOTICE("Now switching to %s interface\n", ptpClock->runningBackupInterface ?
+ "backup":"primary");
+ } else {
+
+ toState(PTP_LISTENING, rtOpts, ptpClock);
+ }
+
+ }
+ } else {
+
+ /* if flipping between primary and backup interface, a full nework re-init is required */
+ if(rtOpts->backupIfaceEnabled) {
+ ptpClock->runningBackupInterface = !ptpClock->runningBackupInterface;
+ toState(PTP_INITIALIZING, rtOpts, ptpClock);
+ NOTICE("Now switching to %s interface\n", ptpClock->runningBackupInterface ?
+ "backup":"primary");
+
+ } else {
+ /*
+ * Force a reset when getting a timeout in state listening, that will lead to an IGMP reset
+ * previously this was not the case when we were already in LISTENING mode
+ */
+ toState(PTP_LISTENING, rtOpts, ptpClock);
+ }
+ }
+
+ }
+
+ /* Reset the slave if clock update timeout configured */
+ if ( ptpClock->portDS.portState == PTP_SLAVE && (rtOpts->clockUpdateTimeout > 0) &&
+ timerExpired(&ptpClock->timers[CLOCK_UPDATE_TIMER])) {
+ if(ptpClock->panicMode || rtOpts->noAdjust) {
+ timerStart(&ptpClock->timers[CLOCK_UPDATE_TIMER], rtOpts->clockUpdateTimeout);
+ } else {
+ WARNING("No offset updates in %d seconds - resetting slave\n",
+ rtOpts->clockUpdateTimeout);
+ toState(PTP_LISTENING, rtOpts, ptpClock);
+ }
+ }
+
+ if (timerExpired(&ptpClock->timers[OPERATOR_MESSAGES_TIMER])) {
+ resetWarnings(rtOpts, ptpClock);
+ }
+
+ if(ptpClock->portDS.portState==PTP_SLAVE && rtOpts->calibrationDelay && !ptpClock->isCalibrated) {
+ if(timerExpired(&ptpClock->timers[CALIBRATION_DELAY_TIMER])) {
+ ptpClock->isCalibrated = TRUE;
+ if(ptpClock->clockControl.granted) {
+ NOTICE("Offset computation now calibrated, enabled clock control\n");
+ } else {
+ NOTICE("Offset computation now calibrated\n");
+ }
+ } else if(!timerRunning(&ptpClock->timers[CALIBRATION_DELAY_TIMER])) {
+ timerStart(&ptpClock->timers[CALIBRATION_DELAY_TIMER], rtOpts->calibrationDelay);
+ }
+ }
+
+ if (ptpClock->portDS.delayMechanism == E2E) {
+ if(timerExpired(&ptpClock->timers[DELAYREQ_INTERVAL_TIMER])) {
+ DBG("event DELAYREQ_INTERVAL_TIMEOUT_EXPIRES\n");
+ /* if unicast negotiation is enabled, only request if granted */
+ if(!rtOpts->unicastNegotiation ||
+ (ptpClock->parentGrants &&
+ ptpClock->parentGrants->grantData[DELAY_RESP_INDEXED].granted)) {
+ issueDelayReq(rtOpts,ptpClock);
+ }
+ }
+ } else if (ptpClock->portDS.delayMechanism == P2P) {
+ if (timerExpired(&ptpClock->timers[PDELAYREQ_INTERVAL_TIMER])) {
+ DBGV("event PDELAYREQ_INTERVAL_TIMEOUT_EXPIRES\n");
+ /* if unicast negotiation is enabled, only request if granted */
+ if(!rtOpts->unicastNegotiation ||
+ ( ptpClock->peerGrants.grantData[PDELAY_RESP_INDEXED].granted)) {
+ issuePdelayReq(rtOpts,ptpClock);
+ }
+ }
+
+ /* FIXME: Path delay should also rearm its timer with the value received from the Master */
+ }
+
+ if (ptpClock->timePropertiesDS.leap59 || ptpClock->timePropertiesDS.leap61)
+ DBGV("seconds to midnight: %.3f\n",secondsToMidnight());
+
+ /* leap second period is over */
+ if(timerExpired(&ptpClock->timers[LEAP_SECOND_PAUSE_TIMER]) &&
+ ptpClock->leapSecondInProgress) {
+ /*
+ * do not unpause offset calculation just
+ * yet, just indicate and it will be
+ * unpaused in handleAnnounce()
+ */
+ ptpClock->leapSecondPending = FALSE;
+ timerStop(&ptpClock->timers[LEAP_SECOND_PAUSE_TIMER]);
+ }
+ /* check if leap second is near and if we should pause updates */
+ if( ptpClock->leapSecondPending &&
+ !ptpClock->leapSecondInProgress &&
+ (secondsToMidnight() <=
+ getPauseAfterMidnight(ptpClock->portDS.logAnnounceInterval,
+ rtOpts->leapSecondPausePeriod))) {
+ WARNING("Leap second event imminent - pausing "
+ "clock and offset updates\n");
+ ptpClock->leapSecondInProgress = TRUE;
+ /*
+ * start pause timer from now until [pause] after
+ * midnight, plus an extra second if inserting
+ * a leap second
+ */
+ timerStart(&ptpClock->timers[LEAP_SECOND_PAUSE_TIMER],
+ ((secondsToMidnight() +
+ (int)ptpClock->timePropertiesDS.leap61) +
+ getPauseAfterMidnight(ptpClock->portDS.logAnnounceInterval,
+ rtOpts->leapSecondPausePeriod)));
+ }
+
+/* Update PTP slave statistics from online statistics containers */
+#ifdef PTPD_STATISTICS
+ if (timerExpired(&ptpClock->timers[STATISTICS_UPDATE_TIMER])) {
+ if(!rtOpts->enablePanicMode || !ptpClock->panicMode)
+ updatePtpEngineStats(ptpClock, rtOpts);
+ }
+#endif /* PTPD_STATISTICS */
+
+ SET_ALARM(ALRM_NO_SYNC, timerExpired(&ptpClock->timers[SYNC_RECEIPT_TIMER]));
+ SET_ALARM(ALRM_NO_DELAY, timerExpired(&ptpClock->timers[DELAY_RECEIPT_TIMER]));
+
+ break;
+#ifndef PTPD_SLAVE_ONLY
+ case PTP_MASTER:
+ /*
+ * handle MASTER timers:
+ * - Time to send new Announce(s)
+ * - Time to send new PathDelay
+ * - Time to send new Sync(s) (last order - so that follow-up always follows sync
+ * in two-step mode: improves interoperability
+ * (DelayResp has no timer - as these are sent and retransmitted by the slaves)
+ */
+
+ /* master leap second triggers */
+
+ /* if we have an offset from some source, we assume it's valid */
+ if(ptpClock->clockStatus.utcOffset != 0) {
+ ptpClock->timePropertiesDS.currentUtcOffset = ptpClock->clockStatus.utcOffset;
+ ptpClock->timePropertiesDS.currentUtcOffsetValid = TRUE;
+
+ }
+
+ /* update the tpDS with clockStatus leap flags - only if running PTP timescale */
+ if(ptpClock->timePropertiesDS.ptpTimescale &&
+ (secondsToMidnight() < rtOpts->leapSecondNoticePeriod)) {
+ ptpClock->timePropertiesDS.leap59 = ptpClock->clockStatus.leapDelete;
+ ptpClock->timePropertiesDS.leap61 = ptpClock->clockStatus.leapInsert;
+ } else {
+ ptpClock->timePropertiesDS.leap59 = FALSE;
+ ptpClock->timePropertiesDS.leap61 = FALSE;
+ }
+
+ if(ptpClock->timePropertiesDS.leap59 ||
+ ptpClock->timePropertiesDS.leap61 ) {
+ if(!ptpClock->leapSecondInProgress) {
+ ptpClock->leapSecondPending = TRUE;
+ }
+ DBGV("seconds to midnight: %.3f\n",secondsToMidnight());
+ }
+
+ /* check if leap second is near and if we should pause updates */
+ if( ptpClock->leapSecondPending &&
+ !ptpClock->leapSecondInProgress &&
+ (secondsToMidnight() <=
+ getPauseAfterMidnight(ptpClock->portDS.logAnnounceInterval,
+ rtOpts->leapSecondPausePeriod))) {
+ WARNING("Leap second event imminent - pausing "
+ "event message processing\n");
+ ptpClock->leapSecondInProgress = TRUE;
+ /*
+ * start pause timer from now until [pause] after
+ * midnight, plus an extra second if inserting
+ * a leap second
+ */
+ timerStart(&ptpClock->timers[LEAP_SECOND_PAUSE_TIMER],
+ ((secondsToMidnight() +
+ (int)ptpClock->timePropertiesDS.leap61) +
+ getPauseAfterMidnight(ptpClock->portDS.logAnnounceInterval,
+ rtOpts->leapSecondPausePeriod)));
+ }
+
+ /* leap second period is over */
+ if(timerExpired(&ptpClock->timers[LEAP_SECOND_PAUSE_TIMER]) &&
+ ptpClock->leapSecondInProgress) {
+ ptpClock->leapSecondPending = FALSE;
+ timerStop(&ptpClock->timers[LEAP_SECOND_PAUSE_TIMER]);
+ ptpClock->leapSecondInProgress = FALSE;
+ NOTICE("Leap second event over - resuming "
+ "event message processing\n");
+ }
+
+
+ if (timerExpired(&ptpClock->timers[ANNOUNCE_INTERVAL_TIMER])) {
+ DBGV("event ANNOUNCE_INTERVAL_TIMEOUT_EXPIRES\n");
+ /* restart the timer with current interval in case if it changed */
+ if(pow(2,ptpClock->portDS.logAnnounceInterval) != ptpClock->timers[ANNOUNCE_INTERVAL_TIMER].interval) {
+ timerStart(&ptpClock->timers[ANNOUNCE_INTERVAL_TIMER],
+ pow(2,ptpClock->portDS.logAnnounceInterval));
+ }
+ issueAnnounce(rtOpts, ptpClock);
+
+ }
+
+ if (ptpClock->portDS.delayMechanism == P2P) {
+ if (timerExpired(&ptpClock->timers[PDELAYREQ_INTERVAL_TIMER])) {
+ DBGV("event PDELAYREQ_INTERVAL_TIMEOUT_EXPIRES\n");
+ /* if unicast negotiation is enabled, only request if granted */
+ if(!rtOpts->unicastNegotiation ||
+ ( ptpClock->peerGrants.grantData[PDELAY_RESP_INDEXED].granted)) {
+ issuePdelayReq(rtOpts,ptpClock);
+ }
+ }
+ }
+
+ if(rtOpts->do_IGMP_refresh &&
+ rtOpts->transport == UDP_IPV4 &&
+ rtOpts->ipMode != IPMODE_UNICAST &&
+ rtOpts->masterRefreshInterval > 9 &&
+ timerExpired(&ptpClock->timers[MASTER_NETREFRESH_TIMER])) {
+ DBGV("Master state periodic IGMP refresh - next in %d seconds...\n",
+ rtOpts->masterRefreshInterval);
+ /* if multicast refresh failed, restart network - helps recover after driver reloads and such */
+ if(!netRefreshIGMP(&ptpClock->netPath, rtOpts, ptpClock)) {
+ WARNING("Error while refreshing multicast - restarting transports\n");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ break;
+ }
+ }
+
+ if (timerExpired(&ptpClock->timers[SYNC_INTERVAL_TIMER])) {
+ DBGV("event SYNC_INTERVAL_TIMEOUT_EXPIRES\n");
+ /* re-arm timer if changed */
+ if(pow(2,ptpClock->portDS.logSyncInterval) != ptpClock->timers[SYNC_INTERVAL_TIMER].interval) {
+ timerStart(&ptpClock->timers[SYNC_INTERVAL_TIMER],
+ pow(2,ptpClock->portDS.logSyncInterval));
+ }
+
+ issueSync(rtOpts, ptpClock);
+ }
+ if(!ptpClock->warnedUnicastCapacity) {
+ if(ptpClock->slaveCount >= UNICAST_MAX_DESTINATIONS ||
+ ptpClock->unicastDestinationCount >= UNICAST_MAX_DESTINATIONS) {
+ if(rtOpts->ipMode == IPMODE_UNICAST) {
+ WARNING("Maximum unicast slave capacity reached: %d\n",UNICAST_MAX_DESTINATIONS);
+ ptpClock->warnedUnicastCapacity = TRUE;
+ }
+ }
+ }
+
+ if (timerExpired(&ptpClock->timers[OPERATOR_MESSAGES_TIMER])) {
+ resetWarnings(rtOpts, ptpClock);
+ }
+
+ // TODO: why is handle() below expiretimer, while in slave is the opposite
+ handle(rtOpts, ptpClock);
+
+ if(timerExpired(&ptpClock->timers[DELAY_RECEIPT_TIMER])) {
+ INFO("NO_DELAY\n");
+ }
+
+ if (ptpClock->defaultDS.slaveOnly || ptpClock->defaultDS.clockQuality.clockClass == SLAVE_ONLY_CLOCK_CLASS)
+ toState(PTP_LISTENING, rtOpts, ptpClock);
+
+ break;
+#endif /* PTPD_SLAVE_ONLY */
+
+ case PTP_DISABLED:
+ handle(rtOpts, ptpClock);
+ if(!ptpClock->disabled) {
+ toState(PTP_LISTENING, rtOpts, ptpClock);
+ }
+ break;
+
+ default:
+ DBG("(doState) do unrecognized state\n");
+ break;
+ }
+
+ if(rtOpts->periodicUpdates && timerExpired(&ptpClock->timers[PERIODIC_INFO_TIMER])) {
+ periodicUpdate(rtOpts, ptpClock);
+ }
+
+ if(rtOpts->statusLog.logEnabled && timerExpired(&ptpClock->timers[STATUSFILE_UPDATE_TIMER])) {
+ writeStatusFile(ptpClock,rtOpts,TRUE);
+ /* ensures that the current updare interval is used */
+ timerStart(&ptpClock->timers[STATUSFILE_UPDATE_TIMER],rtOpts->statusFileUpdateInterval);
+ }
+
+ if(rtOpts->enablePanicMode && timerExpired(&ptpClock->timers[PANIC_MODE_TIMER])) {
+
+ DBG("Panic check\n");
+
+ if(--ptpClock->panicModeTimeLeft <= 0) {
+ ptpClock->panicMode = FALSE;
+ ptpClock->panicOver = TRUE;
+ DBG("panic over!\n");
+ }
+ }
+
+}
+
+static Boolean
+isFromCurrentParent(const PtpClock *ptpClock, const MsgHeader* header)
+{
+
+ return(!memcmp(
+ ptpClock->parentDS.parentPortIdentity.clockIdentity,
+ header->sourcePortIdentity.clockIdentity,
+ CLOCK_IDENTITY_LENGTH) &&
+ (ptpClock->parentDS.parentPortIdentity.portNumber ==
+ header->sourcePortIdentity.portNumber));
+
+}
+
+/* apply any corrections and manglings required to the timestamp */
+static void
+timestampCorrection(const RunTimeOpts * rtOpts, PtpClock *ptpClock, TimeInternal *timeStamp)
+{
+
+ TimeInternal fudge = {0,0};
+ if(rtOpts->leapSecondHandling == LEAP_SMEAR && (ptpClock->leapSecondPending)) {
+ DBG("Leap second smear: correction %.09f ns, seconds to midnight %f, leap smear period %d\n", ptpClock->leapSmearFudge,
+ secondsToMidnight(), rtOpts->leapSecondSmearPeriod);
+ if(secondsToMidnight() <= rtOpts->leapSecondSmearPeriod) {
+ ptpClock->leapSmearFudge = 1.0 - (secondsToMidnight() + 0.0) / (rtOpts->leapSecondSmearPeriod+0.0);
+ if(ptpClock->timePropertiesDS.leap59) {
+ ptpClock->leapSmearFudge *= -1;
+ }
+ fudge = doubleToTimeInternal(ptpClock->leapSmearFudge);
+ }
+ }
+
+ if(ptpClock->portDS.portState == PTP_SLAVE && ptpClock->leapSecondPending && !ptpClock->leapSecondInProgress) {
+ addTime(timeStamp, timeStamp, &fudge);
+ }
+
+}
+
+
+void
+processMessage(RunTimeOpts* rtOpts, PtpClock* ptpClock, TimeInternal* timeStamp, ssize_t length)
+{
+
+ Boolean isFromSelf;
+
+ /*
+ * make sure we use the TAI to UTC offset specified, if the
+ * master is sending the UTC_VALID bit
+ *
+ * On the slave, all timestamps that we handle here have been
+ * collected by our local clock (loopback+kernel-level
+ * timestamp) This includes delayReq just send, and delayResp,
+ * when it arrives.
+ *
+ * these are then adjusted to the same timebase of the Master
+ * (+35 leap seconds, as of July 2012)
+ *
+ * wowczarek: added compatibility flag to always respect the
+ * announced UTC offset, preventing clock jumps with some GMs
+ */
+ DBGV("__UTC_offset: %d %d \n", ptpClock->timePropertiesDS.currentUtcOffsetValid, ptpClock->timePropertiesDS.currentUtcOffset);
+ if (respectUtcOffset(rtOpts, ptpClock) == TRUE) {
+ timeStamp->seconds += ptpClock->timePropertiesDS.currentUtcOffset;
+ }
+
+ ptpClock->message_activity = TRUE;
+ if (length < HEADER_LENGTH) {
+ DBG("Error: message shorter than header length\n");
+ ptpClock->counters.messageFormatErrors++;
+ return;
+ }
+
+ msgUnpackHeader(ptpClock->msgIbuf, &ptpClock->msgTmpHeader);
+
+ /* packet is not from self, and is from a non-zero source address - check ACLs */
+ if(ptpClock->netPath.lastSourceAddr &&
+ (ptpClock->netPath.lastSourceAddr != ptpClock->netPath.interfaceAddr.s_addr)) {
+#if defined(RUNTIME_DEBUG) || defined (PTPD_DBGV)
+ struct in_addr tmpAddr;
+ tmpAddr.s_addr = ptpClock->netPath.lastSourceAddr;
+#endif /* RUNTIME_DEBUG */
+ if(ptpClock->msgTmpHeader.messageType == MANAGEMENT) {
+ if(rtOpts->managementAclEnabled) {
+ if (!matchIpv4AccessList(
+ ptpClock->netPath.managementAcl,
+ ntohl(ptpClock->netPath.lastSourceAddr))) {
+ DBG("ACL dropped management message from %s\n", inet_ntoa(tmpAddr));
+ ptpClock->counters.aclManagementMessagesDiscarded++;
+ return;
+ } else
+ DBG("ACL Accepted management message from %s\n", inet_ntoa(tmpAddr));
+ }
+ } else if(rtOpts->timingAclEnabled) {
+ if(!matchIpv4AccessList(ptpClock->netPath.timingAcl,
+ ntohl(ptpClock->netPath.lastSourceAddr))) {
+ DBG("ACL dropped timing message from %s\n", inet_ntoa(tmpAddr));
+ ptpClock->counters.aclTimingMessagesDiscarded++;
+ return;
+ } else
+ DBG("ACL accepted timing message from %s\n", inet_ntoa(tmpAddr));
+ }
+ }
+
+ if (ptpClock->msgTmpHeader.versionPTP != ptpClock->portDS.versionNumber) {
+ DBG("ignore version %d message\n", ptpClock->msgTmpHeader.versionPTP);
+ ptpClock->counters.discardedMessages++;
+ ptpClock->counters.versionMismatchErrors++;
+ return;
+ }
+
+ if(ptpClock->msgTmpHeader.domainNumber != ptpClock->defaultDS.domainNumber) {
+ Boolean domainOK = FALSE;
+ int i = 0;
+ if (rtOpts->unicastNegotiation && ptpClock->unicastDestinationCount) {
+ for (i = 0; i < ptpClock->unicastDestinationCount; i++) {
+ if(ptpClock->msgTmpHeader.domainNumber == ptpClock->unicastGrants[i].domainNumber) {
+ domainOK = TRUE;
+ DBG("Accepted message type %s from domain %d (unicast neg)\n",
+ getMessageTypeName(ptpClock->msgTmpHeader.messageType),ptpClock->msgTmpHeader.domainNumber);
+ break;
+ }
+ }
+ }
+ if(ptpClock->defaultDS.slaveOnly && rtOpts->anyDomain) {
+ DBG("anyDomain enabled: accepting announce from domain %d (we are %d)\n",
+ ptpClock->msgTmpHeader.domainNumber,
+ ptpClock->defaultDS.domainNumber
+ );
+ } else if(!domainOK) {
+ DBG("Ignored message %s received from %d domain\n",
+ getMessageTypeName(ptpClock->msgTmpHeader.messageType),
+ ptpClock->msgTmpHeader.domainNumber);
+ ptpClock->portDS.lastMismatchedDomain = ptpClock->msgTmpHeader.domainNumber;
+ ptpClock->counters.discardedMessages++;
+ ptpClock->counters.domainMismatchErrors++;
+
+ SET_ALARM(ALRM_DOMAIN_MISMATCH, ((ptpClock->counters.domainMismatchErrors >= DOMAIN_MISMATCH_MIN) &&
+ ptpClock->netPath.receivedPacketsTotal == ptpClock->counters.domainMismatchErrors));
+ return;
+ }
+ } else {
+ SET_ALARM(ALRM_DOMAIN_MISMATCH, FALSE);
+ }
+
+
+ if(rtOpts->transport != IEEE_802_3) {
+
+ /* received a UNICAST message */
+ if((ptpClock->msgTmpHeader.flagField0 & PTP_UNICAST) == PTP_UNICAST) {
+ /* in multicast mode, accept only management unicast messages, in hybrid mode accept only unicast delay messages */
+ if((rtOpts->ipMode == IPMODE_MULTICAST && ptpClock->msgTmpHeader.messageType != MANAGEMENT) ||
+ (rtOpts->ipMode == IPMODE_HYBRID && ptpClock->msgTmpHeader.messageType != DELAY_REQ &&
+ ptpClock->msgTmpHeader.messageType != DELAY_RESP)) {
+ DBG("ignored unicast message in multicast mode%d\n");
+ ptpClock->counters.discardedMessages++;
+ return;
+ }
+ /* received a MULTICAST message */
+ } else {
+ /* in unicast mode, accept only management multicast messages */
+ if(rtOpts->ipMode == IPMODE_UNICAST && ptpClock->msgTmpHeader.messageType != MANAGEMENT) {
+ DBG("ignored multicast message in unicast mode%d\n");
+ ptpClock->counters.discardedMessages++;
+ return;
+ }
+ }
+
+ }
+
+ /* what shall we do with the drunken sailor? */
+ timestampCorrection(rtOpts, ptpClock, timeStamp);
+
+ /*Spec 9.5.2.2*/
+ isFromSelf = !cmpPortIdentity(&ptpClock->portDS.portIdentity, &ptpClock->msgTmpHeader.sourcePortIdentity);
+
+ /*
+ * subtract the inbound latency adjustment if it is not a loop
+ * back and the time stamp seems reasonable
+ */
+ if (!isFromSelf && timeStamp->seconds > 0)
+ subTime(timeStamp, timeStamp, &rtOpts->inboundLatency);
+
+ DBG(" ==> %s message received, sequence %d\n", getMessageTypeName(ptpClock->msgTmpHeader.messageType),
+ ptpClock->msgTmpHeader.sequenceId);
+
+ /*
+ * on the table below, note that only the event messsages are passed the local time,
+ * (collected by us by loopback+kernel TS, and adjusted with UTC seconds
+ *
+ * (SYNC / DELAY_REQ / PDELAY_REQ / PDELAY_RESP)
+ */
+ switch(ptpClock->msgTmpHeader.messageType)
+ {
+ case ANNOUNCE:
+ handleAnnounce(&ptpClock->msgTmpHeader,
+ length, isFromSelf, rtOpts, ptpClock);
+ break;
+ case SYNC:
+ handleSync(&ptpClock->msgTmpHeader,
+ length, timeStamp, isFromSelf, ptpClock->netPath.lastSourceAddr, ptpClock->netPath.lastDestAddr, rtOpts, ptpClock);
+ break;
+ case FOLLOW_UP:
+ handleFollowUp(&ptpClock->msgTmpHeader,
+ length, isFromSelf, rtOpts, ptpClock);
+ break;
+ case DELAY_REQ:
+ handleDelayReq(&ptpClock->msgTmpHeader,
+ length, timeStamp, ptpClock->netPath.lastSourceAddr, isFromSelf, rtOpts, ptpClock);
+ break;
+ case PDELAY_REQ:
+ handlePdelayReq(&ptpClock->msgTmpHeader,
+ length, timeStamp, ptpClock->netPath.lastSourceAddr, isFromSelf, rtOpts, ptpClock);
+ break;
+ case DELAY_RESP:
+ handleDelayResp(&ptpClock->msgTmpHeader,
+ length, rtOpts, ptpClock);
+ break;
+ case PDELAY_RESP:
+ handlePdelayResp(&ptpClock->msgTmpHeader,
+ timeStamp, length, isFromSelf, ptpClock->netPath.lastSourceAddr, ptpClock->netPath.lastDestAddr, rtOpts, ptpClock);
+ break;
+ case PDELAY_RESP_FOLLOW_UP:
+ handlePdelayRespFollowUp(&ptpClock->msgTmpHeader,
+ length, isFromSelf, rtOpts, ptpClock);
+ break;
+ case MANAGEMENT:
+ handleManagement(&ptpClock->msgTmpHeader,
+ isFromSelf, ptpClock->netPath.lastSourceAddr, rtOpts, ptpClock);
+ break;
+ case SIGNALING:
+ handleSignaling(&ptpClock->msgTmpHeader, isFromSelf,
+ ptpClock->netPath.lastSourceAddr, rtOpts, ptpClock);
+ break;
+ default:
+ DBG("handle: unrecognized message\n");
+ ptpClock->counters.discardedMessages++;
+ ptpClock->counters.unknownMessages++;
+ break;
+ }
+
+ if (rtOpts->displayPackets)
+ msgDump(ptpClock);
+
+
+}
+
+
+/* check and handle received messages */
+void
+handle(RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ int ret;
+ ssize_t length = -1;
+
+ TimeInternal timeStamp = { 0, 0 };
+ fd_set readfds;
+
+ FD_ZERO(&readfds);
+ if (!ptpClock->message_activity) {
+ ret = netSelect(NULL, &ptpClock->netPath, &readfds);
+ if (ret < 0) {
+ PERROR("failed to poll sockets");
+ ptpClock->counters.messageRecvErrors++;
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ return;
+ } else if (!ret) {
+ /* DBGV("handle: nothing\n"); */
+ return;
+ }
+ /* else length > 0 */
+ }
+
+ DBG("handle: something\n");
+
+#ifdef PTPD_PCAP
+ if (rtOpts->pcap == TRUE) {
+ if (ptpClock->netPath.pcapEventSock >=0 && FD_ISSET(ptpClock->netPath.pcapEventSock, &readfds)) {
+ length = netRecvEvent(ptpClock->msgIbuf, &timeStamp,
+ &ptpClock->netPath,0);
+ if (length == 0){ /* timeout, return for now */
+ return;
+ }
+
+
+ if (length < 0) {
+ PERROR("failed to receive event on pcap");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ ptpClock->counters.messageRecvErrors++;
+
+ return;
+ }
+ if(ptpClock->leapSecondInProgress) {
+ DBG("Leap second in progress - will not process event message\n");
+ } else {
+
+ processMessage(rtOpts, ptpClock, &timeStamp, length);
+ }
+ }
+ if (ptpClock->netPath.pcapGeneralSock >=0 && FD_ISSET(ptpClock->netPath.pcapGeneralSock, &readfds)) {
+ length = netRecvGeneral(ptpClock->msgIbuf, &ptpClock->netPath);
+ if (length == 0) /* timeout, return for now */
+ return;
+ if (length < 0) {
+ PERROR("failed to receive general on pcap");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ ptpClock->counters.messageRecvErrors++;
+ return;
+ }
+ processMessage(rtOpts, ptpClock, &timeStamp, length);
+ }
+ } else {
+#endif
+ if (FD_ISSET(ptpClock->netPath.eventSock, &readfds)) {
+ length = netRecvEvent(ptpClock->msgIbuf, &timeStamp,
+ &ptpClock->netPath, 0);
+ if (length < 0) {
+ PERROR("failed to receive on the event socket");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ ptpClock->counters.messageRecvErrors++;
+ return;
+ }
+ if(ptpClock->leapSecondInProgress) {
+ DBG("Leap second in progress - will not process event message\n");
+ } else {
+ processMessage(rtOpts, ptpClock, &timeStamp, length);
+ }
+ }
+
+ if (FD_ISSET(ptpClock->netPath.generalSock, &readfds)) {
+ length = netRecvGeneral(ptpClock->msgIbuf, &ptpClock->netPath);
+ if (length < 0) {
+ PERROR("failed to receive on the general socket");
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ ptpClock->counters.messageRecvErrors++;
+ return;
+ }
+ processMessage(rtOpts, ptpClock, &timeStamp, length);
+ }
+#ifdef PTPD_PCAP
+ }
+#endif
+
+}
+
+/*spec 9.5.3*/
+static void
+handleAnnounce(MsgHeader *header, ssize_t length,
+ Boolean isFromSelf, const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+
+ UnicastGrantTable *nodeTable = NULL;
+ UInteger8 localPreference = LOWEST_LOCALPREFERENCE;
+
+ DBGV("HandleAnnounce : Announce message received : \n");
+
+ if(length < ANNOUNCE_LENGTH) {
+ DBG("Error: Announce message too short\n");
+ ptpClock->counters.messageFormatErrors++;
+ return;
+ }
+
+ /* if we're ignoring announces (telecom) */
+ if(ptpClock->defaultDS.clockQuality.clockClass <= 127 && rtOpts->disableBMCA) {
+ DBG("unicast master only and disableBMCA: dropping Announce\n");
+ ptpClock->counters.discardedMessages++;
+ return;
+ }
+
+ if(rtOpts->unicastNegotiation && rtOpts->ipMode == IPMODE_UNICAST) {
+
+ nodeTable = findUnicastGrants(&header->sourcePortIdentity, 0,
+ ptpClock->unicastGrants, &ptpClock->grantIndex, UNICAST_MAX_DESTINATIONS,
+ FALSE);
+ if(nodeTable == NULL || !(nodeTable->grantData[ANNOUNCE_INDEXED].granted)) {
+ if(!rtOpts->unicastAcceptAny) {
+ DBG("Ignoring announce from master: unicast transmission not granted\n");
+ ptpClock->counters.discardedMessages++;
+ return;
+ }
+ } else {
+ localPreference = nodeTable->localPreference;
+ nodeTable->grantData[ANNOUNCE_INDEXED].receiving = header->sequenceId;
+ }
+ }
+
+ switch (ptpClock->portDS.portState) {
+ case PTP_INITIALIZING:
+ case PTP_FAULTY:
+ case PTP_DISABLED:
+ DBG("Handleannounce : disregard\n");
+ ptpClock->counters.discardedMessages++;
+ return;
+
+ case PTP_UNCALIBRATED:
+ case PTP_SLAVE:
+ if (isFromSelf) {
+ DBGV("HandleAnnounce : Ignore message from self \n");
+ return;
+ }
+ if(rtOpts->requireUtcValid && !IS_SET(header->flagField1, UTCV)) {
+ ptpClock->counters.ignoredAnnounce++;
+ return;
+ }
+
+ /*
+ * Valid announce message is received : BMC algorithm
+ * will be executed
+ */
+ ptpClock->counters.announceMessagesReceived++;
+ ptpClock->record_update = TRUE;
+
+ switch (isFromCurrentParent(ptpClock, header)) {
+ case TRUE:
+ msgUnpackAnnounce(ptpClock->msgIbuf,
+ &ptpClock->msgTmp.announce);
+
+ /* update datasets (file bmc.c) */
+ s1(header,&ptpClock->msgTmp.announce,ptpClock, rtOpts);
+
+ /* update current master in the fmr as well */
+ memcpy(&ptpClock->bestMaster->header,
+ header,sizeof(MsgHeader));
+ memcpy(&ptpClock->bestMaster->announce,
+ &ptpClock->msgTmp.announce,sizeof(MsgAnnounce));
+
+ if(ptpClock->leapSecondInProgress) {
+ /*
+ * if leap second period is over
+ * (pending == FALSE, inProgress ==
+ * TRUE), unpause offset calculation
+ * (received first announce after leap
+ * second) - make sure the upstream knows about this.
+ */
+ if (!ptpClock->leapSecondPending) {
+ WARNING("Leap second event over - "
+ "resuming clock and offset updates\n");
+ ptpClock->leapSecondInProgress = FALSE;
+ if(rtOpts->leapSecondHandling==LEAP_STEP) {
+ ptpClock->clockControl.stepRequired = TRUE;
+ }
+ /* reverse frequency shift */
+ if(rtOpts->leapSecondHandling==LEAP_SMEAR){
+ /* the flags are probably off by now, using the shift sign to detect leap second type */
+ if(ptpClock->leapSmearFudge < 0) {
+ DBG("Reversed LEAP59 smear frequency offset\n");
+ ptpClock->servo.observedDrift += 1E9 / rtOpts->leapSecondSmearPeriod;
+ }
+ if(ptpClock->leapSmearFudge > 0) {
+ DBG("Reversed LEAP61 smear frequency offset\n");
+ ptpClock->servo.observedDrift -= 1E9 / rtOpts->leapSecondSmearPeriod;
+ }
+ ptpClock->leapSmearFudge = 0;
+ }
+ ptpClock->timePropertiesDS.leap59 = FALSE;
+ ptpClock->timePropertiesDS.leap61 = FALSE;
+ ptpClock->clockStatus.leapInsert = FALSE;
+ ptpClock->clockStatus.leapDelete = FALSE;
+ ptpClock->clockStatus.update = TRUE;
+
+ }
+ }
+
+ DBG2("___ Announce: received Announce from current Master, so reset the Announce timer\n");
+
+ /* Reset Timer handling Announce receipt timeout - in anyDomain, time out from current domain first */
+ if(header->domainNumber == ptpClock->defaultDS.domainNumber) {
+ timerStart(&ptpClock->timers[ANNOUNCE_RECEIPT_TIMER],
+ (ptpClock->portDS.announceReceiptTimeout) *
+ (pow(2,ptpClock->portDS.logAnnounceInterval)));
+ }
+#ifdef PTPD_STATISTICS
+ if(!timerRunning(&ptpClock->timers[STATISTICS_UPDATE_TIMER])) {
+ timerStart(&ptpClock->timers[STATISTICS_UPDATE_TIMER], rtOpts->statsUpdateInterval);
+ }
+#endif /* PTPD_STATISTICS */
+
+ if (rtOpts->announceTimeoutGracePeriod &&
+ ptpClock->announceTimeouts > 0) {
+ NOTICE("Received Announce message from current master - cancelling timeout\n");
+ ptpClock->announceTimeouts = 0;
+ /* we are available for clock control again */
+ ptpClock->clockControl.available = TRUE;
+ }
+
+ break;
+
+ case FALSE:
+ /* addForeign takes care of AnnounceUnpacking */
+
+ /* the actual decision to change masters is
+ * only done in doState() / record_update ==
+ * TRUE / bmc()
+ */
+
+ /*
+ * wowczarek: do not restart timer here:
+ * the slave will sit idle if current parent
+ * is not announcing, but another GM is
+ */
+ addForeign(ptpClock->msgIbuf,header,ptpClock,localPreference,ptpClock->netPath.lastSourceAddr);
+ break;
+
+ default:
+ DBG("HandleAnnounce : (isFromCurrentParent)"
+ "strange value ! \n");
+ return;
+
+ } /* switch on (isFromCurrentParent) */
+ break;
+
+ /*
+ * Passive case: previously, this was handled in the default, just like the master case.
+ * This the announce would call addForeign(), but NOT reset the timer, so after 12s it would expire and we would come alive periodically
+ *
+ * This code is now merged with the slave case to reset the timer, and call addForeign() if it's a third master
+ *
+ */
+ case PTP_PASSIVE:
+ if (isFromSelf) {
+ DBGV("HandleAnnounce : Ignore message from self \n");
+ return;
+ }
+ if(rtOpts->requireUtcValid && !IS_SET(header->flagField1, UTCV)) {
+ ptpClock->counters.ignoredAnnounce++;
+ return;
+ }
+ /*
+ * Valid announce message is received : BMC algorithm
+ * will be executed
+ */
+ ptpClock->counters.announceMessagesReceived++;
+ ptpClock->record_update = TRUE;
+
+ if (isFromCurrentParent(ptpClock, header)) {
+ msgUnpackAnnounce(ptpClock->msgIbuf,
+ &ptpClock->msgTmp.announce);
+
+ /* TODO: not in spec
+ * datasets should not be updated by another master
+ * this is the reason why we are PASSIVE and not SLAVE
+ * this should be p1(ptpClock, rtOpts);
+ */
+ /* update datasets (file bmc.c) */
+ s1(header,&ptpClock->msgTmp.announce,ptpClock, rtOpts);
+
+ DBG("___ Announce: received Announce from current Master, so reset the Announce timer\n\n");
+
+ /* Reset Timer handling Announce receipt timeout: different domain my get here
+ when using anyDomain.*/
+ timerStart(&ptpClock->timers[ANNOUNCE_RECEIPT_TIMER],
+ (ptpClock->portDS.announceReceiptTimeout) *
+ (pow(2,ptpClock->portDS.logAnnounceInterval)));
+
+ } else {
+ /*addForeign takes care of AnnounceUnpacking*/
+ /* the actual decision to change masters is only done in doState() / record_update == TRUE / bmc() */
+ /* the original code always called: addforeign(new master) + timerstart(announce) */
+
+ DBG("___ Announce: received Announce from another master, will add to the list, as it might be better\n\n");
+ DBGV("this is to be decided immediatly by bmc())\n\n");
+ addForeign(ptpClock->msgIbuf,header,ptpClock,localPreference,ptpClock->netPath.lastSourceAddr);
+ }
+ break;
+
+
+ case PTP_MASTER:
+ case PTP_LISTENING: /* listening mode still causes timeouts in order to send IGMP refreshes */
+ default :
+ if (isFromSelf) {
+ DBGV("HandleAnnounce : Ignore message from self \n");
+ return;
+ }
+ if(rtOpts->requireUtcValid && !IS_SET(header->flagField1, UTCV)) {
+ ptpClock->counters.ignoredAnnounce++;
+ return;
+ }
+ ptpClock->counters.announceMessagesReceived++;
+ DBGV("Announce message from another foreign master\n");
+ addForeign(ptpClock->msgIbuf,header,ptpClock, localPreference,ptpClock->netPath.lastSourceAddr);
+ ptpClock->record_update = TRUE; /* run BMC() as soon as possible */
+ break;
+
+ } /* switch on (port_state) */
+}
+
+
+static void
+handleSync(const MsgHeader *header, ssize_t length,
+ TimeInternal *tint, Boolean isFromSelf, Integer32 sourceAddress, Integer32 destinationAddress,
+ const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+
+ TimeInternal OriginTimestamp;
+ TimeInternal correctionField;
+
+ Integer32 dst = 0;
+
+ if((rtOpts->ipMode == IPMODE_UNICAST) && destinationAddress) {
+ dst = destinationAddress;
+ } else {
+ dst = 0;
+ }
+
+ DBGV("Sync message received : \n");
+
+ if (length < SYNC_LENGTH) {
+ DBG("Error: Sync message too short\n");
+ ptpClock->counters.messageFormatErrors++;
+ return;
+ }
+
+ if(!isFromSelf && rtOpts->unicastNegotiation && rtOpts->ipMode == IPMODE_UNICAST) {
+ UnicastGrantTable *nodeTable = NULL;
+ nodeTable = findUnicastGrants(&header->sourcePortIdentity, 0,
+ ptpClock->unicastGrants, &ptpClock->grantIndex, UNICAST_MAX_DESTINATIONS,
+ FALSE);
+ if(nodeTable != NULL) {
+ nodeTable->grantData[SYNC_INDEXED].receiving = header->sequenceId;
+ }
+ }
+
+ switch (ptpClock->portDS.portState) {
+ case PTP_INITIALIZING:
+ case PTP_FAULTY:
+ case PTP_DISABLED:
+ case PTP_PASSIVE:
+ case PTP_LISTENING:
+ DBGV("HandleSync : disregard\n");
+ ptpClock->counters.discardedMessages++;
+ return;
+
+ case PTP_UNCALIBRATED:
+ case PTP_SLAVE:
+ if (isFromSelf) {
+ DBGV("HandleSync: Ignore message from self \n");
+ return;
+ }
+
+ if (isFromCurrentParent(ptpClock, header)) {
+ ptpClock->counters.syncMessagesReceived++;
+ timerStart(&ptpClock->timers[SYNC_RECEIPT_TIMER], max(
+ (ptpClock->portDS.announceReceiptTimeout) * (pow(2,ptpClock->portDS.logAnnounceInterval)),
+ MISSED_MESSAGES_MAX * (pow(2,ptpClock->portDS.logSyncInterval))
+ ));
+
+ /* We only start our own delayReq timer after receiving the first sync */
+ if (ptpClock->syncWaiting) {
+ ptpClock->syncWaiting = FALSE;
+
+ NOTICE("Received first Sync from Master\n");
+
+ if (ptpClock->portDS.delayMechanism == E2E)
+ timerStart(&ptpClock->timers[DELAYREQ_INTERVAL_TIMER],
+ pow(2,ptpClock->portDS.logMinDelayReqInterval));
+ else if (ptpClock->portDS.delayMechanism == P2P)
+ timerStart(&ptpClock->timers[PDELAYREQ_INTERVAL_TIMER],
+ pow(2,ptpClock->portDS.logMinPdelayReqInterval));
+ } else if ( rtOpts->syncSequenceChecking ) {
+ if( (ptpClock->counters.consecutiveSequenceErrors < MAX_SEQ_ERRORS) &&
+ ((((UInteger16)(ptpClock->recvSyncSequenceId + 32768)) >
+ (header->sequenceId + 32767)) ||
+ (((UInteger16)(ptpClock->recvSyncSequenceId + 1)) >
+ (header->sequenceId))) ) {
+ ptpClock->counters.discardedMessages++;
+ ptpClock->counters.sequenceMismatchErrors++;
+ ptpClock->counters.consecutiveSequenceErrors++;
+ DBG("HandleSync : sequence mismatch - "
+ "received: %d, expected %d or greater, consecutive errors %d\n",
+ header->sequenceId,
+ (UInteger16)(ptpClock->recvSyncSequenceId + 1),
+ ptpClock->counters.consecutiveSequenceErrors
+ );
+ break;
+ } else {
+ if(ptpClock->counters.consecutiveSequenceErrors >= MAX_SEQ_ERRORS) {
+ DBG("HandleSync: rejected %d out of order sequences, accepting current sync\n",
+ MAX_SEQ_ERRORS);
+ }
+ ptpClock->counters.consecutiveSequenceErrors = 0;
+ }
+ }
+
+ ptpClock->portDS.logSyncInterval = header->logMessageInterval;
+
+ /* this will be 0x7F for unicast so if we have a grant, use the granted value */
+ if(rtOpts->unicastNegotiation && ptpClock->parentGrants
+ && ptpClock->parentGrants->grantData[SYNC_INDEXED].granted) {
+ ptpClock->portDS.logSyncInterval = ptpClock->parentGrants->grantData[SYNC_INDEXED].logInterval;
+ }
+
+
+ recordSync(header->sequenceId, tint);
+
+ if ((header->flagField0 & PTP_TWO_STEP) == PTP_TWO_STEP) {
+ DBG2("HandleSync: waiting for follow-up \n");
+ ptpClock->defaultDS.twoStepFlag=TRUE;
+ if(ptpClock->waitingForFollow) {
+ ptpClock->followUpGap++;
+ if(ptpClock->followUpGap < MAX_FOLLOWUP_GAP) {
+ DBG("Received Sync while waiting for follow-up - "
+ "will wait for another %d messages\n",
+ MAX_FOLLOWUP_GAP - ptpClock->followUpGap);
+ break;
+ }
+ DBG("No follow-up for %d sync - waiting for new follow-up\n",
+ ptpClock->followUpGap);
+ }
+
+ ptpClock->sync_receive_time.seconds = tint->seconds;
+ ptpClock->sync_receive_time.nanoseconds = tint->nanoseconds;
+
+ ptpClock->waitingForFollow = TRUE;
+ /*Save correctionField of Sync message*/
+ integer64_to_internalTime(
+ header->correctionField,
+ &correctionField);
+ ptpClock->lastSyncCorrectionField.seconds =
+ correctionField.seconds;
+ ptpClock->lastSyncCorrectionField.nanoseconds =
+ correctionField.nanoseconds;
+ ptpClock->recvSyncSequenceId =
+ header->sequenceId;
+ break;
+ } else {
+
+ ptpClock->sync_receive_time.seconds = tint->seconds;
+ ptpClock->sync_receive_time.nanoseconds = tint->nanoseconds;
+
+ ptpClock->recvSyncSequenceId =
+ header->sequenceId;
+ msgUnpackSync(ptpClock->msgIbuf,
+ &ptpClock->msgTmp.sync);
+ integer64_to_internalTime(
+ ptpClock->msgTmpHeader.correctionField,
+ &correctionField);
+ timeInternal_display(&correctionField);
+ ptpClock->waitingForFollow = FALSE;
+ toInternalTime(&OriginTimestamp,
+ &ptpClock->msgTmp.sync.originTimestamp);
+ updateOffset(&OriginTimestamp,
+ &ptpClock->sync_receive_time,
+ &ptpClock->ofm_filt,rtOpts,
+ ptpClock,&correctionField);
+ checkOffset(rtOpts,ptpClock);
+ if (ptpClock->clockControl.updateOK) {
+ ptpClock->acceptedUpdates++;
+ updateClock(rtOpts,ptpClock);
+ }
+ ptpClock->offsetUpdates++;
+
+ ptpClock->defaultDS.twoStepFlag=FALSE;
+ break;
+ }
+ } else {
+ DBG("HandleSync: Sync message received from "
+ "another Master not our own \n");
+ ptpClock->counters.discardedMessages++;
+ }
+ break;
+
+ case PTP_MASTER:
+ default :
+ if (!isFromSelf) {
+ DBGV("HandleSync: Sync message received from "
+ "another Master \n");
+ /* we are the master, but another is sending */
+ ptpClock->counters.discardedMessages++;
+ break;
+ } if (ptpClock->defaultDS.twoStepFlag) {
+ DBGV("HandleSync: going to send followup message\n");
+
+ /* who do we send the followUp to? no destination given - try looking up index */
+ if((rtOpts->ipMode == IPMODE_UNICAST) && !dst) {
+ msgUnpackSync(ptpClock->msgIbuf,
+ &ptpClock->msgTmp.sync);
+ toInternalTime(&OriginTimestamp, &ptpClock->msgTmp.sync.originTimestamp);
+ dst = lookupSyncIndex(&OriginTimestamp, header->sequenceId, ptpClock->syncDestIndex);
+
+#ifdef RUNTIME_DEBUG
+ {
+ struct in_addr tmpAddr;
+ tmpAddr.s_addr = dst;
+ DBG("handleSync: master sync dest cache hit: %s\n", inet_ntoa(tmpAddr));
+ }
+#endif /* RUNTIME_DEBUG */
+
+ if(!dst) {
+ DBG("handleSync: master sync dest cache miss - searching\n");
+ dst = findSyncDestination(&OriginTimestamp, rtOpts, ptpClock);
+ /* give up. Better than sending FollowUp to random destinations*/
+ if(!dst) {
+ DBG("handleSync: master sync dest not found for followUp. Giving up.\n");
+ return;
+ } else {
+ DBG("unicast destination found.\n");
+ }
+ }
+
+ }
+
+#ifndef PTPD_SLAVE_ONLY /* does not get compiled when building slave only */
+ processSyncFromSelf(tint, rtOpts, ptpClock, dst, header->sequenceId);
+#endif /* PTPD_SLAVE_ONLY */
+ break;
+ } else {
+ DBGV("HandleSync: Sync message received from self\n");
+ }
+ }
+}
+
+#ifndef PTPD_SLAVE_ONLY /* does not get compiled when building slave only */
+static void
+processSyncFromSelf(const TimeInternal * tint, const RunTimeOpts * rtOpts, PtpClock * ptpClock, Integer32 dst, const UInteger16 sequenceId) {
+ TimeInternal timestamp;
+ /*Add latency*/
+ addTime(×tamp, tint, &rtOpts->outboundLatency);
+ /* Issue follow-up CORRESPONDING TO THIS SYNC */
+ issueFollowup(×tamp, rtOpts, ptpClock, dst, sequenceId);
+}
+#endif /* PTPD_SLAVE_ONLY */
+
+static void
+handleFollowUp(const MsgHeader *header, ssize_t length,
+ Boolean isFromSelf, const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ TimeInternal preciseOriginTimestamp;
+ TimeInternal correctionField;
+
+ DBGV("Handlefollowup : Follow up message received \n");
+
+ if (length < FOLLOW_UP_LENGTH)
+ {
+ DBG("Error: Follow up message too short\n");
+ ptpClock->counters.messageFormatErrors++;
+ return;
+ }
+
+ if (isFromSelf)
+ {
+ DBGV("Handlefollowup : Ignore message from self \n");
+ return;
+ }
+
+ switch (ptpClock->portDS.portState)
+ {
+ case PTP_INITIALIZING:
+ case PTP_FAULTY:
+ case PTP_DISABLED:
+ case PTP_LISTENING:
+ DBGV("Handfollowup : disregard\n");
+ ptpClock->counters.discardedMessages++;
+ return;
+
+ case PTP_UNCALIBRATED:
+ case PTP_SLAVE:
+ if (isFromCurrentParent(ptpClock, header)) {
+ ptpClock->counters.followUpMessagesReceived++;
+ ptpClock->portDS.logSyncInterval = header->logMessageInterval;
+ /* this will be 0x7F for unicast so if we have a grant, use the granted value */
+ if(rtOpts->unicastNegotiation && ptpClock->parentGrants
+ && ptpClock->parentGrants->grantData[SYNC_INDEXED].granted) {
+ ptpClock->portDS.logSyncInterval = ptpClock->parentGrants->grantData[SYNC_INDEXED].logInterval;
+ }
+
+ if (ptpClock->waitingForFollow) {
+ if (ptpClock->recvSyncSequenceId ==
+ header->sequenceId) {
+ ptpClock->followUpGap = 0;
+ msgUnpackFollowUp(ptpClock->msgIbuf,
+ &ptpClock->msgTmp.follow);
+ ptpClock->waitingForFollow = FALSE;
+ toInternalTime(&preciseOriginTimestamp,
+ &ptpClock->msgTmp.follow.preciseOriginTimestamp);
+ integer64_to_internalTime(ptpClock->msgTmpHeader.correctionField,
+ &correctionField);
+ addTime(&correctionField,&correctionField,
+ &ptpClock->lastSyncCorrectionField);
+
+ /*
+ send_time = preciseOriginTimestamp (received inside followup)
+ recv_time = sync_receive_time (received as CMSG in handleEvent)
+ */
+ updateOffset(&preciseOriginTimestamp,
+ &ptpClock->sync_receive_time,&ptpClock->ofm_filt,
+ rtOpts,ptpClock,
+ &correctionField);
+ checkOffset(rtOpts,ptpClock);
+ if (ptpClock->clockControl.updateOK) {
+ ptpClock->acceptedUpdates++;
+ updateClock(rtOpts,ptpClock);
+ }
+ ptpClock->offsetUpdates++;
+
+ break;
+ } else {
+ DBG("HandleFollowUp : sequence mismatch - "
+ "last Sync: %d, this FollowUp: %d\n",
+ ptpClock->recvSyncSequenceId,
+ header->sequenceId);
+ ptpClock->counters.sequenceMismatchErrors++;
+ ptpClock->counters.discardedMessages++;
+ }
+ } else {
+ DBG2("Ignored followup, Slave was not waiting a follow up "
+ "message \n");
+ ptpClock->counters.discardedMessages++;
+ }
+ } else {
+ DBG2("Ignored, Follow up message is not from current parent \n");
+ ptpClock->counters.discardedMessages++;
+ }
+
+ case PTP_MASTER:
+ case PTP_PASSIVE:
+ if(isFromCurrentParent(ptpClock, header))
+ DBGV("Ignored, Follow up message received from current master \n");
+ else {
+ /* follow-ups and syncs are expected to be seen from parent, but not from others */
+ DBGV("Follow up message received from foreign master!\n");
+ ptpClock->counters.discardedMessages++;
+ }
+ break;
+
+ default:
+ DBG("do unrecognized state1\n");
+ ptpClock->counters.discardedMessages++;
+ break;
+ } /* Switch on (port_state) */
+
+}
+
+void
+handleDelayReq(const MsgHeader *header, ssize_t length,
+ const TimeInternal *tint, Integer32 sourceAddress, Boolean isFromSelf,
+ const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+
+ UnicastGrantTable *nodeTable = NULL;
+
+ if (ptpClock->portDS.delayMechanism == E2E) {
+
+ if(!isFromSelf && rtOpts->unicastNegotiation && rtOpts->ipMode == IPMODE_UNICAST) {
+ nodeTable = findUnicastGrants(&header->sourcePortIdentity, 0,
+ ptpClock->unicastGrants, &ptpClock->grantIndex, UNICAST_MAX_DESTINATIONS,
+ FALSE);
+ if(nodeTable == NULL || !(nodeTable->grantData[DELAY_RESP_INDEXED].granted)) {
+ DBG("Ignoring Delay Request from slave: unicast transmission not granted\n");
+ ptpClock->counters.discardedMessages++;
+ return;
+ } else {
+ nodeTable->grantData[DELAY_REQ].receiving = header->sequenceId;
+ }
+
+ }
+
+
+ DBG("delayReq message received : \n");
+
+ if (length < DELAY_REQ_LENGTH) {
+ DBG("Error: DelayReq message too short\n");
+ ptpClock->counters.messageFormatErrors++;
+ return;
+ }
+
+ switch (ptpClock->portDS.portState) {
+ case PTP_INITIALIZING:
+ case PTP_FAULTY:
+ case PTP_DISABLED:
+ case PTP_UNCALIBRATED:
+ case PTP_LISTENING:
+ case PTP_PASSIVE:
+ DBGV("HandledelayReq : disregard\n");
+ ptpClock->counters.discardedMessages++;
+ return;
+
+ case PTP_SLAVE:
+ if (isFromSelf) {
+ DBG("==> Handle DelayReq (%d)\n",
+ header->sequenceId);
+ if ( ((UInteger16)(header->sequenceId + 1)) !=
+ ptpClock->sentDelayReqSequenceId) {
+ DBG("HandledelayReq : sequence mismatch - "
+ "last DelayReq sent: %d, received: %d\n",
+ ptpClock->sentDelayReqSequenceId,
+ header->sequenceId
+ );
+ ptpClock->counters.discardedMessages++;
+ ptpClock->counters.sequenceMismatchErrors++;
+ break;
+ }
+
+ /*
+ * Get sending timestamp from IP stack
+ * with SO_TIMESTAMP
+ */
+
+ /*
+ * Make sure we process the REQ
+ * _before_ the RESP. While we could
+ * do this by any order, (because
+ * it's implicitly indexed by
+ * (ptpClock->sentDelayReqSequenceId
+ * - 1), this is now made explicit
+ */
+ processDelayReqFromSelf(tint, rtOpts, ptpClock);
+
+ break;
+ } else {
+ DBG2("HandledelayReq : disregard delayreq from other client\n");
+ ptpClock->counters.discardedMessages++;
+ }
+ break;
+
+ case PTP_MASTER:
+ msgUnpackHeader(ptpClock->msgIbuf,
+ &ptpClock->delayReqHeader);
+ ptpClock->counters.delayReqMessagesReceived++;
+
+ issueDelayResp(tint,&ptpClock->delayReqHeader, sourceAddress,
+ rtOpts,ptpClock);
+ break;
+
+ default:
+ DBG("do unrecognized state2\n");
+ ptpClock->counters.discardedMessages++;
+ break;
+ }
+ } else if (ptpClock->portDS.delayMechanism == P2P) {/* (Peer to Peer mode) */
+ DBG("Delay messages are ignored in Peer to Peer mode\n");
+ ptpClock->counters.discardedMessages++;
+ ptpClock->counters.delayMechanismMismatchErrors++;
+ /* no delay mechanism */
+ } else {
+ DBG("DelayReq ignored - we are in DELAY_DISABLED mode");
+ ptpClock->counters.discardedMessages++;
+ }
+}
+
+
+static void
+processDelayReqFromSelf(const TimeInternal * tint, const RunTimeOpts * rtOpts, PtpClock * ptpClock) {
+
+
+ ptpClock->waitingForDelayResp = TRUE;
+
+ ptpClock->delay_req_send_time.seconds = tint->seconds;
+ ptpClock->delay_req_send_time.nanoseconds = tint->nanoseconds;
+
+ /*Add latency*/
+ addTime(&ptpClock->delay_req_send_time,
+ &ptpClock->delay_req_send_time,
+ &rtOpts->outboundLatency);
+
+ DBGV("processDelayReqFromSelf: %s %d\n",
+ dump_TimeInternal(&ptpClock->delay_req_send_time),
+ rtOpts->outboundLatency);
+
+}
+
+static void
+handleDelayResp(const MsgHeader *header, ssize_t length,
+ const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+
+ if (ptpClock->portDS.delayMechanism == E2E) {
+
+
+ TimeInternal requestReceiptTimestamp;
+ TimeInternal correctionField;
+
+ if(rtOpts->unicastNegotiation && rtOpts->ipMode == IPMODE_UNICAST) {
+ UnicastGrantTable *nodeTable = NULL;
+ nodeTable = findUnicastGrants(&header->sourcePortIdentity, 0,
+ ptpClock->unicastGrants, &ptpClock->grantIndex, UNICAST_MAX_DESTINATIONS,
+ FALSE);
+ if(nodeTable != NULL) {
+ nodeTable->grantData[DELAY_RESP_INDEXED].receiving = header->sequenceId;
+ } else {
+ DBG("delayResp - unicast master not identified\n");
+ }
+ }
+
+ DBGV("delayResp message received : \n");
+
+ if(length < DELAY_RESP_LENGTH) {
+ DBG("Error: DelayResp message too short\n");
+ ptpClock->counters.messageFormatErrors++;
+ return;
+ }
+
+ switch(ptpClock->portDS.portState) {
+ case PTP_INITIALIZING:
+ case PTP_FAULTY:
+ case PTP_DISABLED:
+ case PTP_UNCALIBRATED:
+ case PTP_LISTENING:
+ DBGV("HandledelayResp : disregard\n");
+ ptpClock->counters.discardedMessages++;
+ return;
+ case PTP_SLAVE:
+ msgUnpackDelayResp(ptpClock->msgIbuf,
+ &ptpClock->msgTmp.resp);
+
+ if ((memcmp(ptpClock->portDS.portIdentity.clockIdentity,
+ ptpClock->msgTmp.resp.requestingPortIdentity.clockIdentity,
+ CLOCK_IDENTITY_LENGTH) == 0) &&
+ (ptpClock->portDS.portIdentity.portNumber ==
+ ptpClock->msgTmp.resp.requestingPortIdentity.portNumber)
+ && isFromCurrentParent(ptpClock, header)) {
+ DBG("==> Handle DelayResp (%d)\n",
+ header->sequenceId);
+
+ timerStart(&ptpClock->timers[DELAY_RECEIPT_TIMER], max(
+ (ptpClock->portDS.announceReceiptTimeout) * (pow(2,ptpClock->portDS.logAnnounceInterval)),
+ MISSED_MESSAGES_MAX * (pow(2,ptpClock->portDS.logMinDelayReqInterval))));
+
+ if (!ptpClock->waitingForDelayResp) {
+ DBG("Ignored DelayResp sequence %d - wasn't waiting for one\n",
+ header->sequenceId);
+ ptpClock->counters.discardedMessages++;
+ break;
+ }
+
+ if (ptpClock->sentDelayReqSequenceId !=
+ ((UInteger16)(header->sequenceId + 1))) {
+ DBG("HandledelayResp : sequence mismatch - "
+ "last DelayReq sent: %d, delayResp received: %d\n",
+ ptpClock->sentDelayReqSequenceId,
+ header->sequenceId
+ );
+ ptpClock->counters.discardedMessages++;
+ ptpClock->counters.sequenceMismatchErrors++;
+ break;
+ }
+
+ ptpClock->counters.delayRespMessagesReceived++;
+ ptpClock->waitingForDelayResp = FALSE;
+
+ toInternalTime(&requestReceiptTimestamp,
+ &ptpClock->msgTmp.resp.receiveTimestamp);
+ ptpClock->delay_req_receive_time.seconds =
+ requestReceiptTimestamp.seconds;
+ ptpClock->delay_req_receive_time.nanoseconds =
+ requestReceiptTimestamp.nanoseconds;
+
+ integer64_to_internalTime(
+ header->correctionField,
+ &correctionField);
+ /*
+ send_time = delay_req_send_time (received as CMSG in handleEvent)
+ recv_time = requestReceiptTimestamp (received inside delayResp)
+ */
+
+ updateDelay(&ptpClock->mpd_filt,
+ rtOpts,ptpClock, &correctionField);
+ if (ptpClock->delayRespWaiting) {
+
+ NOTICE("Received first Delay Response from Master\n");
+ }
+
+ if (rtOpts->ignore_delayreq_interval_master == 0) {
+ DBGV("current delay_req: %d new delay req: %d \n",
+ ptpClock->portDS.logMinDelayReqInterval,
+ header->logMessageInterval);
+ if (ptpClock->portDS.logMinDelayReqInterval != header->logMessageInterval) {
+
+ if(header->logMessageInterval == UNICAST_MESSAGEINTERVAL &&
+ rtOpts->autoDelayReqInterval) {
+
+ if(rtOpts->unicastNegotiation && ptpClock->parentGrants && ptpClock->parentGrants->grantData[DELAY_RESP_INDEXED].granted) {
+ if(ptpClock->delayRespWaiting) {
+ NOTICE("Received Delay Interval %d from master\n",
+ ptpClock->parentGrants->grantData[DELAY_RESP_INDEXED].logInterval);
+ }
+ ptpClock->portDS.logMinDelayReqInterval = ptpClock->parentGrants->grantData[DELAY_RESP_INDEXED].logInterval;
+ } else {
+
+ if(ptpClock->delayRespWaiting) {
+ NOTICE("Received Delay Interval %d from master (unicast-unknown) - overriding with %d\n",
+ header->logMessageInterval, rtOpts->logMinDelayReqInterval);
+ }
+ ptpClock->portDS.logMinDelayReqInterval = rtOpts->logMinDelayReqInterval;
+
+ }
+ } else {
+ /* Accept new DelayReq value from the Master */
+
+ NOTICE("Received new Delay Request interval %d from Master (was: %d)\n",
+ header->logMessageInterval, ptpClock->portDS.logMinDelayReqInterval );
+
+ // collect new value indicated by the Master
+ ptpClock->portDS.logMinDelayReqInterval = header->logMessageInterval;
+ }
+ /* FIXME: the actual rearming of this timer with the new value only happens later in doState()/issueDelayReq() */
+ }
+ } else {
+
+ if (ptpClock->portDS.logMinDelayReqInterval != rtOpts->logMinDelayReqInterval) {
+ INFO("New Delay Request interval applied: %d (was: %d)\n",
+ rtOpts->logMinDelayReqInterval, ptpClock->portDS.logMinDelayReqInterval);
+ }
+ ptpClock->portDS.logMinDelayReqInterval = rtOpts->logMinDelayReqInterval;
+ }
+ /* arm the timer again now that we have the correct delayreq interval */
+ timerStart(&ptpClock->timers[DELAY_RECEIPT_TIMER], max(
+ (ptpClock->portDS.announceReceiptTimeout) * (pow(2,ptpClock->portDS.logAnnounceInterval)),
+ MISSED_MESSAGES_MAX * (pow(2,ptpClock->portDS.logMinDelayReqInterval))));
+ } else {
+
+ DBG("HandledelayResp : delayResp doesn't match with the delayReq. \n");
+ ptpClock->counters.discardedMessages++;
+ break;
+ }
+
+ ptpClock->delayRespWaiting = FALSE;
+ }
+ } else if (ptpClock->portDS.delayMechanism == P2P) { /* (Peer to Peer mode) */
+ DBG("Delay messages are disregarded in Peer to Peer mode \n");
+ ptpClock->counters.discardedMessages++;
+ ptpClock->counters.delayMechanismMismatchErrors++;
+ /* no delay mechanism */
+ } else {
+ DBG("DelayResp ignored - we are in DELAY_DISABLED mode");
+ ptpClock->counters.discardedMessages++;
+ }
+
+}
+
+
+static void
+handlePdelayReq(MsgHeader *header, ssize_t length,
+ const TimeInternal *tint, Integer32 sourceAddress, Boolean isFromSelf,
+ const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+
+ UnicastGrantTable *nodeTable = NULL;
+
+ if (ptpClock->portDS.delayMechanism == P2P) {
+
+ if(!isFromSelf && rtOpts->unicastNegotiation && rtOpts->ipMode == IPMODE_UNICAST) {
+ nodeTable = findUnicastGrants(&header->sourcePortIdentity, 0,
+ ptpClock->unicastGrants, &ptpClock->grantIndex, UNICAST_MAX_DESTINATIONS,
+ FALSE);
+ if(nodeTable == NULL || !(nodeTable->grantData[PDELAY_RESP_INDEXED].granted)) {
+ DBG("Ignoring Peer Delay Request from peer: unicast transmission not granted\n");
+ ptpClock->counters.discardedMessages++;
+ return;
+ } else {
+ nodeTable->grantData[PDELAY_RESP_INDEXED].receiving = header->sequenceId;
+ }
+
+ }
+
+ DBGV("PdelayReq message received : \n");
+
+ if(length < PDELAY_REQ_LENGTH) {
+ DBG("Error: PdelayReq message too short\n");
+ ptpClock->counters.messageFormatErrors++;
+ return;
+ }
+
+ switch (ptpClock->portDS.portState ) {
+ case PTP_INITIALIZING:
+ case PTP_FAULTY:
+ case PTP_DISABLED:
+ case PTP_UNCALIBRATED:
+ case PTP_LISTENING:
+ DBGV("HandlePdelayReq : disregard\n");
+ ptpClock->counters.discardedMessages++;
+ return;
+
+ case PTP_SLAVE:
+ case PTP_MASTER:
+ case PTP_PASSIVE:
+
+ if (isFromSelf) {
+ processPdelayReqFromSelf(tint, rtOpts, ptpClock);
+ break;
+ } else {
+ ptpClock->counters.pdelayReqMessagesReceived++;
+ msgUnpackHeader(ptpClock->msgIbuf,
+ &ptpClock->PdelayReqHeader);
+ issuePdelayResp(tint, header, sourceAddress, rtOpts,
+ ptpClock);
+ break;
+ }
+ default:
+ DBG("do unrecognized state3\n");
+ ptpClock->counters.discardedMessages++;
+ break;
+ }
+ } else if (ptpClock->portDS.delayMechanism == E2E){/* (End to End mode..) */
+ DBG("Peer Delay messages are disregarded in End to End "
+ "mode \n");
+ ptpClock->counters.discardedMessages++;
+ ptpClock->counters.delayMechanismMismatchErrors++;
+ /* no delay mechanism */
+ } else {
+ DBG("PdelayReq ignored - we are in DELAY_DISABLED mode");
+ ptpClock->counters.discardedMessages++;
+ }
+}
+
+static void
+processPdelayReqFromSelf(const TimeInternal * tint, const RunTimeOpts * rtOpts, PtpClock * ptpClock) {
+ /*
+ * Get sending timestamp from IP stack
+ * with SO_TIMESTAMP
+ */
+ ptpClock->pdelay_req_send_time.seconds = tint->seconds;
+ ptpClock->pdelay_req_send_time.nanoseconds = tint->nanoseconds;
+
+ /*Add latency*/
+ addTime(&ptpClock->pdelay_req_send_time,
+ &ptpClock->pdelay_req_send_time,
+ &rtOpts->outboundLatency);
+}
+
+static void
+handlePdelayResp(const MsgHeader *header, TimeInternal *tint,
+ ssize_t length, Boolean isFromSelf, Integer32 sourceAddress, Integer32 destinationAddress,
+ const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ if (ptpClock->portDS.delayMechanism == P2P) {
+
+ Integer32 dst = 0;
+
+ if(destinationAddress) {
+ dst = destinationAddress;
+ } else {
+ /* last resort: may cause PdelayRespFollowUp to be sent to incorrect destinations */
+ dst = ptpClock->lastPdelayRespDst;
+ }
+
+
+ /* Boolean isFromCurrentParent = FALSE; NOTE: This is never used in this function */
+ TimeInternal requestReceiptTimestamp;
+ TimeInternal correctionField;
+
+ DBG("PdelayResp message received : \n");
+
+ if (length < PDELAY_RESP_LENGTH) {
+ DBG("Error: PdelayResp message too short\n");
+ ptpClock->counters.messageFormatErrors++;
+ return;
+ }
+
+ switch (ptpClock->portDS.portState ) {
+ case PTP_INITIALIZING:
+ case PTP_FAULTY:
+ case PTP_DISABLED:
+ case PTP_UNCALIBRATED:
+ case PTP_LISTENING:
+ DBGV("HandlePdelayResp : disregard\n");
+ ptpClock->counters.discardedMessages++;
+ return;
+
+ case PTP_SLAVE:
+ case PTP_MASTER:
+ if (ptpClock->defaultDS.twoStepFlag && isFromSelf) {
+ processPdelayRespFromSelf(tint, rtOpts, ptpClock, dst, header->sequenceId);
+ break;
+ }
+ msgUnpackPdelayResp(ptpClock->msgIbuf,
+ &ptpClock->msgTmp.presp);
+
+ if (ptpClock->sentPdelayReqSequenceId !=
+ ((UInteger16)(header->sequenceId + 1))) {
+ DBG("PdelayResp: sequence mismatch - sent: %d, received: %d\n",
+ ptpClock->sentPdelayReqSequenceId,
+ header->sequenceId);
+ ptpClock->counters.discardedMessages++;
+ ptpClock->counters.sequenceMismatchErrors++;
+ break;
+ }
+ if ((!memcmp(ptpClock->portDS.portIdentity.clockIdentity,ptpClock->msgTmp.presp.requestingPortIdentity.clockIdentity,CLOCK_IDENTITY_LENGTH))
+ && ( ptpClock->portDS.portIdentity.portNumber == ptpClock->msgTmp.presp.requestingPortIdentity.portNumber)) {
+ ptpClock->counters.pdelayRespMessagesReceived++;
+ /* Two Step Clock */
+ if ((header->flagField0 & PTP_TWO_STEP) == PTP_TWO_STEP) {
+ /*Store t4 (Fig 35)*/
+ ptpClock->pdelay_resp_receive_time.seconds = tint->seconds;
+ ptpClock->pdelay_resp_receive_time.nanoseconds = tint->nanoseconds;
+ /*store t2 (Fig 35)*/
+ toInternalTime(&requestReceiptTimestamp,
+ &ptpClock->msgTmp.presp.requestReceiptTimestamp);
+ ptpClock->pdelay_req_receive_time.seconds = requestReceiptTimestamp.seconds;
+ ptpClock->pdelay_req_receive_time.nanoseconds = requestReceiptTimestamp.nanoseconds;
+
+ integer64_to_internalTime(header->correctionField,&correctionField);
+ ptpClock->lastPdelayRespCorrectionField.seconds = correctionField.seconds;
+ ptpClock->lastPdelayRespCorrectionField.nanoseconds = correctionField.nanoseconds;
+ } else {
+ /* One step Clock */
+ /*Store t4 (Fig 35)*/
+ ptpClock->pdelay_resp_receive_time.seconds = tint->seconds;
+ ptpClock->pdelay_resp_receive_time.nanoseconds = tint->nanoseconds;
+
+ integer64_to_internalTime(header->correctionField,&correctionField);
+ updatePeerDelay (&ptpClock->mpd_filt,rtOpts,ptpClock,&correctionField,FALSE);
+ if (rtOpts->ignore_delayreq_interval_master == 0) {
+ DBGV("current pdelay_req: %d new pdelay req: %d \n",
+ ptpClock->portDS.logMinPdelayReqInterval,
+ header->logMessageInterval);
+ if (ptpClock->portDS.logMinPdelayReqInterval != header->logMessageInterval) {
+
+ if(header->logMessageInterval == UNICAST_MESSAGEINTERVAL &&
+ rtOpts->autoDelayReqInterval) {
+
+ if(rtOpts->unicastNegotiation && ptpClock->peerGrants.grantData[PDELAY_RESP_INDEXED].granted) {
+ if(ptpClock->delayRespWaiting) {
+ NOTICE("Received Peer Delay Interval %d from peer\n",
+ ptpClock->peerGrants.grantData[PDELAY_RESP_INDEXED].logInterval);
+ }
+ ptpClock->portDS.logMinPdelayReqInterval = ptpClock->peerGrants.grantData[PDELAY_RESP_INDEXED].logInterval;
+ } else {
+
+ if(ptpClock->delayRespWaiting) {
+ NOTICE("Received Peer Delay Interval %d from peer (unicast-unknown) - overriding with %d\n",
+ header->logMessageInterval, rtOpts->logMinPdelayReqInterval);
+ }
+ ptpClock->portDS.logMinPdelayReqInterval = rtOpts->logMinPdelayReqInterval;
+
+ }
+ } else {
+ /* Accept new DelayReq value from the Master */
+
+ NOTICE("Received new Peer Delay Request interval %d from Master (was: %d)\n",
+ header->logMessageInterval, ptpClock->portDS.logMinPdelayReqInterval );
+
+ // collect new value indicated by the Master
+ ptpClock->portDS.logMinPdelayReqInterval = header->logMessageInterval;
+ }
+ } else {
+
+ if (ptpClock->portDS.logMinPdelayReqInterval != rtOpts->logMinPdelayReqInterval) {
+ INFO("New Peer Delay Request interval applied: %d (was: %d)\n",
+ rtOpts->logMinPdelayReqInterval, ptpClock->portDS.logMinPdelayReqInterval);
+ }
+ ptpClock->portDS.logMinPdelayReqInterval = rtOpts->logMinPdelayReqInterval;
+ }
+
+ }
+ ptpClock->delayRespWaiting = FALSE;
+ timerStart(&ptpClock->timers[DELAY_RECEIPT_TIMER], max(
+ (ptpClock->portDS.announceReceiptTimeout) * (pow(2,ptpClock->portDS.logAnnounceInterval)),
+ MISSED_MESSAGES_MAX * (pow(2,ptpClock->portDS.logMinPdelayReqInterval))));
+ }
+
+ ptpClock->recvPdelayRespSequenceId = header->sequenceId;
+ break;
+ } else {
+ DBG("HandlePdelayResp : Pdelayresp doesn't "
+ "match with the PdelayReq. \n");
+ ptpClock->counters.discardedMessages++;
+ break;
+ }
+ break; /* XXX added by gnn for safety */
+ default:
+ DBG("do unrecognized state4\n");
+ ptpClock->counters.discardedMessages++;
+ break;
+ }
+ } else if (ptpClock->portDS.delayMechanism == E2E) { /* (End to End mode..) */
+ DBG("Peer Delay messages are disregarded in End to End "
+ "mode \n");
+ ptpClock->counters.discardedMessages++;
+ ptpClock->counters.delayMechanismMismatchErrors++;
+ /* no delay mechanism */
+ } else {
+ DBG("PdelayResp ignored - we are in DELAY_DISABLED mode");
+ ptpClock->counters.discardedMessages++;
+ }
+}
+
+static void
+processPdelayRespFromSelf(const TimeInternal * tint, const RunTimeOpts * rtOpts, PtpClock * ptpClock, Integer32 dst, const UInteger16 sequenceId)
+{
+ TimeInternal timestamp;
+
+ addTime(×tamp, tint, &rtOpts->outboundLatency);
+
+ issuePdelayRespFollowUp(×tamp, &ptpClock->PdelayReqHeader, dst,
+ rtOpts, ptpClock, sequenceId);
+}
+
+static void
+handlePdelayRespFollowUp(const MsgHeader *header, ssize_t length,
+ Boolean isFromSelf, const RunTimeOpts *rtOpts,
+ PtpClock *ptpClock)
+{
+
+ if (ptpClock->portDS.delayMechanism == P2P) {
+ TimeInternal responseOriginTimestamp;
+ TimeInternal correctionField;
+
+ DBG("PdelayRespfollowup message received : \n");
+
+ if(length < PDELAY_RESP_FOLLOW_UP_LENGTH) {
+ DBG("Error: PdelayRespfollowup message too short\n");
+ ptpClock->counters.messageFormatErrors++;
+ return;
+ }
+
+ switch(ptpClock->portDS.portState) {
+ case PTP_INITIALIZING:
+ case PTP_FAULTY:
+ case PTP_DISABLED:
+ case PTP_UNCALIBRATED:
+ DBGV("HandlePdelayResp : disregard\n");
+ ptpClock->counters.discardedMessages++;
+ return;
+
+ case PTP_SLAVE:
+ case PTP_MASTER:
+ if (isFromSelf) {
+ DBGV("HandlePdelayRespFollowUp : Ignore message from self \n");
+ return;
+ }
+ if (( ((UInteger16)(header->sequenceId + 1)) ==
+ ptpClock->sentPdelayReqSequenceId) && (header->sequenceId == ptpClock->recvPdelayRespSequenceId)) {
+ msgUnpackPdelayRespFollowUp(
+ ptpClock->msgIbuf,
+ &ptpClock->msgTmp.prespfollow);
+ ptpClock->counters.pdelayRespFollowUpMessagesReceived++;
+ toInternalTime(
+ &responseOriginTimestamp,
+ &ptpClock->msgTmp.prespfollow.responseOriginTimestamp);
+ ptpClock->pdelay_resp_send_time.seconds =
+ responseOriginTimestamp.seconds;
+ ptpClock->pdelay_resp_send_time.nanoseconds =
+ responseOriginTimestamp.nanoseconds;
+ integer64_to_internalTime(
+ ptpClock->msgTmpHeader.correctionField,
+ &correctionField);
+ addTime(&correctionField,&correctionField,
+ &ptpClock->lastPdelayRespCorrectionField);
+ updatePeerDelay (&ptpClock->mpd_filt,
+ rtOpts, ptpClock,
+ &correctionField,TRUE);
+
+/* pdelay interval handling begin */
+ if (rtOpts->ignore_delayreq_interval_master == 0) {
+ DBGV("current delay_req: %d new delay req: %d \n",
+ ptpClock->portDS.logMinPdelayReqInterval,
+ header->logMessageInterval);
+ if (ptpClock->portDS.logMinPdelayReqInterval != header->logMessageInterval) {
+
+ if(header->logMessageInterval == UNICAST_MESSAGEINTERVAL &&
+ rtOpts->autoDelayReqInterval) {
+
+ if(rtOpts->unicastNegotiation && ptpClock->peerGrants.grantData[PDELAY_RESP_INDEXED].granted) {
+ if(ptpClock->delayRespWaiting) {
+ NOTICE("Received Peer Delay Interval %d from peer\n",
+ ptpClock->peerGrants.grantData[PDELAY_RESP_INDEXED].logInterval);
+ }
+ ptpClock->portDS.logMinPdelayReqInterval = ptpClock->peerGrants.grantData[PDELAY_RESP_INDEXED].logInterval;
+ } else {
+
+ if(ptpClock->delayRespWaiting) {
+ NOTICE("Received Peer Delay Interval %d from peer (unicast-unknown) - overriding with %d\n",
+ header->logMessageInterval, rtOpts->logMinPdelayReqInterval);
+ }
+ ptpClock->portDS.logMinPdelayReqInterval = rtOpts->logMinPdelayReqInterval;
+
+ }
+ } else {
+ /* Accept new DelayReq value from the Master */
+
+ NOTICE("Received new Peer Delay Request interval %d from Master (was: %d)\n",
+ header->logMessageInterval, ptpClock->portDS.logMinPdelayReqInterval );
+
+ // collect new value indicated by the Master
+ ptpClock->portDS.logMinPdelayReqInterval = header->logMessageInterval;
+ }
+ } else {
+
+ if (ptpClock->portDS.logMinPdelayReqInterval != rtOpts->logMinPdelayReqInterval) {
+ INFO("New Peer Delay Request interval applied: %d (was: %d)\n",
+ rtOpts->logMinPdelayReqInterval, ptpClock->portDS.logMinPdelayReqInterval);
+ }
+ ptpClock->portDS.logMinPdelayReqInterval = rtOpts->logMinPdelayReqInterval;
+ }
+
+/* pdelay interval handling end */
+ }
+ ptpClock->delayRespWaiting = FALSE;
+ timerStart(&ptpClock->timers[DELAY_RECEIPT_TIMER], max(
+ (ptpClock->portDS.announceReceiptTimeout) * (pow(2,ptpClock->portDS.logAnnounceInterval)),
+ MISSED_MESSAGES_MAX * (pow(2,ptpClock->portDS.logMinPdelayReqInterval))));
+
+ break;
+ } else {
+ DBG("PdelayRespFollowup: sequence mismatch - Received: %d "
+ "PdelayReq sent: %d, PdelayResp received: %d\n",
+ header->sequenceId, ptpClock->sentPdelayReqSequenceId,
+ ptpClock->recvPdelayRespSequenceId);
+ ptpClock->counters.discardedMessages++;
+ ptpClock->counters.sequenceMismatchErrors++;
+ break;
+ }
+ default:
+ DBGV("Disregard PdelayRespFollowUp message \n");
+ ptpClock->counters.discardedMessages++;
+ }
+ } else if (ptpClock->portDS.delayMechanism == E2E) { /* (End to End mode..) */
+ DBG("Peer Delay messages are disregarded in End to End "
+ "mode \n");
+ ptpClock->counters.discardedMessages++;
+ ptpClock->counters.delayMechanismMismatchErrors++;
+ /* no delay mechanism */
+ } else {
+ DBG("PdelayRespFollowUp ignored - we are in DELAY_DISABLED mode");
+ ptpClock->counters.discardedMessages++;
+ }
+}
+
+/* Only accept the management / signaling message if it satisfies 15.3.1 Table 36 */
+/* Also 13.12.1 table 32 */
+Boolean
+acceptPortIdentity(PortIdentity thisPort, PortIdentity targetPort)
+{
+ ClockIdentity allOnesClkIdentity;
+ UInteger16 allOnesPortNumber = 0xFFFF;
+ memset(allOnesClkIdentity, 0xFF, sizeof(allOnesClkIdentity));
+
+ /* equal port IDs: equal clock ID and equal port ID */
+ if(!memcmp(targetPort.clockIdentity, thisPort.clockIdentity, CLOCK_IDENTITY_LENGTH) &&
+ (targetPort.portNumber == thisPort.portNumber)) {
+ return TRUE;
+ }
+
+ /* equal clockIDs and target port number is wildcard */
+ if(!memcmp(targetPort.clockIdentity, thisPort.clockIdentity, CLOCK_IDENTITY_LENGTH) &&
+ (targetPort.portNumber == allOnesPortNumber)) {
+ return TRUE;
+ }
+
+ /* target clock ID is wildcard, port number matches */
+ if(!memcmp(targetPort.clockIdentity, allOnesClkIdentity, CLOCK_IDENTITY_LENGTH) &&
+ (targetPort.portNumber == thisPort.portNumber)) {
+ return TRUE;
+ }
+
+ /* target port and clock IDs are both wildcard */
+ if(!memcmp(targetPort.clockIdentity, allOnesClkIdentity, CLOCK_IDENTITY_LENGTH) &&
+ (targetPort.portNumber == allOnesPortNumber)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* This code does not get built in the slave-only build */
+#ifndef PTPD_SLAVE_ONLY
+
+/* send Announce to all destinations */
+static void
+issueAnnounce(const RunTimeOpts *rtOpts,PtpClock *ptpClock)
+{
+ Integer32 dst = 0;
+ int i = 0;
+ UnicastGrantData *grant = NULL;
+ Boolean okToSend = TRUE;
+
+ /* send Announce to Ethernet or multicast */
+ if(rtOpts->transport == IEEE_802_3 || (rtOpts->ipMode != IPMODE_UNICAST)) {
+ issueAnnounceSingle(dst, &ptpClock->sentAnnounceSequenceId, rtOpts, ptpClock);
+ /* send Announce to unicast destination(s) */
+ } else {
+ /* send to granted only */
+ if(rtOpts->unicastNegotiation) {
+ for(i = 0; i < UNICAST_MAX_DESTINATIONS; i++) {
+ grant = &(ptpClock->unicastGrants[i].grantData[ANNOUNCE_INDEXED]);
+ okToSend = TRUE;
+ if(grant->logInterval > ptpClock->portDS.logAnnounceInterval ) {
+ grant->intervalCounter %= (UInteger32)(pow(2,grant->logInterval - ptpClock->portDS.logAnnounceInterval));
+ if(grant->intervalCounter != 0) {
+ okToSend = FALSE;
+ }
+ grant->intervalCounter++;
+ }
+ if(grant->granted) {
+ if(okToSend) {
+ issueAnnounceSingle(ptpClock->unicastGrants[i].transportAddress,
+ &grant->sentSeqId,rtOpts, ptpClock);
+ }
+ }
+ }
+ /* send to fixed unicast destinations */
+ } else {
+ for(i = 0; i < ptpClock->unicastDestinationCount; i++) {
+ issueAnnounceSingle(ptpClock->unicastDestinations[i].transportAddress,
+ &(ptpClock->unicastGrants[i].grantData[ANNOUNCE_INDEXED].sentSeqId),
+ rtOpts, ptpClock);
+ }
+ }
+ }
+
+}
+
+/* send single announce to a single destination */
+static void
+issueAnnounceSingle(Integer32 dst, UInteger16 *sequenceId, const RunTimeOpts *rtOpts,PtpClock *ptpClock)
+{
+
+ Timestamp originTimestamp;
+ TimeInternal internalTime;
+
+ getTime(&internalTime);
+ fromInternalTime(&internalTime,&originTimestamp);
+
+ msgPackAnnounce(ptpClock->msgObuf, *sequenceId, &originTimestamp, ptpClock);
+
+ if (!netSendGeneral(ptpClock->msgObuf,ANNOUNCE_LENGTH,
+ &ptpClock->netPath, rtOpts, dst)) {
+ toState(PTP_FAULTY,rtOpts,ptpClock);
+ ptpClock->counters.messageSendErrors++;
+ DBGV("Announce message can't be sent -> FAULTY state \n");
+ } else {
+ DBGV("Announce MSG sent ! \n");
+ (*sequenceId)++;
+ ptpClock->counters.announceMessagesSent++;
+ }
+}
+
+/* send Sync to all destinations */
+static void
+issueSync(const RunTimeOpts *rtOpts,PtpClock *ptpClock)
+{
+ Integer32 dst = 0;
+ int i = 0;
+ UnicastGrantData *grant = NULL;
+ Boolean okToSend = TRUE;
+
+ /* send Sync to Ethernet or multicast */
+ if(rtOpts->transport == IEEE_802_3 || (rtOpts->ipMode != IPMODE_UNICAST)) {
+ (void)issueSyncSingle(dst, &ptpClock->sentSyncSequenceId, rtOpts, ptpClock);
+
+ /* send Sync to unicast destination(s) */
+ } else {
+ for(i = 0; i < UNICAST_MAX_DESTINATIONS; i++) {
+ ptpClock->syncDestIndex[i].transportAddress = 0;
+ clearTime(&ptpClock->unicastGrants[i].lastSyncTimestamp);
+ clearTime(&ptpClock->unicastDestinations[i].lastSyncTimestamp);
+ }
+ /* send to granted only */
+ if(rtOpts->unicastNegotiation) {
+ for(i = 0; i < UNICAST_MAX_DESTINATIONS; i++) {
+ grant = &(ptpClock->unicastGrants[i].grantData[SYNC_INDEXED]);
+ okToSend = TRUE;
+ /* handle different intervals */
+ if(grant->logInterval > ptpClock->portDS.logSyncInterval ) {
+ grant->intervalCounter %= (UInteger32)(pow(2,grant->logInterval - ptpClock->portDS.logSyncInterval));
+ if(grant->intervalCounter != 0) {
+ okToSend = FALSE;
+ }
+ DBG("mixed interval to %d counter: %d\n", grant->parent->transportAddress,grant->intervalCounter);
+ grant->intervalCounter++;
+ }
+
+ if(grant->granted) {
+ if(okToSend) {
+ ptpClock->unicastGrants[i].lastSyncTimestamp =
+ issueSyncSingle(ptpClock->unicastGrants[i].transportAddress,
+ &grant->sentSeqId,rtOpts, ptpClock);
+ }
+ }
+ }
+ /* send to fixed unicast destinations */
+ } else {
+ for(i = 0; i < ptpClock->unicastDestinationCount; i++) {
+ ptpClock->unicastDestinations[i].lastSyncTimestamp =
+ issueSyncSingle(ptpClock->unicastDestinations[i].transportAddress,
+ &(ptpClock->unicastGrants[i].grantData[SYNC_INDEXED].sentSeqId),
+ rtOpts, ptpClock);
+ }
+ }
+ }
+
+}
+
+/*Pack and send a single Sync message, return the embedded timestamp*/
+static TimeInternal
+issueSyncSingle(Integer32 dst, UInteger16 *sequenceId, const RunTimeOpts *rtOpts,PtpClock *ptpClock)
+{
+ Timestamp originTimestamp;
+ TimeInternal internalTime, now;
+
+ getTime(&internalTime);
+
+ if (respectUtcOffset(rtOpts, ptpClock) == TRUE) {
+ internalTime.seconds += ptpClock->timePropertiesDS.currentUtcOffset;
+ }
+
+ /*
+ * LEAPNOTE01#
+ * This is done here rather than in netSendEvent because we must also
+ * prevent sequence IDs from incrementing so that there's no discontinuity.
+ * Ideally netSendEvent should be incrementing the sequence numbers,
+ * then this could be centrally blocked, but then again this makes
+ * netSendEvent less generic - on the other end in 2.4 this will be
+ * a glue function for libcck transports, so this will be fine.
+ * In other words, ptpClock->netSendEvent() will be a function pointer.
+ * What was I talking about? Yes, right, sequence numbers can be
+ * incremented in netSendEvent.
+ */
+
+ if(ptpClock->leapSecondInProgress) {
+ DBG("Leap second in progress - will not send SYNC\n");
+ clearTime(&internalTime);
+ return internalTime;
+ }
+
+ fromInternalTime(&internalTime,&originTimestamp);
+
+ now = internalTime;
+
+ msgPackSync(ptpClock->msgObuf,*sequenceId,&originTimestamp,ptpClock);
+
+ if (!netSendEvent(ptpClock->msgObuf,SYNC_LENGTH,&ptpClock->netPath,
+ rtOpts, dst, &internalTime)) {
+ toState(PTP_FAULTY,rtOpts,ptpClock);
+ ptpClock->counters.messageSendErrors++;
+ DBGV("Sync message can't be sent -> FAULTY state \n");
+ } else {
+
+ DBGV("Sync MSG sent ! \n");
+
+#ifdef SO_TIMESTAMPING
+
+#ifdef PTPD_PCAP
+ if((ptpClock->netPath.pcapEvent == NULL) && !ptpClock->netPath.txTimestampFailure) {
+#else
+ if(!ptpClock->netPath.txTimestampFailure) {
+#endif /* PTPD_PCAP */
+ if(internalTime.seconds && internalTime.nanoseconds) {
+
+ if (respectUtcOffset(rtOpts, ptpClock) == TRUE) {
+ internalTime.seconds += ptpClock->timePropertiesDS.currentUtcOffset;
+ }
+ processSyncFromSelf(&internalTime, rtOpts, ptpClock, dst, *sequenceId);
+ }
+ }
+#endif
+
+#if defined(__QNXNTO__) && defined(PTPD_EXPERIMENTAL)
+ if(internalTime.seconds && internalTime.nanoseconds) {
+ if (respectUtcOffset(rtOpts, ptpClock) == TRUE) {
+ internalTime.seconds += ptpClock->timePropertiesDS.currentUtcOffset;
+ }
+ processSyncFromSelf(&internalTime, rtOpts, ptpClock, dst, *sequenceId);
+ }
+#endif
+
+
+ ptpClock->lastSyncDst = dst;
+
+ if(!internalTime.seconds && !internalTime.nanoseconds) {
+ internalTime = now;
+ }
+
+ /* index the Sync destination */
+ indexSync(&internalTime, *sequenceId, dst, ptpClock->syncDestIndex);
+
+ (*sequenceId)++;
+ ptpClock->counters.syncMessagesSent++;
+
+ }
+
+ return internalTime;
+}
+
+
+
+/*Pack and send on general multicast ip adress a FollowUp message*/
+static void
+issueFollowup(const TimeInternal *tint,const RunTimeOpts *rtOpts,PtpClock *ptpClock, Integer32 dst, UInteger16 sequenceId)
+{
+ Timestamp preciseOriginTimestamp;
+ fromInternalTime(tint,&preciseOriginTimestamp);
+
+ msgPackFollowUp(ptpClock->msgObuf,&preciseOriginTimestamp,ptpClock,sequenceId);
+
+ if (!netSendGeneral(ptpClock->msgObuf,FOLLOW_UP_LENGTH,
+ &ptpClock->netPath, rtOpts, dst)) {
+ toState(PTP_FAULTY,rtOpts,ptpClock);
+ ptpClock->counters.messageSendErrors++;
+ DBGV("FollowUp message can't be sent -> FAULTY state \n");
+ } else {
+ DBGV("FollowUp MSG sent ! \n");
+ ptpClock->counters.followUpMessagesSent++;
+ }
+}
+
+#endif /* PTPD_SLAVE_ONLY */
+
+/*Pack and send on event multicast ip adress a DelayReq message*/
+static void
+issueDelayReq(const RunTimeOpts *rtOpts,PtpClock *ptpClock)
+{
+ Timestamp originTimestamp;
+ TimeInternal internalTime;
+#if 0 /* PCAP ONLY */
+ MsgHeader ourDelayReq;
+#endif
+ /* see LEAPNOTE01# in this file */
+ if(ptpClock->leapSecondInProgress) {
+ DBG("Leap second in progress - will not send DELAY_REQ\n");
+ return;
+ }
+
+ DBG("==> Issue DelayReq (%d)\n", ptpClock->sentDelayReqSequenceId );
+
+ /*
+ * call GTOD. This time is later replaced in handleDelayReq,
+ * to get the actual send timestamp from the OS
+ */
+ getTime(&internalTime);
+ if (respectUtcOffset(rtOpts, ptpClock) == TRUE) {
+ internalTime.seconds += ptpClock->timePropertiesDS.currentUtcOffset;
+ }
+ fromInternalTime(&internalTime,&originTimestamp);
+
+ // uses current sentDelayReqSequenceId
+ msgPackDelayReq(ptpClock->msgObuf,&originTimestamp,ptpClock);
+
+ Integer32 dst = 0;
+
+ /* in hybrid mode or unicast mode, send delayReq to current master */
+ if (rtOpts->ipMode == IPMODE_HYBRID || rtOpts->ipMode == IPMODE_UNICAST) {
+ if(ptpClock->bestMaster) {
+ dst = ptpClock->bestMaster->sourceAddr;
+ }
+ }
+
+ if (!netSendEvent(ptpClock->msgObuf,DELAY_REQ_LENGTH,
+ &ptpClock->netPath, rtOpts, dst, &internalTime)) {
+ toState(PTP_FAULTY,rtOpts,ptpClock);
+ ptpClock->counters.messageSendErrors++;
+ DBGV("delayReq message can't be sent -> FAULTY state \n");
+ } else {
+ DBGV("DelayReq MSG sent ! \n");
+
+#ifdef SO_TIMESTAMPING
+
+#ifdef PTPD_PCAP
+ if((ptpClock->netPath.pcapEvent == NULL) && !ptpClock->netPath.txTimestampFailure) {
+#else
+ if(!ptpClock->netPath.txTimestampFailure) {
+#endif /* PTPD_PCAP */
+ if (respectUtcOffset(rtOpts, ptpClock) == TRUE) {
+ internalTime.seconds += ptpClock->timePropertiesDS.currentUtcOffset;
+ }
+
+ processDelayReqFromSelf(&internalTime, rtOpts, ptpClock);
+ }
+#endif
+
+#if defined(__QNXNTO__) && defined(PTPD_EXPERIMENTAL)
+ if (respectUtcOffset(rtOpts, ptpClock) == TRUE) {
+ internalTime.seconds += ptpClock->timePropertiesDS.currentUtcOffset;
+ }
+
+ processDelayReqFromSelf(&internalTime, rtOpts, ptpClock);
+#endif
+
+ ptpClock->sentDelayReqSequenceId++;
+ ptpClock->counters.delayReqMessagesSent++;
+
+ /* From now on, we will only accept delayreq and
+ * delayresp of (sentDelayReqSequenceId - 1) */
+
+ /* Explicitly re-arm timer for sending the next delayReq
+ * 9.5.11.2: arm the timer with a uniform range from 0 to 2 x interval
+ * this is only ever used here, so removed the timerStart_random function
+ */
+
+ timerStart(&ptpClock->timers[DELAYREQ_INTERVAL_TIMER],
+ pow(2,ptpClock->portDS.logMinDelayReqInterval) * getRand() * 2.0);
+#if 0 /* PCAP ONLY */
+ msgUnpackHeader(ptpClock->msgObuf, &ourDelayReq);
+ handleDelayReq(&ourDelayReq, DELAY_REQ_LENGTH, &internalTime,
+ TRUE, rtOpts, ptpClock);
+#endif
+ }
+}
+
+/*Pack and send on event multicast ip adress a PdelayReq message*/
+static void
+issuePdelayReq(const RunTimeOpts *rtOpts,PtpClock *ptpClock)
+{
+ Integer32 dst = 0;
+ Timestamp originTimestamp;
+ TimeInternal internalTime;
+
+ /* see LEAPNOTE01# in this file */
+ if(ptpClock->leapSecondInProgress) {
+ DBG("Leap secon in progress - will not send PDELAY_REQ\n");
+ return;
+ }
+
+ getTime(&internalTime);
+ if (respectUtcOffset(rtOpts, ptpClock) == TRUE) {
+ internalTime.seconds += ptpClock->timePropertiesDS.currentUtcOffset;
+ }
+ fromInternalTime(&internalTime,&originTimestamp);
+
+ if(rtOpts->ipMode == IPMODE_UNICAST && ptpClock->unicastPeerDestination.transportAddress) {
+ dst = ptpClock->unicastPeerDestination.transportAddress;
+ }
+
+ msgPackPdelayReq(ptpClock->msgObuf,&originTimestamp,ptpClock);
+ if (!netSendPeerEvent(ptpClock->msgObuf,PDELAY_REQ_LENGTH,
+ &ptpClock->netPath, rtOpts, dst, &internalTime)) {
+ toState(PTP_FAULTY,rtOpts,ptpClock);
+ ptpClock->counters.messageSendErrors++;
+ DBGV("PdelayReq message can't be sent -> FAULTY state \n");
+ } else {
+ DBGV("PdelayReq MSG sent ! \n");
+
+#ifdef SO_TIMESTAMPING
+
+#ifdef PTPD_PCAP
+ if((ptpClock->netPath.pcapEvent == NULL) && !ptpClock->netPath.txTimestampFailure) {
+#else
+ if(!ptpClock->netPath.txTimestampFailure) {
+#endif /* PTPD_PCAP */
+ if (respectUtcOffset(rtOpts, ptpClock) == TRUE) {
+ internalTime.seconds += ptpClock->timePropertiesDS.currentUtcOffset;
+ }
+ processPdelayReqFromSelf(&internalTime, rtOpts, ptpClock);
+ }
+#endif
+
+ ptpClock->sentPdelayReqSequenceId++;
+ ptpClock->counters.pdelayReqMessagesSent++;
+ }
+}
+
+/*Pack and send on event multicast ip adress a PdelayResp message*/
+static void
+issuePdelayResp(const TimeInternal *tint,MsgHeader *header, Integer32 sourceAddress, const RunTimeOpts *rtOpts,
+ PtpClock *ptpClock)
+{
+
+ Timestamp requestReceiptTimestamp;
+ TimeInternal internalTime;
+
+ Integer32 dst = 0;
+
+ /* see LEAPNOTE01# in this file */
+ if(ptpClock->leapSecondInProgress) {
+ DBG("Leap second in progress - will not send PDELAY_RESP\n");
+ return;
+ }
+
+ /* if request was unicast and we're running unicast, reply to source */
+ if ( (rtOpts->ipMode != IPMODE_MULTICAST) &&
+ (header->flagField0 & PTP_UNICAST) == PTP_UNICAST) {
+ dst = sourceAddress;
+ }
+
+ fromInternalTime(tint,&requestReceiptTimestamp);
+ msgPackPdelayResp(ptpClock->msgObuf,header,
+ &requestReceiptTimestamp,ptpClock);
+
+ if (!netSendPeerEvent(ptpClock->msgObuf,PDELAY_RESP_LENGTH,
+ &ptpClock->netPath, rtOpts, dst, &internalTime)) {
+ toState(PTP_FAULTY,rtOpts,ptpClock);
+ ptpClock->counters.messageSendErrors++;
+ DBGV("PdelayResp message can't be sent -> FAULTY state \n");
+ } else {
+ DBGV("PdelayResp MSG sent ! \n");
+
+#ifdef SO_TIMESTAMPING
+
+#ifdef PTPD_PCAP
+ if((ptpClock->netPath.pcapEvent == NULL) && !ptpClock->netPath.txTimestampFailure) {
+#else
+ if(!ptpClock->netPath.txTimestampFailure) {
+#endif /* PTPD_PCAP */
+ if (respectUtcOffset(rtOpts, ptpClock) == TRUE) {
+ internalTime.seconds += ptpClock->timePropertiesDS.currentUtcOffset;
+ }
+ processPdelayRespFromSelf(&internalTime, rtOpts, ptpClock, dst, header->sequenceId);
+ }
+#endif
+
+ ptpClock->counters.pdelayRespMessagesSent++;
+ ptpClock->lastPdelayRespDst = dst;
+ }
+}
+
+
+/*Pack and send a DelayResp message on event socket*/
+static void
+issueDelayResp(const TimeInternal *tint,MsgHeader *header,Integer32 sourceAddress, const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ Timestamp requestReceiptTimestamp;
+ Integer32 dst;
+
+ fromInternalTime(tint,&requestReceiptTimestamp);
+ msgPackDelayResp(ptpClock->msgObuf,header,&requestReceiptTimestamp,
+ ptpClock);
+
+ /* if request was unicast and we're running unicast, reply to source */
+ if ( (rtOpts->ipMode != IPMODE_MULTICAST) &&
+ (header->flagField0 & PTP_UNICAST) == PTP_UNICAST) {
+ dst = sourceAddress;
+ } else {
+ dst = 0;
+ }
+
+ if (!netSendGeneral(ptpClock->msgObuf, DELAY_RESP_LENGTH,
+ &ptpClock->netPath, rtOpts, dst)) {
+ toState(PTP_FAULTY,rtOpts,ptpClock);
+ ptpClock->counters.messageSendErrors++;
+ DBGV("delayResp message can't be sent -> FAULTY state \n");
+ } else {
+ DBGV("PdelayResp MSG sent ! \n");
+ ptpClock->counters.delayRespMessagesSent++;
+ }
+}
+
+static void
+issuePdelayRespFollowUp(const TimeInternal *tint, MsgHeader *header, Integer32 dst,
+ const RunTimeOpts *rtOpts, PtpClock *ptpClock, const UInteger16 sequenceId)
+{
+ Timestamp responseOriginTimestamp;
+ fromInternalTime(tint,&responseOriginTimestamp);
+
+ msgPackPdelayRespFollowUp(ptpClock->msgObuf,header,
+ &responseOriginTimestamp,ptpClock, sequenceId);
+ if (!netSendPeerGeneral(ptpClock->msgObuf,
+ PDELAY_RESP_FOLLOW_UP_LENGTH,
+ &ptpClock->netPath, rtOpts, dst)) {
+ toState(PTP_FAULTY,rtOpts,ptpClock);
+ ptpClock->counters.messageSendErrors++;
+ DBGV("PdelayRespFollowUp message can't be sent -> FAULTY state \n");
+ } else {
+ DBGV("PdelayRespFollowUp MSG sent ! \n");
+ ptpClock->counters.pdelayRespFollowUpMessagesSent++;
+ }
+}
+
+void
+addForeign(Octet *buf,MsgHeader *header,PtpClock *ptpClock, UInteger8 localPreference, UInteger32 sourceAddr)
+{
+ int i,j;
+ Boolean found = FALSE;
+
+ DBGV("addForeign localPref: %d\n", localPreference);
+
+ j = ptpClock->foreign_record_best;
+
+ /*Check if Foreign master is already known*/
+ for (i=0;i<ptpClock->number_foreign_records;i++) {
+ if (!memcmp(header->sourcePortIdentity.clockIdentity,
+ ptpClock->foreign[j].foreignMasterPortIdentity.clockIdentity,
+ CLOCK_IDENTITY_LENGTH) &&
+ (header->sourcePortIdentity.portNumber ==
+ ptpClock->foreign[j].foreignMasterPortIdentity.portNumber))
+ {
+ /*Foreign Master is already in Foreignmaster data set*/
+ ptpClock->foreign[j].foreignMasterAnnounceMessages++;
+ found = TRUE;
+ DBGV("addForeign : AnnounceMessage incremented \n");
+ msgUnpackHeader(buf,&ptpClock->foreign[j].header);
+ msgUnpackAnnounce(buf,&ptpClock->foreign[j].announce);
+ ptpClock->foreign[j].disqualified = FALSE;
+ ptpClock->foreign[j].localPreference = localPreference;
+ break;
+ }
+
+ j = (j+1)%ptpClock->number_foreign_records;
+ }
+
+ /*New Foreign Master*/
+ if (!found) {
+ if (ptpClock->number_foreign_records <
+ ptpClock->max_foreign_records) {
+ ptpClock->number_foreign_records++;
+ }
+
+ /* Preserve best master record from overwriting (sf FR #22) - use next slot */
+ if (ptpClock->foreign_record_i == ptpClock->foreign_record_best) {
+ ptpClock->foreign_record_i++;
+ ptpClock->foreign_record_i %= ptpClock->number_foreign_records;
+ }
+
+ j = ptpClock->foreign_record_i;
+
+ /*Copy new foreign master data set from Announce message*/
+ copyClockIdentity(ptpClock->foreign[j].foreignMasterPortIdentity.clockIdentity,
+ header->sourcePortIdentity.clockIdentity);
+ ptpClock->foreign[j].foreignMasterPortIdentity.portNumber =
+ header->sourcePortIdentity.portNumber;
+ ptpClock->foreign[j].foreignMasterAnnounceMessages = 0;
+ ptpClock->foreign[j].localPreference = localPreference;
+ ptpClock->foreign[j].sourceAddr = sourceAddr;
+ ptpClock->foreign[j].disqualified = FALSE;
+ /*
+ * header and announce field of each Foreign Master are
+ * usefull to run Best Master Clock Algorithm
+ */
+ msgUnpackHeader(buf,&ptpClock->foreign[j].header);
+ msgUnpackAnnounce(buf,&ptpClock->foreign[j].announce);
+ DBGV("New foreign Master added \n");
+
+ ptpClock->foreign_record_i =
+ (ptpClock->foreign_record_i+1) %
+ ptpClock->max_foreign_records;
+ }
+}
+
+/* Update dataset fields which are safe to change without going into INITIALIZING */
+void
+updateDatasets(PtpClock* ptpClock, const RunTimeOpts* rtOpts)
+{
+
+ if(rtOpts->unicastNegotiation) {
+ updateUnicastGrantTable(ptpClock->unicastGrants,
+ ptpClock->unicastDestinationCount, rtOpts);
+ if(rtOpts->unicastPeerDestinationSet) {
+ updateUnicastGrantTable(&ptpClock->peerGrants,
+ 1, rtOpts);
+
+ }
+ }
+
+ memset(ptpClock->userDescription, 0, sizeof(ptpClock->userDescription));
+ memcpy(ptpClock->userDescription, rtOpts->portDescription, strlen(rtOpts->portDescription));
+
+ switch(ptpClock->portDS.portState) {
+
+ /* We are master so update both the port and the parent dataset */
+ case PTP_MASTER:
+
+ if(rtOpts->dot1AS) {
+ ptpClock->portDS.transportSpecific = TSP_ETHERNET_AVB;
+ } else {
+ ptpClock->portDS.transportSpecific = TSP_DEFAULT;
+ }
+
+ ptpClock->defaultDS.numberPorts = NUMBER_PORTS;
+ ptpClock->portDS.portIdentity.portNumber = rtOpts->portNumber;
+
+ ptpClock->portDS.delayMechanism = rtOpts->delayMechanism;
+ ptpClock->portDS.versionNumber = VERSION_PTP;
+
+ ptpClock->defaultDS.clockQuality.clockAccuracy =
+ rtOpts->clockQuality.clockAccuracy;
+ ptpClock->defaultDS.clockQuality.clockClass = rtOpts->clockQuality.clockClass;
+ ptpClock->defaultDS.clockQuality.offsetScaledLogVariance =
+ rtOpts->clockQuality.offsetScaledLogVariance;
+ ptpClock->defaultDS.priority1 = rtOpts->priority1;
+ ptpClock->defaultDS.priority2 = rtOpts->priority2;
+
+ ptpClock->parentDS.grandmasterClockQuality.clockAccuracy =
+ ptpClock->defaultDS.clockQuality.clockAccuracy;
+ ptpClock->parentDS.grandmasterClockQuality.clockClass =
+ ptpClock->defaultDS.clockQuality.clockClass;
+ ptpClock->parentDS.grandmasterClockQuality.offsetScaledLogVariance =
+ ptpClock->defaultDS.clockQuality.offsetScaledLogVariance;
+ ptpClock->defaultDS.clockQuality.clockAccuracy =
+ ptpClock->parentDS.grandmasterPriority1 = ptpClock->defaultDS.priority1;
+ ptpClock->parentDS.grandmasterPriority2 = ptpClock->defaultDS.priority2;
+ ptpClock->timePropertiesDS.currentUtcOffsetValid = rtOpts->timeProperties.currentUtcOffsetValid;
+ ptpClock->timePropertiesDS.currentUtcOffset = rtOpts->timeProperties.currentUtcOffset;
+ ptpClock->timePropertiesDS.timeTraceable = rtOpts->timeProperties.timeTraceable;
+ ptpClock->timePropertiesDS.frequencyTraceable = rtOpts->timeProperties.frequencyTraceable;
+ ptpClock->timePropertiesDS.ptpTimescale = rtOpts->timeProperties.ptpTimescale;
+ ptpClock->timePropertiesDS.timeSource = rtOpts->timeProperties.timeSource;
+ ptpClock->portDS.logAnnounceInterval = rtOpts->logAnnounceInterval;
+ ptpClock->portDS.announceReceiptTimeout = rtOpts->announceReceiptTimeout;
+ ptpClock->portDS.logSyncInterval = rtOpts->logSyncInterval;
+ ptpClock->portDS.logMinPdelayReqInterval = rtOpts->logMinPdelayReqInterval;
+ ptpClock->portDS.logMinDelayReqInterval = rtOpts->initial_delayreq;
+ break;
+ /*
+ * we are not master so update the port dataset only - parent will be updated
+ * by m1() if we go master - basically update the fields affecting BMC only
+ */
+ case PTP_SLAVE:
+ if(ptpClock->portDS.logMinDelayReqInterval == UNICAST_MESSAGEINTERVAL &&
+ rtOpts->autoDelayReqInterval) {
+ NOTICE("Running at %d Delay Interval (unicast) - overriding with %d\n",
+ ptpClock->portDS.logMinDelayReqInterval, rtOpts->logMinDelayReqInterval);
+ ptpClock->portDS.logMinDelayReqInterval = rtOpts->logMinDelayReqInterval;
+ }
+ case PTP_PASSIVE:
+ ptpClock->defaultDS.numberPorts = NUMBER_PORTS;
+ ptpClock->portDS.portIdentity.portNumber = rtOpts->portNumber;
+
+ if(rtOpts->dot1AS) {
+ ptpClock->portDS.transportSpecific = TSP_ETHERNET_AVB;
+ } else {
+ ptpClock->portDS.transportSpecific = TSP_DEFAULT;
+ }
+
+ ptpClock->portDS.delayMechanism = rtOpts->delayMechanism;
+ ptpClock->portDS.versionNumber = VERSION_PTP;
+ ptpClock->defaultDS.clockQuality.clockAccuracy =
+ rtOpts->clockQuality.clockAccuracy;
+ ptpClock->defaultDS.clockQuality.clockClass = rtOpts->clockQuality.clockClass;
+ ptpClock->defaultDS.clockQuality.offsetScaledLogVariance =
+ rtOpts->clockQuality.offsetScaledLogVariance;
+ ptpClock->defaultDS.priority1 = rtOpts->priority1;
+ ptpClock->defaultDS.priority2 = rtOpts->priority2;
+ break;
+ /* in DISABLED state we still listen for management messages */
+ case PTP_DISABLED:
+ ptpClock->portDS.versionNumber = VERSION_PTP;
+ default:
+ /* In all other states the datasets will be updated when going into an operational state */
+ break;
+ }
+
+}
+
+void
+clearCounters(PtpClock * ptpClock)
+{
+ /* TODO: print port info */
+ DBG("Port counters cleared\n");
+ memset(&ptpClock->counters, 0, sizeof(ptpClock->counters));
+}
+
+Boolean
+respectUtcOffset(const RunTimeOpts * rtOpts, PtpClock * ptpClock) {
+ if (ptpClock->timePropertiesDS.currentUtcOffsetValid || rtOpts->alwaysRespectUtcOffset) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
diff --git a/rtemsbsd/ptpd/src/ptp_datatypes.h b/rtemsbsd/ptpd/src/ptp_datatypes.h
new file mode 100644
index 00000000..575ba645
--- /dev/null
+++ b/rtemsbsd/ptpd/src/ptp_datatypes.h
@@ -0,0 +1,592 @@
+#ifndef PTP_DATATYPES_H_
+#define PTP_DATATYPES_H_
+
+#include "ptp_primitives.h"
+
+/*Struct defined in spec*/
+
+
+/**
+*\file
+* \brief Main structures used in ptpdv2
+*
+* This header file defines structures defined by the spec,
+* main program data structure, and all messages structures
+ */
+
+/**
+* \brief Time structure to handle timestamps
+ */
+typedef struct {
+ Integer32 seconds;
+ Integer32 nanoseconds;
+} TimeInternal;
+
+/**
+* \brief The TimeInterval type represents time intervals
+ */
+typedef struct {
+ /* see src/def/README for a note on this X-macro */
+ #define OPERATE( name, size, type ) type name;
+ #include "def/derivedData/timeInterval.def"
+} TimeInterval;
+
+/**
+* \brief The Timestamp type represents a positive time with respect to the epoch
+ */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/derivedData/timestamp.def"
+} Timestamp;
+
+/**
+* \brief The ClockIdentity type identifies a clock
+ */
+typedef Octet ClockIdentity[CLOCK_IDENTITY_LENGTH];
+
+/**
+* \brief The PortIdentity identifies a PTP port.
+ */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/derivedData/portIdentity.def"
+} PortIdentity;
+
+/**
+* \brief The PortAdress type represents the protocol address of a PTP port
+ */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/derivedData/portAddress.def"
+} PortAddress;
+
+/**
+* \brief The ClockQuality represents the quality of a clock
+ */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/derivedData/clockQuality.def"
+} ClockQuality;
+
+/**
+* \brief The TimePropertiesDS type represent time source and traceability properties of a clock
+ */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/derivedData/timePropertiesDS.def"
+} TimePropertiesDS;
+
+/**
+* \brief The TLV type represents TLV extension fields
+ */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/derivedData/tlv.def"
+} TLV;
+
+/**
+* \brief The PTPText data type is used to represent textual material in PTP messages
+ */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/derivedData/ptpText.def"
+} PTPText;
+
+/**
+* \brief The FaultRecord type is used to construct fault logs
+ */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/derivedData/faultRecord.def"
+} FaultRecord;
+
+/**
+* \brief The PhysicalAddress type is used to represent a physical address
+ */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/derivedData/physicalAddress.def"
+} PhysicalAddress;
+
+
+/**
+* \brief The common header for all PTP messages (Table 18 of the spec)
+ */
+/* Message header */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/message/header.def"
+} MsgHeader;
+
+/**
+* \brief Announce message fields (Table 25 of the spec)
+ */
+/*Announce Message */
+typedef struct {
+ Timestamp originTimestamp;
+ Integer16 currentUtcOffset;
+ UInteger8 grandmasterPriority1;
+ ClockQuality grandmasterClockQuality;
+ UInteger8 grandmasterPriority2;
+ ClockIdentity grandmasterIdentity;
+ UInteger16 stepsRemoved;
+ Enumeration8 timeSource;
+}MsgAnnounce;
+
+
+/**
+* \brief Sync message fields (Table 26 of the spec)
+ */
+/*Sync Message */
+typedef struct {
+ Timestamp originTimestamp;
+}MsgSync;
+
+/**
+* \brief DelayReq message fields (Table 26 of the spec)
+ */
+/*DelayReq Message */
+typedef struct {
+ Timestamp originTimestamp;
+}MsgDelayReq;
+
+/**
+* \brief DelayResp message fields (Table 30 of the spec)
+ */
+/*delayResp Message*/
+typedef struct {
+ Timestamp receiveTimestamp;
+ PortIdentity requestingPortIdentity;
+}MsgDelayResp;
+
+/**
+* \brief FollowUp message fields (Table 27 of the spec)
+ */
+/*Follow-up Message*/
+typedef struct {
+ Timestamp preciseOriginTimestamp;
+}MsgFollowUp;
+
+/**
+* \brief PdelayReq message fields (Table 29 of the spec)
+ */
+/*PdelayReq Message*/
+typedef struct {
+ Timestamp originTimestamp;
+}MsgPdelayReq;
+
+/**
+* \brief PdelayResp message fields (Table 30 of the spec)
+ */
+/*PdelayResp Message*/
+typedef struct {
+ Timestamp requestReceiptTimestamp;
+ PortIdentity requestingPortIdentity;
+}MsgPdelayResp;
+
+/**
+* \brief PdelayRespFollowUp message fields (Table 31 of the spec)
+ */
+/*PdelayRespFollowUp Message*/
+typedef struct {
+ Timestamp responseOriginTimestamp;
+ PortIdentity requestingPortIdentity;
+} MsgPdelayRespFollowUp;
+
+
+/**
+ * \brief Management TLV message fields
+ */
+/* Management TLV Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/managementTLV.def"
+ Octet* dataField;
+} ManagementTLV;
+
+/**
+ * \brief Management TLV Clock Description fields (Table 41 of the spec)
+ */
+/* Management TLV Clock Description Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/clockDescription.def"
+} MMClockDescription;
+
+/**
+ * \brief Management TLV User Description fields (Table 43 of the spec)
+ */
+/* Management TLV User Description Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/userDescription.def"
+} MMUserDescription;
+
+/**
+ * \brief Management TLV Initialize fields (Table 44 of the spec)
+ */
+/* Management TLV Initialize Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/initialize.def"
+} MMInitialize;
+
+/**
+ * \brief Management TLV Default Data Set fields (Table 50 of the spec)
+ */
+/* Management TLV Default Data Set Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/defaultDataSet.def"
+} MMDefaultDataSet;
+
+/**
+ * \brief Management TLV Current Data Set fields (Table 55 of the spec)
+ */
+/* Management TLV Current Data Set Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/currentDataSet.def"
+} MMCurrentDataSet;
+
+/**
+ * \brief Management TLV Parent Data Set fields (Table 56 of the spec)
+ */
+/* Management TLV Parent Data Set Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/parentDataSet.def"
+} MMParentDataSet;
+
+/**
+ * \brief Management TLV Time Properties Data Set fields (Table 57 of the spec)
+ */
+/* Management TLV Time Properties Data Set Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/timePropertiesDataSet.def"
+} MMTimePropertiesDataSet;
+
+/**
+ * \brief Management TLV Port Data Set fields (Table 61 of the spec)
+ */
+/* Management TLV Port Data Set Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/portDataSet.def"
+} MMPortDataSet;
+
+/**
+ * \brief Management TLV Priority1 fields (Table 51 of the spec)
+ */
+/* Management TLV Priority1 Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/priority1.def"
+} MMPriority1;
+
+/**
+ * \brief Management TLV Priority2 fields (Table 52 of the spec)
+ */
+/* Management TLV Priority2 Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/priority2.def"
+} MMPriority2;
+
+/**
+ * \brief Management TLV Domain fields (Table 53 of the spec)
+ */
+/* Management TLV Domain Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/domain.def"
+} MMDomain;
+
+/**
+ * \brief Management TLV Slave Only fields (Table 54 of the spec)
+ */
+/* Management TLV Slave Only Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/slaveOnly.def"
+} MMSlaveOnly;
+
+/**
+ * \brief Management TLV Log Announce Interval fields (Table 62 of the spec)
+ */
+/* Management TLV Log Announce Interval Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/logAnnounceInterval.def"
+} MMLogAnnounceInterval;
+
+/**
+ * \brief Management TLV Announce Receipt Timeout fields (Table 63 of the spec)
+ */
+/* Management TLV Announce Receipt Timeout Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/announceReceiptTimeout.def"
+} MMAnnounceReceiptTimeout;
+
+/**
+ * \brief Management TLV Log Sync Interval fields (Table 64 of the spec)
+ */
+/* Management TLV Log Sync Interval Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/logSyncInterval.def"
+} MMLogSyncInterval;
+
+/**
+ * \brief Management TLV Version Number fields (Table 67 of the spec)
+ */
+/* Management TLV Version Number Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/versionNumber.def"
+} MMVersionNumber;
+
+/**
+ * \brief Management TLV Time fields (Table 48 of the spec)
+ */
+/* Management TLV Time Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/time.def"
+} MMTime;
+
+/**
+ * \brief Management TLV Clock Accuracy fields (Table 49 of the spec)
+ */
+/* Management TLV Clock Accuracy Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/clockAccuracy.def"
+} MMClockAccuracy;
+
+/**
+ * \brief Management TLV UTC Properties fields (Table 58 of the spec)
+ */
+/* Management TLV UTC Properties Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/utcProperties.def"
+} MMUtcProperties;
+
+/**
+ * \brief Management TLV Traceability Properties fields (Table 59 of the spec)
+ */
+/* Management TLV Traceability Properties Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/traceabilityProperties.def"
+} MMTraceabilityProperties;
+
+/**
+ * \brief Management TLV Timescale Properties fields (Table 59 of the spec)
+ */
+/* Management TLV Timescale Properties Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/timescaleProperties.def"
+} MMTimescaleProperties;
+
+/**
+ * \brief Management TLV Unicast Negotiation Enable fields (Table 73 of the spec)
+ */
+/* Management TLV Traceability Properties Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/unicastNegotiationEnable.def"
+} MMUnicastNegotiationEnable;
+
+/**
+ * \brief Management TLV Delay Mechanism fields (Table 65 of the spec)
+ */
+/* Management TLV Delay Mechanism Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/delayMechanism.def"
+} MMDelayMechanism;
+
+/**
+ * \brief Management TLV Log Min Pdelay Req Interval fields (Table 66 of the spec)
+ */
+/* Management TLV Log Min Pdelay Req Interval Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/logMinPdelayReqInterval.def"
+} MMLogMinPdelayReqInterval;
+
+/**
+ * \brief Management TLV Error Status fields (Table 71 of the spec)
+ */
+/* Management TLV Error Status Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/managementTLV/errorStatus.def"
+} MMErrorStatus;
+
+/**
+* \brief Management message fields (Table 37 of the spec)
+ */
+/*management Message*/
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/message/management.def"
+ ManagementTLV* tlv;
+} MsgManagement;
+
+/**
+ * \brief Signaling TLV message fields (tab
+ */
+/* Signaling TLV Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/signalingTLV/signalingTLV.def"
+ Octet* valueField;
+} SignalingTLV;
+
+/**
+ * \brief Signaling TLV Request Unicast Transmission fields (Table 73 of the spec)
+ */
+/* Signaling TLV Request Unicast Transmission Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/signalingTLV/requestUnicastTransmission.def"
+} SMRequestUnicastTransmission;
+
+/**
+ * \brief Signaling TLV Grant Unicast Transmission fields (Table 74 of the spec)
+ */
+/* Signaling TLV Grant Unicast Transmission Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/signalingTLV/grantUnicastTransmission.def"
+} SMGrantUnicastTransmission;
+
+/**
+ * \brief Signaling TLV Cancel Unicast Transmission fields (Table 75 of the spec)
+ */
+/* Signaling TLV Cancel Unicast Transmission Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/signalingTLV/cancelUnicastTransmission.def"
+} SMCancelUnicastTransmission;
+
+/**
+ * \brief Signaling TLV Acknowledge Cancel Unicast Transmission fields (Table 76 of the spec)
+ */
+/* Signaling TLV Acknowledge Cancel Unicast Transmission Message */
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/signalingTLV/acknowledgeCancelUnicastTransmission.def"
+} SMAcknowledgeCancelUnicastTransmission;
+
+/**
+* \brief Signaling message fields (Table 33 of the spec)
+ */
+/*Signaling Message*/
+typedef struct {
+ #define OPERATE( name, size, type ) type name;
+ #include "def/message/signaling.def"
+ SignalingTLV* tlv;
+} MsgSignaling;
+
+
+/**
+* \brief ForeignMasterRecord is used to manage foreign masters
+ */
+typedef struct
+{
+ PortIdentity foreignMasterPortIdentity;
+ UInteger16 foreignMasterAnnounceMessages;
+
+ /* Supplementary data */
+ MsgAnnounce announce; /* announce message -> all datasets */
+ MsgHeader header; /* header -> some datasets */
+ UInteger8 localPreference; /* local preference - only used by telecom profile */
+ UInteger32 sourceAddr; /* source address */
+ Boolean disqualified; /* if true, this one always loses */
+} ForeignMasterRecord;
+
+typedef struct {
+
+ /*Static members*/
+ Boolean twoStepFlag;
+ ClockIdentity clockIdentity;
+ UInteger16 numberPorts;
+
+ /*Dynamic members*/
+ ClockQuality clockQuality;
+
+ /*Configurable members*/
+ UInteger8 priority1;
+ UInteger8 priority2;
+ UInteger8 domainNumber;
+ Boolean slaveOnly;
+
+} DefaultDS;
+
+typedef struct {
+
+ UInteger16 stepsRemoved;
+ TimeInternal offsetFromMaster;
+ TimeInternal meanPathDelay;
+
+ /* PTPd additions */
+ UInteger32 offsetFromMasterThreshold; /* maximum offset from master */
+
+} CurrentDS;
+
+typedef struct {
+ /*Dynamic members*/
+ PortIdentity parentPortIdentity;
+ Boolean parentStats;
+ UInteger16 observedParentOffsetScaledLogVariance;
+ Integer32 observedParentClockPhaseChangeRate;
+ ClockIdentity grandmasterIdentity;
+ ClockQuality grandmasterClockQuality;
+ UInteger8 grandmasterPriority1;
+ UInteger8 grandmasterPriority2;
+} ParentDS;
+
+typedef struct {
+
+ /*Static members*/
+ PortIdentity portIdentity;
+
+ /*Dynamic members*/
+ Enumeration8 portState;
+ Integer8 logMinDelayReqInterval;
+ TimeInternal peerMeanPathDelay;
+
+ /*Configurable members*/
+ Integer8 logAnnounceInterval;
+ UInteger8 announceReceiptTimeout;
+ Integer8 logSyncInterval;
+ Enumeration8 delayMechanism;
+ Integer8 logMinPdelayReqInterval;
+ UInteger4 versionNumber;
+
+ /* PTPd additions */
+ Enumeration8 lastPortState; /* previous state */
+ Nibble transportSpecific; /* TransportSpecific for 802.1AS */
+ Integer16 lastMismatchedDomain; /* domain mismatch detection */
+
+} PortDS;
+
+/* structure to hold basic PTP datasets when capturing an event */
+typedef struct {
+ DefaultDS defaultDS;
+ CurrentDS currentDS;
+ TimePropertiesDS timePropertiesDS;
+ PortDS portDS;
+ ParentDS parentDS;
+ ForeignMasterRecord bestMaster;
+ Integer32 ofmAlarmThreshold;
+} PtpEventData;
+
+#endif /*PTP_DATATYPES_H_*/
diff --git a/rtemsbsd/ptpd/src/ptp_primitives.h b/rtemsbsd/ptpd/src/ptp_primitives.h
new file mode 100644
index 00000000..56ded20c
--- /dev/null
+++ b/rtemsbsd/ptpd/src/ptp_primitives.h
@@ -0,0 +1,41 @@
+#ifndef PTP_PRIMITIVES_H_
+#define PTP_PRIMITIVES_H_
+
+
+typedef enum {FALSE=0, TRUE} Boolean;
+typedef char Octet;
+typedef int8_t Integer8;
+typedef int16_t Integer16;
+typedef int32_t Integer32;
+typedef uint8_t UInteger8;
+typedef uint16_t UInteger16;
+typedef uint32_t UInteger32;
+typedef uint16_t Enumeration16;
+typedef unsigned char Enumeration8;
+typedef unsigned char Enumeration4;
+typedef unsigned char Enumeration4Upper;
+typedef unsigned char Enumeration4Lower;
+typedef unsigned char UInteger4;
+typedef unsigned char UInteger4Upper;
+typedef unsigned char UInteger4Lower;
+typedef unsigned char Nibble;
+typedef unsigned char NibbleUpper;
+typedef unsigned char NibbleLower;
+
+/**
+* \brief Implementation specific of UInteger48 type
+ */
+typedef struct {
+ uint32_t lsb;
+ uint16_t msb;
+} UInteger48;
+
+/**
+* \brief Implementation specific of Integer64 type
+ */
+typedef struct {
+ uint32_t lsb;
+ int32_t msb;
+} Integer64;
+
+#endif /*PTP_PRIMITIVES_H_*/
diff --git a/rtemsbsd/ptpd/src/ptp_timers.c b/rtemsbsd/ptpd/src/ptp_timers.c
new file mode 100644
index 00000000..41aeab97
--- /dev/null
+++ b/rtemsbsd/ptpd/src/ptp_timers.c
@@ -0,0 +1,160 @@
+/*-
+ * Copyright (c) 2015 Wojciech Owczarek,
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file timer.c
+ * @date Wed Oct 1 00:41:26 2014
+ *
+ * @brief PTP timer handler code
+ *
+ * Glue code providing PTPd with event timers.
+ * This code can be re-written to make use of any timer
+ * implementation. Provided is an EventTimer which uses
+ * a fixed tick interval timer, or POSIX timers, depending
+ * on what is available. So the options are:
+ * - write another EventTimer implementation,
+ * using the existing framework,
+ * - write something different
+ */
+
+#include "ptpd.h"
+
+void
+timerStop(IntervalTimer * itimer)
+{
+ if (itimer == NULL)
+ return;
+ EventTimer *timer = (EventTimer *)(itimer->data);
+
+ timer->stop(timer);
+}
+
+void
+timerStart(IntervalTimer * itimer, double interval)
+{
+ if (itimer == NULL)
+ return;
+
+ if(interval > PTPTIMER_MAX_INTERVAL) {
+ interval = PTPTIMER_MAX_INTERVAL;
+ }
+
+ itimer->interval = interval;
+ EventTimer* timer = (EventTimer *)(itimer->data);
+
+ timer->start(timer, interval);
+}
+
+Boolean
+timerExpired(IntervalTimer * itimer)
+{
+
+ if (itimer == NULL)
+ return FALSE;
+
+ EventTimer *timer = (EventTimer *)(itimer->data);
+
+ return timer->isExpired(timer);
+}
+
+Boolean
+timerRunning(IntervalTimer * itimer)
+{
+
+ if (itimer==NULL)
+ return FALSE;
+
+ EventTimer *timer = (EventTimer *)(itimer->data);
+
+ return timer->isRunning(timer);
+}
+
+Boolean timerSetup(IntervalTimer *itimers)
+{
+
+ Boolean ret = TRUE;
+
+/* WARNING: these descriptions MUST be in the same order,
+ * and in the same number as the enum in ptp_timers.h
+ */
+
+ static const char* timerDesc[PTP_MAX_TIMER] = {
+ "PDELAYREQ_INTERVAL",
+ "DELAYREQ_INTERVAL",
+ "SYNC_INTERVAL",
+ "ANNOUNCE_RECEIPT",
+ "ANNOUNCE_INTERVAL",
+ "SYNC_RECEIPT",
+ "DELAY_RECEIPT",
+ "UNICAST_GRANT",
+ "OPERATOR_MESSAGES",
+ "LEAP_SECOND_PAUSE",
+ "STATUSFILE_UPDATE",
+ "PANIC_MODE",
+ "PERIODIC_INFO_TIMER",
+#ifdef PTPD_STATISTICS
+ "STATISTICS_UPDATE",
+#endif /* PTPD_STATISTICS */
+ "ALARM_UPDATE",
+ "MASTER_NETREFRESH",
+ "CALIBRATION_DELAY",
+ "CLOCK_UPDATE",
+ "TIMINGDOMAIN_UPDATE"
+ };
+
+ int i = 0;
+
+ startEventTimers();
+
+ for(i=0; i<PTP_MAX_TIMER; i++) {
+
+ itimers[i].data = NULL;
+ itimers[i].data = (void *)(createEventTimer(timerDesc[i]));
+ if(itimers[i].data == NULL) {
+ ret = FALSE;
+ }
+ }
+
+ return ret;
+
+}
+
+void timerShutdown(IntervalTimer *itimers)
+{
+
+ int i = 0;
+ EventTimer *timer = NULL;
+
+
+ for(i=0; i<PTP_MAX_TIMER; i++) {
+ timer = (EventTimer*)(itimers[i].data);
+ freeEventTimer(&timer);
+ }
+
+ shutdownEventTimers();
+
+}
diff --git a/rtemsbsd/ptpd/src/ptp_timers.h b/rtemsbsd/ptpd/src/ptp_timers.h
new file mode 100644
index 00000000..997935cb
--- /dev/null
+++ b/rtemsbsd/ptpd/src/ptp_timers.h
@@ -0,0 +1,95 @@
+#ifndef PTP_TIMERS_H_
+#define PTP_TIMERS_H_
+
+#include "ptpd.h"
+
+/*-
+ * Copyright (c) 2015 Wojciech Owczarek,
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#ifdef PTPD_PTIMERS
+#define LOG_MIN_INTERVAL -7
+#else
+/* 62.5ms tick for interval timers = 16/sec max */
+#define LOG_MIN_INTERVAL -4
+#endif /* PTPD_PTIMERS */
+
+/* safeguard: a week */
+#define PTPTIMER_MAX_INTERVAL 604800
+
+/**
+* \brief Structure used as a timer
+ */
+typedef struct {
+ double interval;
+ Boolean expired;
+ Boolean running;
+ /* hook for a generic timer object that can be assigned */
+ void *data;
+} IntervalTimer;
+
+/* WARNING: when updating these timers,
+ * you MUST update timerSetup() in ptp_timers.c accordingly!
+ * otherwise expect a segfault if there are more timers here
+ * than descriptions in ptp_timers.c
+ */
+
+enum {
+ PDELAYREQ_INTERVAL_TIMER=0,/**<\brief Timer handling the PdelayReq Interval*/
+ DELAYREQ_INTERVAL_TIMER,/**<\brief Timer handling the delayReq Interva*/
+ SYNC_INTERVAL_TIMER,/**<\brief Timer handling Interval between master sends two Syncs messages */
+ ANNOUNCE_RECEIPT_TIMER,/**<\brief Timer handling announce receipt timeout*/
+ ANNOUNCE_INTERVAL_TIMER, /**<\brief Timer handling interval before master sends two announce messages*/
+ /* non-spec timers */
+ SYNC_RECEIPT_TIMER,
+ DELAY_RECEIPT_TIMER,
+ UNICAST_GRANT_TIMER, /* used to age out unicast grants (sent, received) */
+ OPERATOR_MESSAGES_TIMER, /* used to limit the operator messages */
+ LEAP_SECOND_PAUSE_TIMER, /* timer used for pausing updates when leap second is imminent */
+ STATUSFILE_UPDATE_TIMER, /* timer used for refreshing the status file */
+ PANIC_MODE_TIMER, /* timer used for the duration of "panic mode" */
+ PERIODIC_INFO_TIMER, /* timer used for dumping periodic status updates */
+#ifdef PTPD_STATISTICS
+ STATISTICS_UPDATE_TIMER, /* online mean / std dev updare interval (non-moving statistics) */
+#endif /* PTPD_STATISTICS */
+ ALARM_UPDATE_TIMER,
+ MASTER_NETREFRESH_TIMER,
+ CALIBRATION_DELAY_TIMER,
+ CLOCK_UPDATE_TIMER,
+ TIMINGDOMAIN_UPDATE_TIMER,
+ PTP_MAX_TIMER
+};
+
+/* functions used by 1588 only */
+void timerStop(IntervalTimer *itimer);
+void timerStart(IntervalTimer * itimer, double interval);
+Boolean timerExpired(IntervalTimer * itimer);
+Boolean timerRunning(IntervalTimer * itimer);
+Boolean timerSetup(IntervalTimer *itimers);
+void timerShutdown(IntervalTimer *itimers);
+
+#endif /* PTP_TIMERS_H_ */
diff --git a/rtemsbsd/ptpd/src/ptpd.c b/rtemsbsd/ptpd/src/ptpd.c
new file mode 100644
index 00000000..3462dd21
--- /dev/null
+++ b/rtemsbsd/ptpd/src/ptpd.c
@@ -0,0 +1,139 @@
+/*-
+ * Copyright (c) 2012-2013 Wojciech Owczarek,
+ * Copyright (c) 2011-2012 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen,
+ * Inaqui Delgado,
+ * Rick Ratzel,
+ * National Instruments.
+ * Copyright (c) 2009-2010 George V. Neville-Neil,
+ * Steven Kreuzer,
+ * Martin Burnicki,
+ * Jan Breuer,
+ * Gael Mace,
+ * Alexandre Van Kempen
+ *
+ * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file ptpd.c
+ * @date Wed Jun 23 10:13:38 2010
+ *
+ * @brief The main() function for the PTP daemon
+ *
+ * This file contains very little code, as should be obvious,
+ * and only serves to tie together the rest of the daemon.
+ * All of the default options are set here, but command line
+ * arguments and configuration file is processed in the
+ * ptpdStartup() routine called
+ * below.
+ */
+
+#include "ptpd.h"
+
+RunTimeOpts rtOpts; /* statically allocated run-time
+ * configuration data */
+
+Boolean startupInProgress;
+
+/*
+ * Global variable with the main PTP port. This is used to show the current state in DBG()/message()
+ * without having to pass the pointer everytime.
+ *
+ * if ptpd is extended to handle multiple ports (eg, to instantiate a Boundary Clock),
+ * then DBG()/message() needs a per-port pointer argument
+ */
+PtpClock *G_ptpClock = NULL;
+
+TimingDomain timingDomain;
+
+int
+main(int argc, char **argv)
+{
+ PtpClock *ptpClock;
+ Integer16 ret;
+ TimingService *ts;
+
+ startupInProgress = TRUE;
+
+ memset(&timingDomain, 0, sizeof(timingDomain));
+ timingDomainSetup(&timingDomain);
+
+ timingDomain.electionLeft = 10;
+
+ /* Initialize run time options with command line arguments */
+ if (!(ptpClock = ptpdStartup(argc, argv, &ret, &rtOpts))) {
+ if (ret != 0 && !rtOpts.checkConfigOnly)
+ ERROR(USER_DESCRIPTION" startup failed\n");
+ return ret;
+ }
+
+ timingDomain.electionDelay = rtOpts.electionDelay;
+
+ /* configure PTP TimeService */
+
+ timingDomain.services[0] = &ptpClock->timingService;
+ ts = timingDomain.services[0];
+ strncpy(ts->id, "PTP0", TIMINGSERVICE_MAX_DESC);
+ ts->dataSet.priority1 = rtOpts.preferNTP;
+ ts->dataSet.type = TIMINGSERVICE_PTP;
+ ts->config = &rtOpts;
+ ts->controller = ptpClock;
+ ts->timeout = rtOpts.idleTimeout;
+ ts->updateInterval = 1;
+ ts->holdTime = rtOpts.ntpOptions.failoverTimeout;
+ timingDomain.serviceCount = 1;
+
+ if (rtOpts.ntpOptions.enableEngine) {
+ ntpSetup(&rtOpts, ptpClock);
+ } else {
+ timingDomain.serviceCount = 1;
+ timingDomain.services[1] = NULL;
+ }
+
+ timingDomain.init(&timingDomain);
+ timingDomain.updateInterval = 1;
+
+ startupInProgress = FALSE;
+
+ /* global variable for message(), please see comment on top of this file */
+ G_ptpClock = ptpClock;
+
+ /* do the protocol engine */
+ protocol(&rtOpts, ptpClock);
+ /* forever loop.. */
+
+ /* this also calls ptpd shutdown */
+ timingDomain.shutdown(&timingDomain);
+
+ NOTIFY("Self shutdown\n");
+
+ return 1;
+}
diff --git a/rtemsbsd/ptpd/src/ptpd.h b/rtemsbsd/ptpd/src/ptpd.h
new file mode 100644
index 00000000..c5287cf5
--- /dev/null
+++ b/rtemsbsd/ptpd/src/ptpd.h
@@ -0,0 +1,494 @@
+/**
+ * @file ptpd.h
+ * @mainpage Ptpd v2 Documentation
+ * @authors Martin Burnicki, Alexandre van Kempen, Steven Kreuzer,
+ * George Neville-Neil
+ * @version 2.0
+ * @date Fri Aug 27 10:22:19 2010
+ *
+ * @section implementation Implementation
+ * PTPdV2 is not a full implementation of 1588 - 2008 standard.
+ * It is implemented only with use of Transparent Clock and Peer delay
+ * mechanism, according to 802.1AS requierements.
+ *
+ * This header file includes all others headers.
+ * It defines functions which are not dependant of the operating system.
+ */
+
+#ifndef PTPD_H_
+#define PTPD_H_
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+
+#ifdef linux
+# ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+ #endif /* _GNU_SOURCE */
+#endif
+
+#ifdef __sun
+# ifndef _XPG6
+# define _XPG6
+# endif /* _XPG6 */
+# ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 500
+# endif /* _XOPEN_SOURCE */
+# ifndef __EXTENSIONS__
+# define __EXTENSIONS__
+# endif /* __EXTENSIONS */
+#endif /* __sun */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif /* HAVE_STRINGS_H */
+#include <unistd.h>
+#include <math.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <limits.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#ifdef HAVE_SYS_TIMEX_H
+#include <sys/timex.h>
+#endif
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <arpa/inet.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <limits.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif /* HAVE_GETOPT_H */
+#include <ctype.h>
+#include <glob.h>
+#include <stddef.h>
+#include <stdint.h>
+#ifdef HAVE_UTMPX_H
+#include <utmpx.h>
+#else
+#ifdef HAVE_UTMP_H
+#include <utmp.h>
+#endif /* HAVE_UTMP_H */
+#endif /* HAVE_UTMPX_H */
+
+#ifdef HAVE_NET_ETHERNET_H
+#include <net/ethernet.h>
+#endif /* HAVE_NET_ETHERNET_H */
+
+#ifdef HAVE_UNIX_H /* setlinebuf() on QNX */
+#include <unix.h>
+#endif /* HAVE_UNIX_H */
+
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#ifdef HAVE_NETINET_ETHER_H
+#include <netinet/ether.h>
+#endif /* HAVE_NETINET_ETHER_H */
+
+#ifdef HAVE_NET_IF_ARP_H
+#include <net/if_arp.h>
+#endif /* HAVE_NET_IF_ARP_H*/
+
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif /* HAVE_NET_IF_H*/
+
+#ifdef HAVE_NETINET_IF_ETHER_H
+#include <netinet/if_ether.h>
+#endif /* HAVE_NETINET_IF_ETHER_H */
+
+
+#ifdef PTPD_PCAP
+#ifdef HAVE_PCAP_PCAP_H
+#include <pcap/pcap.h>
+#else
+/* Cases like RHEL5 and others where only pcap.h exists */
+#ifdef HAVE_PCAP_H
+#include <pcap.h>
+#endif /* HAVE_PCAP_H */
+#endif
+#endif
+#if defined(linux) && defined(HAVE_SCHED_H)
+#include <sched.h>
+#endif /* linux && HAVE_SCHED_H */
+
+#ifdef HAVE_SYS_CPUSET_H
+#include <sys/cpuset.h>
+#endif /* HAVE_SYS_CPUSET_H */
+
+#include "constants.h"
+#include "limits.h"
+
+/* Disable SO_TIMESTAMPING if configured to do so */
+#ifdef PTPD_DISABLE_SOTIMESTAMPING
+
+#ifdef SO_TIMESTAMPING
+
+#undef SO_TIMESTAMPING
+
+#endif /* SO_TIMESTAMPING */
+
+#endif /* PTPD_DISABLE_SOTIMESTAMPING */
+
+#include "dep/ipv4_acl.h"
+
+#include "dep/constants_dep.h"
+#include "dep/datatypes_dep.h"
+
+#include "ptp_timers.h"
+#include "dep/eventtimer.h"
+
+#include "dep/ntpengine/ntpdcontrol.h"
+#include "dep/ntpengine/ntp_isc_md5.h"
+
+#include "timingdomain.h"
+
+#ifdef PTPD_STATISTICS
+#include "dep/outlierfilter.h"
+#endif
+
+#include "datatypes.h"
+
+#ifdef PTPD_STATISTICS
+#include "dep/statistics.h"
+#endif
+
+#include "dep/ptpd_dep.h"
+#include "dep/iniparser/dictionary.h"
+#include "dep/iniparser/iniparser.h"
+#include "dep/daemonconfig.h"
+
+#include "dep/alarms.h"
+
+
+
+/* NOTE: this macro can be refactored into a function */
+#define XMALLOC(ptr,size) \
+ if(!((ptr)=malloc(size))) { \
+ PERROR("failed to allocate memory"); \
+ ptpdShutdown(ptpClock); \
+ exit(1); \
+ }
+
+#define SAFE_FREE(pointer) \
+ if(pointer != NULL) { \
+ free(pointer); \
+ pointer = NULL; \
+ }
+
+#define IS_SET(data, bitpos) \
+ ((data & ( 0x1 << bitpos )) == (0x1 << bitpos))
+
+#define SET_FIELD(data, bitpos) \
+ data << bitpos
+
+#ifndef min
+#define min(a,b) (((a)<(b))?(a):(b))
+#endif /* min */
+
+#ifndef max
+#define max(a,b) (((a)>(b))?(a):(b))
+#endif /* max */
+
+#ifdef HAVE_LINUX_RTC_H
+#include <linux/rtc.h>
+#endif /* HAVE_LINUX_RTC_H */
+
+#define SET_ALARM(alarm, val) \
+ setAlarmCondition(&ptpClock->alarms[alarm], val, ptpClock)
+
+/** \name arith.c
+ * -Timing management and arithmetic*/
+ /**\{*/
+/* arith.c */
+
+/**
+ * \brief Convert Integer64 into TimeInternal structure
+ */
+void integer64_to_internalTime(Integer64,TimeInternal*);
+/**
+ * \brief Convert TimeInternal structure to Integer64
+ */
+void internalTime_to_integer64(TimeInternal, Integer64*);
+/**
+ * \brief Convert TimeInternal into Timestamp structure (defined by the spec)
+ */
+void fromInternalTime(const TimeInternal*,Timestamp*);
+
+/**
+ * \brief Convert Timestamp to TimeInternal structure (defined by the spec)
+ */
+void toInternalTime(TimeInternal*, const Timestamp*);
+
+void ts_to_InternalTime(const struct timespec *, TimeInternal *);
+void tv_to_InternalTime(const struct timeval *, TimeInternal *);
+
+
+
+
+/**
+ * \brief Use to normalize a TimeInternal structure
+ *
+ * The nanosecondsField member must always be less than 10⁹
+ * This function is used after adding or subtracting TimeInternal
+ */
+void normalizeTime(TimeInternal*);
+
+/**
+ * \brief Add two InternalTime structure and normalize
+ */
+void addTime(TimeInternal*,const TimeInternal*,const TimeInternal*);
+
+/**
+ * \brief Substract two InternalTime structure and normalize
+ */
+void subTime(TimeInternal*,const TimeInternal*,const TimeInternal*);
+/** \}*/
+
+/**
+ * \brief Divied an InternalTime by 2
+ */
+void div2Time(TimeInternal *);
+
+/** \name bmc.c
+ * -Best Master Clock Algorithm functions*/
+ /**\{*/
+/* bmc.c */
+/**
+ * \brief Compare data set of foreign masters and local data set
+ * \return The recommended state for the port
+ */
+
+UInteger8 bmc(ForeignMasterRecord*, const RunTimeOpts*,PtpClock*);
+
+/* compare two portIdentTitties */
+int cmpPortIdentity(const PortIdentity *a, const PortIdentity *b);
+/* check if portIdentity is all zero */
+Boolean portIdentityEmpty(PortIdentity *portIdentity);
+/* check if portIdentity is all ones */
+Boolean portIdentityAllOnes(PortIdentity *portIdentity);
+
+/**
+ * \brief When recommended state is Master, copy local data into parent and grandmaster dataset
+ */
+void m1(const RunTimeOpts *, PtpClock*);
+
+/**
+ * \brief When recommended state is Slave, copy dataset of master into parent and grandmaster dataset
+ */
+void s1(MsgHeader*,MsgAnnounce*,PtpClock*, const RunTimeOpts *);
+
+
+void p1(PtpClock *ptpClock, const RunTimeOpts *rtOpts);
+
+/**
+ * \brief Initialize datas
+ */
+void initData(RunTimeOpts*,PtpClock*);
+/** \}*/
+
+
+/** \name protocol.c
+ * -Execute the protocol engine*/
+ /**\{*/
+/**
+ * \brief Protocol engine
+ */
+/* protocol.c */
+void protocol(RunTimeOpts*,PtpClock*);
+void updateDatasets(PtpClock* ptpClock, const RunTimeOpts* rtOpts);
+void setPortState(PtpClock *ptpClock, Enumeration8 state);
+
+Boolean acceptPortIdentity(PortIdentity thisPort, PortIdentity targetPort);
+
+/** \}*/
+
+/** \name management.c
+ * -Management message support*/
+ /**\{*/
+/* management.c */
+/**
+ * \brief Management message support
+ */
+void handleManagement(MsgHeader *header,
+ Boolean isFromSelf, Integer32 sourceAddress, RunTimeOpts *rtOpts, PtpClock *ptpClock);
+
+/** \}*/
+
+/** \name signaling.c
+ * -Signaling message support*/
+ /**\{*/
+/* signaling.c */
+/**
+ * \brief Signaling message support
+ */
+UnicastGrantTable* findUnicastGrants(const PortIdentity* portIdentity, Integer32 TransportAddress, UnicastGrantTable *grantTable, UnicastGrantIndex *index, int nodeCount, Boolean update);
+void initUnicastGrantTable(UnicastGrantTable *grantTable, Enumeration8 delayMechanism, int nodeCount, UnicastDestination *destinations, const RunTimeOpts *rtOpts, PtpClock *ptpClock);
+
+void cancelUnicastTransmission(UnicastGrantData*, const RunTimeOpts*, PtpClock*);
+void cancelAllGrants(UnicastGrantTable *grantTable, int nodeCount, const RunTimeOpts *rtOpts, PtpClock *ptpClock);
+
+void handleSignaling(MsgHeader*, Boolean, Integer32, const RunTimeOpts*,PtpClock*);
+
+void refreshUnicastGrants(UnicastGrantTable *grantTable, int nodeCount, const RunTimeOpts *rtOpts, PtpClock *ptpClock);
+void updateUnicastGrantTable(UnicastGrantTable *grantTable, int nodeCount, const RunTimeOpts *rtOpts);
+
+
+/* quick shortcut to defining a temporary char array for the purpose of snprintf to it */
+#define tmpsnprintf(var,len, ...) \
+ char var[len+1]; \
+ memset(var, 0, len+1); \
+ snprintf(var, len, __VA_ARGS__);
+
+/*
+ * \brief Packing and Unpacking macros
+ */
+#define DECLARE_PACK( type ) void pack##type( void*, void* );
+
+DECLARE_PACK( NibbleUpper )
+DECLARE_PACK( Enumeration4Lower )
+DECLARE_PACK( UInteger4Lower )
+DECLARE_PACK( UInteger4Upper )
+DECLARE_PACK( UInteger16 )
+DECLARE_PACK( UInteger8 )
+DECLARE_PACK( Octet )
+DECLARE_PACK( Integer8 )
+DECLARE_PACK( UInteger48 )
+DECLARE_PACK( Integer64 )
+
+#define DECLARE_UNPACK( type ) void unpack##type( void*, void*, PtpClock *ptpClock );
+
+DECLARE_UNPACK( Boolean )
+DECLARE_UNPACK( Enumeration4Lower )
+DECLARE_UNPACK( Enumeration4Upper )
+DECLARE_UNPACK( Octet )
+DECLARE_UNPACK( UInteger48 )
+DECLARE_UNPACK( Integer64 )
+
+/* display.c */
+void displayRunTimeOpts(const RunTimeOpts*);
+void displayDefault (const PtpClock*);
+void displayCurrent (const PtpClock*);
+void displayParent (const PtpClock*);
+void displayGlobal (const PtpClock*);
+void displayPort (const PtpClock*);
+void displayForeignMaster (const PtpClock*);
+void displayOthers (const PtpClock*);
+void displayBuffer (const PtpClock*);
+void displayPtpClock (const PtpClock*);
+void timeInternal_display(const TimeInternal*);
+void clockIdentity_display(const ClockIdentity);
+void netPath_display(const NetPath*);
+void intervalTimer_display(const IntervalTimer*);
+void integer64_display (const Integer64*);
+void timeInterval_display(const TimeInterval*);
+void portIdentity_display(const PortIdentity*);
+void clockQuality_display (const ClockQuality*);
+void PTPText_display(const PTPText*, const PtpClock*);
+void iFaceName_display(const Octet*);
+void unicast_display(const Octet*);
+const char *portState_getName(Enumeration8 portState);
+const char *getMessageTypeName(Enumeration8 messageType);
+const char* accToString(uint8_t acc);
+const char* delayMechToString(uint8_t mech);
+void timestamp_display(const Timestamp * timestamp);
+
+void displayCounters(const PtpClock*);
+void displayStatistics(const PtpClock*);
+void clearCounters(PtpClock *);
+
+void msgHeader_display(const MsgHeader*);
+void msgAnnounce_display(const MsgAnnounce*);
+void msgSync_display(const MsgSync *sync);
+void msgFollowUp_display(const MsgFollowUp*);
+void msgPdelayReq_display(const MsgPdelayReq*);
+void msgDelayReq_display(const MsgDelayReq * req);
+void msgDelayResp_display(const MsgDelayResp * resp);
+void msgPdelayResp_display(const MsgPdelayResp * presp);
+void msgPdelayRespFollowUp_display(const MsgPdelayRespFollowUp * prespfollow);
+void msgManagement_display(const MsgManagement * manage);
+void msgSignaling_display(const MsgSignaling * signaling);
+
+void mMSlaveOnly_display(const MMSlaveOnly*, const PtpClock*);
+void mMClockDescription_display(const MMClockDescription*, const PtpClock*);
+void mMUserDescription_display(const MMUserDescription*, const PtpClock*);
+void mMInitialize_display(const MMInitialize*, const PtpClock*);
+void mMDefaultDataSet_display(const MMDefaultDataSet*, const PtpClock*);
+void mMCurrentDataSet_display(const MMCurrentDataSet*, const PtpClock*);
+void mMParentDataSet_display(const MMParentDataSet*, const PtpClock*);
+void mMTimePropertiesDataSet_display(const MMTimePropertiesDataSet*, const PtpClock*);
+void mMPortDataSet_display(const MMPortDataSet*, const PtpClock*);
+void mMPriority1_display(const MMPriority1*, const PtpClock*);
+void mMPriority2_display(const MMPriority2*, const PtpClock*);
+void mMDomain_display(const MMDomain*, const PtpClock*);
+void mMLogAnnounceInterval_display(const MMLogAnnounceInterval*, const PtpClock*);
+void mMAnnounceReceiptTimeout_display(const MMAnnounceReceiptTimeout*, const PtpClock*);
+void mMLogSyncInterval_display(const MMLogSyncInterval*, const PtpClock*);
+void mMVersionNumber_display(const MMVersionNumber*, const PtpClock*);
+void mMTime_display(const MMTime*, const PtpClock*);
+void mMClockAccuracy_display(const MMClockAccuracy*, const PtpClock*);
+void mMUtcProperties_display(const MMUtcProperties*, const PtpClock*);
+void mMTraceabilityProperties_display(const MMTraceabilityProperties*, const PtpClock*);
+void mMTimescaleProperties_display(const MMTimescaleProperties*, const PtpClock*);
+void mMUnicastNegotiationEnable_display(const MMUnicastNegotiationEnable*, const PtpClock*);
+void mMDelayMechanism_display(const MMDelayMechanism*, const PtpClock*);
+void mMLogMinPdelayReqInterval_display(const MMLogMinPdelayReqInterval*, const PtpClock*);
+void mMErrorStatus_display(const MMErrorStatus*, const PtpClock*);
+
+void sMRequestUnicastTransmission_display(const SMRequestUnicastTransmission*, const PtpClock*);
+void sMGrantUnicastTransmission_display(const SMGrantUnicastTransmission*, const PtpClock*);
+void sMCancelUnicastTransmission_display(const SMCancelUnicastTransmission*, const PtpClock*);
+void sMAcknowledgeCancelUnicastTransmission_display(const SMAcknowledgeCancelUnicastTransmission*, const PtpClock*);
+
+void clearTime(TimeInternal *time);
+
+char *dump_TimeInternal(const TimeInternal * p);
+char *dump_TimeInternal2(const char *st1, const TimeInternal * p1, const char *st2, const TimeInternal * p2);
+const char * getTimeSourceName(Enumeration8 timeSource);
+
+int snprint_TimeInternal(char *s, int max_len, const TimeInternal * p);
+
+void nano_to_Time(TimeInternal *time, int nano);
+int gtTime(const TimeInternal *x, const TimeInternal *b);
+void absTime(TimeInternal *time);
+int is_Time_close(const TimeInternal *x, const TimeInternal *b, int nanos);
+int isTimeInternalNegative(const TimeInternal * p);
+double timeInternalToDouble(const TimeInternal * p);
+TimeInternal doubleToTimeInternal(const double d);
+
+uint32_t fnvHash(void *input, size_t len, int modulo);
+
+int check_timestamp_is_fresh2(const TimeInternal * timeA, const TimeInternal * timeB);
+int check_timestamp_is_fresh(const TimeInternal * timeA);
+
+
+void toState(UInteger8,const RunTimeOpts*,PtpClock*);
+
+/* helper functions for leap second handling */
+double secondsToMidnight(void);
+double getPauseAfterMidnight(Integer8 announceInterval, int pausePeriod);
+
+Boolean respectUtcOffset(const RunTimeOpts * rtOpts, PtpClock * ptpClock);
+
+/* alarms.c - this will be moved */
+void capturePtpEventData(PtpEventData *data, PtpClock *ptpClock, RunTimeOpts *rtOpts); /* capture data from an alarm event */
+void setAlarmCondition(AlarmEntry *alarm, Boolean condition, PtpClock *ptpClock); /* set alarm condition and capture data */
+
+#endif /*PTPD_H_*/
diff --git a/rtemsbsd/ptpd/src/ptpd2.8.in b/rtemsbsd/ptpd/src/ptpd2.8.in
new file mode 100644
index 00000000..8efe288d
--- /dev/null
+++ b/rtemsbsd/ptpd/src/ptpd2.8.in
@@ -0,0 +1,397 @@
+.\" -*- nroff -*"
+.TH ptpd2 8 "@RELEASE_DATE@" "version @VERSION_NUMBER@" "Precision Time Protocol daemon"
+.SH NAME
+ptpd2 \- Precision Time Protocol daemon (1588-2008)
+.SH SYNOPSIS
+.B ptpd2
+\fB[ -?hH ]\fR
+\fB[ -e \fISETTING\fB ]\fR
+\fB[ -kvOLAl ]\fR
+\fB[ -smMyEPanCV ]\fR
+\fB[ -c \fIFILE\fB ]\fR
+\fB[ -R \fIDIR\fB ]\fR
+\fB[ -f \fIFILE\fB ]\fR
+\fB[ -S \fIFILE\fB ]\fR
+\fB[ -d \fIDOMAIN\fB ]\fR
+\fB[ -u \fIADDRESS\fB ]\fR
+\fB[ -r \fINUMBER\fB ]\fR
+\fB-i \fIINTERFACE\fB\fR
+.SH DESCRIPTION
+PTPd is a daemon that implements the Precision Time Protocol (PTP)
+Version 2 as defined by the IEEE 1588-2008 standard. PTP was developed
+to provide very precise time coordination of LAN connected computers.
+The daemon must run as
+.B root
+in order to be able to manipluate the system clock and use low port numbers.
+PTPd is feature rich, supports IPv4 multicast, unicast and hybrid mode (mixed) operation,
+as well as Ethernet mode. Even without hardware assistance, PTPd is able to achieve and
+maintain sub-microsecond level timing precision and is able to withstand PTP Grandmaster
+failovers, link failures and restarts with minimal impact to timing performance.
+PTPd is lightweight, portable and currently supports Linux, FreeBSD
+and Mac OS X and runs on multiple CPU architectures, 32-bit and 64-bit, including x86 and ARM.
+.SH COMMAND-LINE CONFIGURATION
+As of version 2.3.0, configuration file is the preferred mechanism for configuring PTPd, therefore
+the options available as short (\fI-x\fR) and long options (\fI--xxxxx\fR) mostly provide basic control
+over the daemon operation, and only provide the very basic PTP protocol settings. The rest of the settings (see \fBptpd2.conf(5)\fR)
+can also be specified as command-line options, but they take the long \fI--key:section="value"\fR form.
+
+.SH BASIC DAEMON OPTIONS
+.TP
+\fB-c --config-file \fIPATH\fR
+Path to configuration file (see \fBptpd2.conf(5)\fR)
+.TP
+\fB-k --check-config\fR
+Check configuration and exit - return 0 if configuration is correct.
+.TP
+\fB-v --version\fR
+Print version string and exit
+.TP
+\fB-h --help\fR
+Show help screen
+.TP
+\fB-H --long-help\fR
+Show detailed help for all settings and behaviours
+.TP
+\fB-e --explain \fISETTING\fR
+Show help for a single setting (\fIsection:key\fR)
+.TP
+\fB-O --default-config\fR
+Show default configuration and exit (output usable as a configuration file)
+.TP
+\fB-L --ignore-lock\fR
+Skip lock file checks and locking (also \fIglobal:ignore_lock\fR)
+.TP
+\fB-A --auto-lock\fR
+Use preset / port mode specific lock file names - useful when running multiple instances
+.TP
+\fB-l --lockfile\fR
+Specify lock file path (also \fIglobal:lock_file\fR)
+.TP
+\fB-p --print-lockfile\fR
+Print path to lock file and exit (useful for init scripts in combination with auto lock files)
+.TP
+\fB-R --lock-directory \fIDIR\fR
+Directory to store lock files (also \fIglobal:lock_directory\fR)
+.TP
+\fB-f --log-file \fIPATH\fR
+Path to log file (also \fIglobal:logfile\fR)
+.TP
+\fB-S --statistics-file \fIPATH\fR
+Path to statistics file (also \fIglobal:statistics_file\fR)
+.TP
+\fB-T --show-templates
+Display built-in configuration templates
+.TP
+\fB-t --templates \fI[name],[name],...\fR
+Apply one or more configuration templates in this order (\fIsee man(5) ptpd2.conf\fR),
+also see \fIptpengine:template_files\fR and the \
+.TP
+\fB-S --statistics-file \fIPATH\fR
+Path to statistics file (also \fIglobal:statistics_file\fR)
+
+.SH BASIC PTP PROTOCOL OPTIONS
+.TP
+\fB-i --interface \fIDEV\fR
+\fIREQUIRED:\fRInterface to use - eth0, etc (also \fIptpengine:interface\fR)
+.TP
+\fB-d --domain \fINUMBER\fR
+PTP domain number to become part of (also \fIptpengine:domain\fR)
+.TP
+\fB-s --slaveonly\fR
+Slave only mode (also \fIptpengine:preset=slaveonly\fR)
+.TP
+\fB-m --masterslave\fR
+Full IEEE 1588 implementation: master, slave when not best GM (also \fIptpengine:preset=masterslave\fR)
+.TP
+\fB-M --masteronly\fR
+Master only mode: passive when not best GM (also \fIptpengine:preset=masteronly\fR)
+.TP
+\fB-y --hybrid\fR
+Hybrid mode - mixed multicast and unicast operation (multicast for sync and announce, unicast
+for delay request and response (also \fIptpengine:ip_mode=hybrid\fR)
+.TP
+\fB-U --unicast\fR
+Unicast operation - (\fIalso ptpengine:ip_mode=unicast\fR). For a GM, unicast destinations
+must be specified (\fI-u, --unicast-destinations, ptpengine:unicast_destinations\fR) unless
+using unicast negotiation (\fI-g, --unicast-negotiation, ptpengine:unicast_negotiation=y\fR)
+for delay request and response (also \fIptpengine:ip_mode=hybrid\fR). For a slave, unicast
+destinations must be specified if not using unicast negotiation.
+.TP
+\fB-g --unicast-negotiation\fR
+Enable unicast message delivery and interval negotiation usin signaling messages, as used
+by the Telecom profile (also enables \fIptpengine:ip_mode=unicast\fR)
+.TP
+\fB-u --unicast-destinations \fIip/host, ip/host, ...\fR
+List of unicast destinations - see \fI--unicast\fR
+(also \fIptpengine:ip_mode=unicast\fR + \fIptpengine:unicast_destinations\fR)
+\fB-E --e2e\fR
+End to end delay mechanism (also \fIptpengine:delay_mechanism=E2E\fR)
+.TP
+\fB-E --p2p\fR
+Peer to peer delay mechanism (also \fIptpengine:delay_mechanism=P2P\fR)
+.TP
+\fB-a --delay-override\fR
+In slave state, override delay request interval announced by master (also \fIptpengine:log_delayreq_override\fR) - the value
+of \fIptpengine:log_delayreq_interval\fR is used
+.TP
+\fB-r --delay-interval \fINUMBER\fR
+Specify delay request message interval (log 2) - (also \fIptpengine:log_delayreq_interval\fR)
+.TP
+\fB-n --clock:no_adjust\fR
+Do not adjust the clock (also \fIclock:no_adjust\fR)
+.TP
+\fB-D<DD...> --debug\fR
+Debug level (also \fIglobal:debug_level\fR) - only if compiled with RUNTIME_DEBUG
+.TP
+\fB-C --foreground\fR
+Don't run in background (also \fIglobal:foreground=Y\fR)
+.TP
+\fB-V --verbose\fR
+Run in foreground, log all the messages to standard output (also \fIglobal:verbose_foreground=Y\fR)
+
+.SH COMPATIBILITY OPTIONS
+
+.TP
+PTPd supports the following options compatible with versions before 2.3.0:
+.RS 8
+.TP 8
+\fB-b \fIDEV\fR
+Network interface to use
+.TP 8
+\fB-i \fINUMBER\fR
+PTP domain number
+.TP 8
+\fB-G\fR
+\'Master mode with NTP\' (master only mode)
+.TP 8
+\fB-W\fR
+\'Master mode without NTP\' (master / slave mode)
+.TP 8
+\fB-Y \fINUMBER\fR
+Delay request interval (log 2)
+.TP 8
+\fB-t\fR
+Do not adjust the clock
+.RE
+.TP
+\fBNOTE:\fR the above options are deprecated and will be removed in subsequent versions. Until then, their use will issue a warning.
+
+
+.SH PTPD PORT STATES
+
+.RS 8
+.TP
+\fIinit\fR
+INITIALIZING
+.TP
+\fIflt\fR
+FAULTY
+.TP
+\fIlstn_init\fR
+LISTENING (first time)
+.TP
+\fIlstn_reset\fR
+LISTENING (subsequent reset)
+.TP
+\fIpass\fR
+PASSIVE (not best master, not announcing)
+.TP
+\fIuncl\fR
+UNCALIBRATED
+.TP
+\fIslv\fR
+SLAVE
+.TP
+\fIpmst\fR
+PRE-MASTER
+.TP
+\fImst\fR
+MASTER (active)
+.TP
+\fIdsbl\fR
+DISABLED
+.TP
+\fI? (unk)\fR
+UNKNOWN state
+.RE
+
+.SH STATISTICS LOG FILE FORMAT
+
+When the statistics log is enabled (\fIptpengine:log_statistics\fR, verbose foreground mode or log file - \fIptpengine:statistics_file\fR),
+a PTPd slave will log clock sync information upon the receipt of every Sync and Delay Response message.
+When PTPd starts up or flushes the log, a comment line (starting with #) will be logged, containing the names
+of all columns. The format of this log is a series of comma-separated values (CSV) and can be easily
+imported into statistics tools and most spreadsheet software packages for analysis and graphing.
+This log can get very large when running PTPd for longer periods of time and with high message rates, therefore to reduce
+the number of messages logged, the \fIglobal:statistics_log_interval\fR setting can be used, to limit the log output
+to one message per configured interval only. The size and maximum number of the statistics
+log can also be controlled - (see \fBptpd2.conf(5)\fR).
+.TP
+The meaning of the columns is as follows:
+.RS 8
+.TP 8
+\fBTimestamp\fR
+Time when message received. This can take the form of text date / time or Unix timestamp (with fractional seconds),
+or both (in which case an exra field is added), depending onthe \fIglobal:statistics_timestamp_format\fR setting (see \fBptpd2.conf(5)\fR).
+When importing the log into plotting software, if the software can understand Unix time, it is best to
+set the timestamp format to unix or both, as some software will not properly deal with the fractional part of the second when converting
+the date and time from text.
+.TP 8
+\fBState\fR
+Port state (see \fBPTP PORT STATES\fR).
+.TP 8
+\fBClock ID\fR
+Port identity of the current best master, as defined by IEEE 1588. This will be the local clock's ID if
+the local clock is the best master. Displayed as \fIclock_id/port(host)\fR
+Port is the PTP clock port number, not to be confused with UDP ports. The clock ID is an EUI-64 64-bit
+ID, usually converted from the 48-bit MAC address, by inserting 0xfffe between the lower and upper
+half of the MAC address. PTPd will attempt to convert the clock ID back to MAC address and look up
+the hostname from \fI/etc/ethers\fR (see \fBethers(5)\fR). Populating the ethers file will help the
+administrator recognise the masters by familiar hostnames.
+.TP 8
+\fBOne Way Delay\fR
+Current value of one-way delay (or mean path delay) in seconds, calculated by PTPd in slave state
+from the Delay Request - Delay Response message exchange. \fINote:\fR if this value remains at zero,
+this usually means that no Delay Response messages are being received, likely due to a network issue.
+.TP 8
+\fBOffset From Master\fR
+Current value of offset from master in seconds - this is the main output of the PTP engine in slave
+state, which is the input of the clock servo for clock corrections. This is the value typically
+observed when estimating the slave performance.
+.TP 8
+\fBSlave to Master\fR
+Intermediate offset value (seconds) extracted from the Delay Request - Delay Response message exchange, used for
+computing one-way delay. If the last value was rejected by a filter, the previous value will
+be shown in the log. This value will also be zero if no Delay Response messages are being received.
+.TP 8
+\fBMaster to Slave\fR
+Intermediate offset value (seconds) extracted from the Sync messages, used for computing the offset from master.
+If the last value was rejected by a filter, the previous value will be shown in the log.
+.TP 8
+\fBObserved Drift\fR
+The integral accumulator of the clock control PI servo model - frequency difference between the slave clock and
+master clock. This value becomes stable when the clock offset has stabilised, and can be used (and is) to detect
+clock stability.
+.TP 8
+\fBLast Packet Received\fR
+This field shows what message was received last - this will be S for Sync, D for Delay Response and P for Peer
+Delay Response when using P2P delay mode. If a slave logs no D (or P) entries, this means it's not receiving
+Delay Response messages, which could be a network issue. For two-step clocks, "S" will still be printed when
+Follow-up was received.
+.TP 8
+\fBSequence ID\fR
+Sequence number of the last received message (Sync, Follow-Up, Delay Response, Peer Delay Response). Sequence
+numbers in the statistics log file can help identify timing issues when analysing capture files; a change of
+offset or path delay can be traced to a particular packet that matches the sequence ID.
+.TP 8
+\fBOne Way Delay Mean\fR
+One-way delay mean computed over the last sampling window.
+.TP 8
+\fBOne Way Delay Std Dev\fR
+One-way delay standard deviation computed over the last sampling window.
+.TP 8
+\fBOffset From Master Mean\fR
+Offset from master mean computed over the last sampling window.
+.TP 8
+\fBOffset From Master Std Dev\fR
+Offset from master standard deviation computed over the last sampling window.
+.TP 8
+\fBObserved Drift Mean\fR
+Observed drift / local clock frequency adjustment mean computed over the last sampling window.
+.TP 8
+\fBObserved Drift Std Dev\fR
+Observed drift / local clock frequency adjustment standard deviation computed over the last sampling window.
+The lower the value, the less aggressively the clock is being controlled and therefore the more stable it is.
+.TP 8
+\fBraw delayMS\fR
+Raw (unfiltered) delayMS value - useful for evaluating outliers and filter performance.
+.TP 8
+\fBraw delaySM\fR
+Raw (unfiltered) delaySM value - useful for evaluating outliers and filter performance.
+.RE
+
+\fBNOTE:\fR All the statistical measures (mean and std dev) will only be computed and displayed if PTPd was
+built without --disable-statistics. The duration of the sampling period is controlled with the
+\fIglobal:statistics_update_interval\fR setting - (see \fBptpd2.conf(5)\fR).
+
+.SH HANDLED SIGNALS
+.TP
+\fBPTPd handles the following signals:\fR
+.RS 8
+.TP 8
+\fISIGHUP\fR
+Reload configuration file (if used) and reopen log files
+.TP 8
+\fISIGUSR1\fR
+When in slave state, force clock step to current Offset from Master value
+.TP 8
+\fISIGUSR2\fR
+Dump all PTP protocol counters to current log target
+(and clear if \fIptpengine:sigusr2_clears_counters\fR set)
+.TP 8
+\fISIGINT|SIGTERM\fR
+Clean exit - close logs and other open files, clean up lock file and exit.
+.TP 8
+\fISIGKILL\fR
+Force an unclean exit.
+.RE
+.SH EXIT CODES
+Upon exit, ptpd2 returns \fB0\fR on success - either successfully started in daemon mode, or otherwise exited cleanly.
+\fB0\fR is also returned when the \fI-k\fR (\fI--check-config\fR)
+option is used and the configuration was correct. A non-zero exit code is returned on errors.
+\fB3\fR is returned on lock file errors and when ptpd2 could not be started as daemon.
+\fB2\fR is returned on memory allocation errors during startup. For all other error conditions such as
+configuration errors, running ptpd2 in help mode or with no parameters, on self shutdown,
+network startup errors and when attempting to run ptpd2 as non-root - \fB1\fR is returned.
+
+.SH SUPPORTED PLATFORMS AND ARCHITECTURES
+
+PTPd is fully supported on Linux and FreeBSD as this is what the core developers focus on.
+OpenBSD and NetBSD are also supported, but get less developers' attention.
+So is Max OS X, and as of PTPd 2.3.1 also OpenSolaris (11) derivatives (tested on OmniOS).
+Sun's / Oracle's Solaris 11 has not been tested but in essence, it should work as intended.
+Solaris 10 is NOT supported because it does not provide the SO_TIMESTAMP socket option.
+It should theoretically be possible to use Solaris 10 using the \fIpf\fR facility as used by \fIsnoop\fR,
+but there is currently no ongoing effort to acheive this. Patches for QNX/Neutrino have been provided,
+but cannot yet officially be merged because of no availability of QNX to the developers. Some users
+have ported PTPd to other RTOS, but this has not been merged either.
+
+As of 2.3.1, PTPd runs entirely in software and only relies on kernel and OS APIs, so
+there are no hardware dependencies. Any little-endian or big-endian port of modern versions
+of the supported OSes should work, but only x86 and ARM are actively tested. The only dependencies close to hardware
+can be NIC drivers and how and if they impact software timestamping.
+
+.SH HARDWARE TIMESTAMPING SUPPORT
+
+As of 2.3.1, PTPd still does not support hardware timestamping. This functionality will appear
+in the upcoming version 2.4 - potentially an interim version of 2.3.x may be delivered that will
+support hardware clocks and timestamping on Linux. This is very much OS-specific and to a large extent,
+hardware-specific. Linux has a PTP kernel API but not all hardware supports it.
+Because PTPd supports multiple OS platforms, where hardware timestamping may use different mechanisms
+on every platform, it has to be re-written in a modular way to allow this without unnecessarily increasing
+code complexity, which already is a problem.
+
+.SH BUGS
+As of ptpd 2.3.1, the (Open)Solaris (11) OS family is supported, but libpcap functionality is
+currently broken - IPv4/pcap and Ethernet transports cannot be used on those systems. PTPd will compile and run,
+ but will not receive any data.
+
+Please report any bugs using the bug tracker on the SourceForge page:
+http://sourceforge.net/projects/ptpd/
+
+.SH SEE ALSO
+.Xr ptpd2.conf 5
+ptpd2.conf(5)
+.SH AUTHORS
+.P
+Gael Mace <gael_mace at users.sourceforge.net>
+.P
+Alexandre Van Kempen
+.P
+Steven Kreuzer <skreuzer at freebsd.org>
+.P
+George Neville-Neil <gnn at freebsd.org>
+.P
+Wojciech Owczarek <wojciech at owczarek.co.uk>
+
+\fBptpd2(8)\fR man page was written by Wojciech Owczarek for ptpd 2.3.0 in November 2013
diff --git a/rtemsbsd/ptpd/src/ptpd2.conf.5.in b/rtemsbsd/ptpd/src/ptpd2.conf.5.in
new file mode 100644
index 00000000..fb9de3a6
--- /dev/null
+++ b/rtemsbsd/ptpd/src/ptpd2.conf.5.in
@@ -0,0 +1,3100 @@
+.\" -*- nroff -*"
+.TH ptpd2.conf 5 "@RELEASE_DATE@" "version @VERSION_NUMBER@" "PTPd config file"
+.SH NAME
+ptpd2.conf \- Precision Time Protocol daemon config file
+
+.SH CONFIGURATION FILE FORMAT
+Settings in the PTPd configuration file are grouped into sections and take
+the form of \fIsection:key="value"\fR variables. The configuration file can either
+be formatted that way (preferred) or in .ini file style where a series of
+\fIkey="value"\fR variables is grouped into sections using \fB[\fIsection\fB]\fR headers.
+Every setting listed here can also be specified
+as a command line parameter \fB(\fI--section:key=value\fB)\fR. Quotation marks are optional.
+\fBNOTE:\fR the configuration file must end with a newline.
+
+.SH RELOADING CONFIGURATION
+Only a small number of configuration file settings (SNMP, lock file configuration) requires a restart
+of the PTPd process to take effect. All other settings can be changed while ptpd is running - configuration file
+is reloaded and checked for changes when PTPd receives the SIGHUP signal. When reloading configuration,
+PTPd will always attempt to test settings before applying them and once running, will never exit as
+a result of configuration errors. If it does exit during config refresh, this is most likely a bug.
+
+.SH COMMAND-LINE PRIORITY
+Any setting passed as a command line parameter will always take priority over the configuration file,
+so once ptpd is running, those settings cannot be changed - a warning will be logged on every
+attempt to change those settings using the configuration file.
+
+.SH CONFIGURATION SECTIONS
+.B ptpengine
+PTP protocol specific configuration
+.TP
+.B clock
+Clock related settings
+.TP
+.B servo
+Clock control PI servo configuration
+.TP
+.B global
+Global configuration - logging, etc.
+.TP
+.B ntpengine
+NTP control configuration
+.TP
+.B variables
+User-defined variables
+
+.SH USER-DEFINED VARIABLES
+To allow for easier management and automated generation of configuration, PTPd supports user variables,
+which can be defined in the configuration file or in command line. They are defined as \fIvariables:[name]=[value]\fR,
+or if using .ini style format, in the \fI[variables]\fR section. Once defined, a variable can be referred to
+in the remaining configuration settings as \fI at name@\fR, and is substituted with the value of the variable
+
+\fBExample\fR:
+
+variables:instance=server15
+
+global:status_file=/var/run/ptpd2. at instance@.status
+
+global:log_file=/var/run/ptpd2. at instance@.status
+
+\fBNote:\fR for the same effect, ptpd can be run from command line, such as \fI --config=/path/to/file --variables:instance=server15\fR
+
+.SH BUILT_IN VARIABLES
+PTPd includes suppport for built-in variables, automatically defined. The following variables are automatically substituted:
+
+ at pid@ - current PTPd process ID
+ at hostname@ - current host name
+
+
+.SH CONFIGURATION TEMPLATES AND TEMPLATE FILES
+As of version 2.3.1.1, ptpd enables the user to minimise the configuration effort for common scenarios, using built-in templates
+and template files. A template is a named set of pre-defined settings whic are prepended before any other settings, so user can
+still overwrite settings provided by the template. To use this feature, set \fIglobal:config_templates=[name],[name],...\fR in the
+configuration file, or run ptpd with \fI--global:config_templates=[name],[name],...\fR. Multiple templates can be specified,
+separated by comma, space or tab; they are applied in the order they are provided, so template settings override any overlapping
+settings from previous templates specified. Templates can include \fIuser-defined variables\fR.
+
+A number of \fItemplate files\fR can also be supplied with the \fIglobal:template_files\fR setting
+(comma, space or tab separated lis of file paths). The template files will be processed in the order they are provided in,
+so for overlapping settings, the last template applied overrides settings applied by any previous
+templates. PTPd will also try to load a default template file on startup: \fItemplates.conf\fR
+from the default data directory: \fI at prefix@/share/@PACKAGE_NAME@/templates.conf\fR
+
+The template file is formatted in .ini style - each template is a section defined as
+\fI[template-name]\fR, followed by a number of settings specified as \fIsection:setting\fR.
+
+\fBExample\fR:
+
+[my-template]
+
+global:verbose_foreground=Y
+
+ptpengine:preset=slaveonly
+
+
+To see the list of available built-in templates, run ptpd with \fI-T\fR or \fI--show-templates\fR
+
+.SH CONFIGURATION VARIABLES
+.RS 0
+.TP 8
+\fBptpengine:interface [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Network interface to use - eth0, igb0 etc. (\fBrequired\fR). See also \fIptpengine:backup_interface\fR.
+.TP 8
+\fBdefault\fR
+\fI[none]\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:backup_interface [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Backup network interface to use - eth0, igb0 etc. When no GM available,
+slave will keep alternating between primary and secondary until a GM is found.
+.TP 8
+\fBdefault\fR
+\fI[none]\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:preset [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fInone slaveonly masteronly masterslave \fR
+.TP 8
+\fBusage\fR
+PTP engine preset:
+.RS 12
+.TP 12
+\fInone\fR
+Defaults, no clock class restrictions
+.TP 12
+\fIslaveonly\fR
+Slave only (clock class 255 only)
+.TP 12
+\fImasteronly\fR
+Master, passive when not best master (clock class 0..127)
+.TP 12
+\fImasterslave\fR
+Full IEEE 1588 implementation: Master, slave when not best master (clock class 128..254)
+.RE
+.TP 8
+\fBdefault\fR
+\fIslaveonly\fR
+.TP 8
+\fBNOTE:\fR
+Presets affect the following settings: \fIptpengine:slave_only\fR, \fIclock_no_adjust\fR and \fIptpengine:clock_class\fR (range and default value).
+To see all preset settings, run ptpd2 \fI-H\fR (\fI--long-help\fR)
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:transport [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fIipv4 ethernet\fR
+.TP 8
+\fBusage\fR
+Transport type for PTP packets. \fBNOTE:\fR Ethernet transport requires building with \fIlibpcap\fR and is not supported on Solaris as of 2.3.1,
+and cannot be enabled on those systems unless ptpd is compiled with \fB--enable-experimental-options\fR.
+.TP 8
+\fBdefault\fR
+\fIipv4\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:dot1as [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable IEEE 802.1AS / AVB compatibility (transportSpecific field in PTP message headers).
+Requires Ethernet transport as this is the only mapping used by 802.1AS that PTP supports
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:ip_mode [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fImulticast unicast hybrid \fR
+.TP 8
+\fBusage\fR
+IP transmission mode (requires IP transport):
+.RS 12
+.TP 12
+\fImulticast\fR
+uses multicast for all messages
+.TP 12
+\fIhybrid\fR
+uses multicast for sync and announce, and unicast for delay request and response
+.TP 12
+\fIunicast\fR
+uses unicast for all transmission. When unicast mode is selected, destination IP(s) (\fIptpengine:unicast_
+destinations\fR) must be configured
+depending on unicast negotiation setting (\fIptpengine:unicast_negotiation\fR) and master or slave role
+(see: \fIptpengine:unicast_destinations\fR)
+.RE
+.TP 8
+\fBdefault\fR
+\fImulticast\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:disabled [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Disable PTP port. Causes the PTP state machine to stay in PTP_DISABLED state indefinitely,
+until it is re-enabled via configuration change or ENABLE_PORT management message.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:unicast_negotiation [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable unicast negotiation support using signaling messages - as used by the Telecom profile
+(ITU-T G.8265.1).
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:unicast_grant_duration [\fIINT\fB: 30 .. 604800]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Duration (seconds) for which the transmission of unicast messages is granted by a master,
+or requested by a slave when unicast negotiation is used (\fIptpengine:unicast_negotiation\fR).
+When using PTPd with other PTP implementations, PTPd will never refuse to grant a message based
+on the requested duration: it will grant for 30 seconds if requested for any less than 30 seconds,
+and will grant for 7 days (604800) if requested for any longer.
+.TP 8
+\fBdefault\fR
+\fI300\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:disable_bmca [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Disable Best Master Clock Algorithm for unicast masters. Only effective for masteronly preset -
+all Announce messages will be ignored and the cock will transition directly into MASTER state
+and remain an active master. This behaviour is required for Telecom profile operation.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:unicast_any_master [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+When using unicast negotiation (slave), accept PTP messages from any grandmaster.
+By default, only messages from acceptable masters (\fIptpengine:unicast_destinations\fR)
+are accepted, and only if transmission was granted by the GM. This setting can be used
+when mixing GMs supporting G.8265.1 and manual unicast (no negotiation), or to assist
+with interoperability issues where signaling messages and timing messages come from
+different port identities.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:unicast_port_mask [\fIINT\fB: 0 .. 65535 (0xFFFF)]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+PTP port number wildcard mask (16-bit) applied onto port identities when running unicast negotiation:
+allows multiple port identities (with the same clock ID) to be accepted as coming from the same port.
+This option can be used as a workaround where a node sends signaling messages and timing messages
+with different port identities. \fBNOTE:\fR This can also be entered in hexadecimal notation (0xNNNN).
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:disable_udp_checksums [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Disable UDP checksum validation on UDP sockets (Linux only). Workaround for situations where a node
+(like Transparent Clock) does not rewrite checksums. Enabled by default.
+.TP 8
+\fBdefault\fR
+\fIY\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:use_libpcap [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Use libpcap for sending and receiving traffic (automatically enabled in Ethernet mode).
+Requires building with libpcap - builds made with \fB--disable-pcap\fR cannot use this feature, and as of 2.3.1, Solaris systems will
+not attempt to use libpcap unless compiled with \fB--enable-experimental-options\fR
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_mechanism [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fIE2E P2P DELAY_DISABLED \fR
+.TP 8
+\fBusage\fR
+Delay detection mechanism used - use DELAY_DISABLED for syntonisation only (no synchronisation). E2E uses Delay Request messages,
+P2P uses Peer Delay Request messages.
+.TP 8
+\fBdefault\fR
+\fIE2E\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:domain [\fIINT\fB: 0 .. 127]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+PTP domain number.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:any_domain [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Usability extension: if enabled, a slave-only clock will accept masters from any domain,
+while preferring the configured domain, and preferring lower domain number. This option should be
+used for slave-only clocks and should not be used with unicast negotiation.
+\fBNOTE:\fR this behaviour is not part of the standard.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:port_number [\fIINT\fB: 1 .. 65534]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+PTP port number (part of PTP Port Identity - not UDP port).
+For ordinary clocks (single port), the default should be used,
+but when running multiple instances to simulate a boundary clock,
+The port number can be changed.
+.TP 8
+\fBdefault\fR
+\fI1\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:port_description [\fISTRING: 64 characters max\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+User description of the PTP port - this value is returned in response to USER_DESCRIPTION management message and
+CLOCK_DESCRIPTION management message.
+.TP 8
+\fBdefault\fR
+\fI[ptpd]\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:slave_only [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Slave only mode (sets clock class to 255, overriding value from preset).
+.TP 8
+\fBdefault\fR
+\fIY\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:inbound_latency [\fIINT\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Specify latency correction (nanoseconds) for incoming packets.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:outbound_latency [\fIINT\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Specify latency correction (nanoseconds) for outgoing packets.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:offset_shift [\fIINT\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Apply an arbitrary shift (nanoseconds) to offset from master when in slave state. Value can be positive or negative - useful for correcting for of antenna latencies, delay assymetry and IP stack latencies. This will not be visible in the offset from master value - only in the resulting clock correction.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:always_respect_utc_offset [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Compatibility option: In slave state, always respect UTC offset announced by best master, even if the the
+ currrentUtcOffsetValid flag is announced FALSE. \fBNOTE\fR: this behaviour is not part of the standard.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:prefer_utc_offset_valid [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Compatibility extension to BMC algorithm: when enabled, BMC for both master and save clocks will prefer masters announcing currrentUtcOffsetValid as TRUE.
+ \fBNOTE\fR: this behaviour is not part of the standard.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:require_utc_offset_valid [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Compatibility option: when enabled, ptpd2 will ignore Announce messages from masters announcing currentUtcOffsetValid as FALSE. \fBNOTE\fR: this behaviour is not part of the standard.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:log_announce_interval [\fIINT\fB: -4 .. 7]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+PTP announce message interval in master state. When using unicast negotiation (\fIptpengine:unicast_negotiation\fR),
+for slaves this is the initial (minimum) interval requested and for masters this is the minimum interval granted.
+(expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+.TP 8
+\fBdefault\fR
+\fI1\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:log_announce_interval_max [\fIINT\fB: -1 .. 7]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+When using unicast negtiation (\fIptpengine:unicast_negotiation\fR), this is the maximum announce
+interval granted by a master, and the maximum interval a slave will attempt to request.
+(expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+.TP 8
+\fBdefault\fR
+\fI5\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:announce_receipt_timeout [\fIINT\fB: 2 .. 255]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+PTP announce receipt timeout announced in master state.
+.TP 8
+\fBdefault\fR
+\fI6\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:announce_receipt_grace_period [\fIINT\fB: 0 .. 20]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+PTP announce receipt timeout grace period in slave state: when announce receipt timeout occurs, disqualify current best GM,
+ then wait n times announce receipt timeout before resetting. Allows for a seamless GM failover when standby GMs are slow
+ to react. When set to 0, this option is not used.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:log_sync_interval [\fIINT\fB: -7 .. 7]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+PTP sync message interval in master state. When using unicast negotiation (\fIptpengine:unicast_negotiation\fR),
+for slaves this is the initial (minimum) interval requested and for masters this is the minimum interval granted.
+ (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:log_sync_interval_max [\fIINT\fB: -1 .. 7]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+When using unicast negtiation (\fIptpengine:unicast_negotiation\fR), this is the maximum sync
+interval granted by a master, and the maximum interval a slave will attempt to request.
+(expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+.TP 8
+\fBdefault\fR
+\fI5\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:log_delayreq_override [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Override the Delay Request interval provided by best master.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:log_delayreq_auto [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Automatically override the Delay Request interval (with \fI ptpengine:log_delayreq_interval\fR)
+if the received value is 127 (0X7F), such as in unicast messages,
+unless using unicast negotiation (\fIptpengine:unicast_negotiation\fR)
+.TP 8
+\fBdefault\fR
+\fIY\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:log_delayreq_interval_initial [\fIINT\fB: -7 .. 7]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Delay request interval used before receiving first delay response
+ (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:log_delayreq_interval [\fIINT\fB: -7 .. 7]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Minimum delay request interval announced when in master state, in slave state overrides the master interval.
+ (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.). When using unicast negotiation (\fIptpengine:unicast_negotiation\fR),
+for slaves this is the initial (minimum) interval requested and for masters this is the minimum interval granted.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:log_delayreq_interval_max [\fIINT\fB: -1 .. 7]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+When using unicast negtiation (\fIptpengine:unicast_negotiation\fR), this is the maximum delay request
+interval granted by a master, and the maximum interval a slave will attempt to request.
+ (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.).
+.TP 8
+\fBdefault\fR
+\fI5\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:log_peer_delayreq_interval [\fIINT\fB: -7 .. 7]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Minimum peer delay request message interval in peer to peer delay mode
+(expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.). When using unicast negotiation (\fIptpengine:unicast_negotiation\fR),
+this is the initial (minimum) interval requested by a node from its peer and this is the minimum interval granted for a peer.
+.TP 8
+\fBdefault\fR
+\fI1\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:log_peer_delayreq_interval_max [\fIINT\fB: -1 .. 7]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+When using unicast negtiation (\fIptpengine:unicast_negotiation\fR), this is the maximum peer delay request
+interval granted by a node, and the maximum interval a node will attempt to request from its peer.
+ (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.).
+.TP 8
+\fBdefault\fR
+\fI5\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:foreignrecord_capacity [\fIINT\fB: 5 .. 10]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Foreign master record size (Maximum number of foreign masters).
+.TP 8
+\fBdefault\fR
+\fI5\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:ptp_allan_variance [\fIINT\fB: 0 .. 65535]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Specify Allan variance announced in master state.
+.TP 8
+\fBdefault\fR
+\fI28768\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:ptp_clock_accuracy [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fIACC_25NS ACC_100NS ACC_250NS ACC_1US ACC_2.5US ACC_10US ACC_25US ACC_100US ACC_250US ACC_1MS ACC_2.5MS ACC_10MS ACC_25MS ACC_100MS ACC_250MS ACC_1S ACC_10S ACC_10SPLUS ACC_UNKNOWN \fR
+.TP 8
+\fBusage\fR
+Clock accuracy range announced in master state.
+.TP 8
+\fBdefault\fR
+\fIACC_UNKNOWN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:utc_offset [\fIINT\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Underlying time source UTC offset announced in master state.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:utc_offset_valid [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Underlying time source UTC offset validity announced in master state.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:time_traceable [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Underlying time source time traceability announced in master state.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:frequency_traceable [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Underlying time source frequency traceability announced in master state.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:ptp_timescale [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fIPTP ARB \fR
+.TP 8
+\fBusage\fR
+Time scale announced in master state (with ARB, UTC properties are ignored by slaves). When clock class is set to 13 (application specific), this value is ignored and ARB is used.
+.TP 8
+\fBdefault\fR
+\fIARB\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:ptp_timesource [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fIATOMIC_CLOCK GPS TERRESTRIAL_RADIO PTP NTP HAND_SET OTHER INTERNAL_OSCILLATOR \fR
+.TP 8
+\fBusage\fR
+Time source announced in master state.
+.TP 8
+\fBdefault\fR
+\fIINTERNAL_OSCILLATOR\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:clock_class [\fIINT\fB: 0 .. 255]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Clock class - announced in master state. Always 255 for slave-only.
+Minimum, maximum and default values are controlled by presets.
+If set to 13 (application specific time source), announced time scale is always set to ARB.
+This setting controls the states a PTP port can be in. If below 128, port will only be in MASTER or PASSIVE states (master only). If above 127, port will be in MASTER or SLAVE states.
+.TP 8
+\fBdefault\fR
+\fI255\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:priority1 [\fIINT\fB: 0 .. 248]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Priority 1 announced in master state,used for Best Master
+ Clock selection.
+.TP 8
+\fBdefault\fR
+\fI128\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:priority2 [\fIINT\fB: 0 .. 248]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Priority 2 announced in master state, used for Best Master
+ Clock selection.
+.TP 8
+\fBdefault\fR
+\fI128\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:max_listen [\fIINT\fB: min: 1 ]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Number of consecutive protocol resets to LISTENING before full network reset.
+.TP 8
+\fBdefault\fR
+\fI5\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:unicast_destinations [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+An IPv4 address or list of IPv4 addresses to be used as unicast destinations.
+When unicast negotiation (\fIptpengine:unicast_negotiation\fR) is enabled, setting this
+is mandatory for slaves as they must be aware of which GMs to request messages from.
+When unicast negotiation is disabled, setting this is mandatory for GMs, as they must
+deliver messages to a pre-configured group of slaves.
+.TP 8
+\fBdefault\fR
+\fI[none]\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:unicast_domains [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Specify PTP domain number for each configured unicast destination (\fIptpengine:unicast_destinations\fR).
+This is only used by slave-only clocks using multiple unicast destinations to allow for each master
+to be in a separate domain, such as with Telecom Profile. The number of entries should match the number
+of unicast destinations, otherwise unconfigured domains or domains set to 0 are set to domain configured in \fIptpengine:domain\fR.
+The format is a comma, tab or space-separated list of 8-bit unsigned integers (0 .. 255).
+.TP 8
+\fBdefault\fR
+\fI[none]\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:unicast_local_preference [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Specify a local preference for each configured unicast destination (\fIptpengine:unicast_destinations\fR).
+This is only used by slave-only clocks using multiple unicast destinations to allow for each master's
+BMC selection to be influenced locally by the slave, such as with Telecom Profile. The number of entries should match the number
+of unicast destinations, otherwise unconfigured preference is set to 255 (lowest), so that the unconfigurest entries do not
+pre-empt the configured entries. The format is a comma, tab or space-separated list of 8-bit unsigned integers (0 .. 255).
+.TP 8
+\fBdefault\fR
+\fI[none]\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:unicast_peer_destination [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+When using IP unicast mode (\fIptpengine:ip_mode=unicast\fr) and Peer to Peer delay mechanism
+(\fIptpengine:delay_mechanism=P2P\fR), a peer unicast destination must be configured to request
+the peer delay from. Format is a single unicast IPv4 address.
+.TP 8
+\fBdefault\fR
+\fI[none]\fR
+
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:management_enable [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable handling of PTP management messages. Only GET messages are processed by default.
+See \fIptpengine:management_set_enable\fR.
+.TP 8
+\fBdefault\fR
+\fIY\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:management_set_enable [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Accept SET and COMMAND management messages.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:igmp_refresh [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Send explicit IGMP joins between engine resets and periodically
+ in master state.
+.TP 8
+\fBdefault\fR
+\fIY\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:master_igmp_refresh_interval [\fIINT\fB: 0 .. 255]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Periodic IGMP join interval (seconds) in master state when running
+IPv4 multicast: when set below 10 or when ptpengine:igmp_refresh
+is disabled, this setting has no effect.
+.TP 8
+\fBdefault\fR
+\fI60\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:multicast_ttl [\fIINT\fB: 1 .. 64]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Multicast time to live for multicast PTP packets (ignored and set to 1
+for peer to peer messages).
+.TP 8
+\fBdefault\fR
+\fI64\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:ip_dscp [\fIINT\fB: 0 .. 63]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+DiffServ CodepPoint for packet prioritisation (decimal). When set to zero,
+this option is not used. Use 46 for Expedited Forwarding (0x2e).
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_stat_filter_enable [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable statistical filter for Sync messages
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_stat_filter_type [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fInone mean min max absmin absmax median \fR
+.TP 8
+\fBusage\fR
+Type of filter used for Sync message filtering:
+.RS 12
+.TP 12
+\fInone\fR
+no filtering - pass-through
+.TP 12
+\fImean\fR
+mean (average) - smooth results but influenced by outliers
+.TP 12
+\fImin\fR
+minimal value - useful for high packet delay variation ("lucky packets")
+.TP 12
+\fImax\fR
+maximal value - useful for testing worst case scenarios
+.TP 12
+\fIabsmin\fR
+absolute minimum - value closest to zero. Also useful for test purposes.
+.TP 12
+\fIabsmax\fR
+absolute maximun value farthest away from zero
+.TP 12
+\fImedian\fR
+median (middle value) - more robust than mean, not influenced by outliers
+.RE
+.TP 8
+\fBdefault\fR
+\fImin\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_stat_filter_window [\fIINT\fB: 3 .. 128]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Number of samples used for the Sync statistical filter
+.TP 8
+\fBdefault\fR
+\fI4\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_stat_filter_window_type [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fIsliding interval\fR
+.TP 8
+\fBusage\fR
+Sampling window behaviour for the Sync statistical filter:
+.RS 12
+.TP 12
+\fIsliding\fR
+sliding window - a value is output every time the filter runs, which can result in duplicates
+.TP 12
+\fIinterval\fR
+only output a value every n-th sample (full window) - independent sampling periods
+.RE
+.TP 8
+\fBdefault\fR
+\fIsliding\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_stat_filter_enable [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable statistical filter for Delay messages
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_stat_filter_type [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fInone mean min max absmin absmax median \fR
+.TP 8
+\fBusage\fR
+Type of filter used for Delay message filtering:
+.RS 12
+.TP 12
+\fInone\fR
+no filtering - pass-through
+.TP 12
+\fImean\fR
+mean (average) - smooth results but influenced by outliers
+.TP 12
+\fImin\fR
+minimal value - useful for high packet delay variation ("lucky packets")
+.TP 12
+\fImax\fR
+maximal value - useful for testing worst case scenarios
+.TP 12
+\fIabsmin\fR
+absolute minimum - value closest to zero. Also useful for test purposes.
+.TP 12
+\fIabsmax\fR
+absolute maximun value farthest away from zero
+.TP 12
+\fImedian\fR
+median (middle value) - more robust than mean, not influenced by outliers
+.RE
+.TP 8
+\fBdefault\fR
+\fImin\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_stat_filter_window [\fIINT\fB: 3 .. 128]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Number of samples used for the Delay statistical filter
+.TP 8
+\fBdefault\fR
+\fI4\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_stat_filter_window_type [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fIsliding interval\fR
+.TP 8
+\fBusage\fR
+Sampling window behaviour for the Delay statistical filter:
+.RS 12
+.TP 12
+\fIsliding\fR
+sliding window - a value is output every time the filter runs, which can result in duplicates
+.TP 12
+\fIinterval\fR
+only output a value every n-th sample (full window) - independent sampling periods
+.RE
+.TP 8
+\fBdefault\fR
+\fIsliding\fR
+
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_outlier_filter_enable [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable outlier filter for the Delay Response component in slave state
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_outlier_filter_action [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fIdiscard filter \fR
+.TP 8
+\fBusage\fR
+Delay Response outlier filter action. If set to 'filter', outliers are
+ replaced with moving average.
+.TP 8
+\fBdefault\fR
+\fIdiscard\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_outlier_filter_capacity [\fIINT\fB: 4 .. 60]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Number of samples in the Delay Response outlier filter buffer
+.TP 8
+\fBdefault\fR
+\fI20\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_outlier_filter_threshold [\fIFLOAT\fB: 0.001000 .. 1000.000000]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Delay Response outlier filter threshold: multiplier for Peirce's maximum
+ standard deviation. When set below 1.0, filter is tighter, when set above
+ 1.0, filter is looser than standard Peirce's test.
+.TP 8
+\fBdefault\fR
+\fI1.000000\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_outlier_filter_always_filter [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Always run the Delay Response outlier filter, even if clock is being slewed at maximum rate
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_outlier_filter_autotune_enable [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable automatic threshold control for Delay Response outlier filter.
+.TP 8
+\fBdefault\fR
+\fIY\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_outlier_filter_autotune_minpercent [\fIINT\fB: 0 .. 99]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Delay Response outlier filter autotune low watermark - minimum percentage
+of discarded samples in the update period before filter is tightened
+by the autotune step value
+.TP 8
+\fBdefault\fR
+\fI20\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_outlier_filter_autotune_maxpercent [\fIINT\fB: 1 .. 100]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Delay Response outlier filter autotune high watermark - maximum percentage
+of discarded samples in the update period before filter is loosened
+by the autotune step value
+.TP 8
+\fBdefault\fR
+\fI95\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_outlier_filter_autotune_step [\fIFLOAT\fB: 0.010000 .. 10.000000]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+The value the Delay Response outlier filter threshold is increased
+or decreased by when auto-tuning
+.TP 8
+\fBdefault\fR
+\fI0.100000\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_outlier_filter_autotune_minthreshold [\fIFLOAT\fB: 0.010000 .. 10.000000]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Minimum Delay Response filter threshold value used when auto-tuning
+.TP 8
+\fBdefault\fR
+\fI0.100000\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_outlier_filter_autotune_maxthreshold [\fIFLOAT\fB: 0.010000 .. 10.000000]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Maximum Delay Response filter threshold value used when auto-tuning
+.TP 8
+\fBdefault\fR
+\fI5.000000\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_outlier_weight [\fIFLOAT\fB: 0.010000 .. 2.000000]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Delay Response outlier weight: if an outlier is detected, determines
+ the amount of its deviation from mean that is used to build the standard
+ deviation statistics and influence further outlier detection.
+ When set to 1.0, the outlier is used as is.
+.TP 8
+\fBdefault\fR
+\fI1.000000\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_outlier_filter_stepdetect_enable [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable Delay Response filter step detection (delaySM) to block when certain level exceeded
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_outlier_filter_stepdetect_threshold [\fIINT\fB: 50000 .. 999999999]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Delay step detection threshold. Step detection is performed only
+when delaySM is below this threshold (nanoseconds)
+.TP 8
+\fBdefault\fR
+\fI1000000\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_outlier_filter_stepdetect_level [\fIINT\fB: 50000 .. 999999999]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Delay step level. When step detection enabled and operational, delaySM above this level
+(nanoseconds) is considered a clock step and updates are paused
+.TP 8
+\fBdefault\fR
+\fI500000\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_outlier_filter_stepdetect_credit [\fIINT\fB: 50 .. 1000]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Initial credit (number of samples) the Delay step detection filter can block for.
+When credit is exhausted, filter stops blocking. Credit is gradually restored
+(see \fIptpengine:delay_outlier_filter_stepdetect_credit_increment\fR)
+.TP 8
+\fBdefault\fR
+\fI200\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:delay_outlier_filter_stepdetect_credit_increment [\fIINT\fB: 1 .. 100]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Amount of credit for the Delay step detection filter restored every full sample window
+.TP 8
+\fBdefault\fR
+\fI10\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_outlier_filter_enable [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable outlier filter for the Sync component in slave state.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_outlier_filter_action [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fIdiscard filter \fR
+.TP 8
+\fBusage\fR
+Sync outlier filter action. If set to 'filter', outliers are replaced
+ with moving average.
+.TP 8
+\fBdefault\fR
+\fIdiscard\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_outlier_filter_capacity [\fIINT\fB: 4 .. 60]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Number of samples in the Sync outlier filter buffer.
+.TP 8
+\fBdefault\fR
+\fI20\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_outlier_filter_threshold [\fIFLOAT\fB: 0.001000 .. 1000.000000]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Sync outlier filter threshold: multiplier for the Peirce's maximum standard
+ deviation. When set below 1.0, filter is tighter, when set above 1.0,
+ filter is looser than standard Peirce's test.
+.TP 8
+\fBdefault\fR
+\fI1.000000\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_outlier_filter_always_filter [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Always run the Sync outlier filter, even if clock is being slewed at maximum rate
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_outlier_filter_autotune_enable [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable automatic threshold control for Sync outlier filter.
+.TP 8
+\fBdefault\fR
+\fIY\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_outlier_filter_autotune_minpercent [\fIINT\fB: 0 .. 99]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Sync outlier filter autotune low watermark - minimum percentage
+of discarded samples in the update period before filter is tightened
+by the autotune step value
+.TP 8
+\fBdefault\fR
+\fI20\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_outlier_filter_autotune_maxpercent [\fIINT\fB: 1 .. 100]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Sync outlier filter autotune high watermark - maximum percentage
+of discarded samples in the update period before filter is loosened
+by the autotune step value
+.TP 8
+\fBdefault\fR
+\fI95\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_outlier_filter_autotune_step [\fIFLOAT\fB: 0.010000 .. 10.000000]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+The value the Sync outlier filter threshold is increased
+or decreased by when auto-tuning
+.TP 8
+\fBdefault\fR
+\fI0.100000\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_outlier_filter_autotune_minthreshold [\fIFLOAT\fB: 0.010000 .. 10.000000]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Minimum Sync filter threshold value used when auto-tuning
+.TP 8
+\fBdefault\fR
+\fI0.100000\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_outlier_filter_autotune_maxthreshold [\fIFLOAT\fB: 0.010000 .. 10.000000]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Maximum Sync filter threshold value used when auto-tuning
+.TP 8
+\fBdefault\fR
+\fI5.000000\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_outlier_weight [\fIFLOAT\fB: 0.010000 .. 2.000000]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Sync outlier weight: if an outlier is detected, this value determines the
+ amount of its deviation from mean that is used to build the standard
+ deviation statistics and influence further outlier detection.
+ When set to 1.0, the outlier is used as is
+.TP 8
+\fBdefault\fR
+\fI1.000000\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_outlier_filter_stepdetect_enable [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable Sync filter step detection (delayMS) to block when certain level exceeded
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_outlier_filter_stepdetect_threshold [\fIINT\fB: 50000 .. 999999999]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Sync step detection threshold. Step detection is performed only
+when delayMS is below this threshold (nanoseconds)
+.TP 8
+\fBdefault\fR
+\fI1000000\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_outlier_filter_stepdetect_level [\fIINT\fB: 50000 .. 999999999]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Sync step level. When step detection enabled and operational, delayMS above this level
+(nanoseconds) is considered a clock step and updates are paused
+.TP 8
+\fBdefault\fR
+\fI500000\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_outlier_filter_stepdetect_credit [\fIINT\fB: 50 .. 1000]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Initial credit (number of samples) the Sync step detection filter can block for.
+When credit is exhausted, filter stops blocking. Credit is gradually restored
+(see \fIptpengine:sync_outlier_filter_stepdetect_credit_increment\fR)
+.TP 8
+\fBdefault\fR
+\fI200\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_outlier_filter_stepdetect_credit_increment [\fIINT\fB: 1 .. 100]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Amount of credit for the Sync step detection filter restored every full sample window
+.TP 8
+\fBdefault\fR
+\fI10\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sync_sequence_checking [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+When enabled, Sync messages will only be accepted if sequence ID is increasing.
+\fBnote:\fR This can cause the slave to temporarily lock up if GM restarts before announce timeout,
+so this is limited to 50 consecutive sequence errors. Alternatively, \fIptpengine:clock_update_timeout\fR
+can be used to reset the slave beforehand.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:clock_update_timeout [\fIINT\fB: 0 .. 3600]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+If set to non-zero, time (seconds) before slave is reset back into PTP_LISTENING, if thetre
+were no clock updates. This is useful for situations where slave is in SLAVE state
+(receiving Announce) but is not receiving or not accepting Sync messages.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:calibration_delay [\fIINT\fB: 0 .. 300]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Delay between moving to slave state and enabling clock updates (seconds).
+This allows mean path delay to stabilise before starting clock updates.
+Activated when going into slave state and during slave's GM failover.
+0 - not used.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:idle_timeout [\fIINT\fB: 10 .. 3600]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+PTP idle timeout (seconds): if PTPd is in SLAVE state and there have been no clock
+updates for this amout of time, PTPd releases clock control.\n
+.TP 8
+\fBdefault\fR
+\f60\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:offset_alarm_threshold [\fIINT\fB: 0 .. 999999999]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+PTP Slave Offset from Master alarm threshold (nanoseconds) - absolute value. When set to non-zero, an alarm is raised
+when PTP slave's offset from master crosses this value. The alarm is logged, indicated in the status file, and SNMP
+traps are sent if SNMP is enabled. Similar notifications are created when offset returns within the threshold.
+When zet to 0, offset is not checked against the threshold.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:panic_mode [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable panic mode: when offset from master is above 1 second, stop updating
+the clock for a period of time and then step the clock if offset remains
+above 1 second.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:panic_mode_duration [\fIINT\fB: 1 .. 60]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Duration (minutes) of the panic mode period (no clock updates) when offset
+above 1 second detected.
+.TP 8
+\fBdefault\fR
+\fI2\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:panic_mode_release_clock [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+When entering panic mode, release clock control while panic mode lasts.
+If not set, PTP will hold clock control during panic mode.
+If set together with ntpengine:* configured, this will fail over to NTP.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:panic_mode_exit_threshold [\fIINT\fB: 0 .. 999999999]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Do not exit panic mode until offset drops below this value (nanoseconds).
+0 = not used.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:pid_as_clock_identity [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Use PTPd's process ID as the middle part of the PTP clock ID - useful for running multiple instances.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:ntp_failover [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Fail over to NTP when PTP time sync not available - requires
+ntpengine:enabled, but does not require the rest of NTP configuration:
+will warn instead of failing over if cannot control ntpd.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:ntp_failover_timeout [\fIINT\fB: 0 .. 1800]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+NTP failover timeout in seconds: time between PTP slave going into
+LISTENING state, and failing over to NTP. 0 = fail over immediately.
+This setting controls the time provider election hold time.
+.TP 8
+\fBdefault\fR
+\fI60\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:prefer_ntp [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Prefer NTP time synchronisation. Only use PTP when NTP not available.
+Could be used when NTP runs with a local GPS receiver or another hardware reference.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:panic_mode_ntp [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Deprecated as of 2.3.1, but still supported: see \fIptppengine:panic_mode_release_clock\fR.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:sigusr2_clears_counters [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Clear counters after dumping all counter values on SIGUSR2.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:timing_acl_permit [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Permit access control list for timing and signaling messages. Format is a series of
+network prefixes and/or IP addresses separated by commas, spaces, tabs or semicolons.
+Accepted format is CIDR notation (a.b.c.d/mm), single IP address (a.b.c.d),
+or full network/mask (a.b.c.d/m.m.m.m). Shortcuts can be used: 172.16/12
+is expanded to 172.16.0.0/12; 192.168/255.255 is expanded to
+192.168.0.0/255.255.0.0, etc. The match is performed
+on the source IP address of the incoming messages. IP access lists are
+only supported when using the IP transport.
+.TP 8
+\fBdefault\fR
+\fI[none]\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:timing_acl_deny [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Deny access control list for timing and signaling messages. Format is a series of
+network prefixes and/or IP addresses separated by commas, spaces, tabs or semicolons.
+Accepted format is CIDR notation (a.b.c.d/mm), single IP address (a.b.c.d),
+or full network/mask (a.b.c.d/m.m.m.m). Shortcuts can be used: 172.16/12
+is expanded to 172.16.0.0/12; 192.168/255.255 is expanded to
+192.168.0.0/255.255.0.0, etc. The match is performed
+on the source IP address of the incoming messages. IP access lists are
+only supported when using the IP transport.
+.TP 8
+\fBdefault\fR
+\fI[none]\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:management_acl_permit [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Permit access control list for management messages. Format is a series of
+network prefixes and/or IP addresses separated by commas, spaces, tabs or semicolons.
+Accepted format is CIDR notation (a.b.c.d/mm), single IP address (a.b.c.d),
+or full network/mask (a.b.c.d/m.m.m.m). Shortcuts can be used: 172.16/12
+is expanded to 172.16.0.0/12; 192.168/255.255 is expanded to
+192.168.0.0/255.255.0.0, etc. The match is performed
+on the source IP address of the incoming messages. IP access lists are
+only supported when using the IP transport.
+.TP 8
+\fBdefault\fR
+\fI[none]\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:management_acl_deny [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Deny access control list for management messages. Format is a series of
+network prefixes and/or IP addresses separated by commas, spaces, tabs or semicolons.
+Accepted format is CIDR notation (a.b.c.d/mm), single IP address (a.b.c.d),
+or full network/mask (a.b.c.d/m.m.m.m). Shortcuts can be used: 172.16/12
+is expanded to 172.16.0.0/12; 192.168/255.255 is expanded to
+192.168.0.0/255.255.0.0, etc. The match is performed
+on the source IP address of the incoming messages. IP access lists are
+only supported when using the IP transport.
+.TP 8
+\fBdefault\fR
+\fI[none]\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:timing_acl_order [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fIpermit-deny deny-permit \fR
+.TP 8
+\fBusage\fR
+Order in which permit and deny access lists are evaluated for timing
+and signaling messages, the evaluation process is the same as for Apache httpd. \fBSee:
+\fIhttp://httpd.apache.org/docs/current/mod/mod_access_compat.html#order\fR
+.TP 8
+\fBdefault\fR
+\fIdeny-permit\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBptpengine:management_acl_order [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fIpermit-deny deny-permit \fR
+.TP 8
+\fBusage\fR
+Order in which permit and deny access lists are evaluated for management
+messages, the evaluation process is the same as for Apache httpd. \fBSee:
+\fIhttp://httpd.apache.org/docs/current/mod/mod_access_compat.html#order\fR
+.TP 8
+\fBdefault\fR
+\fIdeny-permit\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBclock:no_adjust [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Do not adjust the clock.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBclock:no_reset [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Do not reset the clock - only slew.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBclock:step_startup_force [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Force clock step on first sync after startup regardless of offset and
+\fIclock:no_reset\fR
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBclock:step_startup [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Step clock on startup only if offset >= 1 second, ignoring
+panic mode and \fIclock:no_reset\fR
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBclock:set_rtc_on_step [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Attempt setting the RTC when stepping clock (Linux only - FreeBSD does
+this for us. \fBWARNING:\fR this will always set the RTC to OS clock time,
+regardless of time zones, so this assumes that RTC runs in UTC or otherwise
+in the same timescale as PTP. True at least on most single-boot x86 Linux systems.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBclock:drift_handling [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fIreset preserve file \fR
+.TP 8
+\fBusage\fR
+Observed drift handling method between servo restarts:
+.RS 12
+.TP 12
+\fIreset\fR
+set to zero (not recommended)
+.TP 12
+\fIpreserve\fR
+use kernel value
+.TP 12
+\fIfile\fR
+load/save to drift file on startup/shutdown, use kernel value inbetween.
+To specify drift file, use the \fBclock:drift_file\fR setting.
+.RE
+.TP 8
+\fBdefault\fR
+\fIpreserve\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBclock:drift_file [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Specify drift file
+.TP 8
+\fBdefault\fR
+\fI/etc/ptpd2_kernelclock.drift\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBclock:leap_seconds_file [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Specify leap second file location (up to date version can be downloaded from:
+http://www.ietf.org/timezones/data/leap-seconds.list). When configured,
+PTP master will use data from this file to announce leap flags and UTC offset,
+overriding OS information, and PTP slave will use data from this file as well as
+information supplied by the GM. If configured, this file is always reloaded
+on configuration reload (SIGHUP), reloaded on clock step and reloaded after
+a leap second event to ensure the information is up to date. As of ptpd 2.3.1,
+the file is bundled with ptpd and is installed into
+(prefix)/share/ptpd/leap-seconds.list.ddMMMyyyy where ddMMMyyyy
+is the leap seconds file expiry date.
+.TP 8
+\fBdefault\fR
+\fI[none]\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBclock:leap_second_pause_period [\fIINT\fB: 5 .. 600]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Time (seconds) before and after midnight that clock updates should be
+suspended for during a leap second event. The total duration
+of the pause is twice the configured duration. Clock updates are suspended
+when there is a leap second event pending and time to midnight is less than
+or equal to this value and resumed no earlier than this value after midnight.
+Clock updates are resumed in a controlled manner - after a control message,
+such as PTP announce. This ensures that the updated UTC offset is received
+before any further clock updates.
+.TP 8
+\fBdefault\fR
+\fI5\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBclock:leap_second_notice_period [\fIINT\fB: 3600 .. 86400]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Time (seconds) before midnight that PTPd starts announcing the leap second
+if it's running as master. The IEEE 1588 standard suggests 12 hours notice
+and this is the default, but it may be changed to allow more flexibility.
+.TP 8
+\fBdefault\fR
+\fI43200\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBclock:leap_second_handling [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fIaccept ignore step smear\fR
+.TP 8
+\fBusage\fR
+Clock sync behaviour during leap second events:
+.RS 12
+.TP 12
+\fIaccept\fR
+Inform OS kernel about the leap second and let the kernel insert or delete the leap second
+.TP 12
+\fIignore\fR
+Do not inform the kernel - this ends with a +/-1-second offset which is then slewed back down
+.TP 12
+\fIstep\fR
+Do not inform the kernel and step the clock immediately after the leap second event
+.TP 12
+\fIsmear\fR
+Gradually introduce an extra offset over a period of time before the leap second event,
+which accumulates to +/-1 second (see \fIclock:leap_second_smear_period\fR). Once the clock
+stabilises, this results in a clock frequency shift which is taken off after the event.
+Once the leap second event is over, the extra offset is also removed and time is back in
+line with master time.
+.RE
+.TP 8
+\fBdefault\fR
+\fIaccept\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBclock:leap_second_smear_period [\fIINT\fB: 3600 .. 86400]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+When \fIclock:leap_second_handling\fR is set to \fIsmear\fR, this setting
+defines the period (in seconds) before the leap second event, over which
+the leap second offset is gradually added. Example: when set to 24 hours (86400),
+an extra +/-11.5 microseconds is added every second (11.5 ppm clock frequency
+offset).
+.TP 8
+\fBdefault\fR
+\fI86400\fR
+
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBclock:max_offset_ppm [\fIINT\fB: 500 .. 1000]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Maximum absolute frequency shift which can be applied to the clock servo
+ when slewing the clock. Expressed in parts per million (1 ppm = shift of
+ 1 us per second. Values above 512 will use the tick duration correction
+ to allow even faster slewing. Default maximum is 512 without using tick.
+.TP 8
+\fBdefault\fR
+\fI500\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBservo:delayfilter_stiffness [\fIINT\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Mean Path Delay filter stiffness.
+.TP 8
+\fBdefault\fR
+\fI6\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBservo:kp [\fIFLOAT\fB: min: 0.000001 ]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Clock servo PI controller proportional component gain (kP).
+.TP 8
+\fBdefault\fR
+\fI0.100000\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBservo:ki [\fIFLOAT\fB: min: 0.000001 ]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Clock servo PI controller integral component gain (kI).
+.TP 8
+\fBdefault\fR
+\fI0.001000\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBservo:dt_method [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fInone constant measured \fR
+.TP 8
+\fBusage\fR
+How servo update interval (delta t) is calculated:
+.RS 12
+.TP 12
+\fInone\fR
+servo not corrected for update interval (dt always 1),
+.TP 12
+\fIconstant\fR
+constant value (target servo update rate) - sync interval for PTP,
+.TP 12
+\fImeasured\fR
+servo measures how often it's updated and uses this interval.
+.RE
+.TP 8
+\fBdefault\fR
+\fIconstant\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBservo:dt_max [\fIFLOAT\fB: 1.500000 .. 100.000000]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Maximum servo update interval (delta t) when using measured servo update interval
+(\fIservo:dt_method\fR = \fBmeasured\fR), specified as sync interval multiplier
+.TP 8
+\fBdefault\fR
+\fI5.000000\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBservo:stability_detection [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable clock synchronisation servo stability detection
+(based on standard deviation of the observed drift value) - drift will be saved to drift file / cached when considered stable,
+also clock stability status will be logged.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBservo:stability_threshold [\fIFLOAT\fB: 1.000000 .. 10000.000000]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Specify the observed drift standard deviation threshold in parts per billion (ppb) - if stanard deviation is within the threshold, servo
+is considered stable.
+.TP 8
+\fBdefault\fR
+\fI5.000000\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBservo:stability_period [\fIINT\fB: 1 .. 100]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Specify for how many statistics update intervals the observed drift
+standard deviation has to stay within threshold to be considered stable.
+.TP 8
+\fBdefault\fR
+\fI3\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBservo:stability_timeout [\fIINT\fB: 1 .. 60]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Specify after how many minutes without stabilisation servo is considered
+unstable. Assists with logging servo stability information and
+allows to preserve observed drift if servo cannot stabilise.
+.TP 8
+\fBdefault\fR
+\fI10\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBservo:max_delay [\fIINT\fB: 0 .. 999999999]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Do not accept master to slave delay (delayMS - from Sync message) or slave to master delay (delaySM - from Delay Response message)
+if greater than this value (nanoseconds). 0 = not used.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBservo:max_delay_max_rejected [\fIINT\fB: min: 0 ]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Maximum number of consecutive rejected delay measurements exceeding the maxDelay threshold (\fIservo:max_delay\fR),
+before slave is reset. 0 = not checked.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBservo:max_delay_stable_only [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+If \fBservo:max_delay\fR is set, perform the check only if clock servo has stabilised.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBservo:max_offset [\fIINT\fB: 0 .. 999999999]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Do not reset the clock if offset from master is greater than this value (nanoseconds). 0 = not used.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:config_templates [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Comma, space or tab-separated list of template names to be applied to the configuration
+(see \fICONFIGURATION TEMPLATES AND TEMPLATE FILES\fR section). Templates are applied in the order
+they are specified, so any overlapping settings from one template are overridden with settings
+from the following template(s). PTPd provides some built-in templates - see the templates section
+above; to see the built-in templates, run ptpd with \fI-T\fR or \fI--show-templates\fR.
+.TP 8
+\fBdefault\fR
+\fI[none]\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:template_files [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Comma, space or tab-separated list of template file paths to be loaded
+(see \fICONFIGURATION TEMPLATES AND TEMPLATE FILES\fR section). Template
+files are also loaded in the order they are provided, so templates in one file
+can be extended by templates in the next file(s); any overlapping settings are overridden
+by following files. PTPd will not exit when one or more template files cannot be opened.
+PTPd will always try to load \fI at prefix@/share/@PACKAGE_NAME@/templates.conf\fR on startup.
+.TP 8
+\fBdefault\fR
+\fI[none]\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:enable_alarms [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable support for alarm and event notifications (see \fBALARMS\fR section). Alarms
+enable self-diagnosing of common error conditions and events such as master change
+or time properties change. When SNMP support is enabled (\fBglobal:enable_snmp\fR)
+and SNMP trap support is enabled (\fBglobal:enable_snmp_traps\fR), alarms trigger
+SNMP traps.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:alarm_timeout [\fIINT\fB: 0 .. 3600]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Mininmum alarm age (seconds) - minimal time between alarm set and clear notifications.
+The condition can clear while alarm lasts, but notification (log or SNMP) will only
+be triggered after the timeout. This option prevents from alarms flapping (repeated
+set and clear notifications).
+.TP 8
+\fBdefault\fR
+\fI30\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:alarm_initial_delay [\fIINT\fB: 0 .. 3600]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Delay the start of alarm processing (seconds) after ptpd startup. This option
+allows to avoid unnecessary alarms before PTPd starts synchronising, which should happen
+after a few seconds, but could take longer in cases where multicast has to converge upstream,
+or when there is a mismatch in message intervals and unicast signaling has to negotiate them down
+(or up) to acceptable values. This also prevents from alerting on offset from master too soon
+after startup (see \fBptpengine:offset_alarm_threshold\fR) - delay can be increased to cover
+the initial sync period, however this is not recommended as an offset alarm after startup
+can indicate a slave cold start.
+.TP 8
+\fBdefault\fR
+\fI10\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:enable_snmp [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable SNMP agent (if compiled with PTPD_SNMP).
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:enable_snmp_traps [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable reporting of alarms and events as SNMP traps. Requires PTPd to be compiled with PTPD_SNMP,
+and requires alarms to be enabled (\fBglobal:enable_alarms\fR)
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:use_syslog [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Send log messages to syslog. Disabling this sends all messages to stdout (or speficied log file).
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:lock_file [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Lock file location
+.TP 8
+\fBdefault\fR
+\fI[none]\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:auto_lockfile [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Use mode specific and interface specific lock file (overrides \fBglobal:lock_file\fR).
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:lock_directory [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Lock file directory: used with automatic mode-specific lock files,
+also used when no lock file is specified. When lock file
+is specified, it's expected to be an absolute path.
+.TP 8
+\fBdefault\fR
+\fI/var/run\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:ignore_lock [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Skip lock file checking and locking.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:quality_file [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+File used to record data about sync packets. Enables recording when set.
+.TP 8
+\fBdefault\fR
+\fI[none]\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:quality_file_max_size [\fIINT\fB: min: 0 ]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Maximum sync packet record file size (in kB) - file will be truncated
+if size exceeds the limit. 0 - no limit.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:quality_file_max_files [\fIINT\fB: 0 .. 100]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable log rotation of the sync packet record file up to n files.
+0 - do not rotate.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:quality_file_truncate [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Truncate the sync packet record file every time it is (re) opened:
+startup and SIGHUP.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:status_file [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+File used to log ptpd2 status information.
+.TP 8
+\fBdefault\fR
+\fI/var/run/ptpd2.status\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:log_status [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable / disable writing status information to file.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:status_update_interval [\fIINT\fB: 1 .. 30]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Status file update interval in seconds.
+.TP 8
+\fBdefault\fR
+\fI1\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:log_file [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Specify log file path (event log). Setting this enables logging to file.
+.TP 8
+\fBdefault\fR
+\fI[none]\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:log_file_max_size [\fIINT\fB: min: 0 ]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Maximum log file size (in kB) - log file will be truncated if size exceeds
+the limit. 0 - no limit.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:log_file_max_files [\fIINT\fB: 0 .. 100]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable log rotation of the sync packet record file up to n files.
+0 - do not rotate.
+
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:log_file_truncate [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Truncate the log file every time it is (re) opened: startup and SIGHUP.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:log_level [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fILOG_ERR LOG_WARNING LOG_NOTICE LOG_INFO LOG_ALL \fR
+.TP 8
+\fBusage\fR
+Specify log level (only messages at this priority or higer will be logged).
+The minimal level is LOG_ERR. LOG_ALL enables debug output if compiled with
+RUNTIME_DEBUG.
+.TP 8
+\fBdefault\fR
+\fILOG_ALL\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:statistics_file [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Specify statistics log file path. Setting this enables logging of
+statistics, but can be overriden with \fBglobal:log_statistics\fR.
+.TP 8
+\fBdefault\fR
+\fI[none]\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:statistics_log_interval [\fIINT\fB: min: 0 ]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Log timing statistics every n seconds for Sync and Delay messages (0 - log all).
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:statistics_file_max_size [\fIINT\fB: min: 0 ]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Maximum statistics log file size (in kB) - log file will be truncated if size exceeds the limit. 0 - no limit.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:statistics_file_max_files [\fIINT\fB: 0 .. 100]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable log rotation of the statistics file up to n files. 0 - do not rotate.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:statistics_file_truncate [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Truncate the statistics file every time it is (re) opened: startup and SIGHUP.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:dump_packets [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Dump the contents of every PTP packet.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:verbose_foreground [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Run in foreground with statistics and all messages logged to stdout. Overrides log file and statistics file settings and disables syslog.
+
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:foreground [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Run in foreground - ignored when global:verbose_foreground is set.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:log_statistics [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Log timing statistics for every PTP packet received. Output is in CSV format and field headers
+are always printed when starting or refreshing the statistics log.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:statistics_timestamp_format [\fISELECT\fB]\fR
+.RS 8
+.TP 8
+\fBoptions\fR
+\fIdatetime unix both \fR
+.TP 8
+\fBusage\fR
+Timestamp format used when logging timing statistics (when \fBglobal:log_statistics\fR is enabled):
+.RS 12
+.TP 12
+\fIdatetime\fR
+Formatted date and time: \fBYYYY-MM-DD hh:mm:ss.uuuuuu\fR
+.TP 12
+\fIunix\fR
+Unix timestamp with nanoseconds: \fBs.ns\fR
+.TP 12
+\fIboth\fR
+Formatted date and time followed by unix timestamp (adds one extra field to the log)
+.RE
+.TP 8
+\fBdefault\fR
+\fIdatetime\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:periodic_updates [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Log a status update every time statistics are updated (\fIglobal:statistics_update_interval\fR).
+This update is written to the main log target. Status updates are logged even if ptpd is configured
+without support for statistics.
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:cpuaffinity_cpucore [\fIINT\fB: -1 .. 255]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Bind ptpd2 process to a selected CPU core number. 0 = first CPU core, etc. -1 = do not bind to a single core.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:statistics_update_interval [\fIINT\fB: 1 .. 60]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Clock synchronisation statistics update interval in seconds. Also controls how often periodic status information
+is logged (when using \fIglobal:statistics_update_interval\fR).
+.TP 8
+\fBdefault\fR
+\fI30\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBglobal:timingdomain_election_delay [\fIINT\fB: 0 .. 3600 ]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Delay (seconds) before releasing a time service (NTP or PTP)
+and electing a new one to control a clock. 0 = elect immediately
+.TP 8
+\fBdefault\fR
+\fI15\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBntpengine:enabled [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable NTPd integration
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBntpengine:control_enabled [\fIBOOLEAN\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+Enable control over local NTPd daemon
+.TP 8
+\fBdefault\fR
+\fIN\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBntpengine:check_interval [\fIINT\fB: 5 .. 600]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+NTP control check interval in seconds
+.TP 8
+\fBdefault\fR
+\fI15\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBntpengine:key_id [\fIINT\fB: 0 .. 65535]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+NTP key number - must be configured as a trusted control key in ntp.conf,
+and be non-zero for the ntpengine:control_enabled setting to take effect.
+.TP 8
+\fBdefault\fR
+\fI0\fR
+
+.RE
+.RE
+.RS 0
+.TP 8
+\fBntpengine:key [\fISTRING\fB]\fR
+.RS 8
+.TP 8
+\fBusage\fR
+NTP key (plain text, max. 20 characters) - must match the key configured in
+ntpd's keys file, and must be non-zero for the ntpengine:control_enabled
+setting to take effect.
+.TP 8
+\fBdefault\fR
+\fI[none]\fR
+
+.RE
+.RE
+
+.SH BUGS
+Configuration file support has only been introduced in version 2.3. There may still be some inconsistencies
+in the way some settings are parsed and while order should not make any difference, for some complex behaviours
+it may still be the case.
+
+Please report any bugs using the bug tracker on the SourceForge page:
+http://sourceforge.net/projects/ptpd/
+
+.SH SEE ALSO
+.xR ptpd2 8
+ptpd2(8)
+
+.SH AUTHORS
+.PP
+Steven Kreuzer <skreuzer at freebsd.org>
+.PP
+Gael Mace <gael_mace at users.sourceforge.net>
+.PP
+George Neville-Neil <gnn at freebsd.org>
+.PP
+Wojciech Owczarek <wojciech at owczarek.co.uk>
+.PP
+Alexandre Van Kempen
+
+\fBptpd2.conf(5)\fR man page first written by Wojciech Owczarek for ptpd 2.3.0 in November 2013
diff --git a/rtemsbsd/ptpd/src/ptpd2.conf.default-full b/rtemsbsd/ptpd/src/ptpd2.conf.default-full
new file mode 100644
index 00000000..2ceb5739
--- /dev/null
+++ b/rtemsbsd/ptpd/src/ptpd2.conf.default-full
@@ -0,0 +1,841 @@
+; ========================================
+; PTPDv2 version 2.3.2 default configuration
+; ========================================
+
+; NOTE: the following settings are affected by ptpengine:preset selection:
+; ptpengine:slave_only
+; clock:no_adjust
+; ptpengine:clock_class - allowed range and default value
+; To see all preset settings, run ptpd2 -H (--long-help)
+
+; Network interface to use - eth0, igb0 etc. (required).
+ptpengine:interface =
+
+; Backup network interface to use - eth0, igb0 etc. When no GM available,
+; slave will keep alternating between primary and secondary until a GM is found.
+;
+ptpengine:backup_interface =
+
+; PTP engine preset:
+; none = Defaults, no clock class restrictions
+; masteronly = Master, passive when not best master (clock class 0..127)
+; masterslave = Full IEEE 1588 implementation:
+; Master, slave when not best master
+; (clock class 128..254)
+; slaveonly = Slave only (clock class 255 only)
+;
+; Options: none masteronly masterslave slaveonly
+ptpengine:preset = slaveonly
+
+; Transport type for PTP packets. Ethernet transport requires libpcap support.
+; Options: ipv4 ethernet
+ptpengine:transport = ipv4
+
+; Enable TransportSpecific field compatibility with 802.1AS / AVB (requires Ethernet transport)
+ptpengine:dot1as = N
+
+; Disable PTP port. Causes the PTP state machine to stay in PTP_DISABLED state indefinitely,
+; until it is re-enabled via configuration change or ENABLE_PORT management message.
+ptpengine:disabled = N
+
+; IP transmission mode (requires IP transport) - hybrid mode uses
+; multicast for sync and announce, and unicast for delay request and
+; response; unicast mode uses unicast for all transmission.
+; When unicast mode is selected, destination IP(s) may need to be configured
+; (ptpengine:unicast_destinations).
+; Options: multicast unicast hybrid
+ptpengine:ip_mode = multicast
+
+; Enable unicast negotiation support using signaling messages
+;
+ptpengine:unicast_negotiation = N
+
+; When using unicast negotiation (slave), accept PTP messages from any master.
+; By default, only messages from acceptable masters (ptpengine:unicast_destinations)
+; are accepted, and only if transmission was granted by the master
+;
+ptpengine:unicast_any_master = N
+
+; PTP port number wildcard mask applied onto port identities when running
+; unicast negotiation: allows multiple port identities to be accepted as one.
+; This option can be used as a workaround where a node sends signaling messages and
+; timing messages with different port identities
+ptpengine:unicast_port_mask = 0
+
+; Disable Best Master Clock Algorithm for unicast masters:
+; Only effective for masteronly preset - all Announce messages
+; will be ignored and clock will transition directly into MASTER state.
+;
+ptpengine:disable_bmca = N
+
+; When unicast negotiation enabled on a master clock,
+; reply to transmission requests also in LISTENING state.
+ptpengine:unicast_negotiation_listening = N
+
+; Use libpcap for sending and receiving traffic (automatically enabled
+; in Ethernet mode).
+ptpengine:use_libpcap = N
+
+; Disable UDP checksum validation on UDP sockets (Linux only).
+; Workaround for situations where a node (like Transparent Clock).
+; does not rewrite checksums
+;
+ptpengine:disable_udp_checksums = Y
+
+; Delay detection mode used - use DELAY_DISABLED for syntonisation only
+; (no full synchronisation).
+; Options: E2E P2P DELAY_DISABLED
+ptpengine:delay_mechanism = E2E
+
+; PTP domain number.
+ptpengine:domain = 0
+
+; PTP port number (part of PTP Port Identity - not UDP port).
+; For ordinary clocks (single port), the default should be used,
+; but when running multiple instances to simulate a boundary clock,
+; The port number can be changed.
+ptpengine:port_number = 1
+
+; Port description (returned in the userDescription field of PORT_DESCRIPTION management message and USER_DESCRIPTION management message) - maximum 64 characters
+ptpengine:port_description = ptpd
+
+; Usability extension: if enabled, a slave-only clock will accept
+; masters from any domain, while preferring the configured domain,
+; and preferring lower domain number.
+; NOTE: this behaviour is not part of the standard.
+ptpengine:any_domain = N
+
+; Slave only mode (sets clock class to 255, overriding value from preset).
+ptpengine:slave_only = Y
+
+; Specify latency correction (nanoseconds) for incoming packets.
+ptpengine:inbound_latency = 0
+
+; Specify latency correction (nanoseconds) for outgoing packets.
+ptpengine:outbound_latency = 0
+
+; Apply an arbitrary shift (nanoseconds) to offset from master when
+; in slave state. Value can be positive or negative - useful for
+; correcting for antenna latencies, delay assymetry
+; and IP stack latencies. This will not be visible in the offset
+; from master value - only in the resulting clock correction.
+ptpengine:offset_shift = 0
+
+; Compatibility option: In slave state, always respect UTC offset
+; announced by best master, even if the the
+; currrentUtcOffsetValid flag is announced FALSE.
+; NOTE: this behaviour is not part of the standard.
+ptpengine:always_respect_utc_offset = Y
+
+; Compatibility extension to BMC algorithm: when enabled,
+; BMC for both master and save clocks will prefer masters
+; nannouncing currrentUtcOffsetValid as TRUE.
+; NOTE: this behaviour is not part of the standard.
+ptpengine:prefer_utc_offset_valid = N
+
+; Compatibility option: when enabled, ptpd2 will ignore
+; Announce messages from masters announcing currentUtcOffsetValid
+; as FALSE.
+; NOTE: this behaviour is not part of the standard.
+ptpengine:require_utc_offset_valid = N
+
+; Time (seconds) unicast messages are requested for by slaves
+; when using unicast negotiation, and maximum time unicast message
+; transmission is granted to slaves by masters
+;
+ptpengine:unicast_grant_duration = 300
+
+; PTP announce message interval in master state. When using unicast negotiation, for
+; slaves this is the minimum interval requested, and for masters
+; this is the only interval granted.
+; (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_announce_interval = 1
+
+; Maximum Announce message interval requested by slaves when using unicast negotiation,
+; (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_announce_interval_max = 5
+
+; PTP announce receipt timeout announced in master state.
+ptpengine:announce_receipt_timeout = 6
+
+; PTP announce receipt timeout grace period in slave state:
+; when announce receipt timeout occurs, disqualify current best GM,
+; then wait n times announce receipt timeout before resetting.
+; Allows for a seamless GM failover when standby GMs are slow
+; to react. When set to 0, this option is not used.
+ptpengine:announce_receipt_grace_period = 0
+
+; PTP sync message interval in master state. When using unicast negotiation, for
+; slaves this is the minimum interval requested, and for masters
+; this is the only interval granted.
+; (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_sync_interval = 0
+
+; Maximum Sync message interval requested by slaves when using unicast negotiation,
+; (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_sync_interval_max = 5
+
+; Override the Delay Request interval announced by best master.
+ptpengine:log_delayreq_override = N
+
+; Automatically override the Delay Request interval
+; if the announced value is 127 (0X7F), such as in
+; unicast messages (unless using unicast negotiation)
+ptpengine:log_delayreq_auto = Y
+
+; Delay request interval used before receiving first delay response
+; (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_delayreq_interval_initial = 0
+
+; Minimum delay request interval announced when in master state,
+; in slave state overrides the master interval,
+; required in hybrid mode. When using unicast negotiation, for
+; slaves this is the minimum interval requested, and for masters
+; this is the minimum interval granted.
+; (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_delayreq_interval = 0
+
+; Maximum Delay Response interval requested by slaves when using unicast negotiation,
+; (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_delayreq_interval_max = 5
+
+; Minimum peer delay request message interval in peer to peer delay mode.
+; When using unicast negotiation, this is the minimum interval requested,
+; and the only interval granted.
+; (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_peer_delayreq_interval = 1
+
+; Maximum Peer Delay Response interval requested by slaves when using unicast negotiation,
+; (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_peer_delayreq_interval_max = 5
+
+; Foreign master record size (Maximum number of foreign masters).
+ptpengine:foreignrecord_capacity = 5
+
+; Specify Allan variance announced in master state.
+ptpengine:ptp_allan_variance = 65535
+
+; Clock accuracy range announced in master state.
+; Options: ACC_25NS ACC_100NS ACC_250NS ACC_1US ACC_2_5US ACC_10US ACC_25US ACC_100US ACC_250US ACC_1MS ACC_2_5MS ACC_10MS ACC_25MS ACC_100MS ACC_250MS ACC_1S ACC_10S ACC_10SPLUS ACC_UNKNOWN
+ptpengine:ptp_clock_accuracy = ACC_UNKNOWN
+
+; Underlying time source UTC offset announced in master state.
+ptpengine:utc_offset = 0
+
+; Underlying time source UTC offset validity announced in master state.
+ptpengine:utc_offset_valid = N
+
+; Underlying time source time traceability announced in master state.
+ptpengine:time_traceable = N
+
+; Underlying time source frequency traceability announced in master state.
+ptpengine:frequency_traceable = N
+
+; Time scale announced in master state (with ARB, UTC properties
+; are ignored by slaves). When clock class is set to 13 (application
+; specific), this value is ignored and ARB is used.
+; Options: PTP ARB
+ptpengine:ptp_timescale = PTP
+
+; Time source announced in master state.
+; Options: ATOMIC_CLOCK GPS TERRESTRIAL_RADIO PTP NTP HAND_SET OTHER INTERNAL_OSCILLATOR
+ptpengine:ptp_timesource = INTERNAL_OSCILLATOR
+
+; Clock class - announced in master state. Always 255 for slave-only.
+; Minimum, maximum and default values are controlled by presets.
+; If set to 13 (application specific time source), announced
+; time scale is always set to ARB. This setting controls the
+; states a PTP port can be in. If below 128, port will only
+; be in MASTER or PASSIVE states (master only). If above 127,
+; port will be in MASTER or SLAVE states.
+ptpengine:clock_class = 255
+
+; Priority 1 announced in master state,used for Best Master
+; Clock selection.
+ptpengine:priority1 = 128
+
+; Priority 2 announced in master state, used for Best Master
+; Clock selection.
+ptpengine:priority2 = 128
+
+; Number of consecutive resets to LISTENING before full network reset
+;
+ptpengine:max_listen = 5
+
+; Specify unicast slave addresses for unicast master operation, or unicast
+; master addresses for slave operation. Format is similar to an ACL: comma,
+; tab or space-separated IPv4 unicast addresses, one or more. For a slave,
+; when unicast negotiation is used, setting this is mandatory.
+ptpengine:unicast_destinations =
+
+; Specify PTP domain number for each configured unicast destination (ptpengine:unicast_destinations).
+; This is only used by slave-only clocks using unicast destinations to allow for each master
+; to be in a separate domain, such as with Telecom Profile. The number of entries should match the number
+; of unicast destinations, otherwise unconfigured domains or domains set to 0 are set to domain configured in
+; ptpengine:domain. The format is a comma, tab or space-separated list of 8-bit unsigned integers (0 .. 255)
+ptpengine:unicast_domains =
+
+; Specify a local preference for each configured unicast destination (ptpengine:unicast_destinations).
+; This is only used by slave-only clocks using unicast destinations to allow for each master's
+; BMC selection to be influenced by the slave, such as with Telecom Profile. The number of entries should match the number
+; of unicast destinations, otherwise unconfigured preference is set to 0 (highest).
+; The format is a comma, tab or space-separated list of 8-bit unsigned integers (0 .. 255)
+ptpengine:unicast_local_preference =
+
+; Specify peer unicast adress for P2P unicast. Mandatory when
+; running unicast mode and P2P delay mode.
+ptpengine:unicast_peer_destination =
+
+; Enable handling of PTP management messages.
+ptpengine:management_enable = Y
+
+; Accept SET and COMMAND management messages.
+ptpengine:management_set_enable = N
+
+; Send explicit IGMP joins between engine resets and periodically
+; in master state.
+ptpengine:igmp_refresh = Y
+
+; Periodic IGMP join interval (seconds) in master state when running
+; IPv4 multicast: when set below 10 or when ptpengine:igmp_refresh
+; is disabled, this setting has no effect.
+ptpengine:master_igmp_refresh_interval = 60
+
+; Multicast time to live for multicast PTP packets (ignored and set to 1
+; for peer to peer messages).
+ptpengine:multicast_ttl = 64
+
+; DiffServ CodepPoint for packet prioritisation (decimal). When set to zero,
+; this option is not used. Use 46 for Expedited Forwarding (0x2e).
+ptpengine:ip_dscp = 0
+
+; Enable statistical filter for Sync messages.
+ptpengine:sync_stat_filter_enable = N
+
+; Type of filter used for Sync message filtering
+; Options: none mean min max absmin absmax median
+ptpengine:sync_stat_filter_type = min
+
+; Number of samples used for the Sync statistical filter
+ptpengine:sync_stat_filter_window = 4
+
+; Sample window type used for Sync message statistical filter. Delay Response outlier filter action.
+; Sliding window is continuous, interval passes every n-th sample only.
+; Options: sliding interval
+ptpengine:sync_stat_filter_window_type = sliding
+
+; Enable statistical filter for Delay messages.
+ptpengine:delay_stat_filter_enable = N
+
+; Type of filter used for Delay message statistical filter
+; Options: none mean min max absmin absmax median
+ptpengine:delay_stat_filter_type = min
+
+; Number of samples used for the Delay statistical filter
+ptpengine:delay_stat_filter_window = 4
+
+; Sample window type used for Delay message statistical filter
+; Sliding window is continuous, interval passes every n-th sample only
+; Options: sliding interval
+ptpengine:delay_stat_filter_window_type = sliding
+
+; Enable outlier filter for the Delay Response component in slave state
+ptpengine:delay_outlier_filter_enable = N
+
+; Delay Response outlier filter action. If set to 'filter', outliers are
+; replaced with moving average.
+; Options: discard filter
+ptpengine:delay_outlier_filter_action = discard
+
+; Number of samples in the Delay Response outlier filter buffer
+ptpengine:delay_outlier_filter_capacity = 20
+
+; Delay Response outlier filter threshold (: multiplier for Peirce's maximum
+; standard deviation. When set below 1.0, filter is tighter, when set above
+; 1.0, filter is looser than standard Peirce's test.
+; When autotune enabled, this is the starting threshold.
+ptpengine:delay_outlier_filter_threshold = 1.000000
+
+; Always run the Delay Response outlier filter, even if clock is being slewed at maximum rate
+ptpengine:delay_outlier_filter_always_filter = N
+
+; Enable automatic threshold control for Delay Response outlier filter.
+ptpengine:delay_outlier_filter_autotune_enable = Y
+
+; Delay Response outlier filter autotune low watermark - minimum percentage
+; of discarded samples in the update period before filter is tightened
+; by the autotune step value.
+ptpengine:delay_outlier_filter_autotune_minpercent = 20
+
+; Delay Response outlier filter autotune high watermark - maximum percentage
+; of discarded samples in the update period before filter is loosened
+; by the autotune step value.
+ptpengine:delay_outlier_filter_autotune_maxpercent = 95
+
+; The value the Delay Response outlier filter threshold is increased
+; or decreased by when auto-tuning.
+ptpengine:delay_outlier_autotune_step = 0.100000
+
+; Minimum Delay Response filter threshold value used when auto-tuning
+ptpengine:delay_outlier_filter_autotune_minthreshold = 0.100000
+
+; Maximum Delay Response filter threshold value used when auto-tuning
+ptpengine:delay_outlier_filter_autotune_maxthreshold = 5.000000
+
+; Enable Delay filter step detection (delaySM) to block when certain level exceeded
+ptpengine:delay_outlier_filter_stepdetect_enable = N
+
+; Delay Response step detection threshold. Step detection is performed
+; only when delaySM is below this threshold (nanoseconds)
+ptpengine:delay_outlier_filter_stepdetect_threshold = 1000000
+
+; Delay Response step level. When step detection enabled and operational,
+; delaySM above this level (nanosecond) is considered a clock step and updates are paused
+ptpengine:delay_outlier_filter_stepdetect_level = 500000
+
+; Initial credit (number of samples) the Delay step detection filter can block for
+; When credit is exhausted, filter stops blocking. Credit is gradually restored
+ptpengine:delay_outlier_filter_stepdetect_credit = 200
+
+; Amount of credit for the Delay step detection filter restored every full sample window
+ptpengine:delay_outlier_filter_stepdetect_credit_increment = 10
+
+; Delay Response outlier weight: if an outlier is detected, determines
+; the amount of its deviation from mean that is used to build the standard
+; deviation statistics and influence further outlier detection.
+; When set to 1.0, the outlier is used as is.
+ptpengine:delay_outlier_weight = 1.000000
+
+; Enable outlier filter for the Sync component in slave state.
+ptpengine:sync_outlier_filter_enable = N
+
+; Sync outlier filter action. If set to 'filter', outliers are replaced
+; with moving average.
+; Options: discard filter
+ptpengine:sync_outlier_filter_action = discard
+
+; Number of samples in the Sync outlier filter buffer.
+ptpengine:sync_outlier_filter_capacity = 20
+
+; Sync outlier filter threshold: multiplier for the Peirce's maximum standard
+; deviation. When set below 1.0, filter is tighter, when set above 1.0,
+; filter is looser than standard Peirce's test.
+ptpengine:sync_outlier_filter_threshold = 1.000000
+
+; Always run the Sync outlier filter, even if clock is being slewed at maximum rate
+ptpengine:sync_outlier_filter_always_filter = N
+
+; Enable automatic threshold control for Sync outlier filter.
+ptpengine:sync_outlier_filter_autotune_enable = Y
+
+; Sync outlier filter autotune low watermark - minimum percentage
+; of discarded samples in the update period before filter is tightened
+; by the autotune step value.
+ptpengine:sync_outlier_filter_autotune_minpercent = 20
+
+; Sync outlier filter autotune high watermark - maximum percentage
+; of discarded samples in the update period before filter is loosened
+; by the autotune step value.
+ptpengine:sync_outlier_filter_autotune_maxpercent = 95
+
+; Value the Sync outlier filter threshold is increased
+; or decreased by when auto-tuning.
+ptpengine:sync_outlier_autotune_step = 0.100000
+
+; Minimum Sync outlier filter threshold value used when auto-tuning
+ptpengine:sync_outlier_filter_autotune_minthreshold = 0.100000
+
+; Maximum Sync outlier filter threshold value used when auto-tuning
+ptpengine:sync_outlier_filter_autotune_maxthreshold = 5.000000
+
+; Enable Sync filter step detection (delayMS) to block when certain level exceeded.
+ptpengine:sync_outlier_filter_stepdetect_enable = N
+
+; Sync step detection threshold. Step detection is performed
+; only when delayMS is below this threshold (nanoseconds)
+ptpengine:sync_outlier_filter_stepdetect_threshold = 1000000
+
+; Sync step level. When step detection enabled and operational,
+; delayMS above this level (nanosecond) is considered a clock step and updates are paused
+ptpengine:sync_outlier_filter_stepdetect_level = 500000
+
+; Initial credit (number of samples) the Sync step detection filter can block for.
+; When credit is exhausted, filter stops blocking. Credit is gradually restored
+ptpengine:sync_outlier_filter_stepdetect_credit = 200
+
+; Amount of credit for the Sync step detection filter restored every full sample window
+ptpengine:sync_outlier_filter_stepdetect_credit_increment = 10
+
+; Sync outlier weight: if an outlier is detected, this value determines the
+; amount of its deviation from mean that is used to build the standard
+; deviation statistics and influence further outlier detection.
+; When set to 1.0, the outlier is used as is.
+ptpengine:sync_outlier_weight = 1.000000
+
+; Delay between moving to slave state and enabling clock updates (seconds).
+; This allows one-way delay to stabilise before starting clock updates.
+; Activated when going into slave state and during slave's GM failover.
+; 0 - not used.
+ptpengine:calibration_delay = 0
+
+; PTP idle timeout: if PTPd is in SLAVE state and there have been no clock
+; updates for this amout of time, PTPd releases clock control.
+;
+ptpengine:idle_timeout = 120
+
+; Enable panic mode: when offset from master is above 1 second, stop updating
+; the clock for a period of time and then step the clock if offset remains
+; above 1 second.
+ptpengine:panic_mode = N
+
+; Duration (minutes) of the panic mode period (no clock updates) when offset
+; above 1 second detected.
+ptpengine:panic_mode_duration = 2
+
+; When entering panic mode, release clock control while panic mode lasts
+; if ntpengine:* configured, this will fail over to NTP,
+; if not set, PTP will hold clock control during panic mode.
+ptpengine:panic_mode_release_clock = N
+
+; Do not exit panic mode until offset drops below this value (nanoseconds).
+; 0 = not used.
+ptpengine:panic_mode_exit_threshold = 0
+
+; Use PTPd's process ID as the middle part of the PTP clock ID - useful for running multiple instances.
+ptpengine:pid_as_clock_identity = N
+
+; Fail over to NTP when PTP time sync not available - requires
+; ntpengine:enabled, but does not require the rest of NTP configuration:
+; will warn instead of failing over if cannot control ntpd.
+ptpengine:ntp_failover = N
+
+; NTP failover timeout in seconds: time between PTP slave going into
+; LISTENING state, and releasing clock control. 0 = fail over immediately.
+ptpengine:ntp_failover_timeout = 120
+
+; Prefer NTP time synchronisation. Only use PTP when NTP not available,
+; could be used when NTP runs with a local GPS receiver or another reference
+ptpengine:prefer_ntp = N
+
+; Legacy option from 2.3.0: same as ptpengine:panic_mode_release_clock
+ptpengine:panic_mode_ntp = N
+
+; Clear counters after dumping all counter values on SIGUSR2.
+ptpengine:sigusr2_clears_counters = N
+
+; Permit access control list for timing packets. Format is a series of
+; comma, space or tab separated network prefixes: IPv4 addresses or full CIDR notation a.b.c.d/x,
+; where a.b.c.d is the subnet and x is the decimal mask, or a.b.c.d/v.x.y.z where a.b.c.d is the
+; subnet and v.x.y.z is the 4-octet mask. The match is performed on the source IP address of the
+; incoming messages. IP access lists are only supported when using the IP transport.
+ptpengine:timing_acl_permit =
+
+; Deny access control list for timing packets. Format is a series of
+; comma, space or tab separated network prefixes: IPv4 addresses or full CIDR notation a.b.c.d/x,
+; where a.b.c.d is the subnet and x is the decimal mask, or a.b.c.d/v.x.y.z where a.b.c.d is the
+; subnet and v.x.y.z is the 4-octet mask. The match is performed on the source IP address of the
+; incoming messages. IP access lists are only supported when using the IP transport.
+ptpengine:timing_acl_deny =
+
+; Permit access control list for management messages. Format is a series of
+; comma, space or tab separated network prefixes: IPv4 addresses or full CIDR notation a.b.c.d/x,
+; where a.b.c.d is the subnet and x is the decimal mask, or a.b.c.d/v.x.y.z where a.b.c.d is the
+; subnet and v.x.y.z is the 4-octet mask. The match is performed on the source IP address of the
+; incoming messages. IP access lists are only supported when using the IP transport.
+ptpengine:management_acl_permit =
+
+; Deny access control list for management messages. Format is a series of
+; comma, space or tab separated network prefixes: IPv4 addresses or full CIDR notation a.b.c.d/x,
+; where a.b.c.d is the subnet and x is the decimal mask, or a.b.c.d/v.x.y.z where a.b.c.d is the
+; subnet and v.x.y.z is the 4-octet mask. The match is performed on the source IP address of the
+; incoming messages. IP access lists are only supported when using the IP transport.
+ptpengine:management_acl_deny =
+
+; Order in which permit and deny access lists are evaluated for timing
+; packets, the evaluation process is the same as for Apache httpd.
+; Options: permit-deny deny-permit
+ptpengine:timing_acl_order = deny-permit
+
+; Order in which permit and deny access lists are evaluated for management
+; messages, the evaluation process is the same as for Apache httpd.
+; Options: permit-deny deny-permit
+ptpengine:management_acl_order = deny-permit
+
+; Do not adjust the clock
+clock:no_adjust = N
+
+; Do not step the clock - only slew
+clock:no_reset = N
+
+; Force clock step on first sync after startup regardless of offset and clock:no_reset
+clock:step_startup_force = N
+
+; Step clock on startup if offset >= 1 second, ignoring
+; panic mode and clock:no_reset
+clock:step_startup = N
+
+; Attempt setting the RTC when stepping clock (Linux only - FreeBSD does
+; this for us. WARNING: this will always set the RTC to OS clock time,
+; regardless of time zones, so this assumes that RTC runs in UTC or
+; at least in the same timescale as PTP. true at least on most
+; single-boot x86 Linux systems.
+clock:set_rtc_on_step = N
+
+; Observed drift handling method between servo restarts:
+; reset: set to zero (not recommended)
+; preserve: use kernel value,
+; file: load/save to drift file on startup/shutdown, use kernel
+; value inbetween. To specify drift file, use the clock:drift_file setting.
+; Options: reset preserve file
+clock:drift_handling = preserve
+
+; Specify drift file
+clock:drift_file = /etc/ptpd2_kernelclock.drift
+
+; Time (seconds) before and after midnight that clock updates should pe suspended for
+; during a leap second event. The total duration of the pause is twice
+; the configured duration
+clock:leap_second_pause_period = 5
+
+; Time (seconds) before midnight that PTPd starts announcing the leap second
+; if it's running as master
+clock:leap_second_notice_period = 43200
+
+; Specify leap second file location - up to date version can be downloaded from
+; http://www.ietf.org/timezones/data/leap-seconds.list
+clock:leap_seconds_file =
+
+; Behaviour during a leap second event:
+; accept: inform the OS kernel of the event
+; ignore: do nothing - ends up with a 1-second offset which is then slewed
+; step: similar to ignore, but steps the clock immediately after the leap second event
+; smear: do not inform kernel, gradually introduce the leap second before the event
+; by modifying clock offset (see clock:leap_second_smear_period)
+; Options: accept ignore step smear
+clock:leap_second_handling = accept
+
+; Time period (Seconds) over which the leap second is introduced before the event.
+; Example: when set to 86400 (24 hours), an extra 11.5 microseconds is added every second
+clock:leap_second_smear_period = 86400
+
+; Maximum absolute frequency shift which can be applied to the clock servo
+; when slewing the clock. Expressed in parts per million (1 ppm = shift of
+; 1 us per second. Values above 512 will use the tick duration correction
+; to allow even faster slewing. Default maximum is 512 without using tick.
+clock:max_offset_ppm = 500
+
+; One-way delay filter stiffness.
+servo:delayfilter_stiffness = 6
+
+; Clock servo PI controller proportional component gain (kP).
+servo:kp = 0.100000
+
+; Clock servo PI controller integral component gain (kI).
+servo:ki = 0.001000
+
+; How servo update interval (delta t) is calculated:
+; none: servo not corrected for update interval (dt always 1),
+; constant: constant value (target servo update rate - sync interval for PTP,
+; measured: servo measures how often it's updated and uses this interval.
+; Options: none constant measured
+servo:dt_method = constant
+
+; Maximum servo update interval (delta t) when using measured servo update interval
+; (servo:dt_method = measured), specified as sync interval multiplier.
+servo:dt_max = 5.000000
+
+; Enable clock synchronisation servo stability detection
+; (based on standard deviation of the observed drift value)
+; - drift will be saved to drift file / cached when considered stable,
+; also clock stability status will be logged.
+servo:stability_detection = N
+
+; Specify the observed drift standard deviation threshold in parts per
+; billion (ppb) - if stanard deviation is within the threshold, servo
+; is considered stable.
+servo:stability_threshold = 10.000000
+
+; Specify for how many statistics update intervals the observed drift
+; standard deviation has to stay within threshold to be considered stable.
+servo:stability_period = 1
+
+; Specify after how many minutes without stabilisation servo is considered
+; unstable. Assists with logging servo stability information and
+; allows to preserve observed drift if servo cannot stabilise.
+;
+servo:stability_timeout = 10
+
+; Do accept master to slave delay (delayMS - from Sync message) or slave to master delay
+; (delaySM - from Delay messages) if greater than this value (nanoseconds). 0 = not used.
+servo:max_delay = 0
+
+; Maximum number of consecutive delay measurements exceeding maxDelay threshold,
+; before slave is reset.
+servo:max_delay_max_rejected = 0
+
+; If servo:max_delay is set, perform the check only if clock servo has stabilised.
+;
+servo:max_delay_stable_only = N
+
+; When enabled, Sync messages will only be accepted if sequence ID is increasing. This is limited to 50 dropped messages.
+;
+ptpengine:sync_sequence_checking = N
+
+; If set to non-zero, timeout in seconds, after which the slave resets if no clock updates made.
+;
+ptpengine:clock_update_timeout = 0
+
+; Do not reset the clock if offset from master is greater
+; than this value (nanoseconds). 0 = not used.
+servo:max_offset = 0
+
+; Enable SNMP agent (if compiled with PTPD_SNMP).
+global:enable_snmp = N
+
+; Send log messages to syslog. Disabling this
+; sends all messages to stdout (or speficied log file).
+global:use_syslog = N
+
+; Lock file location
+global:lock_file =
+
+; Use mode specific and interface specific lock file
+; (overrides global:lock_file).
+global:auto_lockfile = N
+
+; Lock file directory: used with automatic mode-specific lock files,
+; also used when no lock file is specified. When lock file
+; is specified, it's expected to be an absolute path.
+global:lock_directory = /var/run
+
+; Skip lock file checking and locking.
+global:ignore_lock = N
+
+; File used to record data about sync packets. Enables recording when set.
+global:quality_file =
+
+; Maximum sync packet record file size (in kB) - file will be truncated
+; if size exceeds the limit. 0 - no limit.
+global:quality_file_max_size = 0
+
+; Enable log rotation of the sync packet record file up to n files.
+; 0 - do not rotate.
+;
+global:quality_file_max_files = 0
+
+; Truncate the sync packet record file every time it is (re) opened:
+; startup and SIGHUP.
+global:quality_file_truncate = N
+
+; File used to log ptpd2 status information.
+global:status_file = /var/run/ptpd2.status
+
+; Enable / disable writing status information to file.
+global:log_status = N
+
+; Status file update interval in seconds.
+global:status_update_interval = 1
+
+; Specify log file path (event log). Setting this enables logging to file.
+global:log_file =
+
+; Maximum log file size (in kB) - log file will be truncated if size exceeds
+; the limit. 0 - no limit.
+global:log_file_max_size = 0
+
+; Enable log rotation of the sync packet record file up to n files.
+; 0 - do not rotate.
+;
+global:log_file_max_files = 0
+
+; Truncate the log file every time it is (re) opened: startup and SIGHUP.
+global:log_file_truncate = N
+
+; Specify log level (only messages at this priority or higer will be logged).
+; The minimal level is LOG_ERR. LOG_ALL enables debug output if compiled with
+; RUNTIME_DEBUG.
+; Options: LOG_ERR LOG_WARNING LOG_NOTICE LOG_INFO LOG_ALL
+global:log_level = LOG_ALL
+
+; Specify statistics log file path. Setting this enables logging of
+; statistics, but can be overriden with global:log_statistics.
+global:statistics_file =
+
+; Log timing statistics every n seconds for Sync and Delay messages
+; (0 - log all).
+global:statistics_log_interval = 0
+
+; Maximum statistics log file size (in kB) - log file will be truncated
+; if size exceeds the limit. 0 - no limit.
+global:statistics_file_max_size = 0
+
+; Enable log rotation of the statistics file up to n files. 0 - do not rotate.
+global:statistics_file_max_files = 0
+
+; Truncate the statistics file every time it is (re) opened: startup and SIGHUP.
+global:statistics_file_truncate = N
+
+; Dump the contents of every PTP packet
+global:dump_packets = N
+
+; Run in foreground with statistics and all messages logged to stdout.
+; Overrides log file and statistics file settings and disables syslog.
+;
+global:verbose_foreground = N
+
+; Run in foreground - ignored when global:verbose_foreground is set
+global:foreground = N
+
+; Log timing statistics for every PTP packet received
+;
+global:log_statistics = N
+
+; Timestamp format used when logging timing statistics
+; (when global:log_statistics is enabled):
+; datetime - formatttted date and time: YYYY-MM-DD hh:mm:ss.uuuuuu
+; unix - Unix timestamp with nanoseconds: s.ns
+; both - Formatted date and time, followed by unix timestamp
+; (adds one extra field to the log)
+;
+; Options: datetime unix both
+global:statistics_timestamp_format = datetime
+
+; Bind ptpd2 process to a selected CPU core number.
+; 0 = first CPU core, etc. -1 = do not bind to a single core.
+global:cpuaffinity_cpucore = -1
+
+; Clock synchronisation statistics update interval in seconds
+;
+global:statistics_update_interval = 30
+
+; Log a status update every time statistics are updated (global:statistics_update_interval).
+; The updates are logged even when ptpd is configured without statistics support
+global:periodic_updates = N
+
+; Delay (seconds) before releasing a time service (NTP or PTP) and electing a new one to control a clock. 0 = elect immediately
+;
+global:timingdomain_election_delay = 15
+
+; Enable NTPd integration
+ntpengine:enabled = N
+
+; Enable control over local NTPd daemon
+ntpengine:control_enabled = N
+
+; NTP control check interval in seconds
+;
+ntpengine:check_interval = 15
+
+; NTP key number - must be configured as a trusted control key in ntp.conf,
+; and be non-zero for the ntpengine:control_enabled setting to take effect.
+;
+ntpengine:key_id = 0
+
+; NTP key (plain text, max. 20 characters) - must match the key configured in
+; ntpd's keys file, and must be non-zero for the ntpengine:control_enabled
+; setting to take effect.
+;
+ntpengine:key =
+
+; ========= newline required in the end ==========
+
diff --git a/rtemsbsd/ptpd/src/ptpd2.conf.minimal b/rtemsbsd/ptpd/src/ptpd2.conf.minimal
new file mode 100644
index 00000000..d86b68da
--- /dev/null
+++ b/rtemsbsd/ptpd/src/ptpd2.conf.minimal
@@ -0,0 +1,38 @@
+; ==============================================================================
+; This is a minimal configuration for a PTPv2 slave
+; For a full list of options run ptpd2 -H or see the documentation and man pages.
+;
+; NOTE: for best results, do read the ptpd2.conf(5) man page as many settings
+; such as outlier filters and statistics filters can greatly improve
+; the operation of PTPd.
+; ==============================================================================
+
+; interface has to be specified
+ptpengine:interface=
+
+; PTP domain
+ptpengine:domain=0
+
+; available presets are slaveonly, masteronly and masterslave (full IEEE 1588 implementation)
+ptpengine:preset=slaveonly
+
+; multicast for both sync and delay requests - use hybrid for unicast delay requests
+ptpengine:ip_mode=multicast
+
+; when enabled, sniffing is used instead of sockets to send and receive packets
+ptpengine:use_libpcap=n
+
+; log file, event log only. if timing statistics are needed, see statistics_file
+global:log_file=/var/log/ptpd2.log
+
+; status file providing an overview of ptpd's operation and statistics
+global:log_status=y
+
+; required if ip_mode is set to hybrid
+;ptpengine:log_delayreq_interval=0
+
+; uncomment this to log a timing log like in previous ptpd versions
+;global:statistics_file=/var/log/ptpd2.stats
+
+; always keep a new line in the end
+
diff --git a/rtemsbsd/ptpd/src/signaling.c b/rtemsbsd/ptpd/src/signaling.c
new file mode 100644
index 00000000..ca341a33
--- /dev/null
+++ b/rtemsbsd/ptpd/src/signaling.c
@@ -0,0 +1,1376 @@
+/*-
+ * Copyright (c) 2015 Wojciech Owczarek
+ * Copyright (c) 2014 Perseus Telecom
+ *
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file signaling.c
+ * @date Mon Jan 14 00:40:12 GMT 2014
+ *
+ * @brief Routines to handle unicast negotiation and processing of signaling messages
+ *
+ *
+ */
+
+#include "ptpd.h"
+
+/* how many times we send a cancel before we stop waiting for ack */
+#define GRANT_CANCEL_ACK_TIMEOUT 3
+/* every N grant refreshes, we re-request messages we don't seem to be receiving */
+#define GRANT_KEEPALIVE_INTERVAL 5
+/* maximum number of missed messages of given type before we re-request */
+#define GRANT_MAX_MISSED 10
+
+static void updateUnicastIndex(UnicastGrantTable *table, UnicastGrantIndex *index);
+static UnicastGrantTable* lookupUnicastIndex(PortIdentity *portIdentity, Integer32 transportAddress, UnicastGrantIndex *index);
+static int msgIndex(Enumeration8 messageType);
+static Enumeration8 msgXedni(int messageIndex);
+static void initOutgoingMsgSignaling(PortIdentity* targetPortIdentity, MsgSignaling* outgoing, PtpClock *ptpClock);
+static void handleSMRequestUnicastTransmission(MsgSignaling* incoming, MsgSignaling* outgoing, Integer32 sourceAddress, const RunTimeOpts *rtOpts, PtpClock *ptpClock);
+static void handleSMGrantUnicastTransmission(MsgSignaling* incoming, Integer32 sourceAddress, UnicastGrantTable *grantTable, int nodeCount, PtpClock *ptpClock);
+static Boolean handleSMCancelUnicastTransmission(MsgSignaling* incoming, MsgSignaling* outgoing, Integer32 sourceAddress, PtpClock* ptpClock);
+static void handleSMAcknowledgeCancelUnicastTransmission(MsgSignaling* incoming, Integer32 sourceAddress, PtpClock* ptpClock);
+static Boolean prepareSMRequestUnicastTransmission(MsgSignaling* outgoing, UnicastGrantData *grant, PtpClock* ptpClock);
+static Boolean prepareSMCancelUnicastTransmission(MsgSignaling* outgoing, UnicastGrantData* grant, PtpClock* ptpClock);
+static void requestUnicastTransmission(UnicastGrantData *grant, UInteger32 duration, const RunTimeOpts* rtOpts, PtpClock* ptpClock);
+static void issueSignaling(MsgSignaling *outgoing, Integer32 destination, const const RunTimeOpts *rtOpts, PtpClock *ptpclock);
+static void cancelNodeGrants(UnicastGrantTable *nodeTable, const RunTimeOpts *rtOpts, PtpClock *ptpClock);
+
+/* Return unicast grant array index for given message type */
+int
+msgIndex(Enumeration8 messageType)
+{
+
+ switch(messageType) {
+
+ case ANNOUNCE:
+ return ANNOUNCE_INDEXED;
+ case SYNC:
+ return SYNC_INDEXED;
+ case DELAY_RESP:
+ return DELAY_RESP_INDEXED;
+ case PDELAY_RESP:
+ return PDELAY_RESP_INDEXED;
+ case SIGNALING:
+ return SIGNALING_INDEXED;
+ default:
+ return -1;
+
+ }
+
+}
+/* xedni yarra ot gnidnopserroc epyt egassem PTP eht nruteR */
+static Enumeration8
+msgXedni(int messageIndex)
+{
+ switch(messageIndex) {
+ case ANNOUNCE_INDEXED:
+ return ANNOUNCE;
+ case SYNC_INDEXED:
+ return SYNC;
+ case DELAY_RESP_INDEXED:
+ return DELAY_RESP;
+ case PDELAY_RESP_INDEXED:
+ return PDELAY_RESP;
+ case SIGNALING_INDEXED:
+ return SIGNALING;
+ default:
+ return 0xFF;
+ }
+
+}
+
+/* update index table */
+static void
+updateUnicastIndex(UnicastGrantTable *table, UnicastGrantIndex *index)
+{
+ uint32_t hash = fnvHash(&table->portIdentity, sizeof(PortIdentity), UNICAST_MAX_DESTINATIONS);
+
+ /* peer table normally has one entry: if we got here, we might pollute the main index */
+ if(!table->isPeer && index != NULL) {
+ index->data[hash] = table;
+ }
+
+}
+
+/* return matching entry from index table */
+static UnicastGrantTable*
+lookupUnicastIndex(PortIdentity *portIdentity, Integer32 transportAddress, UnicastGrantIndex *index)
+{
+
+ uint32_t hash = fnvHash((void*)portIdentity, sizeof(PortIdentity), UNICAST_MAX_DESTINATIONS);
+
+ UnicastGrantTable* table;
+
+ if(index == NULL) {
+ return NULL;
+ }
+
+ table = index->data[hash];
+
+ if(table == NULL) {
+ DBG("lookupUnicastIndex: empty hit\n");
+ return NULL;
+ }
+
+ if(!cmpPortIdentity(portIdentity, &table->portIdentity)) {
+ DBG("lookupUnicastIndex: cache hit\n");
+ return table;
+ } else {
+ /* hash collision */
+ DBG("lookupUnicastIndex: hash collision\n");
+ return NULL;
+ }
+
+}
+
+
+/* find which grant table entry the given port belongs to:
+ - if not found, return first free entry, store portID and/or address
+ - if found, find the entry it belongs to
+ - look up the index table, only iterate on miss
+ - if update is FALSE, only a search is performed
+*/
+UnicastGrantTable*
+findUnicastGrants
+(const PortIdentity* portIdentity, Integer32 transportAddress, UnicastGrantTable *grantTable, UnicastGrantIndex *index, int nodeCount, Boolean update)
+{
+
+ int i;
+
+ UnicastGrantTable *found = NULL;
+ UnicastGrantTable *firstFree = NULL;
+
+ UnicastGrantTable *nodeTable;
+
+ PortIdentity tmpIdentity = *portIdentity;
+
+ /* look up the index table*/
+ if(index != NULL) {
+ tmpIdentity.portNumber |= index->portMask;
+ found = lookupUnicastIndex(&tmpIdentity, transportAddress, index);
+ }
+
+ if(found != NULL) {
+ DBG("findUnicastGrants: cache hit\n");
+ /* do not overwrite address if zero given
+ * (used by slave to preserve configured master addresses)
+ */
+ if(update && transportAddress) {
+ found->transportAddress = transportAddress;
+ }
+ if(update) {
+ found->portIdentity = tmpIdentity;
+ }
+
+ if(update) {
+ updateUnicastIndex(found, index);
+ }
+
+ } else for(i=0; i < nodeCount; i++) {
+
+ nodeTable = &grantTable[i];
+
+ /* first free entry */
+ if(firstFree == NULL) {
+ if(portIdentityEmpty(&nodeTable->portIdentity) ||
+ (nodeTable->timeLeft == 0)) {
+ firstFree = nodeTable;
+ }
+ }
+
+ /* port identity matches */
+ if(!cmpPortIdentity((const PortIdentity*)&tmpIdentity, &nodeTable->portIdentity)) {
+
+ found = nodeTable;
+
+ /* do not overwrite address if zero given
+ * (used by slave to preserve configured master addresses)
+ */
+
+ if(update && transportAddress) {
+ found->transportAddress = transportAddress;
+ }
+
+ if(update) {
+ found->portIdentity = tmpIdentity;
+ }
+
+ DBG("findUnicastGrants: cache miss - %d iterations %d\n", i);
+
+ if(update) {
+ updateUnicastIndex(found, index);
+ }
+
+ break;
+
+ }
+
+ /* no port identity match but we have a transport address match */
+ if(nodeTable->transportAddress &&
+ (nodeTable->transportAddress==transportAddress)) {
+
+ found = nodeTable;
+ if(update) {
+ found->portIdentity = tmpIdentity;
+ updateUnicastIndex(found, index);
+ }
+ break;
+ }
+
+ }
+
+ if(found != NULL) {
+ return found;
+ }
+
+ /* will return NULL if there are no free slots, otherwise the first free slot */
+ if(update && firstFree != NULL) {
+ firstFree->portIdentity = tmpIdentity;
+ firstFree->transportAddress = transportAddress;
+ updateUnicastIndex(firstFree, index);
+ /* new set of grants - reset sequence numbers */
+ for(i=0; i < PTP_MAX_MESSAGE_INDEXED; i++) {
+ firstFree->grantData[i].sentSeqId = 0;
+ }
+
+ /* - You know, we could as well have sex now.
+ * - Yes, dear, but, let's not.
+ */
+ /* memset(firstFree->grantData,0,PTP_MAX_MESSAGE * sizeof(UnicastGrantData)); */
+ }
+
+ return firstFree;
+
+}
+
+
+/**\brief Initialise outgoing signaling message fields*/
+static void
+initOutgoingMsgSignaling(PortIdentity* targetPortIdentity, MsgSignaling* outgoing, PtpClock *ptpClock)
+{
+ /* set header fields */
+ outgoing->header.transportSpecific = ptpClock->portDS.transportSpecific;
+ outgoing->header.messageType = SIGNALING;
+ outgoing->header.versionPTP = ptpClock->portDS.versionNumber;
+ outgoing->header.domainNumber = ptpClock->defaultDS.domainNumber;
+ /* set header flagField to zero for management messages, Spec 13.3.2.6 */
+ outgoing->header.flagField0 = 0x00;
+ outgoing->header.flagField1 = 0x00;
+ outgoing->header.correctionField.msb = 0;
+ outgoing->header.correctionField.lsb = 0;
+ copyPortIdentity(&outgoing->header.sourcePortIdentity, &ptpClock->portDS.portIdentity);
+
+ outgoing->header.sequenceId = ptpClock->sentSignalingSequenceId++;
+
+ outgoing->header.controlField = 0x5; /* deprecrated for ptp version 2 */
+ outgoing->header.logMessageInterval = 0x7F;
+
+ /* set management message fields */
+ copyPortIdentity( &outgoing->targetPortIdentity, targetPortIdentity);
+
+ /* init managementTLV */
+ XMALLOC(outgoing->tlv, sizeof(SignalingTLV));
+ outgoing->tlv->valueField = NULL;
+ outgoing->tlv->lengthField = 0;
+}
+
+
+
+/**\brief Handle incoming REQUEST_UNICAST_TRANSMISSION signaling TLV type*/
+static void
+handleSMRequestUnicastTransmission(MsgSignaling* incoming, MsgSignaling* outgoing, Integer32 sourceAddress, const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+
+ char portId[PATH_MAX];
+ UnicastGrantData *myGrant;
+ UnicastGrantTable *nodeTable;
+ Boolean granted = TRUE;
+ SMRequestUnicastTransmission* requestData = (SMRequestUnicastTransmission*)incoming->tlv->valueField;
+ SMGrantUnicastTransmission* grantData = NULL;
+ Enumeration8 messageType = requestData->messageType;
+#if defined(RUNTIME_DEBUG) || defined (PTPD_DBGV)
+ struct in_addr tmpAddr;
+ tmpAddr.s_addr = sourceAddress;
+#endif /* RUNTIME_DEBUG */
+ snprint_PortIdentity(portId, PATH_MAX, &incoming->header.sourcePortIdentity);
+
+ initOutgoingMsgSignaling(&incoming->header.sourcePortIdentity, outgoing, ptpClock);
+ XMALLOC(outgoing->tlv->valueField, sizeof(SMGrantUnicastTransmission));
+ grantData = (SMGrantUnicastTransmission*)outgoing->tlv->valueField;
+
+ outgoing->header.flagField0 |= PTP_UNICAST;
+ outgoing->tlv->tlvType = TLV_GRANT_UNICAST_TRANSMISSION;
+ outgoing->tlv->lengthField = 8;
+
+
+ DBG("Received REQUEST_UNICAST_TRANSMISSION message for message %s from %s(%s) - duration %d interval %d\n",
+ getMessageTypeName(messageType), portId, inet_ntoa(tmpAddr), requestData->durationField,
+ requestData->logInterMessagePeriod);
+
+ nodeTable = findUnicastGrants(&incoming->header.sourcePortIdentity, sourceAddress, ptpClock->unicastGrants, &ptpClock->grantIndex, UNICAST_MAX_DESTINATIONS, TRUE);
+
+ if(nodeTable == NULL) {
+ if(ptpClock->slaveCount >= UNICAST_MAX_DESTINATIONS) {
+ DBG("REQUEST_UNICAST_TRANSMISSION (%s): did not find node in slave table : %s (%s) - table full\n", getMessageTypeName(messageType),
+ inet_ntoa(tmpAddr),portId);
+ } else {
+ DBG("REQUEST_UNICAST_TRANSMISSION (%s): did not find node in slave table: %s (%s)\n", getMessageTypeName(messageType),
+ inet_ntoa(tmpAddr),portId);
+ }
+ /* fill the response with basic fields (namely messageType so the deny is valid */
+ goto finaliseResponse;
+ }
+
+ if(msgIndex(messageType) < 0) {
+ DBG("Received unicast request from %s for unsupported message type %d\n", inet_ntoa(tmpAddr), messageType);
+ goto finaliseResponse;
+ }
+
+ myGrant = &nodeTable->grantData[msgIndex(messageType)];
+
+ myGrant->requested = TRUE;
+
+ /* We assume the request is denied */
+ grantData->renewal_invited = 0;
+ grantData->durationField = 0;
+
+ ptpClock->counters.unicastGrantsRequested++;
+
+ if(!myGrant->requestable) {
+ DBG("denied unicast transmission request for non-requestable message %s from %s(%s)\n",
+ getMessageTypeName(messageType), portId, inet_ntoa(tmpAddr));
+ myGrant->granted = FALSE;
+ }
+
+ if(!requestData->durationField) {
+ DBG("denied unicast transmission request for zero duration - message %s from %s(%s)\n",
+ getMessageTypeName(messageType), portId, inet_ntoa(tmpAddr));
+ granted = FALSE;
+ }
+
+ if(requestData->logInterMessagePeriod < myGrant->logMinInterval) {
+ DBG("denied unicast transmission request for too short interval - message %s from %s(%s), interval %d\n",
+ getMessageTypeName(messageType), portId, inet_ntoa(tmpAddr), requestData->logInterMessagePeriod);
+ granted = FALSE;
+ }
+
+
+ if (granted) {
+
+ /* do not deny if requested longer interval than supported - just offer the longest */
+ if(requestData->logInterMessagePeriod > myGrant->logMaxInterval) {
+ grantData->logInterMessagePeriod = myGrant->logMaxInterval;
+ } else {
+ grantData->logInterMessagePeriod = requestData->logInterMessagePeriod;
+ }
+
+ /* only offer up to the maximum duration configured, but preserve a 30 second minimum */
+ if(requestData->durationField > rtOpts->unicastGrantDuration) {
+ grantData->durationField = rtOpts->unicastGrantDuration;
+ } else if(requestData->durationField <= 30) {
+ grantData->durationField = 30;
+ } else {
+ grantData->durationField = requestData->durationField;
+ }
+
+ DBG("granted unicast transmission request - message %s to %s(%s), interval %d, duration %d s\n",
+ getMessageTypeName(messageType), portId, inet_ntoa(tmpAddr), grantData->logInterMessagePeriod,
+ grantData->durationField);
+
+ myGrant->duration = grantData->durationField;
+ /* NEW! 5 seconds for free! Why 5? refreshUnicastGrants expires the grant when it's 5 */
+ myGrant->timeLeft = grantData->durationField + 10;
+
+ /* do not reset the counter if this is being re-requested */
+ if(!myGrant->granted || (myGrant->logInterval != grantData->logInterMessagePeriod)) {
+ myGrant->intervalCounter = 0;
+ }
+
+ ptpClock->counters.unicastGrantsGranted++;
+
+ myGrant->granted = TRUE;
+ myGrant->canceled = FALSE;
+ myGrant->cancelCount = 0;
+ myGrant->logInterval = grantData->logInterMessagePeriod;
+
+ /* this could be the very first grant for this node - update node's timeLeft so it's not seen as free anymore */
+ if(nodeTable->timeLeft <= 0) {
+ /* + 10 seconds for a grace period */
+ nodeTable->timeLeft = myGrant->timeLeft + 10;
+ }
+
+ /* If we've granted once, we're likely to grant again */
+ grantData->renewal_invited = 1;
+
+ outgoing->header.sequenceId = myGrant->parent->grantData[SIGNALING_INDEXED].sentSeqId;
+ myGrant->parent->grantData[SIGNALING_INDEXED].sentSeqId++;
+
+ } else {
+ ptpClock->counters.unicastGrantsDenied++;
+ }
+
+ /* Testing only */
+ /* grantData->logInterMessagePeriod = requestData->logInterMessagePeriod; */
+ /* grantData->durationField = requestData->durationField; */
+
+finaliseResponse:
+ grantData->messageType = requestData->messageType;
+ grantData->reserved0 = 0;
+ grantData->reserved1 = 0;
+}
+
+/**\brief Handle incoming GRANT_UNICAST_TRANSMISSION signaling message type*/
+static void
+handleSMGrantUnicastTransmission(MsgSignaling* incoming, Integer32 sourceAddress, UnicastGrantTable *grantTable, int nodeCount, PtpClock *ptpClock)
+{
+
+ char portId[PATH_MAX];
+ SMGrantUnicastTransmission *incomingGrant = (SMGrantUnicastTransmission*)incoming->tlv->valueField;
+ UnicastGrantData *myGrant;
+ Enumeration8 messageType = incomingGrant->messageType;
+ UnicastGrantTable *nodeTable;
+#if defined(RUNTIME_DEBUG) || defined (PTPD_DBGV)
+ struct in_addr tmpAddr;
+ tmpAddr.s_addr = sourceAddress;
+#endif /* RUNTIME_DEBUG */
+
+ snprint_PortIdentity(portId, PATH_MAX, &incoming->header.sourcePortIdentity);
+
+ DBGV("Received GRANT_UNICAST_TRANSMISSION message for message %s from %s(%s)\n",
+ getMessageTypeName(messageType), portId, inet_ntoa(tmpAddr));
+
+ nodeTable = findUnicastGrants(&incoming->header.sourcePortIdentity, sourceAddress, grantTable, &ptpClock->grantIndex, nodeCount, TRUE);
+
+ if(nodeTable == NULL) {
+ DBG("GRANT_UNICAST_TRANSMISSION: did not find node in master table: %s\n", portId);
+ return;
+ }
+
+ if(msgIndex(messageType) < 0) {
+ DBG("Received unicast grant from %s for unsupported message type %d\n", inet_ntoa(tmpAddr), messageType);
+ return;
+ }
+
+ myGrant = &nodeTable->grantData[msgIndex(messageType)];
+
+ if(!myGrant->requestable) {
+ DBG("received unicat grant for non-requestable message %s from %s(%s)\n",
+ getMessageTypeName(messageType), portId, inet_ntoa(tmpAddr));
+ return;
+ }
+
+ if(!myGrant->requested) {
+ DBG("received unicast grant for not requested message %s from %s(%s)\n",
+ getMessageTypeName(messageType), portId, inet_ntoa(tmpAddr));
+ return;
+ }
+
+ if(incomingGrant->durationField == 0) {
+ DBG("unicast transmission request for message %s interval %d duration %d s denied by %s(%s)\n",
+ getMessageTypeName(messageType), myGrant->logInterval, myGrant->duration,
+ portId, inet_ntoa(tmpAddr));
+
+ ptpClock->counters.unicastGrantsDenied++;
+
+
+ /* grant was denied so let's try a higher interval next time */
+ myGrant->logInterval++;
+
+ /* if we're above max, cycle through back to minimum */
+ if(myGrant->logInterval > myGrant->logMaxInterval) {
+ myGrant->logInterval = myGrant->logMinInterval;
+ }
+
+ /* this is so that we request again */
+ myGrant->requested = FALSE;
+
+ return;
+ }
+
+ DBG("received unicast transmission grant for message %s, interval %d, duration %d s\n",
+ getMessageTypeName(messageType), incomingGrant->logInterMessagePeriod,
+ incomingGrant->durationField);
+
+ ptpClock->counters.unicastGrantsGranted++;
+
+ myGrant->granted = TRUE;
+ myGrant->logInterval = incomingGrant->logInterMessagePeriod;
+ myGrant->duration = incomingGrant->durationField;
+ myGrant->timeLeft = myGrant->duration;
+ myGrant->canceled = FALSE;
+ myGrant->cancelCount = 0;
+
+}
+
+/**\brief Handle incoming CANCEL_UNICAST_TRANSMISSION signaling message type*/
+static Boolean
+handleSMCancelUnicastTransmission(MsgSignaling* incoming, MsgSignaling* outgoing, Integer32 sourceAddress, PtpClock* ptpClock)
+{
+ DBGV("Received CANCEL_UNICAST_TRANSMISSION message\n");
+
+ char portId[PATH_MAX];
+ SMCancelUnicastTransmission* requestData = (SMCancelUnicastTransmission*)incoming->tlv->valueField;
+ SMAcknowledgeCancelUnicastTransmission* acknowledgeData = NULL;
+
+ UnicastGrantData *myGrant;
+ Enumeration8 messageType = requestData->messageType;
+ UnicastGrantTable *nodeTable;
+#if defined(RUNTIME_DEBUG) || defined (PTPD_DBGV)
+ struct in_addr tmpAddr;
+ tmpAddr.s_addr = sourceAddress;
+#endif /* RUNTIME_DEBUG */
+
+ initOutgoingMsgSignaling(&incoming->header.sourcePortIdentity, outgoing, ptpClock);
+ outgoing->header.flagField0 |= PTP_UNICAST;
+ outgoing->tlv->tlvType = TLV_ACKNOWLEDGE_CANCEL_UNICAST_TRANSMISSION;
+ outgoing->tlv->lengthField = 2;
+
+ XMALLOC(outgoing->tlv->valueField, sizeof(SMAcknowledgeCancelUnicastTransmission));
+ acknowledgeData = (SMAcknowledgeCancelUnicastTransmission*)outgoing->tlv->valueField;
+ snprint_PortIdentity(portId, PATH_MAX, &incoming->header.sourcePortIdentity);
+
+ DBGV("Received CANCEL_UNICAST_TRANSMISSION message for message %s from %s(%s)\n",
+ getMessageTypeName(messageType), portId, inet_ntoa(tmpAddr));
+
+ ptpClock->counters.unicastGrantsCancelReceived++;
+
+ nodeTable = findUnicastGrants(&incoming->header.sourcePortIdentity, sourceAddress, ptpClock->unicastGrants, &ptpClock->grantIndex, UNICAST_MAX_DESTINATIONS, FALSE);
+
+ if(nodeTable == NULL) {
+ DBG("CANCEL_UNICAST_TRANSMISSION: did not find node in slave table: %s\n", portId);
+ return FALSE;
+ }
+
+ if(msgIndex(messageType) < 0) {
+ DBG("Received cancel unicast request from %s for unsupported message type %d\n", inet_ntoa(tmpAddr), messageType);
+ return FALSE;
+ }
+
+ myGrant = &nodeTable->grantData[msgIndex(messageType)];
+
+ if(!myGrant->requestable) {
+ DBG("cancel grant attempt for non-requestable message %s from %s(%s)\n",
+ getMessageTypeName(messageType), portId, inet_ntoa(tmpAddr));
+ return FALSE;
+ }
+
+ if(!myGrant->requested) {
+ DBG("cancel grant attempt for not requested message %s from %s(%s)\n",
+ getMessageTypeName(messageType), portId, inet_ntoa(tmpAddr));
+ return FALSE;
+ }
+
+ if(!myGrant->granted) {
+ DBG("cancel grant attempt for not granted message %s from %s(%s)\n",
+ getMessageTypeName(messageType), portId, inet_ntoa(tmpAddr));
+ return FALSE;
+ }
+
+ myGrant->granted = FALSE;
+ myGrant->requested = FALSE;
+
+ outgoing->header.sequenceId = myGrant->parent->grantData[SIGNALING_INDEXED].sentSeqId;
+ myGrant->parent->grantData[SIGNALING_INDEXED].sentSeqId++;
+
+ myGrant->sentSeqId = 0;
+ myGrant->cancelCount = 0;
+ myGrant->timeLeft = 0;
+ myGrant->duration = 0;
+
+ DBG("Accepted CANCEL_UNICAST_TRANSMISSION message for message %s from %s(%s)\n",
+ getMessageTypeName(messageType), portId, inet_ntoa(tmpAddr));
+
+ acknowledgeData->messageType = requestData->messageType;
+ acknowledgeData->reserved0 = 0;
+ acknowledgeData->reserved1 = 0;
+
+ return TRUE;
+}
+
+/**\brief Handle incoming ACKNOWLEDGE_CANCEL_UNICAST_TRANSMISSION signaling message type*/
+static void
+handleSMAcknowledgeCancelUnicastTransmission(MsgSignaling* incoming, Integer32 sourceAddress, PtpClock* ptpClock)
+{
+
+ char portId[PATH_MAX];
+ SMAcknowledgeCancelUnicastTransmission* requestData = (SMAcknowledgeCancelUnicastTransmission*)incoming->tlv->valueField;
+
+ UnicastGrantData *myGrant;
+ Enumeration8 messageType = requestData->messageType;
+ UnicastGrantTable *nodeTable;
+#if defined(RUNTIME_DEBUG) || defined (PTPD_DBGV)
+ struct in_addr tmpAddr;
+ tmpAddr.s_addr = sourceAddress;
+#endif /* RUNTIME_DEBUG */
+
+ snprint_PortIdentity(portId, PATH_MAX, &incoming->header.sourcePortIdentity);
+
+ DBGV("Received ACKNOWLEDGE_CANCEL_UNICAST_TRANSMISSION message for message %s from %s(%s)\n",
+ getMessageTypeName(messageType), portId, inet_ntoa(tmpAddr));
+
+ nodeTable = findUnicastGrants(&incoming->header.sourcePortIdentity, sourceAddress, ptpClock->unicastGrants, &ptpClock->grantIndex, UNICAST_MAX_DESTINATIONS, FALSE);
+
+ if(nodeTable == NULL) {
+ DBG("ACKNOWLEDGE_CANCEL_UNICAST_TRANSMISSION: did not find node in slave table: %s\n", portId);
+ }
+
+ if(msgIndex(messageType) < 0) {
+ DBG("Received unicast acknowledge ancer from %s for unsupported message type %d\n", inet_ntoa(tmpAddr), messageType);
+ return;
+ }
+
+ myGrant = &nodeTable->grantData[msgIndex(messageType)];
+
+ if(!myGrant->requestable) {
+ DBG("acknowledge cancel grant attempt for non-requestable message %s from %s(%s)\n",
+ getMessageTypeName(messageType), portId, inet_ntoa(tmpAddr));
+ }
+
+ if(!myGrant->canceled) {
+ DBG("acknowledge cancel grant received for not canceled message %s from %s(%s)\n",
+ getMessageTypeName(messageType), portId, inet_ntoa(tmpAddr));
+ }
+
+ myGrant->granted = FALSE;
+ myGrant->requested = FALSE;
+ myGrant->sentSeqId = 0;
+ myGrant->cancelCount = 0;
+ myGrant->timeLeft = 0;
+ myGrant->duration = 0;
+ myGrant->canceled = FALSE;
+ myGrant->cancelCount = 0;
+
+ ptpClock->counters.unicastGrantsCancelAckReceived++;
+
+ DBG("Accepted ACKNOWLEDGE_CANCEL_UNICAST_TRANSMISSION message for message %s from %s(%s)\n",
+ getMessageTypeName(messageType), portId, inet_ntoa(tmpAddr));
+
+}
+
+static Boolean
+prepareSMRequestUnicastTransmission(MsgSignaling* outgoing, UnicastGrantData *grant, PtpClock* ptpClock)
+{
+
+ DBGV("Preparing CANCEL_UNICAST_TRANSMISSION message\n");
+
+ /* Clause 16.1.4.1.3 */
+ if(!grant->requestable) {
+ return FALSE;
+ }
+
+ initOutgoingMsgSignaling(&grant->parent->portIdentity, outgoing, ptpClock);
+
+ outgoing->header.flagField0 |= PTP_UNICAST;
+ outgoing->tlv->tlvType = TLV_REQUEST_UNICAST_TRANSMISSION;
+ outgoing->tlv->lengthField = 6;
+
+ SMRequestUnicastTransmission* requestData = NULL;
+
+ XMALLOC(outgoing->tlv->valueField, sizeof(SMRequestUnicastTransmission));
+ requestData = (SMRequestUnicastTransmission*)outgoing->tlv->valueField;
+
+ requestData->messageType = grant->messageType;
+ requestData->reserved0 = 0;
+ requestData->logInterMessagePeriod = grant->logInterval;
+ requestData->durationField = grant->duration;
+
+ DBG(" prepared request unicast transmission request for message type 0x%0x\n",
+ grant->messageType);
+
+ outgoing->header.sequenceId = grant->parent->grantData[SIGNALING_INDEXED].sentSeqId;
+ grant->parent->grantData[SIGNALING_INDEXED].sentSeqId++;
+
+ return TRUE;
+
+}
+
+static Boolean
+prepareSMCancelUnicastTransmission(MsgSignaling* outgoing, UnicastGrantData* grant, PtpClock* ptpClock)
+{
+
+ DBGV("Preparing CANCEL_UNICAST_TRANSMISSION message\n");
+
+ initOutgoingMsgSignaling(&grant->parent->portIdentity, outgoing, ptpClock);
+
+ outgoing->header.flagField0 |= PTP_UNICAST;
+ outgoing->tlv->tlvType = TLV_CANCEL_UNICAST_TRANSMISSION;
+ outgoing->tlv->lengthField = 2;
+
+ SMCancelUnicastTransmission* cancelData = NULL;
+
+ XMALLOC(outgoing->tlv->valueField, sizeof(SMCancelUnicastTransmission));
+ cancelData = (SMCancelUnicastTransmission*)outgoing->tlv->valueField;
+
+ grant->requested = FALSE;
+ grant->timeLeft = 0;
+ grant->expired = FALSE;
+
+ if(!grant->requestable) {
+ DBG("Will not issue cancel unicast transmission for non-requestable message type 0x%0x\n", grant->messageType);
+ return FALSE;
+ }
+
+ if(!grant->granted) {
+ DBG("Will not issue cancel unicast transmission for message type 0x%0x - not granted\n", grant->messageType);
+ return FALSE;
+ }
+
+ grant->canceled = TRUE;
+ grant->cancelCount++;
+
+ cancelData->messageType = grant->messageType;
+
+ DBG(" prepared cancel unicast transmission request for message type %s\n",
+ getMessageTypeName(grant->messageType));
+
+ cancelData->reserved0 = 0;
+ cancelData->reserved1 = 0;
+
+ outgoing->header.sequenceId = grant->parent->grantData[SIGNALING_INDEXED].sentSeqId;
+ grant->parent->grantData[SIGNALING_INDEXED].sentSeqId++;
+
+ return TRUE;
+
+}
+
+/* prepare unicast grant table for use, and mark the right ones requestable */
+void
+initUnicastGrantTable(UnicastGrantTable *grantTable, Enumeration8 delayMechanism, int nodeCount, UnicastDestination *destinations,
+ const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+
+ int i,j;
+
+ UnicastGrantData *grantData;
+ UnicastGrantTable *nodeTable;
+
+ /* initialise the index table */
+ for(i=0; i < UNICAST_MAX_DESTINATIONS; i++) {
+ ptpClock->grantIndex.data[i] = NULL;
+ ptpClock->syncDestIndex[i].transportAddress = 0;
+ }
+
+ ptpClock->grantIndex.portMask = rtOpts->unicastPortMask;
+
+ for(j=0; j<nodeCount; j++) {
+
+ nodeTable = &grantTable[j];
+
+ memset(nodeTable, 0, sizeof(UnicastGrantTable));
+
+ if(destinations != NULL && (destinations[j].transportAddress != 0)) {
+ nodeTable->transportAddress = destinations[j].transportAddress;
+ nodeTable->domainNumber = destinations[j].domainNumber;
+ nodeTable->localPreference = destinations[j].localPreference;
+ if(nodeTable->domainNumber == 0) {
+ nodeTable->domainNumber = rtOpts->domainNumber;
+ }
+ /* for masters: all-ones initially */
+ nodeTable->portIdentity.portNumber = 0xFFFF;
+ memset(&nodeTable->portIdentity.clockIdentity, 0xFF, CLOCK_IDENTITY_LENGTH);
+ }
+
+ for(i=0; i< PTP_MAX_MESSAGE_INDEXED; i++) {
+
+ grantData = &nodeTable->grantData[i];
+
+ memset(grantData, 0, sizeof(UnicastGrantData));
+
+ grantData->parent = nodeTable;
+ grantData->messageType = msgXedni(i);
+
+ switch(grantData->messageType) {
+
+ case PDELAY_RESP:
+
+ grantData->logMinInterval = rtOpts->logMinPdelayReqInterval;
+ grantData->logMaxInterval = rtOpts->logMaxPdelayReqInterval;
+ grantData->logInterval = grantData->logMinInterval;
+ if(delayMechanism != P2P) break;
+ grantData->requestable = TRUE;
+
+ break;
+
+ case ANNOUNCE:
+
+ grantData->logMinInterval = rtOpts->logAnnounceInterval;
+ grantData->logMaxInterval = rtOpts->logMaxAnnounceInterval;
+ grantData->logInterval = grantData->logMinInterval;
+ grantData->requestable = TRUE;
+ break;
+
+ case SYNC:
+
+ grantData->logMinInterval = rtOpts->logSyncInterval;
+ grantData->logMaxInterval = rtOpts->logMaxSyncInterval;
+ grantData->logInterval = grantData->logMinInterval;
+ grantData->requestable = TRUE;
+ break;
+
+ case DELAY_RESP:
+
+ if(delayMechanism != E2E) break;
+ grantData->logMinInterval = rtOpts->logMinDelayReqInterval;
+ grantData->logMaxInterval = rtOpts->logMaxDelayReqInterval;
+ grantData->logInterval = grantData->logMinInterval;
+ grantData->requestable = TRUE;
+ break;
+
+ default:
+ break;
+ }
+
+ }
+
+ }
+
+
+}
+
+
+/* update unicast grant table with configured intervals and expire them,
+ * so that messages are re-requested
+ */
+void
+updateUnicastGrantTable(UnicastGrantTable *grantTable, int nodeCount, const RunTimeOpts *rtOpts)
+{
+
+ int i,j;
+
+ UnicastGrantData *grantData;
+ UnicastGrantTable *nodeTable;
+
+ for(j=0; j<nodeCount; j++) {
+ nodeTable = &grantTable[j];
+
+ for(i=0; i < PTP_MAX_MESSAGE_INDEXED; i++) {
+
+ grantData = &nodeTable->grantData[i];
+
+ if(!grantData->requestable) {
+ continue;
+ }
+
+ switch(grantData->messageType) {
+
+ case PDELAY_RESP:
+
+ grantData->logMinInterval = rtOpts->logMinPdelayReqInterval;
+ grantData->logMaxInterval = rtOpts->logMaxPdelayReqInterval;
+ grantData->logInterval = grantData->logMinInterval;
+ grantData->timeLeft = 0;
+ break;
+
+ case ANNOUNCE:
+
+
+ grantData->logMinInterval = rtOpts->logAnnounceInterval;
+ grantData->logMaxInterval = rtOpts->logMaxAnnounceInterval;
+ grantData->logInterval = grantData->logMinInterval;
+ grantData->timeLeft = 0;
+ break;
+
+ case SYNC:
+
+ grantData->logMinInterval = rtOpts->logSyncInterval;
+ grantData->logMaxInterval = rtOpts->logMaxSyncInterval;
+ grantData->logInterval = grantData->logMinInterval;
+ grantData->timeLeft = 0;
+ break;
+
+ case DELAY_RESP:
+
+ grantData->logMinInterval = rtOpts->logMinDelayReqInterval;
+ grantData->logMaxInterval = rtOpts->logMaxDelayReqInterval;
+ grantData->logInterval = grantData->logMinInterval;
+ grantData->timeLeft = 0;
+ break;
+
+ default:
+ break;
+ }
+
+
+
+ }
+
+ }
+
+
+}
+
+static void
+requestUnicastTransmission(UnicastGrantData *grant, UInteger32 duration, const RunTimeOpts* rtOpts, PtpClock* ptpClock)
+{
+
+ if(duration == 0) {
+ DBG("Will not request unicast transmission for 0 duration\n");
+ }
+
+ grant->duration = duration;
+
+ /* safeguard */
+ if(duration < 5) {
+ grant->duration = 5;
+ }
+
+ /* pack and send */
+ if(prepareSMRequestUnicastTransmission(&ptpClock->outgoingSignalingTmp, grant, ptpClock)) {
+ if(grant->parent->domainNumber != 0) {
+ ptpClock->outgoingSignalingTmp.header.domainNumber = grant->parent->domainNumber;
+ }
+ issueSignaling(&ptpClock->outgoingSignalingTmp, grant->parent->transportAddress, rtOpts, ptpClock);
+ ptpClock->counters.unicastGrantsRequested++;
+ /* ready to be monitored */
+ grant->requested = TRUE;
+ grant->timeLeft = 0;
+ grant->granted = FALSE;
+ grant->expired = FALSE;
+ }
+
+ /* cleanup msgTmp signalingTLV */
+ freeSignalingTLV(&ptpClock->msgTmp.signaling);
+ /* cleanup outgoing signalingTLV */
+ freeSignalingTLV(&ptpClock->outgoingSignalingTmp);
+}
+
+void
+cancelUnicastTransmission(UnicastGrantData* grant, const const RunTimeOpts* rtOpts, PtpClock* ptpClock)
+{
+
+/* todo: dbg sending */
+
+ if(prepareSMCancelUnicastTransmission(&ptpClock->outgoingSignalingTmp, grant, ptpClock)) {
+ if(grant->parent->domainNumber != 0) {
+ ptpClock->outgoingSignalingTmp.header.domainNumber = grant->parent->domainNumber;
+ }
+ issueSignaling(&ptpClock->outgoingSignalingTmp, grant->parent->transportAddress, rtOpts, ptpClock);
+ ptpClock->counters.unicastGrantsCancelSent++;
+ }
+
+ /* cleanup msgTmp signalingTLV */
+ freeSignalingTLV(&ptpClock->msgTmp.signaling);
+ /* cleanup outgoing signalingTLV */
+ freeSignalingTLV(&ptpClock->outgoingSignalingTmp);
+}
+
+static void
+issueSignaling(MsgSignaling *outgoing, Integer32 destination, const const RunTimeOpts *rtOpts,
+ PtpClock *ptpClock)
+{
+
+ /* pack SignalingTLV */
+ msgPackSignalingTLV( ptpClock->msgObuf, outgoing, ptpClock);
+
+ /* set header messageLength, the outgoing->tlv->lengthField is now valid */
+ outgoing->header.messageLength = SIGNALING_LENGTH +
+ TL_LENGTH +
+ outgoing->tlv->lengthField;
+
+ msgPackSignaling( ptpClock->msgObuf, outgoing, ptpClock);
+
+ if(!netSendGeneral(ptpClock->msgObuf, outgoing->header.messageLength,
+ &ptpClock->netPath, rtOpts, destination)) {
+ DBGV("Signaling message can't be sent -> FAULTY state \n");
+ ptpClock->counters.messageSendErrors++;
+ toState(PTP_FAULTY, rtOpts, ptpClock);
+ } else {
+ DBG("Signaling msg sent \n");
+ ptpClock->counters.signalingMessagesSent++;
+ }
+}
+
+/* cancel all given or requested grants for a given clock node */
+static void
+cancelNodeGrants(UnicastGrantTable *nodeTable, const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+
+ int i;
+ UnicastGrantData *grantData;
+
+ for(i=0; i< PTP_MAX_MESSAGE_INDEXED; i++) {
+ grantData = &nodeTable->grantData[i];
+
+ if(!grantData->requestable) {
+ continue;
+ }
+
+ if(grantData->granted) {
+ cancelUnicastTransmission(grantData, rtOpts, ptpClock);
+ /* sleep 250 to 500 us so that we don't flood the node */
+ usleep(250+round(getRand()*250));
+ }
+
+ }
+}
+
+/* cancel all given or requested unicast grants */
+void
+cancelAllGrants(UnicastGrantTable *grantTable, int nodeCount, const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+
+ int i;
+
+ for(i=0; i<nodeCount; i++) {
+ cancelNodeGrants(&grantTable[i], rtOpts, ptpClock);
+ }
+
+}
+
+void
+handleSignaling(MsgHeader *header,
+ Boolean isFromSelf, Integer32 sourceAddress, const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+ DBG("Signaling message received : \n");
+
+
+ if (isFromSelf) {
+ DBGV("handleSignaling: Ignore message from self \n");
+ return;
+ }
+
+ int tlvOffset = 0;
+ int tlvFound = 0;
+
+ /* loop over all supported TLVs as if they came in separate messages */
+ while(msgUnpackSignaling(ptpClock->msgIbuf,&ptpClock->msgTmp.signaling, header, ptpClock, tlvOffset)) {
+
+ if(ptpClock->msgTmp.signaling.tlv == NULL) {
+
+ if(tlvFound==0) {
+ DBGV("handleSignaling: No TLVs in message\n");
+ ptpClock->counters.messageFormatErrors++;
+ } else {
+ DBGV("handleSignaling: No more TLVs\n");
+ }
+ return;
+ }
+
+ tlvFound++;
+
+ /* accept the message if directed either to us or to all-ones */
+ if(!acceptPortIdentity(ptpClock->portDS.portIdentity, ptpClock->msgTmp.signaling.targetPortIdentity))
+ {
+ DBG("handleSignaling: The signaling message was not accepted");
+ ptpClock->counters.discardedMessages++;
+ goto end;
+ }
+
+ /* Can we handle this? */
+
+ switch(ptpClock->msgTmp.signaling.tlv->tlvType)
+ {
+ case TLV_REQUEST_UNICAST_TRANSMISSION:
+ DBGV("handleSignaling: Request Unicast Transmission\n");
+ if(!rtOpts->unicastNegotiation || rtOpts->ipMode!=IPMODE_UNICAST) {
+ DBGV("handleSignaling: Ignoring unicast negotiation message - not running unicast or negotiation not enabled\n");
+ ptpClock->counters.discardedMessages++;
+ goto end;
+ }
+ if( (ptpClock->portDS.portState==PTP_LISTENING && !rtOpts->unicastNegotiationListening) ||
+ ptpClock->portDS.portState==PTP_DISABLED || ptpClock->portDS.portState == PTP_INITIALIZING ||
+ ptpClock->portDS.portState==PTP_FAULTY) {
+ DBG("Will not grant unicast transmission requests in %s state\n",
+ portState_getName(ptpClock->portDS.portState));
+ ptpClock->counters.discardedMessages++;
+ goto end;
+ }
+ unpackSMRequestUnicastTransmission(ptpClock->msgIbuf + tlvOffset, &ptpClock->msgTmp.signaling, ptpClock);
+ handleSMRequestUnicastTransmission(&ptpClock->msgTmp.signaling, &ptpClock->outgoingSignalingTmp, sourceAddress, rtOpts, ptpClock);
+ /* send back the outgoing signaling message */
+ if(ptpClock->outgoingSignalingTmp.tlv->tlvType == TLV_GRANT_UNICAST_TRANSMISSION) {
+ issueSignaling(&ptpClock->outgoingSignalingTmp, sourceAddress,rtOpts, ptpClock);
+ }
+ break;
+ case TLV_CANCEL_UNICAST_TRANSMISSION:
+ DBGV("handleSignaling: Cancel Unicast Transmission\n");
+ if(!rtOpts->unicastNegotiation || rtOpts->ipMode!=IPMODE_UNICAST) {
+ DBGV("handleSignaling: Ignoring unicast negotiation message - not running unicast or negotiation not enabled\n");
+ ptpClock->counters.discardedMessages++;
+ goto end;
+ }
+ if( (ptpClock->portDS.portState==PTP_LISTENING && !rtOpts->unicastNegotiationListening) ||
+ ptpClock->portDS.portState==PTP_DISABLED || ptpClock->portDS.portState == PTP_INITIALIZING ||
+ ptpClock->portDS.portState==PTP_FAULTY) {
+ DBG("Will not cancel unicast transmission requests in %s state\n",
+ portState_getName(ptpClock->portDS.portState));
+ ptpClock->counters.discardedMessages++;
+ goto end;
+ }
+ unpackSMCancelUnicastTransmission(ptpClock->msgIbuf + tlvOffset, &ptpClock->msgTmp.signaling, ptpClock);
+ /* send back the cancel acknowledgment - if we have something to acknowledge*/
+ if(handleSMCancelUnicastTransmission(&ptpClock->msgTmp.signaling, &ptpClock->outgoingSignalingTmp, sourceAddress, ptpClock)) {
+ issueSignaling(&ptpClock->outgoingSignalingTmp, sourceAddress,rtOpts, ptpClock);
+ ptpClock->counters.unicastGrantsCancelAckSent++;
+ }
+ break;
+ case TLV_ACKNOWLEDGE_CANCEL_UNICAST_TRANSMISSION:
+ DBGV("handleSignaling: Acknowledge Cancel Unicast Transmission\n");
+ if(!rtOpts->unicastNegotiation || rtOpts->ipMode!=IPMODE_UNICAST) {
+ DBGV("handleSignaling: Ignoring unicast negotiation message - not running unicast or negotiation not enabled\n");
+ ptpClock->counters.discardedMessages++;
+ goto end;
+ }
+ if( (ptpClock->portDS.portState==PTP_LISTENING && !rtOpts->unicastNegotiationListening) ||
+ ptpClock->portDS.portState==PTP_DISABLED || ptpClock->portDS.portState == PTP_INITIALIZING ||
+ ptpClock->portDS.portState==PTP_FAULTY) {
+ DBG("Will not process acknowledge cancel unicast transmission in %s state\n",
+ portState_getName(ptpClock->portDS.portState));
+ ptpClock->counters.discardedMessages++;
+ goto end;
+ }
+ unpackSMAcknowledgeCancelUnicastTransmission(ptpClock->msgIbuf + tlvOffset, &ptpClock->msgTmp.signaling, ptpClock);
+ handleSMAcknowledgeCancelUnicastTransmission(&ptpClock->msgTmp.signaling, sourceAddress, ptpClock);
+ break;
+ case TLV_GRANT_UNICAST_TRANSMISSION:
+ DBGV("handleSignaling: Grant Unicast Transmission\n");
+ if(!rtOpts->unicastNegotiation || rtOpts->ipMode!=IPMODE_UNICAST) {
+ DBGV("handleSignaling: Ignoring unicast negotiation message - not running unicast or negotiation not enabled\n");
+ ptpClock->counters.discardedMessages++;
+ goto end;
+ }
+ if(
+ ptpClock->portDS.portState==PTP_DISABLED || ptpClock->portDS.portState == PTP_INITIALIZING ||
+ ptpClock->portDS.portState==PTP_FAULTY) {
+ DBG("Will not process acknowledge cancel unicast transmission in %s state\n",
+ portState_getName(ptpClock->portDS.portState));
+ ptpClock->counters.discardedMessages++;
+ goto end;
+ }
+ unpackSMGrantUnicastTransmission(ptpClock->msgIbuf + tlvOffset, &ptpClock->msgTmp.signaling, ptpClock);
+ handleSMGrantUnicastTransmission(&ptpClock->msgTmp.signaling, sourceAddress, ptpClock->unicastGrants, ptpClock->unicastDestinationCount, ptpClock);
+ if(ptpClock->portDS.delayMechanism == P2P) {
+ handleSMGrantUnicastTransmission(&ptpClock->msgTmp.signaling, sourceAddress, &ptpClock->peerGrants, 1, ptpClock);
+ }
+ break;
+
+ default:
+
+ DBGV("handleSignaling: received unsupported TLV type %04x\n",
+ ptpClock->msgTmp.signaling.tlv->tlvType );
+ ptpClock->counters.discardedMessages++;
+ goto end;
+ }
+
+ end:
+ /* Movin' on up! */
+ tlvOffset += TL_LENGTH + ptpClock->msgTmp.signaling.tlv->lengthField;
+ /* cleanup msgTmp signalingTLV */
+ freeSignalingTLV(&ptpClock->msgTmp.signaling);
+ /* cleanup outgoing signalingTLV */
+ freeSignalingTLV(&ptpClock->outgoingSignalingTmp);
+ }
+
+ if(!tlvFound) {
+ DBGV("handleSignaling: No TLVs in message\n");
+ ptpClock->counters.messageFormatErrors++;
+ } else {
+ ptpClock->counters.signalingMessagesReceived++;
+ DBGV("handleSignaling: No more TLVs\n");
+ }
+
+}
+
+void
+refreshUnicastGrants(UnicastGrantTable *grantTable, int nodeCount, const RunTimeOpts *rtOpts, PtpClock *ptpClock)
+{
+
+ static int everyN = 0;
+ int i,j;
+
+ UnicastGrantData *grantData = NULL;
+ UnicastGrantTable *nodeTable = NULL;
+ Boolean actionRequired;
+ int maxTime = 0;
+
+ /* modulo N counter: used for requesting announce while other master is selected */
+ everyN++;
+ everyN %= GRANT_KEEPALIVE_INTERVAL;
+
+ /* only notSlave or slaveOnly - nothing inbetween */
+ if(ptpClock->defaultDS.clockQuality.clockClass > 127 && ptpClock->defaultDS.clockQuality.clockClass < 255) {
+ return;
+ }
+
+
+ ptpClock->slaveCount = 0;
+
+ for(j=0; j<nodeCount; j++) {
+
+ nodeTable = &grantTable[j];
+ maxTime = 0;
+
+ for(i=0; i < PTP_MAX_MESSAGE_INDEXED; i++) {
+ grantData = &nodeTable->grantData[i];
+ if(grantData->granted && !grantData->expired) {
+ maxTime = grantData->timeLeft;
+ break;
+ }
+ }
+
+ for(i=0; i < PTP_MAX_MESSAGE_INDEXED; i++) {
+
+ grantData = &nodeTable->grantData[i];
+
+ if(!grantData->requestable) {
+ continue;
+ }
+
+ actionRequired = FALSE;
+
+ if(grantData->granted) {
+ /* re-request 5 seconds before expiry for continuous service.
+ * masters set this to +10 sec so will keep +5 sec extra
+ */
+ if(grantData->timeLeft <= 5) {
+ DBG("grant for message %s expired\n", getMessageTypeName(grantData->messageType));
+ grantData->expired = TRUE;
+ } else {
+ if(grantData->timeLeft > maxTime) {
+ maxTime = grantData->timeLeft;
+ }
+ grantData->timeLeft--;
+ }
+
+
+ }
+
+ if(grantData->canceled && grantData->cancelCount >= GRANT_CANCEL_ACK_TIMEOUT) {
+ grantData->cancelCount = 0;
+ grantData->canceled = FALSE;
+ grantData->granted = FALSE;
+ grantData->requested = FALSE;
+ grantData->sentSeqId = 0;
+ grantData->timeLeft = 0;
+ grantData->duration = 0;
+ }
+
+ if(grantData->expired || (ptpClock->defaultDS.slaveOnly && grantData->requested && !grantData->granted)) {
+ actionRequired = TRUE;
+ }
+
+ if(nodeTable->isPeer && grantData->messageType==PDELAY_RESP && !grantData->granted) {
+ actionRequired = TRUE;
+ }
+
+ if(!nodeTable->isPeer && ptpClock->defaultDS.slaveOnly) {
+ if(grantData->messageType == ANNOUNCE && !grantData->requested) {
+ actionRequired = TRUE;
+ }
+ }
+
+ if((ptpClock->defaultDS.slaveOnly || nodeTable->isPeer) && (everyN == (GRANT_KEEPALIVE_INTERVAL -1))){
+ if(grantData->receiving == 0 && grantData->granted ) {
+ /* if we mixed n consecutive messages (checked every m seconds), re-request */
+ if( (everyN * UNICAST_GRANT_REFRESH_INTERVAL) > (GRANT_MAX_MISSED * grantData->logInterval)) {
+ DBG("foreign master: no %s being received - will request again\n",
+ getMessageTypeName(grantData->messageType));
+ actionRequired = TRUE;
+ }
+ }
+ grantData->receiving = 0;
+ }
+
+ /* if we're slave, we request; if we're master, we cancel */
+ if(actionRequired) {
+ if (ptpClock->defaultDS.slaveOnly || nodeTable->isPeer) {
+ requestUnicastTransmission(grantData, rtOpts->unicastGrantDuration, rtOpts, ptpClock);
+ } else {
+ cancelUnicastTransmission(grantData, rtOpts, ptpClock);
+ }
+ }
+
+ if(grantData->messageType == ANNOUNCE && ptpClock->portDS.portState == PTP_MASTER
+ && grantData->granted) {
+ ptpClock->slaveCount++;
+ }
+
+ }
+
+ nodeTable->timeLeft = maxTime;
+ /* Wild West version: Murdering Murphy! You done killed my paw! */
+ /* Reggae version: Matic in dem way, chopper in dem hand, hey, some a dem have M16 'pon dem shoulder */
+ /* Factual version: Make sure the node is re-usable: reset PortIdentity to all-ones again */
+ if(nodeTable->timeLeft == 0) {
+ nodeTable->portIdentity.portNumber = 0xFFFF;
+ memset(&nodeTable->portIdentity.clockIdentity, 0xFF, CLOCK_IDENTITY_LENGTH);
+ DBG("Unicast node %d now free and reusable\n", j);
+ }
+ }
+
+ if(nodeCount == 1 && nodeTable && nodeTable->isPeer) {
+ return;
+ }
+ /* we have some old requests to cancel, we changed the GM - keep the Announce coming though */
+ if(ptpClock->previousGrants != NULL) {
+ cancelUnicastTransmission(&(ptpClock->previousGrants->grantData[SYNC_INDEXED]), rtOpts, ptpClock);
+ cancelUnicastTransmission(&(ptpClock->previousGrants->grantData[DELAY_RESP_INDEXED]), rtOpts, ptpClock);
+ cancelUnicastTransmission(&(ptpClock->previousGrants->grantData[PDELAY_RESP_INDEXED]), rtOpts, ptpClock);
+ /* do not reset the other master's clock ID! ...you little bollocks you */
+ /*
+ ptpClock->previousGrants->portIdentity.portNumber = 0xFFFF;
+ memset(&ptpClock->previousGrants->portIdentity.clockIdentity, 0xFF, CLOCK_IDENTITY_LENGTH);
+ */
+ ptpClock->previousGrants = NULL;
+ }
+
+ if(ptpClock->defaultDS.slaveOnly && ptpClock->parentGrants != NULL && ptpClock->portDS.portState == PTP_SLAVE) {
+
+ nodeTable = ptpClock->parentGrants;
+
+ if (!nodeTable->grantData[SYNC_INDEXED].requested) {
+ grantData=&nodeTable->grantData[SYNC_INDEXED];
+ requestUnicastTransmission(grantData,
+ rtOpts->unicastGrantDuration, rtOpts, ptpClock);
+
+ }
+
+ if (nodeTable->grantData[SYNC_INDEXED].granted) {
+ switch(ptpClock->portDS.delayMechanism) {
+ case E2E:
+ if(!nodeTable->grantData[DELAY_RESP_INDEXED].requested) {
+ grantData=&nodeTable->grantData[DELAY_RESP_INDEXED];
+ requestUnicastTransmission(grantData,
+ rtOpts->unicastGrantDuration, rtOpts, ptpClock);
+ }
+ break;
+ case P2P:
+ if(!nodeTable->grantData[PDELAY_RESP_INDEXED].requested) {
+ grantData=&nodeTable->grantData[PDELAY_RESP_INDEXED];
+ requestUnicastTransmission(grantData,
+ rtOpts->unicastGrantDuration, rtOpts, ptpClock);
+ }
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ }
+
+}
diff --git a/rtemsbsd/ptpd/src/templates.conf b/rtemsbsd/ptpd/src/templates.conf
new file mode 100644
index 00000000..91253f45
--- /dev/null
+++ b/rtemsbsd/ptpd/src/templates.conf
@@ -0,0 +1,22 @@
+; templates.conf - PTPd user template file.
+
+; this file can be used to add PTPd config templates loaded on startup
+; this file is always loaded if any templates are specified using:
+; global:config_templates
+
+; PTPd also provides built-in templates. To see what is provided,
+; run ptpd with -T or --show-templates (or as part of full help, -H)
+
+; for more details, see man ptpd2(8) and ptpd2.conf(5)
+
+; As multiple templates are loaded, multiple instances of the same
+; template are merged - one template file can extend templates
+; in previously loaded files and can extend the built-in templates.
+; This file can be used for this purpose.
+
+; example entry:
+
+; [template-name]
+; section:setting=1"value"
+; section:setting2="value"
+
diff --git a/rtemsbsd/ptpd/src/timingdomain.c b/rtemsbsd/ptpd/src/timingdomain.c
new file mode 100644
index 00000000..706af9ac
--- /dev/null
+++ b/rtemsbsd/ptpd/src/timingdomain.c
@@ -0,0 +1,969 @@
+#include "ptpd.h"
+#include "dep/ntpengine/ntpdcontrol.h"
+
+#ifdef LOCAL_PREFIX
+#undef LOCAL_PREFIX
+#endif
+
+#define LOCAL_PREFIX "TimingService"
+
+static int cmpTimingService(TimingService *a, TimingService *b, Boolean useableOnly);
+static const char* reasonToString(int reason);
+static void prepareLeapFlags(RunTimeOpts *rtOpts, PtpClock *ptpClock);
+
+static int ptpServiceInit (TimingService* service);
+static int ptpServiceShutdown (TimingService* service);
+static int ptpServiceAcquire (TimingService* service);
+static int ptpServiceRelease (TimingService* service, int reason);
+static int ptpServiceUpdate (TimingService* service);
+static int ptpServiceClockUpdate (TimingService* service);
+
+static int ntpServiceInit (TimingService* service);
+static int ntpServiceShutdown (TimingService* service);
+static int ntpServiceAcquire (TimingService* service);
+static int ntpServiceRelease (TimingService* service, int reason);
+static int ntpServiceUpdate (TimingService* service);
+static int ntpServiceClockUpdate (TimingService* service);
+
+
+static int timingDomainInit(TimingDomain *domain);
+static int timingDomainShutdown(TimingDomain *domain);
+static int timingDomainUpdate(TimingDomain *domain);
+
+int
+timingDomainSetup(TimingDomain *domain)
+{
+ domain->init = timingDomainInit;
+ domain->shutdown = timingDomainShutdown;
+ domain->update = timingDomainUpdate;
+ domain->current = NULL;
+ return 1;
+}
+
+int
+timingServiceSetup(TimingService *service)
+{
+
+ if(service == NULL) {
+ return 0;
+ }
+
+ switch (service->dataSet.type) {
+
+ case TIMINGSERVICE_NTP:
+ service->init = ntpServiceInit;
+ service->shutdown = ntpServiceShutdown;
+ service->acquire = ntpServiceAcquire;
+ service->release = ntpServiceRelease;
+ service->update = ntpServiceUpdate;
+ service->clockUpdate = ntpServiceClockUpdate;
+ break;
+ case TIMINGSERVICE_PTP:
+ service->init = ptpServiceInit;
+ service->shutdown = ptpServiceShutdown;
+ service->acquire = ptpServiceAcquire;
+ service->release = ptpServiceRelease;
+ service->update = ptpServiceUpdate;
+ service->clockUpdate = ptpServiceClockUpdate;
+ break;
+ default:
+ break;
+ }
+
+ return 1;
+
+}
+
+static
+const char*
+reasonToString(int reason) {
+ switch(reason) {
+
+ case REASON_IDLE:
+ return("idle");
+ case REASON_ELECTION:
+ return("election");
+ case REASON_CTRL_NOT_BEST:
+ return("in control but not elected");
+ case REASON_ELIGIBLE:
+ return("no longer eligible");
+ default:
+ return "";
+ }
+}
+
+
+/* a'la BMCA. Eventually quality estimation will get here and the fun begins */
+static int
+cmpTimingService(TimingService *a, TimingService *b, Boolean useableOnly)
+{
+
+ /* if called with false, only dataset is examined */
+ if(useableOnly) {
+ /* operational is always better */
+ /* well.. for now. */
+ CMP2H((a->flags & TIMINGSERVICE_OPERATIONAL),
+ (b->flags & TIMINGSERVICE_OPERATIONAL))
+ /* only evaluate if the service wants to control the clock. */
+ /* this is meant to select the best service that can be used right now */
+ CMP2H((a->flags & TIMINGSERVICE_AVAILABLE),
+ (b->flags & TIMINGSERVICE_AVAILABLE))
+ /* should not happen really, when idle is triggered, available goes */
+// CMP2L((a->flags & TIMINGSERVICE_IDLE),
+// (b->flags & TIMINGSERVICE_IDLE))
+ }
+ /* lower p1 = better */
+ CMP2L(a->dataSet.priority1, b->dataSet.priority1);
+ /* lower type = better */
+ CMP2L((uint8_t)a->dataSet.type, (uint8_t)b->dataSet.type);
+ /* lower p2 = better */
+ CMP2L(a->dataSet.priority2, b->dataSet.priority2);
+
+ /* tiebreaker needed eventually: some uuid? age? */
+ /* CMP2L(a->dataSet.TBD, b->dataSet.TBD); */
+
+ /*
+ * Son, you don't have bad luck.
+ * The reason that bad things happen to you,
+ * is because you're a dumbass
+ */
+ return 1;
+}
+
+/* version suitable for quicksort */
+/*
+static int
+cmpTimingServiceQS (void *pA, void *pB)
+{
+
+ TimingService *a = (TimingService*)pA;
+ TimingService *b = (TimingService*)pB;
+
+ return cmpTimingService(a, b, TRUE);
+
+}
+*/
+
+static int
+ptpServiceInit (TimingService* service)
+{
+ RunTimeOpts *rtOpts = (RunTimeOpts*)service->config;
+ PtpClock *ptpClock = (PtpClock*)service->controller;
+
+ memset(&rtOpts->leapInfo, 0, sizeof(LeapSecondInfo));
+ if(strcmp(rtOpts->leapFile,"")) {
+ parseLeapFile(rtOpts->leapFile, &rtOpts->leapInfo);
+ }
+
+ /* read current UTC offset from leap file or from kernel if not configured */
+ if(ptpClock->timePropertiesDS.ptpTimescale &&
+ rtOpts->timeProperties.currentUtcOffset == 0) {
+ prepareLeapFlags(rtOpts, ptpClock);
+ }
+
+ INFO_LOCAL_ID(service, "PTP service init\n");
+
+ return 1;
+}
+
+static int
+ptpServiceShutdown (TimingService* service)
+{
+ PtpClock *ptpClock = (PtpClock*)service->controller;
+ INFO_LOCAL_ID(service,"PTP service shutdown\n");
+ ptpdShutdown(ptpClock);
+ return 1;
+}
+
+static int
+ptpServiceAcquire (TimingService* service)
+{
+// RunTimeOpts *rtOpts = (RunTimeOpts*)service->config;
+ PtpClock *ptpClock = (PtpClock*)service->controller;
+ ptpClock->clockControl.granted = TRUE;
+ INFO_LOCAL_ID(service,"acquired clock control\n");
+ FLAGS_SET(service->flags, TIMINGSERVICE_IN_CONTROL);
+ return 1;
+}
+
+static int
+ptpServiceRelease (TimingService* service, int reason)
+{
+// RunTimeOpts *rtOpts = (RunTimeOpts*)service->config;
+ PtpClock *ptpClock = (PtpClock*)service->controller;
+ ptpClock->clockControl.granted = FALSE;
+ if(!service->released) INFO_LOCAL_ID(service,"released clock control, reason: %s\n",
+ reasonToString(reason));
+ FLAGS_UNSET(service->flags, TIMINGSERVICE_IN_CONTROL);
+ return 1;
+}
+
+/*
+ * configure the UTC offset and leap flags according to
+ * information from kernel or leap file. Note: no updates to ptpClock.
+ * only clockStatus is being picked up in protocol.c
+ */
+static void
+prepareLeapFlags(RunTimeOpts *rtOpts, PtpClock *ptpClock) {
+
+ TimeInternal now;
+ Boolean leapInsert = FALSE, leapDelete = FALSE;
+#ifdef HAVE_SYS_TIMEX_H
+ int flags;
+
+ /* first get the offset from kernel if we can */
+#if defined(MOD_TAI) && NTP_API == 4
+ int utcOffset;
+
+ if(getKernelUtcOffset(&utcOffset) && utcOffset != 0) {
+ ptpClock->clockStatus.utcOffset = utcOffset;
+ }
+#endif /* MOD_TAI */
+
+ flags= getTimexFlags();
+
+ leapInsert = ((flags & STA_INS) == STA_INS);
+ leapDelete = ((flags & STA_DEL) == STA_DEL);
+
+#endif /* HAVE_SYS_TIMEX_H */
+
+ getTime(&now);
+
+
+ ptpClock->clockStatus.override = FALSE;
+
+ /* then we try the offset from leap file if valid - takes priority over kernel */
+ if(rtOpts->leapInfo.offsetValid) {
+ ptpClock->clockStatus.utcOffset =
+ rtOpts->leapInfo.currentOffset;
+ ptpClock->clockStatus.override = TRUE;
+ }
+
+ /* if we have valid leap second info from leap file, we use it */
+ if(rtOpts->leapInfo.valid) {
+
+ ptpClock->clockStatus.leapInsert = FALSE;
+ ptpClock->clockStatus.leapDelete = FALSE;
+
+ if( now.seconds >= rtOpts->leapInfo.startTime &&
+ now.seconds < rtOpts->leapInfo.endTime) {
+ DBG("Leap second pending - leap file\n");
+ if(rtOpts->leapInfo.leapType == 1) {
+ ptpClock->clockStatus.leapInsert = TRUE;
+ }
+ if(rtOpts->leapInfo.leapType == -1) {
+ ptpClock->clockStatus.leapDelete = TRUE;
+ }
+
+ ptpClock->clockStatus.override = TRUE;
+
+ }
+ if(now.seconds >= rtOpts->leapInfo.endTime) {
+ ptpClock->clockStatus.utcOffset =
+ rtOpts->leapInfo.nextOffset;
+ ptpClock->clockStatus.override = TRUE;
+ if(strcmp(rtOpts->leapFile,"")) {
+ memset(&rtOpts->leapInfo, 0, sizeof(LeapSecondInfo));
+ parseLeapFile(rtOpts->leapFile, &rtOpts->leapInfo);
+
+ if(rtOpts->leapInfo.offsetValid) {
+ ptpClock->clockStatus.utcOffset =
+ rtOpts->leapInfo.currentOffset;
+
+ }
+
+ }
+
+ }
+ /* otherwise we try using the kernel info, but not when we're slave */
+ } else if(ptpClock->portDS.portState != PTP_SLAVE) {
+ ptpClock->clockStatus.leapInsert = leapInsert;
+ ptpClock->clockStatus.leapDelete = leapDelete;
+ }
+
+}
+
+static int
+ptpServiceUpdate (TimingService* service)
+{
+ RunTimeOpts *rtOpts = (RunTimeOpts*)service->config;
+ PtpClock *ptpClock = (PtpClock*)service->controller;
+
+ ptpClock->counters.messageSendRate = ptpClock->netPath.sentPackets / service->updateInterval;
+ ptpClock->counters.messageReceiveRate = ptpClock->netPath.receivedPackets / service->updateInterval;
+
+ ptpClock->netPath.sentPackets = 0;
+ ptpClock->netPath.receivedPackets = 0;
+
+ if(service->reloadRequested && strcmp(rtOpts->leapFile,"")) {
+ memset(&rtOpts->leapInfo, 0, sizeof(LeapSecondInfo));
+ parseLeapFile(rtOpts->leapFile, &rtOpts->leapInfo);
+ service->reloadRequested = FALSE;
+ }
+
+ /* read current UTC offset from leap file or from kernel if not configured */
+ if(ptpClock->timePropertiesDS.ptpTimescale && ( ptpClock->portDS.portState == PTP_SLAVE ||
+ (ptpClock->portDS.portState == PTP_MASTER && rtOpts->timeProperties.currentUtcOffset == 0))) {
+ prepareLeapFlags(rtOpts, ptpClock);
+ }
+
+ /* temporary: this is only to maintain PTPd's current config options */
+ /* if NTP failover disabled, pretend PTP always owns the clock */
+ if(!rtOpts->ntpOptions.enableFailover) {
+ FLAGS_SET(service->flags, TIMINGSERVICE_OPERATIONAL);
+ if(ptpClock->clockControl.available) {
+ ptpClock->clockControl.granted = TRUE;
+ }
+ service->activity = TRUE;
+ FLAGS_SET(service->flags, TIMINGSERVICE_AVAILABLE);
+ return 1;
+ }
+
+ /* keep the activity heartbeat in check */
+ if(ptpClock->clockControl.activity) {
+ service->activity = TRUE;
+ ptpClock->clockControl.activity = FALSE;
+ DBGV("TimingService %s activity seen\n", service->id);
+ } else {
+ service->activity = FALSE;
+ }
+
+ /* initializing or faulty: not operational */
+ if((ptpClock->portDS.portState == PTP_INITIALIZING) ||
+ (ptpClock->portDS.portState == PTP_FAULTY)) {
+ FLAGS_UNSET(service->flags, TIMINGSERVICE_OPERATIONAL);
+ } else {
+ FLAGS_SET(service->flags, TIMINGSERVICE_OPERATIONAL);
+ DBGV("TimingService %s is operational\n", service->id);
+ }
+
+ /* not slave: release control or start hold timer */
+ if(ptpClock->portDS.portState != PTP_SLAVE) {
+ FLAGS_UNSET(service->flags, TIMINGSERVICE_IDLE);
+ if(service->flags & TIMINGSERVICE_HOLD) {
+ if((service->holdTimeLeft)<=0) {
+ FLAGS_UNSET(service->flags, TIMINGSERVICE_AVAILABLE);
+ FLAGS_UNSET(service->flags, TIMINGSERVICE_HOLD);
+
+ ptpClock->clockControl.available = FALSE;
+ if(service->holdTime) {
+ INFO_LOCAL_ID(service,"hold time expired\n");
+ }
+ }
+ } else if(ptpClock->clockControl.available) {
+ FLAGS_SET(service->flags, TIMINGSERVICE_HOLD);
+ /* if we're already in hold time, don't restart it */
+ if(service->holdTimeLeft <=0) {
+ service->holdTimeLeft = service->holdTime;
+ }
+ if(service->holdTimeLeft>0) {
+ DBG_LOCAL_ID(service,"hold started - %d seconds\n",service->holdTimeLeft);
+ }
+ }
+ } else {
+ /* slave: ready to acquire clock control, hold time over */
+ if(ptpClock->clockControl.available) {
+ if(!(service->flags & TIMINGSERVICE_AVAILABLE)) {
+
+ INFO_LOCAL_ID(service,"now available\n");
+ FLAGS_SET(service->flags, TIMINGSERVICE_AVAILABLE);
+
+ }
+
+ if(service->flags & TIMINGSERVICE_HOLD) {
+ DBG_LOCAL_ID(service, "hold cancelled\n");
+ FLAGS_UNSET(service->flags, TIMINGSERVICE_HOLD);
+ service->holdTimeLeft = 0;
+ }
+
+ DBGV("TimingService %s available for clock control\n", service->id);
+ } else {
+ FLAGS_UNSET(service->flags, TIMINGSERVICE_AVAILABLE);
+ }
+ }
+
+ /* if we're idle, release clock control */
+ if((service->flags & TIMINGSERVICE_IDLE) && (service->holdTimeLeft <= 0)) {
+ ptpClock->clockControl.available = FALSE;
+ FLAGS_UNSET(service->flags, TIMINGSERVICE_AVAILABLE);
+ }
+
+ return 1;
+}
+
+static int
+ptpServiceClockUpdate (TimingService* service)
+{
+ RunTimeOpts *rtOpts = (RunTimeOpts*)service->config;
+ PtpClock *ptpClock = (PtpClock*)service->controller;
+
+ TimeInternal newTime, oldTime;
+
+#ifdef HAVE_SYS_TIMEX_H
+ int flags = getTimexFlags();
+
+ Boolean leapInsert = flags & STA_INS;
+ Boolean leapDelete = flags & STA_DEL;
+ Boolean inSync = !(flags & STA_UNSYNC);
+#endif /* HAVE_SYS_TIMEX_H */
+
+ ClockStatusInfo *clockStatus = &ptpClock->clockStatus;
+
+
+ if(!clockStatus->update) {
+ return 0;
+ }
+
+ DBG_LOCAL_ID(service, "clock status update\n");
+
+#if defined(MOD_TAI) && NTP_API == 4
+ setKernelUtcOffset(clockStatus->utcOffset);
+
+ DBG_LOCAL_ID(service,"Set kernel UTC offset to %d\n",
+ clockStatus->utcOffset);
+#endif
+
+#ifdef HAVE_SYS_TIMEX_H
+
+ if(clockStatus->inSync & !inSync) {
+ clockStatus->inSync = FALSE;
+ unsetTimexFlags(STA_UNSYNC, TRUE);
+ }
+
+ informClockSource(ptpClock);
+
+ if(rtOpts->leapSecondHandling == LEAP_ACCEPT) {
+ /* withdraw kernel flags if needed, unless this is during the event */
+ if(!ptpClock->leapSecondInProgress) {
+ if(leapInsert && !clockStatus->leapInsert) {
+ DBG_LOCAL_ID(service,"STA_INS in kernel but not in PTP: withdrawing\n");
+ unsetTimexFlags(STA_INS, TRUE);
+ }
+
+ if(leapDelete && !clockStatus->leapDelete) {
+ DBG_LOCAL_ID(service,"STA_DEL in kernel but not in PTP: withdrawing\n");
+ unsetTimexFlags(STA_DEL, TRUE);
+ }
+ }
+
+ if(!leapInsert && clockStatus->leapInsert) {
+ WARNING("Leap second pending! Setting clock to insert one second at midnight\n");
+ setTimexFlags(STA_INS, FALSE);
+ }
+
+ if(!leapDelete && clockStatus->leapDelete) {
+ WARNING("Leap second pending! Setting clock to delete one second at midnight\n");
+ setTimexFlags(STA_DEL, FALSE);
+ }
+ } else {
+ if(leapInsert || leapDelete) {
+ DBG("leap second handling is not set to accept - withdrawing kernel leap flags!\n");
+ unsetTimexFlags(STA_INS, TRUE);
+ unsetTimexFlags(STA_DEL, TRUE);
+ }
+ }
+#else
+ if(clockStatus->leapInsert || clockStatus->leapDelete) {
+ if(rtOpts->leapSecondHandling != LEAP_SMEAR) {
+ WARNING("Leap second pending! No kernel leap second "
+ "API support - expect a clock offset at "
+ "midnight!\n");
+ }
+ }
+#endif /* HAVE_SYS_TIMEX_H */
+
+ getTime(&oldTime);
+ subTime(&newTime, &oldTime, &ptpClock->currentDS.offsetFromMaster);
+
+ /* Major time change */
+ if(clockStatus->majorChange){
+ /* re-parse leap seconds file */
+ if(strcmp(rtOpts->leapFile,"")) {
+ memset(&rtOpts->leapInfo, 0, sizeof(LeapSecondInfo));
+ parseLeapFile(rtOpts->leapFile, &rtOpts->leapInfo);
+ }
+#ifdef HAVE_LINUX_RTC_H
+ if(rtOpts->setRtc) {
+ NOTICE_LOCAL_ID(service, "Major time change - syncing the RTC\n");
+ setRtc(&newTime);
+ clockStatus->majorChange = FALSE;
+ }
+#endif /* HAVE_LINUX_RTC_H */
+ /* need to inform utmp / wtmp */
+ if(oldTime.seconds != newTime.seconds) {
+ updateXtmp(oldTime, newTime);
+ }
+ }
+
+ ptpClock->clockStatus.update = FALSE;
+ return 1;
+}
+
+static int
+ntpServiceInit (TimingService* service)
+{
+ NTPoptions *config = (NTPoptions*) service->config;
+ NTPcontrol *controller = (NTPcontrol*) service->controller;
+
+ INFO_LOCAL_ID(service,"NTP service init\n");
+
+ if(!config->enableEngine) {
+ INFO_LOCAL_ID(service,"NTP service not enabled\n");
+ FLAGS_UNSET(service->flags, TIMINGSERVICE_OPERATIONAL);
+ FLAGS_UNSET(service->flags, TIMINGSERVICE_AVAILABLE);
+ return 1;
+ }
+
+ if(ntpInit(config, controller)) {
+ FLAGS_SET(service->flags, TIMINGSERVICE_OPERATIONAL);
+ INFO_LOCAL_ID(service,"NTP service started\n");
+ return 1;
+ } else {
+ FLAGS_UNSET(service->flags, TIMINGSERVICE_OPERATIONAL);
+ FLAGS_UNSET(service->flags, TIMINGSERVICE_AVAILABLE);
+ INFO_LOCAL_ID(service,"Could not start NTP service. Will keep retrying.\n");
+ return 0;
+ }
+
+}
+
+static int
+ntpServiceShutdown (TimingService* service)
+{
+ NTPoptions *config = (NTPoptions*) service->config;
+ NTPcontrol *controller = (NTPcontrol*) service->controller;
+
+ INFO_LOCAL_ID(service,"NTP service shutting down\n");
+
+ if(controller->flagsCaptured) {
+ INFO_LOCAL_ID(service,"Restoring original NTP state\n");
+ ntpShutdown(config, controller);
+ }
+
+ FLAGS_UNSET(service->flags, TIMINGSERVICE_OPERATIONAL);
+ FLAGS_UNSET(service->flags, TIMINGSERVICE_AVAILABLE);
+ return 1;
+}
+
+static int
+ntpServiceAcquire (TimingService* service)
+{
+ NTPoptions *config = (NTPoptions*) service->config;
+ NTPcontrol *controller = (NTPcontrol*) service->controller;
+
+ if(!config->enableControl) {
+ if (!controller->requestFailed) {
+ WARNING_LOCAL_ID(service, "control disabled, cannot acquire clock control\n");
+ }
+ controller->requestFailed = TRUE;
+ return 1;
+ }
+
+ switch(ntpdSetFlags(config, controller, SYS_FLAG_KERNEL | SYS_FLAG_NTP)) {
+ case INFO_OKAY:
+ FLAGS_SET(service->flags, TIMINGSERVICE_IN_CONTROL);
+ controller->requestFailed = FALSE;
+ INFO_LOCAL_ID(service, "acquired clock control\n");
+ return 1;
+ default:
+ if (!controller->requestFailed) {
+ WARNING_LOCAL_ID(service,"failed to acquire clock control - clock may drift!\n");
+ }
+ controller->requestFailed = TRUE;
+ return 0;
+ }
+
+}
+
+static int
+ntpServiceRelease (TimingService* service, int reason)
+{
+ NTPoptions *config = (NTPoptions*) service->config;
+ NTPcontrol *controller = (NTPcontrol*) service->controller;
+
+ int res = 0;
+
+ if(!config->enableControl) {
+ if (!controller->requestFailed) {
+ WARNING_LOCAL_ID(service, "control disabled, cannot release clock control\n");
+ }
+ controller->requestFailed = TRUE;
+ return 1;
+ }
+
+ res = ntpdClearFlags(config, controller, SYS_FLAG_KERNEL | SYS_FLAG_NTP);
+
+ switch(res) {
+ case INFO_OKAY:
+ controller->requestFailed = FALSE;
+ if(!service->released) INFO_LOCAL_ID(service, "released clock control, reason: %s\n",
+ reasonToString(reason));
+ FLAGS_UNSET(service->flags, TIMINGSERVICE_IN_CONTROL);
+ return 1;
+ default:
+ if(!controller->requestFailed) {
+ WARNING_LOCAL_ID(service,"failed to release clock control, reason: %s - clock may be unstable!\n",
+ reasonToString(reason));
+ }
+ controller->requestFailed = TRUE;
+ return 0;
+ }
+
+}
+
+static int
+ntpServiceUpdate (TimingService* service)
+{
+
+ int res;
+
+ NTPoptions *config = (NTPoptions*) service->config;
+ NTPcontrol *controller = (NTPcontrol*) service->controller;
+
+ if(!config->enableEngine) {
+ return 0;
+ }
+
+ res = ntpdInControl(config, controller);
+
+ if (res != INFO_YES && res != INFO_NO) {
+ if(!controller->checkFailed) {
+ WARNING_LOCAL_ID(service,"Could not verify NTP status - will keep checking\n");
+ }
+ controller->checkFailed = TRUE;
+ FLAGS_UNSET(service->flags, TIMINGSERVICE_OPERATIONAL);
+ FLAGS_UNSET(service->flags, TIMINGSERVICE_AVAILABLE);
+ return 0;
+ }
+
+ FLAGS_SET(service->flags, TIMINGSERVICE_OPERATIONAL);
+
+ if(service->flags & TIMINGSERVICE_AVAILABLE) {
+ service->activity = TRUE;
+ } else {
+ controller->checkFailed = FALSE;
+ INFO_LOCAL_ID(service,"now available\n");
+ FLAGS_SET(service->flags, TIMINGSERVICE_AVAILABLE);
+ }
+
+ /*
+ * notify the service that we are in control,
+ * so that the watchdog can react if we are and it wasn't granted
+ */
+ if (res == INFO_YES) {
+ FLAGS_SET(service->flags, TIMINGSERVICE_IN_CONTROL);
+ }
+
+ controller->checkFailed = FALSE;
+
+ return 1;
+}
+
+static int
+ntpServiceClockUpdate (TimingService* service)
+{
+ return 1;
+}
+
+static int
+timingDomainInit(TimingDomain *domain)
+{
+ int i = 0;
+
+ TimingService *service;
+
+ DBG("Timing domain init\n");
+
+ for(i=0; i < domain->serviceCount; i++) {
+ service = domain->services[i];
+ timingServiceSetup(service);
+ service->init(service);
+ service->parent = domain;
+ }
+
+ domain->current = NULL;
+ domain->best = NULL;
+ domain->preferred = NULL;
+
+ return 1;
+
+}
+
+static int
+timingDomainShutdown(TimingDomain *domain)
+{
+ int i = 0;
+
+ TimingService *service;
+
+ INFO_LOCAL("Timing domain shutting down\n");
+
+ for(i=domain->serviceCount - 1; i >= 0; i--) {
+ service = domain->services[i];
+ if(service != NULL) {
+ service->shutdown(service);
+ }
+ }
+
+ INFO_LOCAL("Timing domain shutdown complete\n");
+
+ return 1;
+}
+
+static int
+timingDomainUpdate(TimingDomain *domain)
+{
+
+ int i = 0;
+ int cmp = 0;
+
+ TimingService *best = NULL;
+ TimingService *preferred = NULL;
+ TimingService *service = NULL;
+
+
+ /* update the election delay timer */
+ if(domain->electionLeft > 0) {
+ if(domain->electionLeft == domain->electionDelay) {
+ NOTIFY_LOCAL("election hold timer started: %d seconds\n", domain->electionDelay);
+ }
+ domain->electionLeft -= domain->updateInterval;
+ DBG_LOCAL("electionLeft %d\n", domain->electionLeft);
+ }
+
+ DBGV("Timing domain update\n");
+
+ if(domain->serviceCount < 1) {
+ DBG("No TimingServices in TimingDomain: nothing to do\n");
+ return 0;
+ }
+
+ /* first pass: check if alive, update idle times, release where needed */
+ for(i=0; i < domain->serviceCount; i++) {
+
+ service = domain->services[i];
+
+ service->lastUpdate += domain->updateInterval;
+
+ /* decrement the hold time counter */
+ if(service->holdTimeLeft > 0) {
+ service->holdTimeLeft -= domain->updateInterval;
+ DBG_LOCAL_ID(service, "hold time left %d\n", service->holdTimeLeft);
+ } else {
+ service->holdTimeLeft = 0;
+ }
+
+ /* each TimingService can have a different update interval: skip when not due */
+ if(service->lastUpdate >= service->updateInterval) {
+ service->updateDue = TRUE;
+ service->lastUpdate = 0;
+ } else {
+ continue;
+ }
+
+ DBG("TimingService %s due for update\n", service->id);
+ service->update(service);
+
+
+ DBGV("Service %s flags %03x\n",service->id, service->flags);
+
+ /* update idle times for operational services */
+ if(service->flags & TIMINGSERVICE_OPERATIONAL) {
+
+ if(!service->activity) {
+ service->idleTime += domain->updateInterval;
+ } else {
+ if(service->flags & TIMINGSERVICE_IDLE)
+ INFO_LOCAL_ID(service,"no longer idle\n");
+ FLAGS_UNSET(service->flags, TIMINGSERVICE_IDLE);
+ service->idleTime = 0;
+ if((service->holdTimeLeft>0) && !(service->flags & TIMINGSERVICE_HOLD)) {
+ service->holdTimeLeft = 0;
+ }
+ }
+
+ service->activity = FALSE;
+
+ if( (service->flags & TIMINGSERVICE_AVAILABLE) &&
+ !(service->flags & TIMINGSERVICE_HOLD) &&
+ (service->idleTime > service->minIdleTime) &&
+ (service->idleTime > service->timeout)) {
+
+ service->idleTime = 0;
+ if(!(service->flags & TIMINGSERVICE_IDLE)) {
+ INFO_LOCAL_ID(service, "has gone idle\n");
+ if(service == domain->current) {
+ service->holdTimeLeft = service->holdTime;
+ }
+ }
+
+ if(service->holdTimeLeft <= 0) {
+ // FLAGS_UNSET(service->flags, TIMINGSERVICE_AVAILABLE);
+ }
+
+ FLAGS_SET(service->flags, TIMINGSERVICE_IDLE);
+ if((service == domain->current) &&
+ (service->holdTimeLeft <= 0)) {
+ INFO_LOCAL_ID(service,"idle time hold start\n");
+ service->release(service, REASON_IDLE);
+ service->released = TRUE;
+ domain->electionLeft = domain->electionDelay;
+ domain->current = NULL;
+
+ }
+
+ }
+ }
+
+ /* inactive or inoperational and in control: release */
+ if((!(service->flags & TIMINGSERVICE_AVAILABLE) ||
+ !(service->flags & TIMINGSERVICE_OPERATIONAL)) &&
+ (service->flags & TIMINGSERVICE_IN_CONTROL) ) {
+ if(service->holdTimeLeft <= 0) {
+ service->release(service, REASON_ELIGIBLE);
+ service->released = TRUE;
+ if(service == domain->current) {
+ domain->electionLeft = domain->electionDelay;
+ domain->current = NULL;
+ }
+ }
+ }
+
+ }
+
+ /* second pass: elect the best service, establish preferred */
+ best = domain->services[0];
+ preferred = domain->services[0];
+
+ for(i=0; i < domain->serviceCount; i++) {
+ service = domain->services[i];
+ cmp = cmpTimingService(service, best, TRUE);
+ DBG("TimingService %s vs. best %s: cmp %d\n", service->id, best->id, cmp);
+ if(cmp >= 0) {
+ best = service;
+ }
+
+ if(cmpTimingService(service, preferred, FALSE) > 0) {
+ preferred = service;
+ }
+
+ }
+
+ /* best service, does not mean best selected */
+ domain->best = best;
+
+ /* best from DS perspective only */
+ domain->preferred = preferred;
+
+ /* best has changed - release previous, but let it know about the new best */
+ if((best != domain->current) && (domain->electionLeft == 0)) {
+ /* here used as temp variable */
+ service = domain->current;
+
+ /* release previous if we had one, kick off election hold */
+ if (service != NULL && (service->holdTimeLeft <= 0)) {
+ service->release(service, REASON_ELECTION);
+ service->released = TRUE;
+ domain->electionLeft = domain->electionDelay;
+ domain->current = NULL;
+ /* this way the election is 1 check interval minimum */
+ return 1;
+ }
+
+ domain->current = best;
+
+ if(FLAGS_ARESET(best->flags, TIMINGSERVICE_OPERATIONAL | TIMINGSERVICE_AVAILABLE)) {
+ NOTIFY_LOCAL_ID(best,"elected best TimingService\n");
+ } else {
+ domain->current = NULL;
+ domain->best = NULL;
+ }
+ }
+
+ /* third pass: release services in control which aren't best (can happen) */
+ for(i=0; i < domain->serviceCount; i++) {
+ service = domain->services[i];
+ if((service != domain->current) && (service->flags & TIMINGSERVICE_IN_CONTROL)) {
+ if (service->updateDue) {
+ if(service->holdTimeLeft <= 0) {
+ service->release(service, REASON_CTRL_NOT_BEST);
+ service->released = FALSE;
+ }
+ }
+ }
+ }
+
+ /* fourth pass: do the sums */
+
+ domain->availableCount = 0;
+ domain->operationalCount = 0;
+ domain->idleCount = 0;
+ domain->controlCount = 0;
+
+ for(i=0; i < domain->serviceCount; i++) {
+
+ service = domain->services[i];
+
+ if(service->flags & TIMINGSERVICE_OPERATIONAL)
+ domain->operationalCount++;
+
+ if(service->flags & TIMINGSERVICE_AVAILABLE)
+ domain->availableCount++;
+
+ if(service->flags & TIMINGSERVICE_IDLE)
+ domain->idleCount++;
+
+ if(service->flags & TIMINGSERVICE_IN_CONTROL)
+ domain->controlCount++;
+
+ }
+
+ /* special case, aye */
+ if((domain->serviceCount == 1) && (domain->current == NULL)) {
+ domain->best = NULL;
+ }
+
+ /* clear updateDue */
+ for(i=0; i < domain->serviceCount; i++) {
+ domain->services[i]->updateDue = FALSE;
+ }
+
+ if(best == NULL) {
+ if (!domain->noneAvailable)
+ WARNING_LOCAL("No TimingService available\n");
+ domain->noneAvailable = TRUE;
+ domain->current = NULL;
+ return 0;
+ }
+
+ if(!(best->flags & TIMINGSERVICE_OPERATIONAL)) {
+ if (!domain->noneAvailable)
+ WARNING_LOCAL("No operational TimingService available\n");
+ domain->noneAvailable = TRUE;
+ domain->current = NULL;
+ return 0;
+ }
+
+ if(!(best->flags & TIMINGSERVICE_AVAILABLE)) {
+ if (!domain->noneAvailable)
+ WARNING_LOCAL("No TimingService available for clock sync\n");
+ domain->noneAvailable = TRUE;
+ domain->current = NULL;
+ return 0;
+ }
+
+ domain->noneAvailable = FALSE;
+
+ if(!(best->flags & TIMINGSERVICE_IN_CONTROL) && domain->electionLeft == 0) {
+ best->released = FALSE;
+ best->acquire(best);
+ }
+
+ /* update clock source info */
+ if(best->flags & TIMINGSERVICE_IN_CONTROL) {
+ best->clockUpdate(best);
+ }
+
+ return 1;
+}
+
diff --git a/rtemsbsd/ptpd/src/timingdomain.h b/rtemsbsd/ptpd/src/timingdomain.h
new file mode 100644
index 00000000..046872b0
--- /dev/null
+++ b/rtemsbsd/ptpd/src/timingdomain.h
@@ -0,0 +1,131 @@
+#ifndef TIMINGDOMAIN_H_
+#define TIMINGDOMAIN_H_
+
+/* simple compare 2, lower wins */
+#define CMP2L(a,b) \
+ if (a < b ) return 1;\
+ if (a > b ) return -1;
+
+/* simple compare 2, higher wins */
+#define CMP2H(a,b) \
+ if (a > b ) return 1;\
+ if (a < b ) return -1;
+
+/* respect are kulture ye fenion basterb */
+#define FLAGS_ARESET(var, flegs) \
+((var & (flegs)) == (flegs))
+
+#define FLAGS_SET(var, flegs) \
+(var |= flegs)
+
+#define FLAGS_UNSET(var, flegs) \
+(var &= ~flegs)
+
+#define TIMINGSERVICE_OPERATIONAL 0x01 /* functioning */
+#define TIMINGSERVICE_AVAILABLE 0x02 /* ready to control the clock */
+#define TIMINGSERVICE_IN_CONTROL 0x04 /* allowed to control the clock - has control */
+#define TIMINGSERVICE_IDLE 0x08 /* not showing clock activity */
+#define TIMINGSERVICE_HOLD 0x10 /* hold timer running */
+#define TIMINGSERVICE_SINK_ONLY 0x20 /* only servers time */
+#define TIMINGSERVICE_NO_TOD 0x40 /* does not provide time of day */
+
+#define MAX_TIMINGSERVICES 16
+/* #define MAX_CLOCKSOURCES 16 */
+
+#define TIMINGSERVICE_MAX_DESC 20
+
+enum {
+ REASON_NONE,
+ REASON_IDLE,
+ REASON_ELECTION,
+ REASON_CTRL_NOT_BEST,
+ REASON_ELIGIBLE
+};
+
+typedef enum {
+ TIMINGSERVICE_NONE = 0x00,
+ TIMINGSERVICE_PTP = 0x10,
+ TIMINGSERVICE_PPS = 0x20,
+ TIMINGSERVICE_GPS = 0x30,
+ TIMINGSERVICE_NTP = 0x40,
+ TIMINGSERVICE_OTHER = 0xFE,
+ TIMINGSERVICE_MAX = 0xFF
+}
+TimingServiceType;
+
+typedef struct {
+ uint8_t priority1;
+ TimingServiceType type;
+ uint8_t priority2;
+}
+TimingServiceDS;
+
+typedef struct TimingService TimingService;
+typedef struct TimingDomain TimingDomain;
+
+struct TimingService {
+ TimingServiceDS dataSet;
+ char id[TIMINGSERVICE_MAX_DESC+1]; /* description like PTP-eth0 */
+ uint8_t flags; /* see top of file */
+ int updateInterval;/* minimum updatwe interval: some services need to be checked less frequently */
+ int lastUpdate; /* seconds since last update */
+ int minIdleTime; /* minumum and idle time. If we sync every 5 minutes, 2 minute timeout does not make much sense */
+ int idleTime; /* idle time - how long have we not had updated the clock */
+ int timeout; /* time between going idle and handing over clock control to next best */
+ int holdTime; /* how long to hold clock control when stopped controlling the clock */
+ int holdTimeLeft; /* how long to hold clock control when stopped controlling the clock */
+ Boolean updateDue; /* used internally by TimingDomain: should be private... */
+ Boolean activity; /* periodically updated by the time service, used to detect idle state */
+ Boolean reloadRequested; /* WHO WANTS A RELOAD!!! */
+ Boolean restartRequested; /* Service needs restarted */
+ Boolean released; /* so that we don't issue messages twice */
+ void *controller; /* the object behind the TimingService - such as PTPClock, NTPClock etc. */
+ void *config; /* configuration object used by the controller - RunTimeOpts, NTPOptions etc. */
+ TimingDomain *parent; /* pointer to the TimingDomain this service belongs to */
+/* ClockSource clockSource */ /* initially there is only the kernel clock... */
+ int (*init) (TimingService *service); /* init method that the service can assign */
+ int (*shutdown) (TimingService *service); /* shutdown method that the service can assign */
+ int (*acquire) (TimingService *service); /* method called when the given service should acquire clock control */
+ int (*release) (TimingService *service, int reason); /* method called when the given service should release clock control */
+ int (*update) (TimingService *service); /* heartbeat - called to allow the service to report if it's operational */
+ int (*clockUpdate) (TimingService *service); /* allows the service to inform the clock source about things like sync status, UTC offset, etc */
+};
+
+struct TimingDomain {
+ TimingService *services[MAX_TIMINGSERVICES]; /* temporary. this should be dynamic in future */
+ TimingService *current; /* pointer to the currently used service - null before election */
+ TimingService *best; /* pointer to the winning service (best out of all active) */
+ TimingService *preferred; /* naturally preferred = best looking at properties only */
+
+ /* ClockSource *sources[MAX_CLOCKSOURCES];*/ /* eventually */
+ int serviceCount;
+ /* TimingService *best[MAX_CLOCKSOURCES]; */ /* eventually, best service for given clock source */
+
+ int updateInterval; /* this is so TimingDomain knows how often it's being checked */
+ Boolean noneAvailable; /* used so that the no available warning is not repeated */
+
+
+ /* Counters */
+ int availableCount;
+ int operationalCount;
+ int idleCount;
+ int controlCount;
+
+ /* used to prevent flapping: when best is released or better exists, wait first */
+
+ /* the delay */
+ int electionDelay;
+ /* the countdown counter */
+ int electionLeft;
+
+ int (*init) (TimingDomain *domain); /* init method */
+ int (*shutdown) (TimingDomain *domain); /* shutdown method */
+ int (*update) (TimingDomain *domain); /* main update call */
+};
+
+int timingDomainSetup(TimingDomain *domain);
+int timingServiceSetup(TimingService *service);
+
+extern TimingDomain timingDomain;
+
+#endif /* TIMINGDOMAIN_H_ */
diff --git a/rtemsbsd/ptpd/test/client-e2e-8023.conf b/rtemsbsd/ptpd/test/client-e2e-8023.conf
new file mode 100644
index 00000000..fec3dde2
--- /dev/null
+++ b/rtemsbsd/ptpd/test/client-e2e-8023.conf
@@ -0,0 +1,447 @@
+; ========================================
+; PTPDv2 version 2.3.0 802.3 client configuration (requires PCAP)
+; ========================================
+
+; NOTE: the following settings are affected by ptpengine:preset selection:
+; ptpengine:slave_only
+; clock:no_adjust
+; ptpengine:clock_class - allowed range and default value
+; To see all preset settings, run ptpd2 -H (--long-help)
+
+; Network interface to use (required)
+ptpengine:interface =
+
+; PTP engine preset:
+; none = Defaults, no clock class restrictions
+; slaveonly = Slave only (clock class 255 only)
+; masteronly = Master, passive when not best master (clock class 0..127)
+; masterslave = Full IEEE 1588 implementation:
+; Master, slave when not best master
+; (clock class 128..254)
+;
+; Options: none slaveonly masteronly masterslave
+ptpengine:preset = slaveonly
+
+; IP transmission mode (requires IP transport) - hybrid mode uses
+; multicast for sync and announce, and unicast for delay request /
+; response
+; Options: multicast unicast hybrid
+;ptpengine:ip_mode = multicast
+
+; Transport type for PTP packets
+; Options: ipv4 ethernet
+ptpengine:transport = ethernet
+
+; Use libpcap for sending and receiving traffic (automatically enabled
+; in Ethernet mode)
+ptpengine:use_libpcap = N
+
+; Delay detection mode used - use DELAY_DISABLED for syntonisation
+; only (no synchronisation)
+; Options: E2E P2P DELAY_DISABLED
+ptpengine:delay_mechanism = E2E
+
+; PTP domain number
+ptpengine:domain = 0
+
+; Slave only mode (if set, overrides preset setting and sets clock class to 255)
+ptpengine:slave_only = Y
+
+; Specify latency correction for incoming packets
+ptpengine:inbound_latency = 0
+
+; Specify latency correction for outgoing packets
+ptpengine:outbound_latency = 0
+
+; Compatibility option: In slave state, always respect UTC offset
+; announced by best master, even if the the
+; currrentUtcOffsetValid flag is announced FALSE
+ptpengine:always_respect_utc_offset = N
+
+; PTP announce message interval in master state (expressed as log 2
+; i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_announce_interval = 1
+
+; PTP announce receipt timeout announced in master state
+ptpengine:announce_timeout = 6
+
+; PTP announce receipt timeout grace period in slave state:
+; when announce receipt timeout occurs, disqualify current best GM,
+; then wait n times announce receipt timeout before resetting.
+; Allows for a seamless GM failover when standby GMs are slow to react.
+; When set to 0, this option is not used.
+ptpengine:announce_timeout_grace_period = 0
+
+; PTP sync message interval in master state (expressed as log 2
+; i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_sync_interval = 0
+
+; Initial delay request message interval for slave mode, before first
+; delay response is received (expressed as log 2 i.e. -1=0.5s, 0=1s,
+; 1=2s etc.)
+ptpengine:log_delayreq_interval_initial = 0
+
+; Minimum delay request message interval in master state, in slave
+; mode overrides the master interval, required in hybrid mode
+; (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_delayreq_interval = 0
+
+; Minimum peer delay request message interval in master state.
+; (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_peer_delayreq_interval = 1
+
+; Maximum number of foreign masters (foreign master record size
+; allocated at startup)
+
+ptpengine:foreignrecord_capacity = 5
+
+; Specify Allan variance announced in master state
+ptpengine:ptp_allan_variance = 28768
+
+; Clock accuracy range announced in master state
+; Options: ACC_25NS ACC_100NS ACC_250NS ACC_1US ACC_2.5US ACC_10US
+; ACC_25US ACC_100US ACC_250US ACC_1MS ACC_2.5MS ACC_10MS ACC_25MS
+; ACC_100MS ACC_250MS ACC_1S ACC_10S ACC_10SPLUS ACC_UNKNOWN
+ptpengine:ptp_clock_accuracy = ACC_UNKNOWN
+
+; underlying time source UTC offset announced in master state
+ptpengine:utc_offset = 0
+
+; underlying time source UTC offset validity announced in master state
+ptpengine:utc_offset_valid = N
+
+; underlying time source time traceability announced in master state
+ptpengine:time_traceable = N
+
+; underlying time source frequency traceability announced in master state
+ptpengine:frequency_traceable = N
+
+; Time scale announced in master state (with ARB timescale, UTC
+; properties are ignored by slaves), when clock class 13 (application
+; specific), this value is ignored and ARB is used.
+; Options: PTP ARB
+ptpengine:ptp_timescale = ARB
+
+; Time source announced in master state
+; Options: ATOMIC_CLOCK GPS TERRESTRIAL_RADIO PTP NTP HAND_SET OTHER
+; INTERNAL_OSCILLATOR
+ptpengine:ptp_timesource = INTERNAL_OSCILLATOR
+
+; Clock class - announced in master state. Always 255 for slave-only mode.
+; Minimum, maximum and default values are controlled by presets.
+; If set to 13 (application specific time source), announced
+; time scale is always set to ARB. This setting controls the
+; states a PTP port can be in. If below 128, port will only
+; be in MASTER or PASSIVE states (master only). If above 127,
+; port will be in MASTER or SLAVE states.
+ptpengine:clock_class = 255
+
+; Priority 1 value announced in master state and used for Best Master
+; Clock selection
+ptpengine:priority1 = 128
+
+; Priority 2 value announced in master state and used for Best Master
+; Clock selection
+ptpengine:priority2 = 128
+
+; Specify unicast destination for unicast master mode (in unicast
+; slave mode overrides delay request destination)
+ptpengine:unicast_address =
+
+; Send explicit IGMP joins between servo resets
+ptpengine:igmp_refresh = Y
+
+; Multicast time to live for multicast PTP packets (ignored and set to
+; 1 for peer to peer messages)
+ptpengine:multicast_ttl = 64
+
+; DiffServ CodepPoint for packet prioritisation (decimal). When set to
+; zero, this option is not used.
+; 46 = Expedited Forwarding (0x2e)
+ptpengine:ip_dscp = 0
+
+; Enable outlier filter for the Delay Response component in slave state
+ptpengine:delay_outlier_filter_enable = N
+
+; Delay Response outlier filter action. If set to 'filter', outliers
+; are replaced with moving average
+; Options: discard filter
+ptpengine:delay_outlier_filter_action = filter
+
+; Number of samples in the Delay Response outlier filter buffer
+ptpengine:delay_outlier_filter_capacity = 20
+
+; Delay Response outlier filter threshold: multiplier for the Peirce's
+; maximum standard deviation. When set below 1.0, filter is tighter,
+; when set above 1.0, filter is looser than standard Peirce's test.
+ptpengine:delay_outlier_filter_threshold = 1.000000
+
+; Delay Response outlier weight: if an outlier is detected, this value
+; determines the amount of its deviation from mean that is used to
+; build the standard deviation statistics and influence further
+; outlier detection.
+; When set to 1.0, the outlier is used as is.
+;
+ptpengine:delay_outlier_weight = 1.000000
+
+; Enable outlier filter for the Sync component in slave state
+ptpengine:sync_outlier_filter_enable = N
+
+; Sync outlier filter action. If set to 'filter', outliers are
+; replaced with moving average
+; Options: discard filter
+ptpengine:sync_outlier_filter_action = filter
+
+; Number of samples in the Sync outlier filter buffer
+ptpengine:sync_outlier_filter_capacity = 20
+
+; Sync outlier filter threshold: multiplier for the Peirce's maximum
+; standard deviation. When set below 1.0, filter is tighter, when set
+; above 1.0, filter is looser than standard Peirce's test.
+ptpengine:sync_outlier_filter_threshold = 1.000000
+
+; Sync outlier weight: if an outlier is detected, this value
+; determines the amount of its deviation from mean that is used to
+; build the standard deviation statistics and influence further
+; outlier detection. When set to 1.0, the outlier is used as is.
+ptpengine:sync_outlier_weight = 1.000000
+
+; Delay between moving to slave state and enabling clock updates
+; expressed as number of statistics update periods (see
+; global:statistics_update_interval). This allows one-way delay to
+; stabilise before starting clock updates. Activated when going into
+; slave state and during GM failover in slave state.
+; 0 - not used.
+ptpengine:calibration_delay = 0
+
+; Enable panic mode: when offset from master is above 1 second, stop
+; updating the clock for a period of time and then step the clock if
+; offset remains above 1 second.
+ptpengine:panic_mode = N
+
+; Duration of the panic mode period (no clock updates) when offset
+; above 1 second detected
+ptpengine:panic_mode_duration = 2
+
+; Use JobID (PID) for UUID
+ptpengine:pid_as_clock_idendity = N
+
+; Fail over to NTP when PTP time sync not available - requires
+; ntpengine:enabled but does not require the rest of NTP configuration
+; - will warn instead of failing over if cannot control ntpd.
+ptpengine:ntp_failover = N
+
+; NTP failover timeout in seconds: time between PTP slave going into
+; LISTENING state, and failing over to NTP. 0 = fail over immediately.
+ptpengine:ntp_failover_timeout = 60
+
+; Prefer NTP time synchronisation when not controlling the clock (all
+; states, including slave when clock:no_adjust set)
+ptpengine:prefer_ntp = N
+
+; When entering panic mode, fail over to NTP (after the NTP failover
+; timeout period) - requires ntpengine:enabled but does not require
+; the rest of NTP configuration - will warn instead of failing over if
+; it cannot control ntpd.
+ptpengine:panic_mode_ntp = N
+
+; Do not adjust the clock
+clock:no_adjust = N
+
+; Do not reset the clock - only slew
+clock:no_reset = N
+
+; Observed drift handling method between servo restarts:
+; reset: set to zero (not recommended)
+; preserve: use kernel value,
+; file: load and save to drift file on startup/shutdown, use kernel
+; value inbetween.
+; To specify drift file, use the clock:drift_file setting.
+; Options: reset preserve file
+clock:drift_handling = preserve
+
+; Specify drift file
+clock:drift_file = /etc/ptpd2_kernelclock.drift
+
+; Maximum absolute frequency shift which can be applied to the clock servo
+; when slewing the clock. Expressed in parts per million (1 ppm = shift of
+; 1 us per second. Values above 512 will use the tick duration correction
+; to allow even faster slewing. Default maximum is 512 without using tick.
+clock:max_offset_ppm = 500
+
+; One-way delay filter stiffness
+servo:delayfilter_stiffness = 6
+
+; Clock servo PI controller proportional component gain (kP)
+servo:kp = 0.1
+
+; Clock servo PI controller integral component gain (kI)
+servo:ki = 0.001
+
+; Maximum accepted delayMS value in nanoseconds (Sync).
+; 0 = not checked.
+servo:max_delay = 0
+
+; Enable clock synchronisation servo stability detection
+; (based on standard deviation of the observed drift value)
+; - drift will be saved to drift file / cached when considered stable,
+; also clock stability status will be logged
+;
+servo:stability_detection = N
+
+; Specify the observed drift standard deviation threshold in parts per billion
+; (ppb) - if stanard deviation is within the threshold, servo is considered
+; stable.
+servo:stability_threshold = 5.000000
+
+; Specify for how many statistics update intervals the observed drift standard
+; deviation has to stay within threshold to be considered stable
+;
+servo:stability_period = 3
+
+; Specify after how many minutes without stabilisation servo is considered
+; unstable. Assists with logging servo stability information and
+; allows to preserve observed drift if servo cannot stabilise.
+;
+servo:stability_timeout = 10
+
+; Do not update one-way delay if slave to master delay (from Delay Response)
+; is greater than this value (nanoseconds). 0 = not used.
+servo:max_delay = 0
+
+; Do not reset the clock if offset from master is greater
+; than this value (nanoseconds). 0 = not used.
+servo:max_offset = 0
+
+; Send log messages to syslog. Disabling this
+; sends all messages to stdout (or speficied log file)
+global:use_syslog = N
+
+; Lock file location
+global:lock_file =
+
+; Use mode specific and interface specific lock files (overrides
+; global:lock_file)
+global:auto_lockfile = N
+
+; Lock file directory: used with automatic mode-specific lock files,
+; also used when no lock file is specified. When lock file
+; is specified, it's expected to be an absolute path.
+global:lock_directory = /var/run
+
+; Skip lock file checking and locking
+global:ignore_lock = N
+
+; File used to record data about sync packets. Setting this enables recording.
+global:quality_file =
+
+; Maximum sync packet record file size (in kB) - file will be
+; truncated if size exceeds the limit.
+; 0 - no limit.
+global:quality_file_max_size = 0
+
+; Enable log rotation of the sync packet record file up to n files.
+; 0 - do not rotate.
+global:quality_file_max_files = 0
+
+; Truncate the sync packet record file every time it is (re) opened -
+; on startup and SIGHUP
+global:quality_file_truncate = N
+
+; File used to log ptpd2 status information
+global:status_file = /var/run/ptpd2.status.log
+
+; Enable / disable writing status information to file
+global:log_status = Y
+
+; Status file update interval in seconds
+;
+global:status_update_interval = 1
+
+; Specify log file path (event log). Setting this enables logging to file.
+global:log_file = /var/run/ptpd2.event.log
+
+; Maximum log file size (in kB) - log file will be truncated if size
+; exceeds the limit.
+; 0 - no limit.
+global:log_file_max_size = 0
+
+; Enable log rotation of the sync packet record file up to n files.
+; 0 - do not rotate
+global:log_file_max_files = 0
+
+; Truncate the log file every time it is (re) opened - on startup and SIGHUP
+global:log_file_truncate = N
+
+; Specify log level (only messages of the specified priority or higer
+; will be logged).
+; The minimal level is LOG_ERR. LOG_ALL enables debug output if compiled with
+; RUNTIME_DEBUG
+; Options: LOG_ERR LOG_WARNING LOG_NOTICE LOG_INFO LOG_ALL
+global:log_level = LOG_ALL
+
+; Specify statistics log file path. Setting this enables logging of
+; statistics but can be overriden with global:log_statistics
+global:statistics_file = /var/run/ptpd2.stats.log
+
+; Log timing statistics every n seconds for Sync and Delay Response
+; messages (0 - log all)
+global:statistics_log_interval = 0
+
+; Maximum statistics log file size (in kB) - log file will be
+; truncated if size exceeds the limit.
+; 0 - no limit.
+global:statistics_file_max_size = 0
+
+; Enable log rotation of the statistics file up to n files. 0 - do not rotate
+;
+global:statistics_file_max_files = 0
+
+; Truncate the statistics file every time it is (re) opened - on
+; startup and SIGHUP
+global:statistics_file_truncate = N
+
+; Dump the contents of every PTP packet
+global:dump_packets = N
+
+; Run in foreground with statistics and all messages logged to stdout.
+; Overrides log file and statistics file settings and disables syslog.
+;
+global:verbose_foreground = N
+
+; Run in foreground
+global:foreground = N
+
+; Log timing statistics for every PTP packet received
+global:log_statistics = Y
+
+; Linux only: bind ptpd2 process to a selected CPU core number.
+; 0 = first CPU core, etc. -1 = do not bind to a single core.
+global:cpuaffinity_cpucore = -1
+
+; Clock synchronisation statistics update interval in seconds
+;
+global:statistics_update_interval = 5
+
+; Enable NTPd integration
+ntpengine:enabled = N
+
+; Enable control over local NTPd daemon
+ntpengine:control_enabled = N
+
+; NTP control check interval in seconds
+;
+ntpengine:check_interval = 15
+
+; NTP key number - must be configured as a trusted control key in ntp.conf,
+; and must be non-zero for the ntpengine:control_enabled setting to take effect.
+;
+ntpengine:key_id = 0
+
+; NTP key (plain text, max. 20 characters) - must match the key
+; configured in ntpd's keys file, and must be non-zero for the
+; ntpengine:control_enabled setting to take effect.
+ntpengine:key =
+
+; ========= newline required in the end ==========
+
diff --git a/rtemsbsd/ptpd/test/client-e2e-pcap.conf b/rtemsbsd/ptpd/test/client-e2e-pcap.conf
new file mode 100644
index 00000000..8b47fc5d
--- /dev/null
+++ b/rtemsbsd/ptpd/test/client-e2e-pcap.conf
@@ -0,0 +1,447 @@
+; ========================================
+; PTPDv2 version 2.3.0 PCAP + IPv4 client configuration
+; ========================================
+
+; NOTE: the following settings are affected by ptpengine:preset selection:
+; ptpengine:slave_only
+; clock:no_adjust
+; ptpengine:clock_class - allowed range and default value
+; To see all preset settings, run ptpd2 -H (--long-help)
+
+; Network interface to use (required)
+ptpengine:interface =
+
+; PTP engine preset:
+; none = Defaults, no clock class restrictions
+; slaveonly = Slave only (clock class 255 only)
+; masteronly = Master, passive when not best master (clock class 0..127)
+; masterslave = Full IEEE 1588 implementation:
+; Master, slave when not best master
+; (clock class 128..254)
+;
+; Options: none slaveonly masteronly masterslave
+ptpengine:preset = slaveonly
+
+; IP transmission mode (requires IP transport) - hybrid mode uses
+; multicast for sync and announce, and unicast for delay request /
+; response
+; Options: multicast unicast hybrid
+ptpengine:ip_mode = multicast
+
+; Transport type for PTP packets
+; Options: ipv4 ethernet
+ptpengine:transport = ipv4
+
+; Use libpcap for sending and receiving traffic (automatically enabled
+; in Ethernet mode)
+ptpengine:use_libpcap = Y
+
+; Delay detection mode used - use DELAY_DISABLED for syntonisation
+; only (no synchronisation)
+; Options: E2E P2P DELAY_DISABLED
+ptpengine:delay_mechanism = E2E
+
+; PTP domain number
+ptpengine:domain = 0
+
+; Slave only mode (if set, overrides preset setting and sets clock class to 255)
+ptpengine:slave_only = Y
+
+; Specify latency correction for incoming packets
+ptpengine:inbound_latency = 0
+
+; Specify latency correction for outgoing packets
+ptpengine:outbound_latency = 0
+
+; Compatibility option: In slave state, always respect UTC offset
+; announced by best master, even if the the
+; currrentUtcOffsetValid flag is announced FALSE
+ptpengine:always_respect_utc_offset = N
+
+; PTP announce message interval in master state (expressed as log 2
+; i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_announce_interval = 1
+
+; PTP announce receipt timeout announced in master state
+ptpengine:announce_timeout = 6
+
+; PTP announce receipt timeout grace period in slave state:
+; when announce receipt timeout occurs, disqualify current best GM,
+; then wait n times announce receipt timeout before resetting.
+; Allows for a seamless GM failover when standby GMs are slow to react.
+; When set to 0, this option is not used.
+ptpengine:announce_timeout_grace_period = 0
+
+; PTP sync message interval in master state (expressed as log 2
+; i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_sync_interval = 0
+
+; Initial delay request message interval for slave mode, before first
+; delay response is received (expressed as log 2 i.e. -1=0.5s, 0=1s,
+; 1=2s etc.)
+ptpengine:log_delayreq_interval_initial = 0
+
+; Minimum delay request message interval in master state, in slave
+; mode overrides the master interval, required in hybrid mode
+; (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_delayreq_interval = 0
+
+; Minimum peer delay request message interval in master state.
+; (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_peer_delayreq_interval = 1
+
+; Maximum number of foreign masters (foreign master record size
+; allocated at startup)
+
+ptpengine:foreignrecord_capacity = 5
+
+; Specify Allan variance announced in master state
+ptpengine:ptp_allan_variance = 28768
+
+; Clock accuracy range announced in master state
+; Options: ACC_25NS ACC_100NS ACC_250NS ACC_1US ACC_2.5US ACC_10US
+; ACC_25US ACC_100US ACC_250US ACC_1MS ACC_2.5MS ACC_10MS ACC_25MS
+; ACC_100MS ACC_250MS ACC_1S ACC_10S ACC_10SPLUS ACC_UNKNOWN
+ptpengine:ptp_clock_accuracy = ACC_UNKNOWN
+
+; underlying time source UTC offset announced in master state
+ptpengine:utc_offset = 0
+
+; underlying time source UTC offset validity announced in master state
+ptpengine:utc_offset_valid = N
+
+; underlying time source time traceability announced in master state
+ptpengine:time_traceable = N
+
+; underlying time source frequency traceability announced in master state
+ptpengine:frequency_traceable = N
+
+; Time scale announced in master state (with ARB timescale, UTC
+; properties are ignored by slaves), when clock class 13 (application
+; specific), this value is ignored and ARB is used.
+; Options: PTP ARB
+ptpengine:ptp_timescale = ARB
+
+; Time source announced in master state
+; Options: ATOMIC_CLOCK GPS TERRESTRIAL_RADIO PTP NTP HAND_SET OTHER
+; INTERNAL_OSCILLATOR
+ptpengine:ptp_timesource = INTERNAL_OSCILLATOR
+
+; Clock class - announced in master state. Always 255 for slave-only mode.
+; Minimum, maximum and default values are controlled by presets.
+; If set to 13 (application specific time source), announced
+; time scale is always set to ARB. This setting controls the
+; states a PTP port can be in. If below 128, port will only
+; be in MASTER or PASSIVE states (master only). If above 127,
+; port will be in MASTER or SLAVE states.
+ptpengine:clock_class = 255
+
+; Priority 1 value announced in master state and used for Best Master
+; Clock selection
+ptpengine:priority1 = 128
+
+; Priority 2 value announced in master state and used for Best Master
+; Clock selection
+ptpengine:priority2 = 128
+
+; Specify unicast destination for unicast master mode (in unicast
+; slave mode overrides delay request destination)
+ptpengine:unicast_address =
+
+; Send explicit IGMP joins between servo resets
+ptpengine:igmp_refresh = Y
+
+; Multicast time to live for multicast PTP packets (ignored and set to
+; 1 for peer to peer messages)
+ptpengine:multicast_ttl = 64
+
+; DiffServ CodepPoint for packet prioritisation (decimal). When set to
+; zero, this option is not used.
+; 46 = Expedited Forwarding (0x2e)
+ptpengine:ip_dscp = 0
+
+; Enable outlier filter for the Delay Response component in slave state
+ptpengine:delay_outlier_filter_enable = N
+
+; Delay Response outlier filter action. If set to 'filter', outliers
+; are replaced with moving average
+; Options: discard filter
+ptpengine:delay_outlier_filter_action = filter
+
+; Number of samples in the Delay Response outlier filter buffer
+ptpengine:delay_outlier_filter_capacity = 20
+
+; Delay Response outlier filter threshold: multiplier for the Peirce's
+; maximum standard deviation. When set below 1.0, filter is tighter,
+; when set above 1.0, filter is looser than standard Peirce's test.
+ptpengine:delay_outlier_filter_threshold = 1.000000
+
+; Delay Response outlier weight: if an outlier is detected, this value
+; determines the amount of its deviation from mean that is used to
+; build the standard deviation statistics and influence further
+; outlier detection.
+; When set to 1.0, the outlier is used as is.
+;
+ptpengine:delay_outlier_weight = 1.000000
+
+; Enable outlier filter for the Sync component in slave state
+ptpengine:sync_outlier_filter_enable = N
+
+; Sync outlier filter action. If set to 'filter', outliers are
+; replaced with moving average
+; Options: discard filter
+ptpengine:sync_outlier_filter_action = filter
+
+; Number of samples in the Sync outlier filter buffer
+ptpengine:sync_outlier_filter_capacity = 20
+
+; Sync outlier filter threshold: multiplier for the Peirce's maximum
+; standard deviation. When set below 1.0, filter is tighter, when set
+; above 1.0, filter is looser than standard Peirce's test.
+ptpengine:sync_outlier_filter_threshold = 1.000000
+
+; Sync outlier weight: if an outlier is detected, this value
+; determines the amount of its deviation from mean that is used to
+; build the standard deviation statistics and influence further
+; outlier detection. When set to 1.0, the outlier is used as is.
+ptpengine:sync_outlier_weight = 1.000000
+
+; Delay between moving to slave state and enabling clock updates
+; expressed as number of statistics update periods (see
+; global:statistics_update_interval). This allows one-way delay to
+; stabilise before starting clock updates. Activated when going into
+; slave state and during GM failover in slave state.
+; 0 - not used.
+ptpengine:calibration_delay = 0
+
+; Enable panic mode: when offset from master is above 1 second, stop
+; updating the clock for a period of time and then step the clock if
+; offset remains above 1 second.
+ptpengine:panic_mode = N
+
+; Duration of the panic mode period (no clock updates) when offset
+; above 1 second detected
+ptpengine:panic_mode_duration = 2
+
+; Use JobID (PID) for UUID
+ptpengine:pid_as_clock_idendity = N
+
+; Fail over to NTP when PTP time sync not available - requires
+; ntpengine:enabled but does not require the rest of NTP configuration
+; - will warn instead of failing over if cannot control ntpd.
+ptpengine:ntp_failover = N
+
+; NTP failover timeout in seconds: time between PTP slave going into
+; LISTENING state, and failing over to NTP. 0 = fail over immediately.
+ptpengine:ntp_failover_timeout = 60
+
+; Prefer NTP time synchronisation when not controlling the clock (all
+; states, including slave when clock:no_adjust set)
+ptpengine:prefer_ntp = N
+
+; When entering panic mode, fail over to NTP (after the NTP failover
+; timeout period) - requires ntpengine:enabled but does not require
+; the rest of NTP configuration - will warn instead of failing over if
+; it cannot control ntpd.
+ptpengine:panic_mode_ntp = N
+
+; Do not adjust the clock
+clock:no_adjust = N
+
+; Do not reset the clock - only slew
+clock:no_reset = N
+
+; Observed drift handling method between servo restarts:
+; reset: set to zero (not recommended)
+; preserve: use kernel value,
+; file: load and save to drift file on startup/shutdown, use kernel
+; value inbetween.
+; To specify drift file, use the clock:drift_file setting.
+; Options: reset preserve file
+clock:drift_handling = preserve
+
+; Specify drift file
+clock:drift_file = /etc/ptpd2_kernelclock.drift
+
+; Maximum absolute frequency shift which can be applied to the clock servo
+; when slewing the clock. Expressed in parts per million (1 ppm = shift of
+; 1 us per second. Values above 512 will use the tick duration correction
+; to allow even faster slewing. Default maximum is 512 without using tick.
+clock:max_offset_ppm = 500
+
+; One-way delay filter stiffness
+servo:delayfilter_stiffness = 6
+
+; Clock servo PI controller proportional component gain (kP)
+servo:kp = 0.1
+
+; Clock servo PI controller integral component gain (kI)
+servo:ki = 0.001
+
+; Maximum accepted delayMS value in nanoseconds (Sync).
+; 0 = not checked.
+servo:max_delay = 0
+
+; Enable clock synchronisation servo stability detection
+; (based on standard deviation of the observed drift value)
+; - drift will be saved to drift file / cached when considered stable,
+; also clock stability status will be logged
+;
+servo:stability_detection = N
+
+; Specify the observed drift standard deviation threshold in parts per billion
+; (ppb) - if stanard deviation is within the threshold, servo is considered
+; stable.
+servo:stability_threshold = 5.000000
+
+; Specify for how many statistics update intervals the observed drift standard
+; deviation has to stay within threshold to be considered stable
+;
+servo:stability_period = 3
+
+; Specify after how many minutes without stabilisation servo is considered
+; unstable. Assists with logging servo stability information and
+; allows to preserve observed drift if servo cannot stabilise.
+;
+servo:stability_timeout = 10
+
+; Do not update one-way delay if slave to master delay (from Delay Response)
+; is greater than this value (nanoseconds). 0 = not used.
+servo:max_delay = 0
+
+; Do not reset the clock if offset from master is greater
+; than this value (nanoseconds). 0 = not used.
+servo:max_offset = 0
+
+; Send log messages to syslog. Disabling this
+; sends all messages to stdout (or speficied log file)
+global:use_syslog = N
+
+; Lock file location
+global:lock_file =
+
+; Use mode specific and interface specific lock files (overrides
+; global:lock_file)
+global:auto_lockfile = N
+
+; Lock file directory: used with automatic mode-specific lock files,
+; also used when no lock file is specified. When lock file
+; is specified, it's expected to be an absolute path.
+global:lock_directory = /var/run
+
+; Skip lock file checking and locking
+global:ignore_lock = N
+
+; File used to record data about sync packets. Setting this enables recording.
+global:quality_file =
+
+; Maximum sync packet record file size (in kB) - file will be
+; truncated if size exceeds the limit.
+; 0 - no limit.
+global:quality_file_max_size = 0
+
+; Enable log rotation of the sync packet record file up to n files.
+; 0 - do not rotate.
+global:quality_file_max_files = 0
+
+; Truncate the sync packet record file every time it is (re) opened -
+; on startup and SIGHUP
+global:quality_file_truncate = N
+
+; File used to log ptpd2 status information
+global:status_file = /var/run/ptpd2.status.log
+
+; Enable / disable writing status information to file
+global:log_status = Y
+
+; Status file update interval in seconds
+;
+global:status_update_interval = 1
+
+; Specify log file path (event log). Setting this enables logging to file.
+global:log_file = /var/run/ptpd2.event.log
+
+; Maximum log file size (in kB) - log file will be truncated if size
+; exceeds the limit.
+; 0 - no limit.
+global:log_file_max_size = 0
+
+; Enable log rotation of the sync packet record file up to n files.
+; 0 - do not rotate
+global:log_file_max_files = 0
+
+; Truncate the log file every time it is (re) opened - on startup and SIGHUP
+global:log_file_truncate = N
+
+; Specify log level (only messages of the specified priority or higer
+; will be logged).
+; The minimal level is LOG_ERR. LOG_ALL enables debug output if compiled with
+; RUNTIME_DEBUG
+; Options: LOG_ERR LOG_WARNING LOG_NOTICE LOG_INFO LOG_ALL
+global:log_level = LOG_ALL
+
+; Specify statistics log file path. Setting this enables logging of
+; statistics but can be overriden with global:log_statistics
+global:statistics_file = /var/run/ptpd2.stats.log
+
+; Log timing statistics every n seconds for Sync and Delay Response
+; messages (0 - log all)
+global:statistics_log_interval = 0
+
+; Maximum statistics log file size (in kB) - log file will be
+; truncated if size exceeds the limit.
+; 0 - no limit.
+global:statistics_file_max_size = 0
+
+; Enable log rotation of the statistics file up to n files. 0 - do not rotate
+;
+global:statistics_file_max_files = 0
+
+; Truncate the statistics file every time it is (re) opened - on
+; startup and SIGHUP
+global:statistics_file_truncate = N
+
+; Dump the contents of every PTP packet
+global:dump_packets = N
+
+; Run in foreground with statistics and all messages logged to stdout.
+; Overrides log file and statistics file settings and disables syslog.
+;
+global:verbose_foreground = N
+
+; Run in foreground
+global:foreground = N
+
+; Log timing statistics for every PTP packet received
+global:log_statistics = Y
+
+; Linux only: bind ptpd2 process to a selected CPU core number.
+; 0 = first CPU core, etc. -1 = do not bind to a single core.
+global:cpuaffinity_cpucore = -1
+
+; Clock synchronisation statistics update interval in seconds
+;
+global:statistics_update_interval = 5
+
+; Enable NTPd integration
+ntpengine:enabled = N
+
+; Enable control over local NTPd daemon
+ntpengine:control_enabled = N
+
+; NTP control check interval in seconds
+;
+ntpengine:check_interval = 15
+
+; NTP key number - must be configured as a trusted control key in ntp.conf,
+; and must be non-zero for the ntpengine:control_enabled setting to take effect.
+;
+ntpengine:key_id = 0
+
+; NTP key (plain text, max. 20 characters) - must match the key
+; configured in ntpd's keys file, and must be non-zero for the
+; ntpengine:control_enabled setting to take effect.
+ntpengine:key =
+
+; ========= newline required in the end ==========
+
diff --git a/rtemsbsd/ptpd/test/client-e2e-socket.conf b/rtemsbsd/ptpd/test/client-e2e-socket.conf
new file mode 100644
index 00000000..91f52c6d
--- /dev/null
+++ b/rtemsbsd/ptpd/test/client-e2e-socket.conf
@@ -0,0 +1,447 @@
+; ========================================
+; PTPDv2 version 2.3.0-svn default configuration
+; ========================================
+
+; NOTE: the following settings are affected by ptpengine:preset selection:
+; ptpengine:slave_only
+; clock:no_adjust
+; ptpengine:clock_class - allowed range and default value
+; To see all preset settings, run ptpd2 -H (--long-help)
+
+; Network interface to use (required)
+ptpengine:interface =
+
+; PTP engine preset:
+; none = Defaults, no clock class restrictions
+; slaveonly = Slave only (clock class 255 only)
+; masteronly = Master, passive when not best master (clock class 0..127)
+; masterslave = Full IEEE 1588 implementation:
+; Master, slave when not best master
+; (clock class 128..254)
+;
+; Options: none slaveonly masteronly masterslave
+ptpengine:preset = slaveonly
+
+; IP transmission mode (requires IP transport) - hybrid mode uses
+; multicast for sync and announce, and unicast for delay request /
+; response
+; Options: multicast unicast hybrid
+ptpengine:ip_mode = multicast
+
+; Transport type for PTP packets
+; Options: ipv4 ethernet
+ptpengine:transport = ipv4
+
+; Use libpcap for sending and receiving traffic (automatically enabled
+; in Ethernet mode)
+ptpengine:use_libpcap = N
+
+; Delay detection mode used - use DELAY_DISABLED for syntonisation
+; only (no synchronisation)
+; Options: E2E P2P DELAY_DISABLED
+ptpengine:delay_mechanism = E2E
+
+; PTP domain number
+ptpengine:domain = 0
+
+; Slave only mode (if set, overrides preset setting and sets clock class to 255)
+ptpengine:slave_only = Y
+
+; Specify latency correction for incoming packets
+ptpengine:inbound_latency = 0
+
+; Specify latency correction for outgoing packets
+ptpengine:outbound_latency = 0
+
+; Compatibility option: In slave state, always respect UTC offset
+; announced by best master, even if the the
+; currrentUtcOffsetValid flag is announced FALSE
+ptpengine:always_respect_utc_offset = N
+
+; PTP announce message interval in master state (expressed as log 2
+; i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_announce_interval = 1
+
+; PTP announce receipt timeout announced in master state
+ptpengine:announce_timeout = 6
+
+; PTP announce receipt timeout grace period in slave state:
+; when announce receipt timeout occurs, disqualify current best GM,
+; then wait n times announce receipt timeout before resetting.
+; Allows for a seamless GM failover when standby GMs are slow to react.
+; When set to 0, this option is not used.
+ptpengine:announce_timeout_grace_period = 0
+
+; PTP sync message interval in master state (expressed as log 2
+; i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_sync_interval = 0
+
+; Initial delay request message interval for slave mode, before first
+; delay response is received (expressed as log 2 i.e. -1=0.5s, 0=1s,
+; 1=2s etc.)
+ptpengine:log_delayreq_interval_initial = 0
+
+; Minimum delay request message interval in master state, in slave
+; mode overrides the master interval, required in hybrid mode
+; (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_delayreq_interval = 0
+
+; Minimum peer delay request message interval in master state.
+; (expressed as log 2 i.e. -1=0.5s, 0=1s, 1=2s etc.)
+ptpengine:log_peer_delayreq_interval = 1
+
+; Maximum number of foreign masters (foreign master record size
+; allocated at startup)
+
+ptpengine:foreignrecord_capacity = 5
+
+; Specify Allan variance announced in master state
+ptpengine:ptp_allan_variance = 28768
+
+; Clock accuracy range announced in master state
+; Options: ACC_25NS ACC_100NS ACC_250NS ACC_1US ACC_2.5US ACC_10US
+; ACC_25US ACC_100US ACC_250US ACC_1MS ACC_2.5MS ACC_10MS ACC_25MS
+; ACC_100MS ACC_250MS ACC_1S ACC_10S ACC_10SPLUS ACC_UNKNOWN
+ptpengine:ptp_clock_accuracy = ACC_UNKNOWN
+
+; underlying time source UTC offset announced in master state
+ptpengine:utc_offset = 0
+
+; underlying time source UTC offset validity announced in master state
+ptpengine:utc_offset_valid = N
+
+; underlying time source time traceability announced in master state
+ptpengine:time_traceable = N
+
+; underlying time source frequency traceability announced in master state
+ptpengine:frequency_traceable = N
+
+; Time scale announced in master state (with ARB timescale, UTC
+; properties are ignored by slaves), when clock class 13 (application
+; specific), this value is ignored and ARB is used.
+; Options: PTP ARB
+ptpengine:ptp_timescale = ARB
+
+; Time source announced in master state
+; Options: ATOMIC_CLOCK GPS TERRESTRIAL_RADIO PTP NTP HAND_SET OTHER
+; INTERNAL_OSCILLATOR
+ptpengine:ptp_timesource = INTERNAL_OSCILLATOR
+
+; Clock class - announced in master state. Always 255 for slave-only mode.
+; Minimum, maximum and default values are controlled by presets.
+; If set to 13 (application specific time source), announced
+; time scale is always set to ARB. This setting controls the
+; states a PTP port can be in. If below 128, port will only
+; be in MASTER or PASSIVE states (master only). If above 127,
+; port will be in MASTER or SLAVE states.
+ptpengine:clock_class = 255
+
+; Priority 1 value announced in master state and used for Best Master
+; Clock selection
+ptpengine:priority1 = 128
+
+; Priority 2 value announced in master state and used for Best Master
+; Clock selection
+ptpengine:priority2 = 128
+
+; Specify unicast destination for unicast master mode (in unicast
+; slave mode overrides delay request destination)
+ptpengine:unicast_address =
+
+; Send explicit IGMP joins between servo resets
+ptpengine:igmp_refresh = Y
+
+; Multicast time to live for multicast PTP packets (ignored and set to
+; 1 for peer to peer messages)
+ptpengine:multicast_ttl = 64
+
+; DiffServ CodepPoint for packet prioritisation (decimal). When set to
+; zero, this option is not used.
+; 46 = Expedited Forwarding (0x2e)
+ptpengine:ip_dscp = 0
+
+; Enable outlier filter for the Delay Response component in slave state
+ptpengine:delay_outlier_filter_enable = N
+
+; Delay Response outlier filter action. If set to 'filter', outliers
+; are replaced with moving average
+; Options: discard filter
+ptpengine:delay_outlier_filter_action = filter
+
+; Number of samples in the Delay Response outlier filter buffer
+ptpengine:delay_outlier_filter_capacity = 20
+
+; Delay Response outlier filter threshold: multiplier for the Peirce's
+; maximum standard deviation. When set below 1.0, filter is tighter,
+; when set above 1.0, filter is looser than standard Peirce's test.
+ptpengine:delay_outlier_filter_threshold = 1.000000
+
+; Delay Response outlier weight: if an outlier is detected, this value
+; determines the amount of its deviation from mean that is used to
+; build the standard deviation statistics and influence further
+; outlier detection.
+; When set to 1.0, the outlier is used as is.
+;
+ptpengine:delay_outlier_weight = 1.000000
+
+; Enable outlier filter for the Sync component in slave state
+ptpengine:sync_outlier_filter_enable = N
+
+; Sync outlier filter action. If set to 'filter', outliers are
+; replaced with moving average
+; Options: discard filter
+ptpengine:sync_outlier_filter_action = filter
+
+; Number of samples in the Sync outlier filter buffer
+ptpengine:sync_outlier_filter_capacity = 20
+
+; Sync outlier filter threshold: multiplier for the Peirce's maximum
+; standard deviation. When set below 1.0, filter is tighter, when set
+; above 1.0, filter is looser than standard Peirce's test.
+ptpengine:sync_outlier_filter_threshold = 1.000000
+
+; Sync outlier weight: if an outlier is detected, this value
+; determines the amount of its deviation from mean that is used to
+; build the standard deviation statistics and influence further
+; outlier detection. When set to 1.0, the outlier is used as is.
+ptpengine:sync_outlier_weight = 1.000000
+
+; Delay between moving to slave state and enabling clock updates
+; expressed as number of statistics update periods (see
+; global:statistics_update_interval). This allows one-way delay to
+; stabilise before starting clock updates. Activated when going into
+; slave state and during GM failover in slave state.
+; 0 - not used.
+ptpengine:calibration_delay = 0
+
+; Enable panic mode: when offset from master is above 1 second, stop
+; updating the clock for a period of time and then step the clock if
+; offset remains above 1 second.
+ptpengine:panic_mode = N
+
+; Duration of the panic mode period (no clock updates) when offset
+; above 1 second detected
+ptpengine:panic_mode_duration = 2
+
+; Use JobID (PID) for UUID
+ptpengine:pid_as_clock_idendity = N
+
+; Fail over to NTP when PTP time sync not available - requires
+; ntpengine:enabled but does not require the rest of NTP configuration
+; - will warn instead of failing over if cannot control ntpd.
+ptpengine:ntp_failover = N
+
+; NTP failover timeout in seconds: time between PTP slave going into
+; LISTENING state, and failing over to NTP. 0 = fail over immediately.
+ptpengine:ntp_failover_timeout = 60
+
+; Prefer NTP time synchronisation when not controlling the clock (all
+; states, including slave when clock:no_adjust set)
+ptpengine:prefer_ntp = N
+
+; When entering panic mode, fail over to NTP (after the NTP failover
+; timeout period) - requires ntpengine:enabled but does not require
+; the rest of NTP configuration - will warn instead of failing over if
+; it cannot control ntpd.
+ptpengine:panic_mode_ntp = N
+
+; Do not adjust the clock
+clock:no_adjust = N
+
+; Do not reset the clock - only slew
+clock:no_reset = N
+
+; Observed drift handling method between servo restarts:
+; reset: set to zero (not recommended)
+; preserve: use kernel value,
+; file: load and save to drift file on startup/shutdown, use kernel
+; value inbetween.
+; To specify drift file, use the clock:drift_file setting.
+; Options: reset preserve file
+clock:drift_handling = preserve
+
+; Specify drift file
+clock:drift_file = /etc/ptpd2_kernelclock.drift
+
+; Maximum absolute frequency shift which can be applied to the clock servo
+; when slewing the clock. Expressed in parts per million (1 ppm = shift of
+; 1 us per second. Values above 512 will use the tick duration correction
+; to allow even faster slewing. Default maximum is 512 without using tick.
+clock:max_offset_ppm = 500
+
+; One-way delay filter stiffness
+servo:delayfilter_stiffness = 6
+
+; Clock servo PI controller proportional component gain (kP)
+servo:kp = 0.1
+
+; Clock servo PI controller integral component gain (kI)
+servo:ki = 0.001
+
+; Maximum accepted delayMS value in nanoseconds (Sync).
+; 0 = not checked.
+servo:max_delay = 0
+
+; Enable clock synchronisation servo stability detection
+; (based on standard deviation of the observed drift value)
+; - drift will be saved to drift file / cached when considered stable,
+; also clock stability status will be logged
+;
+servo:stability_detection = N
+
+; Specify the observed drift standard deviation threshold in parts per billion
+; (ppb) - if stanard deviation is within the threshold, servo is considered
+; stable.
+servo:stability_threshold = 5.000000
+
+; Specify for how many statistics update intervals the observed drift standard
+; deviation has to stay within threshold to be considered stable
+;
+servo:stability_period = 3
+
+; Specify after how many minutes without stabilisation servo is considered
+; unstable. Assists with logging servo stability information and
+; allows to preserve observed drift if servo cannot stabilise.
+;
+servo:stability_timeout = 10
+
+; Do not update one-way delay if slave to master delay (from Delay Response)
+; is greater than this value (nanoseconds). 0 = not used.
+servo:max_delay = 0
+
+; Do not reset the clock if offset from master is greater
+; than this value (nanoseconds). 0 = not used.
+servo:max_offset = 0
+
+; Send log messages to syslog. Disabling this
+; sends all messages to stdout (or speficied log file)
+global:use_syslog = N
+
+; Lock file location
+global:lock_file =
+
+; Use mode specific and interface specific lock files (overrides
+; global:lock_file)
+global:auto_lockfile = N
+
+; Lock file directory: used with automatic mode-specific lock files,
+; also used when no lock file is specified. When lock file
+; is specified, it's expected to be an absolute path.
+global:lock_directory = /var/run
+
+; Skip lock file checking and locking
+global:ignore_lock = N
+
+; File used to record data about sync packets. Setting this enables recording.
+global:quality_file =
+
+; Maximum sync packet record file size (in kB) - file will be
+; truncated if size exceeds the limit.
+; 0 - no limit.
+global:quality_file_max_size = 0
+
+; Enable log rotation of the sync packet record file up to n files.
+; 0 - do not rotate.
+global:quality_file_max_files = 0
+
+; Truncate the sync packet record file every time it is (re) opened -
+; on startup and SIGHUP
+global:quality_file_truncate = N
+
+; File used to log ptpd2 status information
+global:status_file = /var/run/ptpd2.status.log
+
+; Enable / disable writing status information to file
+global:log_status = Y
+
+; Status file update interval in seconds
+;
+global:status_update_interval = 1
+
+; Specify log file path (event log). Setting this enables logging to file.
+global:log_file = /var/run/ptpd2.event.log
+
+; Maximum log file size (in kB) - log file will be truncated if size
+; exceeds the limit.
+; 0 - no limit.
+global:log_file_max_size = 0
+
+; Enable log rotation of the sync packet record file up to n files.
+; 0 - do not rotate
+global:log_file_max_files = 0
+
+; Truncate the log file every time it is (re) opened - on startup and SIGHUP
+global:log_file_truncate = N
+
+; Specify log level (only messages of the specified priority or higer
+; will be logged).
+; The minimal level is LOG_ERR. LOG_ALL enables debug output if compiled with
+; RUNTIME_DEBUG
+; Options: LOG_ERR LOG_WARNING LOG_NOTICE LOG_INFO LOG_ALL
+global:log_level = LOG_ALL
+
+; Specify statistics log file path. Setting this enables logging of
+; statistics but can be overriden with global:log_statistics
+global:statistics_file = /var/run/ptpd2.stats.log
+
+; Log timing statistics every n seconds for Sync and Delay Response
+; messages (0 - log all)
+global:statistics_log_interval = 0
+
+; Maximum statistics log file size (in kB) - log file will be
+; truncated if size exceeds the limit.
+; 0 - no limit.
+global:statistics_file_max_size = 0
+
+; Enable log rotation of the statistics file up to n files. 0 - do not rotate
+;
+global:statistics_file_max_files = 0
+
+; Truncate the statistics file every time it is (re) opened - on
+; startup and SIGHUP
+global:statistics_file_truncate = N
+
+; Dump the contents of every PTP packet
+global:dump_packets = N
+
+; Run in foreground with statistics and all messages logged to stdout.
+; Overrides log file and statistics file settings and disables syslog.
+;
+global:verbose_foreground = N
+
+; Run in foreground
+global:foreground = N
+
+; Log timing statistics for every PTP packet received
+global:log_statistics = Y
+
+; Linux only: bind ptpd2 process to a selected CPU core number.
+; 0 = first CPU core, etc. -1 = do not bind to a single core.
+global:cpuaffinity_cpucore = -1
+
+; Clock synchronisation statistics update interval in seconds
+;
+global:statistics_update_interval = 5
+
+; Enable NTPd integration
+ntpengine:enabled = N
+
+; Enable control over local NTPd daemon
+ntpengine:control_enabled = N
+
+; NTP control check interval in seconds
+;
+ntpengine:check_interval = 15
+
+; NTP key number - must be configured as a trusted control key in ntp.conf,
+; and must be non-zero for the ntpengine:control_enabled setting to take effect.
+;
+ntpengine:key_id = 0
+
+; NTP key (plain text, max. 20 characters) - must match the key
+; configured in ntpd's keys file, and must be non-zero for the
+; ntpengine:control_enabled setting to take effect.
+ntpengine:key =
+
+; ========= newline required in the end ==========
+
diff --git a/rtemsbsd/ptpd/test/testing.org b/rtemsbsd/ptpd/test/testing.org
new file mode 100644
index 00000000..f5fc928a
--- /dev/null
+++ b/rtemsbsd/ptpd/test/testing.org
@@ -0,0 +1,22 @@
+Unified Test Plan for PTPd Versions
+
+NOTE: This file is in Emacs org mode, do not adjust the markup unless
+absolutely necessary.
+
+* Client
+
+The client needs to be tested in the following matrix. For each test
+there is a relevant config file listed.
+
+| | E2E | P2P |
+| Socket | client-e2e-socket.conf | |
+| PCAP | client-e2e-pcap.conf | |
+
+Note that for each configuration file you MUST update the
+ptpengine:interface line at the beginning of the file. No other
+changes should be necessary.
+
+* Server
+
+The server is tested with a series of its own clients. Not optimal
+
diff --git a/rtemsbsd/ptpd/tools/README.md b/rtemsbsd/ptpd/tools/README.md
new file mode 100644
index 00000000..e883a5ec
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/README.md
@@ -0,0 +1,80 @@
+# Tools for PTP (and NTP) #
+
+This directory contains scripts and libraries for interpreting log
+files from both PTP and NTP. The scripts and libraries are mainly
+written in R <https://www.r-project.org>, which handles comma
+separated value (CSV) files quite well and is far better at graphing
+than other choices.
+
+## Installation ##
+
+If you do not want to rebuild the documentation, which we already
+ship, then you only need to use R to install the packages in the
+following way:
+
+`prompt> R CMD INSTALL ptplib`
+
+`prompt> R CMD INSTALL ntplib`
+
+You may need to have root or sudo like powers to install the package.
+
+## Usage ##
+
+The individual library routines are documented in their respective
+manual pages (see ptplib/man and ntplib/man directories).
+
+```prompt> stats.R ptp.stats.file [output]```
+
+Open a pre-existing PTPd statistics file and output various stats
+about the data contained therein.
+
+````
+Measurements: 175229
+Offset
+min: -1.2248e-05 max: 5.7004e-05 median: -1.99e-06 mean: -1.220213e-06
+std dev: 4.617933e-06 variance: 2.132531e-11
+Delay
+min: 1.5461e-05 max: 1.9573e-05 median: 1.6904e-05 mean: 1.690247e-05
+std dev: 4.441999e-07 variance: 1.973135e-13
+Master -> Slave
+min: 2e-06 max: 0.000127 median: 1.5e-05 mean: 1.569502e-05
+std dev: 6.037791e-06 variance: 3.645492e-11
+Slave -> Master
+min: 3e-06 max: 7.3e-05 median: 1.8e-05 mean: 1.807442e-05
+std dev: 3.502559e-06 variance: 1.226792e-11
+```
+
+The number of measurements found in the file is followed by various
+statistics measured in seconds. The slave's offset from the master,
+the measured network delay, and the network delay in both directions
+are displayed.
+
+Also generate a full graph of all the points in the file for each
+measurement class. The output file name can be specified on the
+command line, otherwise the script uses the basename of the data file
+to generate one for you.
+
+
+```prompt> offset.R ptp.stats.file [output]```
+
+```
+Data file: test-ptpstats
+Measurements: 162970
+Offset
+min: -1.6824e-05 max: 4.1732e-05 median: 2.918e-06 mean: 3.192941e-06
+std dev: 6.529671e-06 variance: 4.26366e-11
+Outside Boundary: 0 Percentage: 0
+```
+
+For the data file mentioned show only the offset statistics. Also
+generate a PNG of the full graph of all the points in the file. The
+output file name can be specified on the command line, otherwise the
+script uses the basename of the data file to generate one for you.
+
+`prompt> ntpoffset.R loopstats [output]`
+
+Generate a graph of NTP offset from a loopstats file generated by
+ntpd. The output file name can be specified on the command line,
+otherwise the script uses the basename of the data file to generate
+one for you
+
diff --git a/rtemsbsd/ptpd/tools/cleanup.sed b/rtemsbsd/ptpd/tools/cleanup.sed
new file mode 100644
index 00000000..7a48a6d5
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/cleanup.sed
@@ -0,0 +1,11 @@
+# Clean up SolarFlare style PTP log output by removing the labels
+# so that we get a clean CSV file. The resulting file is in a
+# different column order to the one expected by the PTP tools but that
+# is handled elsewhere.
+s/offset: //g
+s/freq-adj: //g
+s/in-sync: //g
+s/one-way-delay: //g
+s/i-term: //g
+s/grandmaster: //g
+s/ \[.*\]//g
diff --git a/rtemsbsd/ptpd/tools/cleanup_sf2.sed b/rtemsbsd/ptpd/tools/cleanup_sf2.sed
new file mode 100644
index 00000000..ca22800f
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/cleanup_sf2.sed
@@ -0,0 +1,9 @@
+# Clean up SolarFlare style PTP log output by removing the labels
+# so that we get a clean CSV file. The resulting file is in a
+# different column order to the one expected by the PTP tools but that
+# is handled elsewhere.
+s/offset: //g
+s/freq-adj: //g
+s/in-sync: //g
+s/one-way-delay: //g
+s/ \[.*\]//g
diff --git a/rtemsbsd/ptpd/tools/compare.R b/rtemsbsd/ptpd/tools/compare.R
new file mode 100755
index 00000000..23775123
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/compare.R
@@ -0,0 +1,62 @@
+#!/usr/bin/env Rscript
+# Copyright (c) 2013, Neville-Neil Consulting
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# Neither the name of Neville-Neil Consulting nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Author: George V. Neville-Neil
+#
+# Description: A tool to graph and compare the offset vs. the master to slave
+# delay vs. the slave to mater delay vs. the calculated delay from a
+# PTPd output file generated with the -D flag.
+#
+# Usage: compare.R input_file input_file2 [value] [output_file]
+#
+# Value is one of: offset [default value], delay, master.to.slave, slave.to.master
+#
+
+source("ptplib.R")
+
+argv <- commandArgs(TRUE)
+
+file1 = argv[1]
+file2 = argv[2]
+value = argv[3]
+outputFile = argv[4]
+
+if (is.na(outputFile))
+ outputFile = paste(basename(file2), ".png", sep="")
+
+logA = ptpLogRead(file1)
+logB = ptpLogRead(file2)
+
+if (is.na(value)) {
+ ptpCompare(logA, logB, output=outputFile)
+} else {
+ ptpCompare(logA, logB, value, outputFile)
+}
diff --git a/rtemsbsd/ptpd/tools/filter_response.m b/rtemsbsd/ptpd/tools/filter_response.m
new file mode 100755
index 00000000..511afc01
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/filter_response.m
@@ -0,0 +1,29 @@
+#!/usr/bin/octave -qf
+
+# the IIR filter
+s = 2^4;
+a = [ s -(s-1) ];
+b = [ 1/2 1/2 ];
+
+[h w] = freqz(b, a, 100000);
+
+subplot(211);
+plot(w/pi,abs(h),";;");
+axis();
+#plot(w/pi,20*log(abs(h)));
+#axis([0 1 -120 0]);
+#semilogx(w/pi,20*log(abs(h)),";;");
+#axis([1e-3 1 -120 0]);
+ylabel("gain");
+
+subplot(212);
+plot(w/pi,unwrap(angle(h)),";;");
+axis([0 1 -pi 0]);
+#semilogx(w/pi,unwrap(angle(h)),";;");
+#axis([1e-3 1 -pi 0]);
+ylabel("phase (rad)");
+xlabel("frequency");
+replot;
+
+pause;
+
diff --git a/rtemsbsd/ptpd/tools/graph.R b/rtemsbsd/ptpd/tools/graph.R
new file mode 100755
index 00000000..b4ab4f0a
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/graph.R
@@ -0,0 +1,52 @@
+#!/usr/bin/env Rscript
+# Copyright (c) 2016, Neville-Neil Consulting
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# Neither the name of Neville-Neil Consulting nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Author: George V. Neville-Neil
+#
+# Description: A tool to graph the offset vs. the master to slave
+# delay vs. the slave to mater delay vs. the calculated delay from a
+# PTPd output file generated with the -D flag.
+#
+# Usage: graph.R input_file [output_file]
+#
+
+require(zoo)
+require(ptplib)
+
+argv <- commandArgs(TRUE)
+
+file = argv[1]
+output = argv[2]
+if (is.na(output))
+ output = paste(basename(file), ".png", sep="")
+
+logA = ptpLogRead(file)
+ptpGraph(logA$ts, output=output)
diff --git a/rtemsbsd/ptpd/tools/ntplib/DESCRIPTION b/rtemsbsd/ptpd/tools/ntplib/DESCRIPTION
new file mode 100644
index 00000000..e717681b
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ntplib/DESCRIPTION
@@ -0,0 +1,13 @@
+Package: ntplib
+Type: Package
+Title: Routines for working with NTP log files
+Version: 1.0
+Date: 2015-02-21
+Author: George Neville-Neil
+Maintainer: George Neville-Nei <gnn at neville-neil.com>
+Description: A set of routines for reading NTP loop and peer files into data
+ frames, graphing the frames in various ways and based on different columns, as
+ well as showing generalized statistics about the performance of NTP based on the
+ log data.
+License: BSD_2_clause
+RoxygenNote: 5.0.1
diff --git a/rtemsbsd/ptpd/tools/ntplib/NAMESPACE b/rtemsbsd/ptpd/tools/ntplib/NAMESPACE
new file mode 100644
index 00000000..7d3478b1
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ntplib/NAMESPACE
@@ -0,0 +1,7 @@
+# Generated by roxygen2: do not edit by hand
+
+export(ntpGraph)
+export(ntpHistogram)
+export(ntpLoopRead)
+export(ntpStats)
+export(ntpPeerRead)
diff --git a/rtemsbsd/ptpd/tools/ntplib/R/ntplib.R b/rtemsbsd/ptpd/tools/ntplib/R/ntplib.R
new file mode 100644
index 00000000..1aad0b68
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ntplib/R/ntplib.R
@@ -0,0 +1,184 @@
+# Copyright (c) 2014, Neville-Neil Consulting
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# Neither the name of Neville-Neil Consulting nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Author: George V. Neville-Neil
+#
+# Description: A set of libraries to process NTP peer and loopstat
+# files. Used by various command line tools in this directory.
+#
+
+require(zoo) # Time series objects
+
+# Functions for working with NTP log output
+
+#' Read an NTPd loop file
+#'
+#' @param file A properly formatted NTP loop file generated by the daemon
+#'
+#' @return The raw data frame, merged timeseries (offset, delay, m->s, s->)
+#' offset time series, delay time series,
+#' master.to.slave time series, and slave.to.master time series
+#'
+#' @export
+ntpLoopRead <- function(file) {
+
+ # Attempt to read log files from version 2.3 first.
+ data = read.table(file, fill=TRUE, sep=" ",
+ col.names=c("day", "second", "offset", "drift", "error", "stability", "interval"),
+ blank.lines.skip=TRUE, header=FALSE, skip=100)
+
+ # Remove any duplicates
+ data = data[!duplicated(data$second), ]
+
+ offset = zoo(data$offset, data$second)
+ drift = zoo(data$drift, data$second)
+ stability = zoo(data$stability, data$second)
+ ts = merge(offset, drift, stability)
+
+ return (list(data=data, ts=ts, offset=offset, drift=drift,
+ stability=stability))
+}
+
+#' Read an NTPd peer file
+#'
+#' @param file A properly formatted NTP peer file generated by the daemon
+#'
+#' @return The raw data frame, merged timeseries (offset, drift, error)
+#'
+#' @export
+ntpPeerRead <- function(file) {
+
+ # Attempt to read log files from version 2.3 first.
+ data = read.table(file, fill=TRUE, sep=" ",
+ col.names=c("day", "second", "address", "status", "offset", "delay", "dispersion", "skew"), colClasses=c("NULL", NA, NA, NA, NA, NA, NA, NA),
+ blank.lines.skip=TRUE, header=FALSE, skip=100)
+
+ sources <- list()
+ addrs = unique(data$address)
+ for (src in addrs) {
+ sources[[toString(src)]] <- data[data$address == src,]
+ }
+
+ cat("Returning", length(sources), "sources, index as any of: ")
+ for (src in addrs) {
+ cat(toString(src), " ")
+ }
+ cat("\n")
+ return (sources)
+
+}
+
+#' Graphs a dataframe returned by a call to ntpPeer/LogRead
+#'
+#' @param logframe A data frame returned by ptpLogRead
+#' @param value Graph only a specific value rather than the four major ones.
+#' @param output An optional file name to place a PNG of the graph into
+#'
+#' @export
+ntpGraph <- function(logframe, value="offset", start, end, output) {
+
+ if (!missing(output))
+ png(filename=output, height=960, width=1280, bg="white")
+
+ if (!missing(start))
+ logframe = logframe[index(logframe) > as.POSIXct(start)]
+ if (!missing(end))
+ logframe = logframe[index(logframe) < as.POSIXct(end)]
+ if (!missing(start) && !missing(end))
+ logframe = logframe[index(logframe) > as.POSIXct(start) &
+ index(logframe) < as.POSIXct(end)]
+
+ plot(logframe[[value]], type="p", cex=.1, main="NTP Log Graph", xlab="Time", mar=c(1, 5.1, 1, 5.1))
+ if (!missing(output))
+ dev.off()
+ return(NULL)
+
+}
+
+ntpGraphAll <- function(frames, value="offset") {
+
+ plot(frames[[1]]["offset"], type="p", cex=.1, main="NTP Log Graph", xlab="Time", mar=c(1, 5.1, 1, 5.1))
+
+}
+
+
+#' Draw a simple histogram of some one variable in our log
+#'
+#' @param dataframe returned by ntpLoopRead()
+#' @param start start time in strftime format
+#' @param end end time in strftime format
+#' @param output optional file in which to store the graph
+#'
+#' @export
+ntpHistogram <- function(log, start, end, output) {
+ if (!missing(output))
+ png(filename=output, height=960, width=1280, bg="white")
+ if (!missing(start))
+ log = log[index(log) > as.POSIXct(start)]
+ if (!missing(end))
+ log = log[index(log) < as.POSIXct(end)]
+ if (!missing(start) && !missing(end))
+ log = log[index(log) > as.POSIXct(start) &
+ index(log) < as.POSIXct(end)]
+ left = min(log)
+ right = max(log)
+ height = max(length(log)) * 0.69
+ hist (log, xlim=c(left, right), ylim=c(0,height), breaks=20,
+ col=rgb(1.0, 0, 0, 0.5))
+ if (!missing(output))
+ dev.off()
+}
+
+#' Textual output of the loop or peer file's statistics
+#'
+#' @param dataframe returned by ntpLoopRead()
+#' @param value the value to look at (offset, skew, etc.)
+#' @param boundary measure past which we are out of SLA in ns
+#'
+#' @export
+ntpStats <- function(log, value = "offset", boundary = 1000) {
+
+ if (!(value %in% colnames(log))) {
+ cat("Invalid value, choose one of:", colnames(log))
+ return (NULL)
+ }
+
+ cat("Measurements: ", length(log[value]))
+ cat("\nOffset",
+ "\nmin:", min(log[[value]], na.rm=TRUE),
+ " max: ", max(log[[value]], na.rm=TRUE),
+ " median: ", median(log[[value]], na.rm=TRUE),
+ " mean: ", mean(log[[value]], na.rm=TRUE),
+ "\nstd dev: ", sd(log[[value]], na.rm=TRUE),
+ " variance: ", var(log[[value]], na.rm=TRUE), "\n")
+ cat("\nOutside Boundary: ", sum(log[[value]] > boundary) + sum(log[[value]] < -boundary),
+ "Percentage: ", (sum(log[[value]] > boundary) + sum(log[[value]] < -boundary)) / length(log[[value]]), "\n")
+}
+
diff --git a/rtemsbsd/ptpd/tools/ntplib/man/ntpGraph.Rd b/rtemsbsd/ptpd/tools/ntplib/man/ntpGraph.Rd
new file mode 100644
index 00000000..c632b5bd
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ntplib/man/ntpGraph.Rd
@@ -0,0 +1,19 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ntplib.R
+\name{ntpGraph}
+\alias{ntpGraph}
+\title{Graphs a dataframe returned by a call to ntpPeer/LogRead}
+\usage{
+ntpGraph(logframe, value, start, end, output)
+}
+\arguments{
+\item{logframe}{A data frame returned by ptpLogRead}
+
+\item{value}{Graph only a specific value rather than the four major ones.}
+
+\item{output}{An optional file name to place a PNG of the graph into}
+}
+\description{
+Graphs a dataframe returned by a call to ntpPeer/LogRead
+}
+
diff --git a/rtemsbsd/ptpd/tools/ntplib/man/ntpHistogram.Rd b/rtemsbsd/ptpd/tools/ntplib/man/ntpHistogram.Rd
new file mode 100644
index 00000000..563a3f0e
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ntplib/man/ntpHistogram.Rd
@@ -0,0 +1,21 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ntplib.R
+\name{ntpHistogram}
+\alias{ntpHistogram}
+\title{Draw a simple histogram of some one variable in our log}
+\usage{
+ntpHistogram(log, start, end, output)
+}
+\arguments{
+\item{start}{start time in strftime format}
+
+\item{end}{end time in strftime format}
+
+\item{output}{optional file in which to store the graph}
+
+\item{dataframe}{returned by ntpLoopRead()}
+}
+\description{
+Draw a simple histogram of some one variable in our log
+}
+
diff --git a/rtemsbsd/ptpd/tools/ntplib/man/ntpLoopRead.Rd b/rtemsbsd/ptpd/tools/ntplib/man/ntpLoopRead.Rd
new file mode 100644
index 00000000..d08428a7
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ntplib/man/ntpLoopRead.Rd
@@ -0,0 +1,20 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ntplib.R
+\name{ntpLoopRead}
+\alias{ntpLoopRead}
+\title{Read an NTPd loop file}
+\usage{
+ntpLoopRead(file)
+}
+\arguments{
+\item{file}{A properly formatted NTP loop file generated by the daemon}
+}
+\value{
+The raw data frame, merged timeseries (offset, delay, m->s, s->)
+ offset time series, delay time series,
+ master.to.slave time series, and slave.to.master time series
+}
+\description{
+Read an NTPd loop file
+}
+
diff --git a/rtemsbsd/ptpd/tools/ntplib/man/ntpLoopStats.Rd b/rtemsbsd/ptpd/tools/ntplib/man/ntpLoopStats.Rd
new file mode 100644
index 00000000..27a8616f
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ntplib/man/ntpLoopStats.Rd
@@ -0,0 +1,17 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ntplib.R
+\name{ntpLoopStats}
+\alias{ntpLoopStats}
+\title{Textual output of the loop file's offset statistics}
+\usage{
+ntpLoopStats(log, boundary = 1000)
+}
+\arguments{
+\item{boundary}{measure past which we are out of SLA in ns}
+
+\item{dataframe}{returned by ntpLoopRead()}
+}
+\description{
+Textual output of the loop file's offset statistics
+}
+
diff --git a/rtemsbsd/ptpd/tools/ntplib/man/ntpPeerRead.Rd b/rtemsbsd/ptpd/tools/ntplib/man/ntpPeerRead.Rd
new file mode 100644
index 00000000..50d4b2ae
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ntplib/man/ntpPeerRead.Rd
@@ -0,0 +1,18 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ntplib.R
+\name{ntpPeerRead}
+\alias{ntpPeerRead}
+\title{Read an NTPd peer file}
+\usage{
+ntpPeerRead(file)
+}
+\arguments{
+\item{file}{A properly formatted NTP peer file generated by the daemon}
+}
+\value{
+The raw data frame, merged timeseries (offset, drift, error)
+}
+\description{
+Read an NTPd peer file
+}
+
diff --git a/rtemsbsd/ptpd/tools/ntpoffset.R b/rtemsbsd/ptpd/tools/ntpoffset.R
new file mode 100755
index 00000000..83ca4700
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ntpoffset.R
@@ -0,0 +1,53 @@
+#!/usr/bin/env Rscript
+# Copyright (c) 2016, Neville-Neil Consulting
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# Neither the name of Neville-Neil Consulting nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Author: George V. Neville-Neil
+#
+# Description: A tool to graph the offset from a
+# NTP loop file.
+#
+# Usage: ntpoffset.R input_file [output_file]
+#
+
+require(zoo)
+require(ntplib)
+
+argv <- commandArgs(TRUE)
+
+file = argv[1]
+output = argv[2]
+if (is.na(output))
+ output = paste(basename(file), ".png", sep="")
+
+cat("Data file: ", basename(file), "\n")
+logA = ntpLoopRead(file)
+ntpGraph(logA$data, output=output)
+ntpStats(logA$data)
diff --git a/rtemsbsd/ptpd/tools/offset.R b/rtemsbsd/ptpd/tools/offset.R
new file mode 100755
index 00000000..6085f88f
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/offset.R
@@ -0,0 +1,53 @@
+#!/usr/bin/env Rscript
+# Copyright (c) 2016, Neville-Neil Consulting
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# Neither the name of Neville-Neil Consulting nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Author: George V. Neville-Neil
+#
+# Description: A tool to graph the offset from a
+# PTPd output file.
+#
+# Usage: offset.R input_file [output_file]
+#
+
+require(zoo)
+require(ptplib)
+
+argv <- commandArgs(TRUE)
+
+file = argv[1]
+output = argv[2]
+if (is.na(output))
+ output = paste(basename(file), ".png", sep="")
+
+cat("Data file: ", basename(file), "\n")
+logA = ptpLogRead(file)
+ptpOffsetStats(logA)
+ptpGraph(logA$offset, output=output)
diff --git a/rtemsbsd/ptpd/tools/offset_stats.m b/rtemsbsd/ptpd/tools/offset_stats.m
new file mode 100755
index 00000000..18f17507
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/offset_stats.m
@@ -0,0 +1,102 @@
+#!/usr/bin/octave -qf
+
+printf("start\n");
+
+load t;
+load tr;
+
+# time window to analyze
+t_beg = 1;
+t_fin = length(t);
+
+# parameters for synthetic reference time
+#start_time = round(t(t_beg));
+#sample_interval = 1;
+
+# parameters for histogram
+h_beg = -15e-6;
+h_fin = 15e-6;
+h_sz = 1e-6;
+
+# parameters for allan variance
+# tune for your computational power
+tau_beg = 1;
+tau_fin = length(t)/10;
+tau_maxsamps = 100;
+
+# ----------
+
+printf("data loaded\n");
+
+# create synthetic reference time
+#tr = t;
+#tr(t_beg) = start_time;
+#for k = (t_beg+1):t_fin
+#
+# tr(k) = tr(k-1) + sample_interval;
+#
+#endfor
+
+# time offset computation
+o = t - tr;
+
+o_min = min(o(t_beg:t_fin));
+o_max = max(o(t_beg:t_fin));
+o_mean = mean(o(t_beg:t_fin));
+
+# relative tick rate computation
+r = t;
+for k = (t_beg+1):t_fin
+
+ r(k) = o(k) - o(k-1);
+
+endfor
+
+r_min = min(r((t_beg+1):t_fin));
+r_max = max(r((t_beg+1):t_fin));
+r_mean = mean(r((t_beg+1):t_fin));
+
+figure;
+h_bins = h_beg:h_sz:h_fin;
+hist( o(t_beg:t_fin), h_bins, 1);
+
+printf("histogram plotted\n");
+
+figure;
+subplot(211);
+plot( t_beg:t_fin, o(t_beg:t_fin), "r;time offset;",
+ [t_beg t_fin], [o_min o_min], "g;;",
+ [t_beg t_fin], [o_max o_max], "g;;",
+ [t_beg t_fin], [o_mean o_mean], "b;;");
+subplot(212);
+plot( (t_beg+1):t_fin, r((t_beg+1):t_fin), "r;relative tick rate;",
+ [(t_beg+1) t_fin], [r_min r_min], "g;;",
+ [(t_beg+1) t_fin], [r_max r_max], "g;;",
+ [(t_beg+1) t_fin], [r_mean r_mean], "b;;")
+replot;
+
+printf("offset plotted\n");
+
+# the allan vaiance computation from the IEEE 1588 spec
+a = t;
+for tau = tau_beg:tau_fin
+
+ beg = t_beg;
+ if (t_fin-2*tau) > tau_maxsamps
+ fin = tau_maxsamps;
+ else
+ fin = (t_fin-2*tau);
+ end
+
+ a(tau) = sum((t(beg:fin) - 2*t((beg+tau):(fin+tau)) + t((beg+2*tau):(fin+2*tau))).^2);
+ a(tau) /= 2*(fin-beg)*(tau^2);
+
+endfor
+
+figure;
+loglog( tau_beg:tau_fin, a(tau_beg:tau_fin), ";allan variance;");
+replot;
+
+printf("variance plotted\n");
+input("done\n");
+
diff --git a/rtemsbsd/ptpd/tools/ptplib/DESCRIPTION b/rtemsbsd/ptpd/tools/ptplib/DESCRIPTION
new file mode 100644
index 00000000..9c9efb4f
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ptplib/DESCRIPTION
@@ -0,0 +1,13 @@
+Package: ptplib
+Type: Package
+Title: Routines for working with PTPd log files (ptpd.sf.net)
+Version: 1.0
+Date: 2013-12-04
+Author: George Neville-Neil
+Maintainer: George Neville-Nei <gnn at neville-neil.com>
+Description: A set of routines for reading PTPd log files into data frames,
+ graphing the frames in various ways and based on different columns, as well as
+ showing generalized statistics about the performance of the PTPd client based on
+ the log data.
+License: BSD_2_clause
+RoxygenNote: 5.0.1
diff --git a/rtemsbsd/ptpd/tools/ptplib/NAMESPACE b/rtemsbsd/ptpd/tools/ptplib/NAMESPACE
new file mode 100644
index 00000000..f552e34a
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ptplib/NAMESPACE
@@ -0,0 +1,12 @@
+# Generated by roxygen2: do not edit by hand
+
+export(ptpCompare)
+export(ptpGraph)
+export(ptpHistogram)
+export(ptpHistogramCompare)
+export(ptpLogRead)
+export(ptpOffsetStats)
+export(ptpQualityGraph)
+export(ptpQualityGraphCompare)
+export(ptpQualityStats)
+export(ptpStats)
diff --git a/rtemsbsd/ptpd/tools/ptplib/R/ptplib.R b/rtemsbsd/ptpd/tools/ptplib/R/ptplib.R
new file mode 100644
index 00000000..6ebc7f64
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ptplib/R/ptplib.R
@@ -0,0 +1,397 @@
+# Copyright (c) 2016, Neville-Neil Consulting
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# Neither the name of Neville-Neil Consulting nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+#' @author George V. Neville-Neil, \email{gnn at neville-neil.com}
+#
+# Description: A set of libraries to process PTP log files. Used by
+# various command line tools in this directory.
+#
+
+require(zoo) # Time series objects
+
+# Functions for working with PTP log output
+
+#' Read a PTP log file
+#'
+#' @param file A properly formatted PTP log file generated by the daemon
+#'
+#' @return The raw data frame, merged timeseries (offset, delay, m->s, s->)
+#' offset time series, delay time series,
+#' master.to.slave time series, and slave.to.master time series
+#'
+#' @export
+ptpLogRead <- function(file) {
+
+ # Attempt to read log files from version 2.3 first.
+ try( {
+ data = read.table(file, fill=TRUE, sep=",",
+ col.names=c("timestamp", "state", "clockID", "delay", "offset", "slave.to.master", "master.to.slave", "drift", "packet", "sequence", "one.way.delay.mean", "one.way.delay.dev", "offset.mean", "offset.dev", "drift.mean", "drift.dev", "delay.ms", "delay.sm"),
+ colClasses=c("timestamp"="POSIXct"),
+ blank.lines.skip=TRUE, header=FALSE, skip=100)
+ } ,silent=TRUE)
+
+ if (is.na(data$packet[1])) {
+ print(paste("File is not new style, trying old style.", file))
+ try( {
+ data = read.table(file, fill=TRUE, sep=",",
+ col.names=c("timestamp", "state", "clockID", "delay", "offset", "master.to.slave", "slave.to.master", "steering", "drift", "packet"),
+ colClasses=c("timestamp"="POSIXct"),
+ blank.lines.skip=TRUE, header=FALSE, skip=100)
+ }, silent=TRUE)
+
+ }
+
+ if (is.na(data$packet[1])) {
+ print(paste("File is not old style, trying SolarFlare style.", file))
+ tryCatch( {
+ data = read.table(file, fill=FALSE, sep=",",
+ col.names=c("timestamp", "offset", "freq-adj", "in-sync", "delay", "clockID"),
+ colClasses=c("timestamp"="POSIXct"),
+ blank.lines.skip=TRUE, header=FALSE, skip=100)
+ }, finally = { # Nothing to print here
+
+ } )
+ }
+
+ offset = zoo(data$offset, data$timestamp)
+ delay = zoo(data$delay, data$timestamp)
+ master.to.slave = zoo(data$master.to.slave, data$timestamp)
+ slave.to.master = zoo(data$slave.to.master, data$timestamp)
+ ts = merge(offset, delay, master.to.slave, slave.to.master)
+
+ return (list(data=data, ts=ts, offset=offset, delay=delay,
+ master.to.slave=master.to.slave,
+ slave.to.master=slave.to.master))
+}
+
+#' Graphs a dataframe returned by a call to ptplog
+#'
+#' @param logframe A data frame returned by ptpLogRead
+#' @param value Graph only a specific value rather than the four major ones.
+#' @param output An optional file name to place a PNG of the graph into
+#'
+#' @export
+ptpGraph <- function(logframe, value, start, end, output) {
+ if (!missing(output))
+ png(filename=output, height=960, width=1280, bg="white")
+ if (class(logframe) == "zoo") {
+ if (!missing(start))
+ logframe = logframe[index(logframe) > as.POSIXct(start)]
+ if (!missing(end))
+ logframe = logframe[index(logframe) < as.POSIXct(end)]
+ if (!missing(start) && !missing(end))
+ logframe = logframe[index(logframe) > as.POSIXct(start) &
+ index(logframe) < as.POSIXct(end)]
+ plot(logframe, type="p", cex=.1, main="PTP Log Graph", xlab="Time", mar=c(1, 5.1, 1, 5.1))
+ if (!missing(output))
+ dev.off()
+ return(NULL)
+ }
+ if (missing(value)) {
+ ymin = min(min(logframe$offset, na.rm=TRUE), min(logframe$delay, na.rm=TRUE),
+ min(logframe$master.to.slave, na.rm=TRUE),
+ min(logframe$slave.to.master, na.rm=TRUE))
+ ymax = max(max(logframe$offset, na.rm=TRUE), max(logframe$delay, na.rm=TRUE),
+ max(logframe$master.to.slave, na.rm=TRUE),
+ max(logframe$slave.to.master, na.rm=TRUE))
+ plot(logframe$delay, y=NULL, xaxt = "n" ,type="n", ylim=range(ymin, ymax),
+ main="PTP Results", xlab="Time", ylab="Nanoseconds")
+ legend("topright",
+ c("Delay", "Offset", "M->S", "S->M"), col=c("orange", "blue", "red", "green"), pch=21:24)
+ points(logframe$offset, y=NULL, cex=.1, col="blue", pch=21)
+ points(logframe$master.to.slave, y=NULL, cex=.1, col="red", pch=22)
+ points(logframe$slave.to.master, y=NULL, cex=.1, col="green", pch=23)
+ points(logframe$delay, y=NULL, cex=.1, col="orange", pch=24)
+ } else {
+ ymin = min(logframe[[value]], na.rm=TRUE)
+ ymax = max(logframe[[value]], na.rm=TRUE)
+ plot(logframe[[value]], y=NULL, xaxt = "n" ,type="n",
+ ylim=range(ymin, ymax),
+ main="PTP Results", xlab="Time", ylab="Nanoseconds")
+ points(logframe[[value]], y=NULL, cex=.1, col="red", pch=21)
+ }
+ if (!missing(output))
+ dev.off()
+}
+
+#' Draw a simple histogram of some one variable in our log
+#'
+#' @examples ptpHistogram(foo$log$offset)
+#'
+#' @export
+ptpHistogram <- function(log, start, end, output) {
+ if (!missing(output))
+ png(filename=output, height=960, width=1280, bg="white")
+ if (!missing(start))
+ log = log[index(log) > as.POSIXct(start)]
+ if (!missing(end))
+ log = log[index(log) < as.POSIXct(end)]
+ if (!missing(start) && !missing(end))
+ log = log[index(log) > as.POSIXct(start) &
+ index(log) < as.POSIXct(end)]
+ left = min(log)
+ right = max(log)
+ height = max(length(log)) * 0.69
+ hist (log, xlim=c(left, right), ylim=c(0,height), breaks=20,
+ col=rgb(1.0, 0, 0, 0.5))
+ if (!missing(output))
+ dev.off()
+}
+
+#' Compare two data sets to each other using a histogram
+#'
+#' examples ptpHistogramCompare(foo$log$offset, bar$log$offset)
+#'
+#' @export
+ptpHistogramCompare <- function(loga, logb, starta, enda, startb, endb,
+ output) {
+ if (!missing(output))
+ png(filename=output, height=960, width=1280, bg="white")
+ if (!missing(starta) && !missing(startb)) {
+ loga = loga[index(loga) > as.POSIXct(starta)]
+ logb = logb[index(logb) > as.POSIXct(startb)]
+ }
+ if (!missing(enda) && !missing(endb)) {
+ loga = loga[index(loga) < as.POSIXct(enda)]
+ logb = logb[index(logb) < as.POSIXct(endb)]
+ }
+ if (!missing(starta) && !missing(enda) &&
+ !missing(startb) && !missing(endb)) {
+ loga = loga[index(loga) > as.POSIXct(starta) &
+ index(loga) < as.POSIXct(enda)]
+ logb = logb[index(logb) > as.POSIXct(startb) &
+ index(logb) < as.POSIXct(endb)]
+ }
+ left = min(min(loga), min(logb))
+ right = max(max(loga), max(logb))
+ height = max(length(loga), length(logb)) * 0.69
+ hist (loga, xlim=c(left, right), ylim=c(0,height), breaks=20,
+ col=rgb(1.0, 0, 0, 0.25))
+ hist (logb, xlim=c(left, right), ylim=c(0,height), breaks=20,
+ col=rgb(0, 0, 1.0, 0.25), add=TRUE)
+ legend("topright",
+ c("A", "B"),
+ col=c(rgb(1.0, 0, 0, 1), rgb(0, 0, 1.0, 1)), pch=21:22)
+ if (!missing(output))
+ dev.off()
+}
+
+#' Compare two log files on a graph
+#'
+#' @param loga data frame read using ptplogRead
+#' @param logb data frame read using ptplogRead
+#' @param value column of the log file to compare
+#' e.g. offset, delay, slave.to.master, master.to.slave
+#' @param output an output file name for a PNG image
+#'
+#' @export
+ptpCompare <-function(loga, logb, value, start, end, output) {
+ if (!missing(output))
+ png(filename=output, height=960, width=1280, bg="white")
+ if (!missing(start)) {
+ loga = loga[index(loga) > as.POSIXct(start)]
+ logb = logb[index(logb) > as.POSIXct(start)]
+ }
+ if (!missing(end)) {
+ loga = loga[index(loga) < as.POSIXct(end)]
+ logb = logb[index(logb) < as.POSIXct(end)]
+ }
+ if (!missing(start) && !missing(end)) {
+ loga = loga[index(loga) > as.POSIXct(start) &
+ index(loga) < as.POSIXct(end)]
+ logb = logb[index(logb) > as.POSIXct(start) &
+ index(logb) < as.POSIXct(end)]
+ }
+ # If the caller does not specify which value to compare, compare them all
+ if (missing (value)) {
+ ymin = min(min(loga$offset, na.rm=TRUE), min(loga$delay, na.rm=TRUE),
+ min(loga$master.to.slave, na.rm=TRUE),
+ min(loga$slave.to.master, na.rm=TRUE))
+ ymax = max(max(loga$offset, na.rm=TRUE), max(loga$delay, na.rm=TRUE),
+ max(loga$master.to.slave, na.rm=TRUE),
+ max(loga$slave.to.master, na.rm=TRUE))
+
+ plot(loga$delay, y=NULL, xaxt = "n" ,type="n", ylim=range(ymin, ymax),
+ main="PTP Results", xlab="Time", ylab="Nanoseconds")
+ points(loga$delay, y=NULL, cex=.5, col="black", pch=24)
+ points(loga$offset, y=NULL, cex=.5, col="blue", pch=21)
+ points(loga$master.to.slave, y=NULL, cex=.5, col="purple", pch=22)
+ points(loga$slave.to.master, y=NULL, cex=.5, col="green", pch=23)
+
+ points(logb$delay, y=NULL, cex=.5, col="black", pch=24)
+ points(logb$offset, y=NULL, cex=.5, col="deeppink", pch=21)
+ points(logb$master.to.slave, y=NULL, cex=.5, col="blue", pch=22)
+ points(logb$slave.to.master, y=NULL, cex=.5, col="red", pch=23)
+ legend("topright",
+ c("Delay1", "Offset1", "M->S_1", "S->M_1", "Delay2", "Offset2", "M->S_2", "S->M_w"), col=c("black", "blue", "purple", "green", "black", "deeppink", "blue", "red"), pch=21:24)
+ } else {
+ ymin = min(min(loga[[value]], na.rm=TRUE), min(logb[[value]], na.rm=TRUE))
+ ymax = max(max(loga[[value]], na.rm=TRUE), max(logb[[value]], na.rm=TRUE))
+ plot(loga[[value]], y=NULL, xaxt = "n" ,type="n", ylim=range(ymin, ymax),
+ main="PTP Results", xlab="Time", ylab="Nanoseconds")
+ points(loga[[value]], y=NULL, cex=.5, col="red", pch=21)
+
+ points(logb[[value]], y=NULL, cex=.5, col="blue", pch=22)
+ legend("topright",
+ c(paste(value,"1"), paste(value, "2")), col=c("red", "blue"), pch=21:22)
+ }
+ if (!missing(output))
+ dev.off()
+}
+
+#' Functions for working with PTPd quality files
+#'
+#' @param file quality file to read
+#' @return a data frame based on a PTPd quality file.
+#'
+ptpQualityRead <- function(file) {
+ read.table(file, fill=FALSE, sep=" ",
+ col.names=c("packet", "timestamp"),
+ colClasses=c("packet"="numeric"),
+ blank.lines.skip=TRUE, header=FALSE)
+}
+
+#' Graph quality data
+#'
+#' @param logA data from host A
+#' @param logB data from host B
+#' @param output optional output file for the graph
+#' @return None
+#'
+#'
+#' @export
+ptpQualityGraph <- function(logA, logB, output) {
+ if (!missing(output))
+ png(filename=output, height=960, width=1280, bg="white")
+ quality = merge(logA, logB, by="packet")
+ difference = zoo(quality$timestamp.x - quality$timestamp.y, quality$packet)
+ plot(difference, type="p", cex=.1, ylab="nanoseconds",
+ xlab="Sequence Number")
+ if (!missing(output))
+ dev.off()
+ return(difference)
+}
+
+#' Compare two quality graphs
+#'
+#' @param diffA difference A
+#' @param diffB difference B
+#' @param output optional output file for the graph
+#' @return None
+#'
+#'
+#' @export
+ptpQualityGraphCompare <- function(diffA, diffB, output) {
+ if (!missing(output))
+ png(filename=output, height=960, width=1280, bg="white")
+ plot(diffA, type = "p", ylab="nanoseconds", xlab="Sequence Number", cex=.1, col="red")
+ points(diffB, ylab="nanoseconds", xlab="Sequence Number", cex=.1, col="green")
+ legend("topright",
+ c("1", "2"), col=c("red", "green"), pch=21:22)
+ if (!missing(output))
+ dev.off()
+}
+
+#' Functions for deriving various statistics over a PTP log
+#'
+#' @param log dataframe returned by a ptpLogRead() call
+#' @param start start time in strftime format
+#' @param end end time in strftime format
+#' @return None
+#'
+#'
+#' @export
+ptpStats <- function(log, start, end) {
+ cat("Measurements: ", length(log$offset))
+ cat("\nOffset",
+ "\nmin:", min(log$offset, na.rm=TRUE),
+ " max: ", max(log$offset, na.rm=TRUE),
+ " median: ", median(log$offset, na.rm=TRUE),
+ " mean: ", mean(log$offset, na.rm=TRUE),
+ "\nstd dev: ", sd(log$offset, na.rm=TRUE),
+ " variance: ", var(log$offset, na.rm=TRUE), "\n")
+ cat("Delay",
+ "\nmin:", min(log$delay, na.rm=TRUE),
+ " max: ", max(log$delay, na.rm=TRUE),
+ " median: ", median(log$delay, na.rm=TRUE),
+ " mean: ", mean(log$delay, na.rm=TRUE),
+ "\nstd dev: ", sd(log$delay, na.rm=TRUE),
+ " variance: ", var(log$delay, na.rm=TRUE), "\n")
+ cat("Master -> Slave",
+ "\nmin:", min(log$master.to.slave, na.rm=TRUE),
+ " max: ", max(log$master.to.slave, na.rm=TRUE),
+ " median: ", median(log$master.to.slave, na.rm=TRUE),
+ " mean: ", mean(log$master.to.slave, na.rm=TRUE),
+ "\nstd dev: ", sd(log$master.to.slave, na.rm=TRUE),
+ " variance: ", var(log$master.to.slave, na.rm=TRUE), "\n")
+ cat("Slave -> Master",
+ "\nmin:", min(log$slave.to.master, na.rm=TRUE),
+ " max: ", max(log$slave.to.master, na.rm=TRUE),
+ " median: ", median(log$slave.to.master, na.rm=TRUE),
+ " mean: ", mean(log$slave.to.master, na.rm=TRUE),
+ "\nstd dev: ", sd(log$slave.to.master, na.rm=TRUE),
+ " variance: ", var(log$slave.to.master, na.rm=TRUE), "\n")
+}
+
+#' Statistics on offset measurements
+#'
+#' @param log dataframe returend by a ptpLogRead() call
+#' @param start start time in strftime format
+#' @param end end time in strftime format
+#' @param boundary nanoseconds offset boundary
+#'
+#' @export
+ptpOffsetStats <- function(log, start, end, boundary = 1000) {
+ cat("Measurements: ", length(log$offset))
+ cat("\nOffset",
+ "\nmin:", min(log$offset, na.rm=TRUE),
+ " max: ", max(log$offset, na.rm=TRUE),
+ " median: ", median(log$offset, na.rm=TRUE),
+ " mean: ", mean(log$offset, na.rm=TRUE),
+ "\nstd dev: ", sd(log$offset, na.rm=TRUE),
+ " variance: ", var(log$offset, na.rm=TRUE), "\n")
+ cat("\nOutside Boundary: ", sum(log$offset > boundary) + sum(log$offset < -boundary),
+ "Percentage: ", (sum(log$offset > boundary) + sum(log$offset < -boundary)) / length(log$offset), "\n")
+}
+
+#' Statistics on quality measurements
+#'
+#' @param difference dataframes returend by a ptpQualtiyRead() calls
+#'
+#'
+#' @export
+ptpQualityStats <- function(difference) {
+ cat("\nmin:", min(difference, na.rm=TRUE),
+ " max: ", max(difference, na.rm=TRUE),
+ " median: ", median(difference, na.rm=TRUE),
+ " mean: ", mean(difference, na.rm=TRUE),
+ "\nstd dev: ", sd(difference, na.rm=TRUE),
+ " variance: ", var(difference, na.rm=TRUE), "\n")
+}
diff --git a/rtemsbsd/ptpd/tools/ptplib/man/ptpCompare.Rd b/rtemsbsd/ptpd/tools/ptplib/man/ptpCompare.Rd
new file mode 100644
index 00000000..7ba9eb57
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ptplib/man/ptpCompare.Rd
@@ -0,0 +1,22 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ptplib.R
+\name{ptpCompare}
+\alias{ptpCompare}
+\title{Compare two log files on a graph}
+\usage{
+ptpCompare(loga, logb, value, start, end, output)
+}
+\arguments{
+\item{loga}{data frame read using ptplogRead}
+
+\item{logb}{data frame read using ptplogRead}
+
+\item{value}{column of the log file to compare
+e.g. offset, delay, slave.to.master, master.to.slave}
+
+\item{output}{an output file name for a PNG image}
+}
+\description{
+Compare two log files on a graph
+}
+
diff --git a/rtemsbsd/ptpd/tools/ptplib/man/ptpGraph.Rd b/rtemsbsd/ptpd/tools/ptplib/man/ptpGraph.Rd
new file mode 100644
index 00000000..05acf824
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ptplib/man/ptpGraph.Rd
@@ -0,0 +1,19 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ptplib.R
+\name{ptpGraph}
+\alias{ptpGraph}
+\title{Graphs a dataframe returned by a call to ptplog}
+\usage{
+ptpGraph(logframe, value, start, end, output)
+}
+\arguments{
+\item{logframe}{A data frame returned by ptpLogRead}
+
+\item{value}{Graph only a specific value rather than the four major ones.}
+
+\item{output}{An optional file name to place a PNG of the graph into}
+}
+\description{
+Graphs a dataframe returned by a call to ptplog
+}
+
diff --git a/rtemsbsd/ptpd/tools/ptplib/man/ptpHistogram.Rd b/rtemsbsd/ptpd/tools/ptplib/man/ptpHistogram.Rd
new file mode 100644
index 00000000..a6cec6d9
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ptplib/man/ptpHistogram.Rd
@@ -0,0 +1,16 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ptplib.R
+\name{ptpHistogram}
+\alias{ptpHistogram}
+\title{Draw a simple histogram of some one variable in our log}
+\usage{
+ptpHistogram(log, start, end, output)
+}
+\description{
+Draw a simple histogram of some one variable in our log
+}
+\examples{
+ptpHistogram(foo$log$offset)
+
+}
+
diff --git a/rtemsbsd/ptpd/tools/ptplib/man/ptpHistogramCompare.Rd b/rtemsbsd/ptpd/tools/ptplib/man/ptpHistogramCompare.Rd
new file mode 100644
index 00000000..4f1f0272
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ptplib/man/ptpHistogramCompare.Rd
@@ -0,0 +1,12 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ptplib.R
+\name{ptpHistogramCompare}
+\alias{ptpHistogramCompare}
+\title{Compare two data sets to each other using a histogram}
+\usage{
+ptpHistogramCompare(loga, logb, starta, enda, startb, endb, output)
+}
+\description{
+examples ptpHistogramCompare(foo$log$offset, bar$log$offset)
+}
+
diff --git a/rtemsbsd/ptpd/tools/ptplib/man/ptpLogRead.Rd b/rtemsbsd/ptpd/tools/ptplib/man/ptpLogRead.Rd
new file mode 100644
index 00000000..7a74d1f0
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ptplib/man/ptpLogRead.Rd
@@ -0,0 +1,20 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ptplib.R
+\name{ptpLogRead}
+\alias{ptpLogRead}
+\title{Read a PTP log file}
+\usage{
+ptpLogRead(file)
+}
+\arguments{
+\item{file}{A properly formatted PTP log file generated by the daemon}
+}
+\value{
+The raw data frame, merged timeseries (offset, delay, m->s, s->)
+ offset time series, delay time series,
+ master.to.slave time series, and slave.to.master time series
+}
+\description{
+Read a PTP log file
+}
+
diff --git a/rtemsbsd/ptpd/tools/ptplib/man/ptpOffsetStats.Rd b/rtemsbsd/ptpd/tools/ptplib/man/ptpOffsetStats.Rd
new file mode 100644
index 00000000..c4c3ba74
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ptplib/man/ptpOffsetStats.Rd
@@ -0,0 +1,21 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ptplib.R
+\name{ptpOffsetStats}
+\alias{ptpOffsetStats}
+\title{Statistics on offset measurements}
+\usage{
+ptpOffsetStats(log, start, end, boundary = 1000)
+}
+\arguments{
+\item{log}{dataframe returend by a ptpLogRead() call}
+
+\item{start}{start time in strftime format}
+
+\item{end}{end time in strftime format}
+
+\item{boundary}{nanoseconds offset boundary}
+}
+\description{
+Statistics on offset measurements
+}
+
diff --git a/rtemsbsd/ptpd/tools/ptplib/man/ptpQualityGraph.Rd b/rtemsbsd/ptpd/tools/ptplib/man/ptpQualityGraph.Rd
new file mode 100644
index 00000000..f09fd7dc
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ptplib/man/ptpQualityGraph.Rd
@@ -0,0 +1,22 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ptplib.R
+\name{ptpQualityGraph}
+\alias{ptpQualityGraph}
+\title{Graph quality data}
+\usage{
+ptpQualityGraph(logA, logB, output)
+}
+\arguments{
+\item{logA}{data from host A}
+
+\item{logB}{data from host B}
+
+\item{output}{optional output file for the graph}
+}
+\value{
+None
+}
+\description{
+Graph quality data
+}
+
diff --git a/rtemsbsd/ptpd/tools/ptplib/man/ptpQualityGraphCompare.Rd b/rtemsbsd/ptpd/tools/ptplib/man/ptpQualityGraphCompare.Rd
new file mode 100644
index 00000000..8478d07a
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ptplib/man/ptpQualityGraphCompare.Rd
@@ -0,0 +1,22 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ptplib.R
+\name{ptpQualityGraphCompare}
+\alias{ptpQualityGraphCompare}
+\title{Compare two quality graphs}
+\usage{
+ptpQualityGraphCompare(diffA, diffB, output)
+}
+\arguments{
+\item{diffA}{difference A}
+
+\item{diffB}{difference B}
+
+\item{output}{optional output file for the graph}
+}
+\value{
+None
+}
+\description{
+Compare two quality graphs
+}
+
diff --git a/rtemsbsd/ptpd/tools/ptplib/man/ptpQualityRead.Rd b/rtemsbsd/ptpd/tools/ptplib/man/ptpQualityRead.Rd
new file mode 100644
index 00000000..904d86f9
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ptplib/man/ptpQualityRead.Rd
@@ -0,0 +1,18 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ptplib.R
+\name{ptpQualityRead}
+\alias{ptpQualityRead}
+\title{Functions for working with PTPd quality files}
+\usage{
+ptpQualityRead(file)
+}
+\arguments{
+\item{file}{quality file to read}
+}
+\value{
+a data frame based on a PTPd quality file.
+}
+\description{
+Functions for working with PTPd quality files
+}
+
diff --git a/rtemsbsd/ptpd/tools/ptplib/man/ptpQualityStats.Rd b/rtemsbsd/ptpd/tools/ptplib/man/ptpQualityStats.Rd
new file mode 100644
index 00000000..179a8d97
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ptplib/man/ptpQualityStats.Rd
@@ -0,0 +1,15 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ptplib.R
+\name{ptpQualityStats}
+\alias{ptpQualityStats}
+\title{Statistics on quality measurements}
+\usage{
+ptpQualityStats(difference)
+}
+\arguments{
+\item{difference}{dataframes returend by a ptpQualtiyRead() calls}
+}
+\description{
+Statistics on quality measurements
+}
+
diff --git a/rtemsbsd/ptpd/tools/ptplib/man/ptpStats.Rd b/rtemsbsd/ptpd/tools/ptplib/man/ptpStats.Rd
new file mode 100644
index 00000000..8fc449fb
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/ptplib/man/ptpStats.Rd
@@ -0,0 +1,22 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ptplib.R
+\name{ptpStats}
+\alias{ptpStats}
+\title{Functions for deriving various statistics over a PTP log}
+\usage{
+ptpStats(log, start, end)
+}
+\arguments{
+\item{log}{dataframe returned by a ptpLogRead() call}
+
+\item{start}{start time in strftime format}
+
+\item{end}{end time in strftime format}
+}
+\value{
+None
+}
+\description{
+Functions for deriving various statistics over a PTP log
+}
+
diff --git a/rtemsbsd/ptpd/tools/snmptpq b/rtemsbsd/ptpd/tools/snmptpq
new file mode 100755
index 00000000..8394735e
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/snmptpq
@@ -0,0 +1,303 @@
+#!/bin/bash
+
+# PTPBASE-MIB SNMP poller
+# (c) 2015: Wojciech Owczarek, PTPd project
+
+# constants
+myver=0.9
+timeout=1
+retries=3
+
+# initial values
+firsthost=""
+community=public
+version=2c
+quiet=0
+pollcount=1
+pollinterval=0
+clearscreen=0
+hostlist=""
+hostcount=0
+domainnumber=-1
+
+function usage() {
+ echo "usage: `basename $0` [-h] [-q] [-x] [-v <1|2c>] [-c <community>] [-n <pollcount>] [-i <pollinterval>] [pollhost1] [pollhost2] ... [pollhostN]"
+}
+
+# print help
+[ "$1" == "-h" ] && {
+
+echo
+
+usage
+
+echo
+
+echo "PTP(d)BASE-MIB SNMP poller version $myver, (c) 2015: Wojciech Owczarek, PTPd project"
+echo
+echo "options:"
+echo
+echo " -h : print this help screen"
+echo " -q : CSV style output with headers"
+echo " -x : clear screen on every poll"
+echo " -v <1|2c> : SNMP version to use"
+echo " -c <community> : SNMP community string"
+echo " -n <pollcount> : number of SNMP polls to execute"
+echo " 0 = run indefinitely"
+echo
+echo " -i <pollinterval> : polling interval in seconds"
+echo " 0 = don't wait. When poll count = 0, poll"
+echo " interval is set to 1"
+echo
+echo " [pollhost] [...] : one or more hosts to poll."
+echo " when no hostname given, localhost is used"
+echo
+echo " -d : PTP domain number (default: any)"
+
+exit 1
+
+}
+
+
+
+# parse cmdline arguments (no getopt, silly me)
+
+
+#while [ "${1:0:1}" == "-" ]; do
+while [ "$1x" != "x" ]; do
+
+ argcount=$#
+
+ [ "$1" == "-q" ] && { quiet=1; shift 1; }
+
+ [ "$1" == "-x" ] && { clearscreen=1; shift 1; }
+
+ [ "$1" == "-c" ] && [ "$2x" == "x" ] && usage && exit 1
+
+ [ "$1" == "-c" ] && [ "$2x" != "x" ] && { community=$2; shift 2; }
+
+ [ "$1" == "-v" ] && [ "$2x" == "x" ] && usage && exit 1
+
+ [ "$1" == "-v" ] && [ "$2x" != "x" ] && { version=v$2; shift 2; }
+
+ [ "$1" == "-n" ] && [ "$2x" == "x" ] && usage && exit 1
+
+ [ "$1" == "-n" ] && [ "$2x" != "x" ] && { pollcount=$2; shift 2; }
+
+ [ "$1" == "-i" ] && [ "$2x" == "x" ] && usage && exit 1
+
+ [ "$1" == "-i" ] && [ "$2x" != "x" ] && { pollinterval=$2; shift 2; }
+
+ [ "$1" == "-d" ] && [ "$2x" != "x" ] && { domainnumber=$2; shift 2; }
+
+ [ $argcount -eq $# ] && { hostlist="$hostlist $1"; shift 1; hostcount=$(( $hostcount + 1 )); }
+
+done;
+
+[ $hostcount -eq 0 ] && hostlist="localhost"
+
+[ "$version" != "2c" ] && [ "$version" != "1" ] && echo "Invalid SNMP version: $version" && exit 1
+
+
+[ "$pollcount" -eq "$pollcount" ] 2>/dev/null || { echo "Invalid poll count: $pollcount"; exit 1; }
+[ "$pollinterval" -eq "$pollinterval" ] 2>/dev/null || { echo "Invalid poll interval: $pollinterval"; exit 1; }
+[ "$domainnumber" -eq "$domainnumber" ] 2>/dev/null || { echo "Invalid domain number: $domainnumber"; exit 1; }
+
+if [ $domainnumber == -1 ]; then domainnumber=""; else domainnumber=".$domainnumber"; fi
+
+
+# prevent from flooding if we have an infinite loop
+[ $pollcount -eq 0 ] && [ $pollinterval -eq 0 ] && pollinterval=1
+
+# OIDs to be polled
+
+# in PTPBASE-MIB
+ofm_oid=PTPBASE-MIB::ptpbaseClockCurrentDSOffsetFromMaster$domainnumber
+mpd_oid=PTPBASE-MIB::ptpbaseClockCurrentDSMeanPathDelay$domainnumber
+state_oid=PTPBASE-MIB::ptpbaseClockPortRunningState$domainnumber
+parentid_oid=PTPBASE-MIB::ptpbaseClockParentDSGMClockIdentity$domainnumber
+ptpaddr_oid=PTPBASE-MIB::ptpbaseClockPortCurrentPeerAddress$domainnumber
+# PTPd-specific
+parentaddr_oid=PTPBASE-MIB::ptpbaseClockParentDSParentPortAddress$domainnumber
+
+function getoid() {
+ oidval=`snmpgetnext -v$version -r $retries -t $timeout -c $community -O qUvx $pollhost $* 2>/dev/null` || exit 1
+}
+
+function printoid() {
+ snmpgetnext -v$version -r $retries -t $timeout -c $community -O qUvx $pollhost $* 2>/dev/null || exit 1
+ snmpgetnext -v$version -r $retries -t $timeout -c $community -O qUx $pollhost $1 2>/dev/null || exit 1
+ return $?
+}
+
+function hextoip() {
+
+ # remove quotes and spaces
+ tmp=`echo $* | tr -d " "\"`
+ [ ${#tmp} -ne 8 ] && return
+ octet1=0x${tmp:0:2}
+ octet2=0x${tmp:2:2}
+ octet3=0x${tmp:4:2}
+ octet4=0x${tmp:6:2}
+
+ retval=`printf "%d.%d.%d.%d" $octet1 $octet2 $octet3 $octet4`
+}
+
+function intervaltoint() {
+
+ # remove quotes and spaces
+ tmp=`echo $* | tr -d " "\"`
+ # extract int64
+ [ ${#tmp} -ne 32 ] && retval=" -" && return
+ tmp=${tmp:0:8}${tmp:16:8}
+ # convert to dec
+ tmp=$((16#$tmp)) 2>/dev/null
+ # scale down
+ tmp=$(( $tmp / 65536 ))
+ # get sign and set absolute value if negative
+ sign=""
+ [ $quiet -eq 0 ] && sign="\x20"
+ [ $tmp -lt 0 ] && { sign="-"; tmp=$(( 0 - $tmp )); }
+ # get seconds and nanoseconds value
+ sec=$(( $tmp / 1000000000 ))
+ nsec=$(( $tmp % 1000000000 ))
+ # output in %.09f format
+ retval=`printf "%s%d.%09d" "$sign" $sec $nsec`
+
+}
+
+function getofm() {
+ getoid $ofm_oid
+ intervaltoint $oidval
+ echo -n $retval
+}
+
+function getmpd() {
+ getoid $mpd_oid
+ intervaltoint $oidval
+ echo -n $retval
+}
+
+function getstate() {
+ getoid $state_oid
+ echo -n $oidval
+}
+
+function getparentid() {
+ getoid $parentid_oid
+ echo -n $oidval | tr -d " "\"
+}
+
+function getparentaddr() {
+ getoid $parentaddr_oid
+ hextoip $oidval
+ echo -n $retval
+}
+
+function getptpaddr() {
+ getoid $ptpaddr_oid
+ hextoip $oidval
+ echo -n $retval
+}
+
+
+#ptp_ofm=`getofm`
+#ptp_mpd=`getmpd`
+#ptp_state=`getstate`
+#ptp_parent_id=`getparentid`
+#ptp_parent_addr=`getparentaddr`
+#ptp_addr=`getptpaddr`
+
+function oneshot() {
+
+ lineno=1
+
+ while read line; do
+
+ notfound=0
+
+ [[ "$line" =~ "No Such Object" ]] && notfound=1
+
+ [ "$linex" == "x" ] && echo "$pollhost: no data received" && return 1
+
+ case $lineno in
+ 1)
+ intervaltoint $line
+ ptp_ofm="$retval"
+ [ $notfound -eq 1 ] && ptp_ofm=" -"
+ ;;
+ 2)
+ intervaltoint $line
+ ptp_mpd="$retval"
+ [ $notfound -eq 1 ] && ptp_mpd=" -"
+ ;;
+ 3)
+ ptp_state="$line"
+ [ $notfound -eq 1 ] && ptp_state="-"
+ ;;
+ 4)
+ ptp_parent_id=` echo $line | tr -d " "\"`
+ [ $notfound -eq 1 ] && ptp_parent_id="-"
+ ;;
+ 5)
+ hextoip $line
+ ptp_parent_addr="$retval"
+ [ $notfound -eq 1 ] && ptp_parent_addr="-"
+ ;;
+ 6)
+ hextoip $line
+ ptp_addr="$retval"
+ [ $notfound -eq 1 ] && ptp_addr="-"
+ ;;
+ 7)
+ ptp_domain=`echo $line | awk '{ print $1; }' | awk 'BEGIN{FS=".";} {print $(NF-2);}'`
+ [ $notfound -eq 1 ] && ptp_domain="-"
+ ;;
+
+ *)
+ ;;
+ esac
+
+ lineno=$(( lineno + 1 ))
+
+ done < <(printoid $ofm_oid $mpd_oid $state_oid $parentid_oid $parentaddr_oid $ptpaddr_oid)
+
+ [ $lineno == 1 ] && echo "$pollhost: no data received" && return 1
+
+ return 0
+}
+
+function displayloop() {
+
+ header_printed=0
+
+ while [ "$1x" != "x" ]; do
+
+ pollhost=$1
+
+ oneshot && {
+
+ if [ $quiet -eq 0 ]; then
+ echo -e "Hostname\t\t: $pollhost\nPTP Address\t\t: $ptp_addr\nPTP domain\t\t: $ptp_domain\nPTP state\t\t: $ptp_state\nParent Address\t\t: $ptp_parent_addr\nParent Clock ID\t\t: $ptp_parent_id\nOffset from Master\t: $ptp_ofm s\nMean Path Delay\t\t: $ptp_mpd s\n"
+ else
+ [ $clearscreen -eq 0 ] && [ $# == 1 ] && [ $iter -gt 0 ] && header_printed=1
+ [ $header_printed -eq 0 ] && echo "# Hostname, PTP Address, PTP domain, PTP state, Parent Address, Parent Clock ID, Offset From Master, Mean Path Delay" 1>&2
+ [ $clearscreen -eq 0 ] && header_printed=1
+ echo "$pollhost, $ptp_addr, $ptp_domain, $ptp_state, $ptp_parent_addr, $ptp_parent_id, $ptp_ofm, $ptp_mpd"
+ fi
+ }
+
+ shift 1
+ [ "${1:0:1}" == "-" ] && echo "\"$1\" hostname given: ignoring the rest of host list" && echo && break
+ done
+
+}
+
+iter=0
+
+while [ $pollcount -eq 0 ] || [ $iter -lt $pollcount ]; do
+ [ $clearscreen -eq 1 ] && clear
+ displayloop $hostlist
+ iter=$(( iter + 1 ))
+ [ $pollcount > 1 ] && sleep $pollinterval
+done
diff --git a/rtemsbsd/ptpd/tools/stats.R b/rtemsbsd/ptpd/tools/stats.R
new file mode 100755
index 00000000..d01ee382
--- /dev/null
+++ b/rtemsbsd/ptpd/tools/stats.R
@@ -0,0 +1,48 @@
+#!/usr/bin/env Rscript
+# Copyright (c) 2016, Neville-Neil Consulting
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# Neither the name of Neville-Neil Consulting nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Author: George V. Neville-Neil
+#
+# Description: A tool, written in R, that will process a PTPd log and
+# give various statistics about PTP performance. Statistics are given
+# for the offset from master (Offset), one way delay (Delay), Master
+# to Slave and Slave to Master timings. An argument is required which
+# is a raw ptpd log produced by the -D flag to ptpd2.
+
+require(zoo)
+require(ptplib)
+
+argv <- commandArgs(TRUE)
+
+file = argv[1]
+
+log = ptpLogRead(file)
+ptpStats(log)
--
2.25.1
More information about the devel
mailing list