PTP-сервер дома. Часть 3
Семён сохраняет полезное_)Подумал что стоит подробнее рассказать про взаимосвязь программных компонентов для PTP сервера. А именно про демон ts2phc и ptp4l.
ts2phc — это демон из пакета linuxptp, который синхронизирует PTP Hardware Clocks (PHC) (аппаратные часы на NIC / PTP-устройствах) по внешним сигналам метки времени: например по PPS/1PPS от GPS, по триггеру оборудованию или по другим внешним таймстемпам. Он может брать один внешний источник времени и распределять его на один или несколько PHC.
ts2phc — это демон, который читает внешние метки времени (PPS, hardware timestamp events, GPIO-based timestamps, SDPs от NIC и т.п.) и далкее преобразует их в “реальные” временные события(определяет точное время прихода PPS в системных координатах CLOCK_REALTIME или по PHC). Далее корректирует PHC через стандартный Linux интерфейс адаптации аппаратных часов. Программно он взаимодействует с ядром так же, как ptp4l и phc2sys через ioctl() на /dev/ptpX и через PPSAPI (/dev/ppsX).В моем случае PPS получается через карту Intel i210, этот метод называется - PTP Hardware Clock (PHC) timestamping subsystem.
Если внешний источник подключён к сетевой карте (i210 SDP0 → TS0), то NIC генерирует hardware timestamp events, которые ядро публикует через PTP интерфейс: /dev/ptpX
ts2phc использует:
ioctl(fd, PTP_ENABLE_PPS)ioctl(fd, PTP_SYS_OFFSET_EXTENDED)ioctl(fd, PTP_CLOCK_GETCAPS)read()из event-fd, созданного черезioctl(fd, PTP_EXTTS_REQUEST)
EXTTS (External Timestamping) — главный механизм:
NIC выдаёт таймстемпы (EXTTS) на каждый приход внешнего импульса, а ts2phc подписывается на EXTTS события с помощью:
struct ptp_extts_request req; req.index = 0; // обычно TS0/SDP0 req.flags = PTP_ENABLE_FEATURE | PTP_RISING_EDGE; ioctl(fd, PTP_EXTTS_REQUEST, &req);
После этого read(fd, &event, sizeof(event)) возвращает:
struct ptp_extts_event {
struct ptp_clock_time t;
unsigned int index;
unsigned int flags;
};
Где t.sec / t.nsec — время аппаратного таймстемпа PHC.
Когда ts2phc получил:
- внешнюю метку времени T_ext
- текущее время PHC T_phc
Он вычисляет offset = T_ext – T_phc
Это ровно тот же алгоритм, что используется в ptp4l но:
- источник времени не сетевой (не message timestamp),
- а EXTTS/PPS timestamp.
Внутри используется тот же класс servo (линейный, pi, ntp-похожий), что и в других компонентах LinuxPTP.
Расскажу как данные приходят к PTP4L
1. SDP0 → PTP Hardware Clock (внутри i210)
Когда на SDP0 приходит импульс (PPS от GPS):
- Внутренний блок timestamping i210 фиксирует
- время прихода сигнала в координатах PHC.
- i210 создаёт событие EXTTS (External Timestamp).
- Драйвер NIC (
igb) передаёт его ядру Linux как:
struct ptp_extts_event
{
struct ptp_clock_time t;
unsigned int index; // 0 = SDP0
unsigned int flags;
};
2. Ядро Linux → /dev/ptpX → ts2phc
ts2phc подписывается на эти события:
struct ptp_extts_request req = {
.index = 0, // SDP0
.flags = PTP_ENABLE_FEATURE | PTP_RISING_EDGE,
};
ioctl(ptp_fd, PTP_EXTTS_REQUEST, &req);
После чего read(ptp_fd) возвращает аппаратные метки времени, например:
sec = 1700000000 nsec = 000000123
Это PHC timestamp.
3. ts2phc сравнивает внешнее время с PHC
ts2phc должен понять:
внешний PPS = точное время T_ref
PHC = текущее T_phc
Отсюда:
offset = T_ref - T_phc
Через внешний источник (обычно GNSS/PPS):
- либо PPS → system time (CLOCK_REALTIME)
- либо PPS → встроенное GPS API
- либо конфигурация говорит что PPS = ровная секунда
ts2phc вычисляет этот offset.
4. ts2phc корректирует PHC через clock_adjtime()
После расчёта offset ts2phc делает:
clock_adjtime(clkid, &timex);
или:
ioctl(ptp_fd, PTP_CLOCK_SET_TIME)
(для больших шагов)
В результате:
PHC подгоняется под внешний PPS
Это единственный реальный «путь данных».
Т.е. ts2phc перезаписывает частоту / фазу PHC.
5. ptp4l читает PHC через ioctl и timestamp’ы пакетов
Теперь ptp4l использует этот же самый PHC как:
5.1. Источник timestamp’ов
Каждый приходящий PTP-пакет (Sync, Follow_Up, Delay_Resp) имеет hardware timestamp от PHC.
Драйвер NIC помещает timestamp в skb_hwtstamps → ptp4l читает через:
recvmsg() → cmsg(SCM_TIMESTAMPING)
5.2. Опорные часы
Когда ptp4l (в роли MASTER или SLAVE) нужно:
- узнать текущее время PHC
- пересчитать его в системные часы
- вычислить смещение
Он вызывает:
ioctl(/dev/ptpX, PTP_SYS_OFFSET_EXTENDED)
или
clock_gettime(CLOCKID)
Таким образом ptp4l постоянно видит актуальное состояние PHC.