diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c470a7a..8e0b470 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,8 +14,8 @@ concurrency: env: DKMS_PKG: netdevsim DKMS_VER: "6.9.5" - PTP_REPO: https://github.com/k8snetworkplumbingwg/ptp-operator.git - PTP_BRANCH: main + PTP_REPO: https://github.com/bnshr/ptp-operator.git + PTP_BRANCH: integrate-add-gnss jobs: # ------------------------------------------------------------------ diff --git a/netdevsim/dpll.c b/netdevsim/dpll.c index f95b7a3..75d4332 100644 --- a/netdevsim/dpll.c +++ b/netdevsim/dpll.c @@ -207,6 +207,16 @@ static ssize_t nsim_dpll_lock_status_store(struct kobject *kobj, #define UBX_CFG_VALSET_HDR_LEN 4 #define UBX_CFGKEY_INFIL_NCNOTHRS 0x201100aa +/* CFG-MSGOUT group (bits 23:16 of the 32-bit key). linuxptp-daemon enables + * periodic NAV-STATUS/NAV-CLOCK via CFG-VALSET CFG-MSGOUT-UBX_NAV_* keys + * rather than classic CFG-MSG. */ +#define UBX_CFG_GROUP_MSGOUT 0x91 +/* Item IDs for UBX-NAV-STATUS on I2C/UART1/UART2/USB/SPI (0x2091001a..1e) */ +#define UBX_MSGOUT_NAV_STATUS_FIRST 0x001a +#define UBX_MSGOUT_NAV_STATUS_LAST 0x001e +/* Item IDs for UBX-NAV-CLOCK on I2C/UART1/UART2/USB/SPI (0x20910065..69) */ +#define UBX_MSGOUT_NAV_CLOCK_FIRST 0x0065 +#define UBX_MSGOUT_NAV_CLOCK_LAST 0x0069 #define UBX_NAV_STATUS_LEN 16 #define UBX_NAV_CLOCK_LEN 20 @@ -422,6 +432,43 @@ static u8 nsim_gga_quality_to_gps_fix(u8 gga_quality) * concatenated NMEA sentences from a single write() call, so we * search for '$' start markers rather than only checking buf[0]. */ +/* + * Mirror GGA-derived gpsFix into DPLL lock_status so gnss-sim signal + * loss (NMEA fix quality 0) drives the same holdover path as UBX + * INFIL_NCNOTHRS: + * locked + gpsFix < 2 → HOLDOVER + * gpsFix >= 2 → LOCKED_HO_ACQ + * Do not promote UNLOCKED (sysfs freerun after holdover timeout) back + * to HOLDOVER while GGA remains invalid. + */ +static void nsim_dpll_apply_gps_fix(struct nsim_dpll *ndpll, u8 gps_fix) +{ + enum dpll_lock_status new_status; + + if (gps_fix != ndpll->gnss_gps_fix) + ndpll->gnss_gps_fix = gps_fix; + + if (ndpll->signal_blocked) + return; + + if (gps_fix < 2) { + if (ndpll->lock_status != DPLL_LOCK_STATUS_LOCKED_HO_ACQ) + return; + new_status = DPLL_LOCK_STATUS_HOLDOVER; + } else { + new_status = DPLL_LOCK_STATUS_LOCKED_HO_ACQ; + } + + if (new_status == ndpll->lock_status) + return; + + ndpll->lock_status = new_status; + pr_info("netdevsim: GGA gpsFix=%u → DPLL %s\n", gps_fix, + (new_status == DPLL_LOCK_STATUS_HOLDOVER) ? "holdover" + : "locked"); + schedule_work(&ndpll->ntf_work); +} + static void nsim_parse_gga_fix(struct nsim_dpll *ndpll, const unsigned char *buf, size_t count) { @@ -447,9 +494,10 @@ static void nsim_parse_gga_fix(struct nsim_dpll *ndpll, if (i + 1 < count && buf[i + 1] >= '0' && buf[i + 1] <= '9') { - ndpll->gnss_gps_fix = + nsim_dpll_apply_gps_fix( + ndpll, nsim_gga_quality_to_gps_fix( - buf[i + 1] - '0'); + buf[i + 1] - '0')); } return; } @@ -476,6 +524,38 @@ static void nsim_gnss_close(struct gnss_device *gdev) } } +/* Start the 1 Hz NAV-STATUS/NAV-CLOCK emitter if not already running. */ +static void nsim_ubx_enable_nav_timer(struct nsim_dpll *ndpll) +{ + if (ndpll->ubx_nav_enabled) + return; + + ndpll->ubx_nav_enabled = true; + hrtimer_start(&ndpll->ubx_timer, ns_to_ktime(NSEC_PER_SEC), + HRTIMER_MODE_REL); + pr_info("netdevsim: UBX NAV-STATUS/NAV-CLOCK timer enabled\n"); +} + +/* True if key is CFG-MSGOUT-UBX_NAV_{STATUS,CLOCK}_* with a non-zero rate. */ +static bool nsim_ubx_cfgkey_enables_nav(u32 key, u8 val) +{ + u8 group = (key >> 16) & 0xff; + u16 item = key & 0xffff; + + if (group != UBX_CFG_GROUP_MSGOUT || val == 0) + return false; + + if (item >= UBX_MSGOUT_NAV_STATUS_FIRST && + item <= UBX_MSGOUT_NAV_STATUS_LAST) + return true; + + if (item >= UBX_MSGOUT_NAV_CLOCK_FIRST && + item <= UBX_MSGOUT_NAV_CLOCK_LAST) + return true; + + return false; +} + static int nsim_gnss_write_raw(struct gnss_device *gdev, const unsigned char *buf, size_t count) { @@ -499,11 +579,6 @@ static int nsim_gnss_write_raw(struct gnss_device *gdev, return count; } - // #region agent log - pr_info("netdevsim: d753ad gnss_write_raw: count=%zu first=0x%02x signal_blocked=%d\n", - count, buf[0], ndpll->signal_blocked); - // #endregion - /* UBX frame: sync(2) + class(1) + id(1) + len(2) minimum */ if (count < UBX_HDR_LEN || buf[0] != UBX_SYNC1 || buf[1] != UBX_SYNC2) return count; @@ -511,11 +586,6 @@ static int nsim_gnss_write_raw(struct gnss_device *gdev, cls = buf[2]; id = buf[3]; - // #region agent log - pr_info("netdevsim: d753ad UBX frame: cls=0x%02x id=0x%02x count=%zu\n", - cls, id, count); - // #endregion - switch (cls) { case UBX_CLASS_MON: if (id == UBX_MON_VER) { @@ -531,14 +601,8 @@ static int nsim_gnss_write_raw(struct gnss_device *gdev, if (msg_cls == UBX_CLASS_NAV && (msg_id == UBX_NAV_STATUS || - msg_id == UBX_NAV_CLOCK)) { - if (!ndpll->ubx_nav_enabled) { - ndpll->ubx_nav_enabled = true; - hrtimer_start(&ndpll->ubx_timer, - ns_to_ktime(NSEC_PER_SEC), - HRTIMER_MODE_REL); - } - } + msg_id == UBX_NAV_CLOCK)) + nsim_ubx_enable_nav_timer(ndpll); len = ubx_build_ack(resp, sizeof(resp), cls, id); if (len > 0) @@ -549,41 +613,38 @@ static int nsim_gnss_write_raw(struct gnss_device *gdev, size_t kv_end = count - UBX_CK_LEN; size_t pos = UBX_HDR_LEN + UBX_CFG_VALSET_HDR_LEN; - // #region agent log - pr_info("netdevsim: d753ad CFG-VALSET: payload_start=%zu kv_end=%zu\n", - pos, kv_end); - // #endregion - while (pos + 4 < kv_end) { + /* Key/value pairs are U1-sized for the keys we handle + * (CFG-MSGOUT rates and INFIL_NCNOTHRS). */ + while (pos + 5 <= kv_end) { u32 key = kv[0] | (kv[1] << 8) | (kv[2] << 16) | (kv[3] << 24); - kv += 4; - pos += 4; + u8 val = kv[4]; - // #region agent log - pr_info("netdevsim: d753ad CFG-VALSET key=0x%08x pos=%zu expect=0x%08x\n", - key, pos, UBX_CFGKEY_INFIL_NCNOTHRS); - // #endregion - if (key == UBX_CFGKEY_INFIL_NCNOTHRS && pos < kv_end) { - u8 val = *kv; + kv += 5; + pos += 5; + if (nsim_ubx_cfgkey_enables_nav(key, val)) { + nsim_ubx_enable_nav_timer(ndpll); + continue; + } + + if (key == UBX_CFGKEY_INFIL_NCNOTHRS) { if (val > 0) { ndpll->signal_blocked = true; ndpll->gnss_gps_fix = 0x00; - ndpll->lock_status = DPLL_LOCK_STATUS_HOLDOVER; - pr_info("netdevsim: UBX CFG-VALSET INFIL_NCNOTHRS=%u → signal blocked, holdover\n", val); + ndpll->lock_status = + DPLL_LOCK_STATUS_HOLDOVER; + pr_info("netdevsim: UBX CFG-VALSET INFIL_NCNOTHRS=%u → signal blocked, holdover\n", + val); } else { - ndpll->signal_blocked = false; - ndpll->gnss_gps_fix = 0x03; - ndpll->lock_status = DPLL_LOCK_STATUS_LOCKED_HO_ACQ; + ndpll->signal_blocked = false; + ndpll->gnss_gps_fix = 0x03; + ndpll->lock_status = + DPLL_LOCK_STATUS_LOCKED_HO_ACQ; pr_info("netdevsim: UBX CFG-VALSET INFIL_NCNOTHRS=0 → signal restored, locked\n"); } schedule_work(&ndpll->ntf_work); - kv++; - pos++; - break; } - kv++; - pos++; } len = ubx_build_ack(resp, sizeof(resp), cls, id); @@ -683,7 +744,10 @@ int nsim_dpll_init(struct nsim_dev *nsim_dev) if (err) goto err_eec_put; - /* GNSS input pin on PPS DPLL */ + /* GNSS input pin on both EEC and PPS DPLLs (matches real E810 behavior). + * linuxptp-daemon expects pin.ParentDevice to have entries at both + * index 0 (EEC) and index 1 (PPS) — the phase offset is read from + * ParentDevice[PPS_PIN_INDEX=1].PhaseOffset. */ ndpll->gnss_pin_priv.direction = DPLL_PIN_DIRECTION_INPUT; ndpll->gnss_pin_priv.frequency = DPLL_PIN_FREQUENCY_1_HZ; @@ -695,12 +759,18 @@ int nsim_dpll_init(struct nsim_dev *nsim_dev) goto err_eec_unreg; } - err = dpll_pin_register(ndpll->pps_dpll, ndpll->gnss_pin, + err = dpll_pin_register(ndpll->eec_dpll, ndpll->gnss_pin, &nsim_dpll_gnss_pin_ops, &ndpll->gnss_pin_priv); if (err) goto err_gnss_put; + err = dpll_pin_register(ndpll->pps_dpll, ndpll->gnss_pin, + &nsim_dpll_gnss_pin_ops, + &ndpll->gnss_pin_priv); + if (err) + goto err_gnss_unreg_eec; + /* E810-compatible external pins (SMA1, SMA2, U.FL1, U.FL2) */ { int i; @@ -729,10 +799,21 @@ int nsim_dpll_init(struct nsim_dev *nsim_dev) ndpll->ext_pin_privs[i].frequency = DPLL_PIN_FREQUENCY_1_HZ; + err = dpll_pin_register(ndpll->eec_dpll, epin, + &nsim_dpll_gnss_pin_ops, + &ndpll->ext_pin_privs[i]); + if (err) { + dpll_pin_put(epin); + goto err_ext_cleanup; + } + err = dpll_pin_register(ndpll->pps_dpll, epin, &nsim_dpll_gnss_pin_ops, &ndpll->ext_pin_privs[i]); if (err) { + dpll_pin_unregister(ndpll->eec_dpll, epin, + &nsim_dpll_gnss_pin_ops, + &ndpll->ext_pin_privs[i]); dpll_pin_put(epin); goto err_ext_cleanup; } @@ -796,9 +877,19 @@ int nsim_dpll_init(struct nsim_dev *nsim_dev) goto err_ports_cleanup; } + err = dpll_pin_register(ndpll->eec_dpll, pin, + &nsim_dpll_rclk_pin_ops, npin); + if (err) { + dpll_pin_put(pin); + kfree(npin); + goto err_ports_cleanup; + } + err = dpll_pin_register(ndpll->pps_dpll, pin, &nsim_dpll_rclk_pin_ops, npin); if (err) { + dpll_pin_unregister(ndpll->eec_dpll, pin, + &nsim_dpll_rclk_pin_ops, npin); dpll_pin_put(pin); kfree(npin); goto err_ports_cleanup; @@ -933,12 +1024,19 @@ int nsim_dpll_init(struct nsim_dev *nsim_dev) ndpll->ext_pins[i], &nsim_dpll_gnss_pin_ops, &ndpll->ext_pin_privs[i]); + dpll_pin_unregister(ndpll->eec_dpll, + ndpll->ext_pins[i], + &nsim_dpll_gnss_pin_ops, + &ndpll->ext_pin_privs[i]); dpll_pin_put(ndpll->ext_pins[i]); } ndpll->num_ext_pins = 0; } dpll_pin_unregister(ndpll->pps_dpll, ndpll->gnss_pin, &nsim_dpll_gnss_pin_ops, &ndpll->gnss_pin_priv); +err_gnss_unreg_eec: + dpll_pin_unregister(ndpll->eec_dpll, ndpll->gnss_pin, + &nsim_dpll_gnss_pin_ops, &ndpll->gnss_pin_priv); err_gnss_put: dpll_pin_put(ndpll->gnss_pin); err_eec_unreg: @@ -962,6 +1060,8 @@ static void nsim_dpll_cleanup_port_pins(struct nsim_dpll *ndpll) dpll_netdev_pin_clear(npin->ns->netdev); dpll_pin_unregister(ndpll->pps_dpll, npin->pin, &nsim_dpll_rclk_pin_ops, npin); + dpll_pin_unregister(ndpll->eec_dpll, npin->pin, + &nsim_dpll_rclk_pin_ops, npin); dpll_pin_put(npin->pin); list_del(&npin->list); kfree(npin); @@ -1002,6 +1102,10 @@ void nsim_dpll_exit(struct nsim_dev *nsim_dev) ndpll->ext_pins[i], &nsim_dpll_gnss_pin_ops, &ndpll->ext_pin_privs[i]); + dpll_pin_unregister(ndpll->eec_dpll, + ndpll->ext_pins[i], + &nsim_dpll_gnss_pin_ops, + &ndpll->ext_pin_privs[i]); dpll_pin_put(ndpll->ext_pins[i]); } ndpll->num_ext_pins = 0; @@ -1009,6 +1113,8 @@ void nsim_dpll_exit(struct nsim_dev *nsim_dev) dpll_pin_unregister(ndpll->pps_dpll, ndpll->gnss_pin, &nsim_dpll_gnss_pin_ops, &ndpll->gnss_pin_priv); + dpll_pin_unregister(ndpll->eec_dpll, ndpll->gnss_pin, + &nsim_dpll_gnss_pin_ops, &ndpll->gnss_pin_priv); dpll_pin_put(ndpll->gnss_pin); dpll_device_unregister(ndpll->eec_dpll, &nsim_dpll_device_ops, ndpll);