/* Hub 按需启停。契约、判据表与退路在 hubctl.h。 */ #include "hubctl.h" #include #include "board_pins.h" #include "chgmode.h" #include "evlog.h" #include "hal_gpio.h" #include "netcfg.h" #include "phonerail.h" #include "slot.h" #include "t_actuator.h" static hub_mode_t s_mode; static hub_save_fn s_save; static void *s_save_ctx; static uint32_t s_last_need; /* 最后一次「需要」的时刻 */ static bool s_idle; /* idle 窗口还开着(过期即清:32 位时钟 49.7 天回绕,放着不管会「复活」) */ static uint32_t s_hold_until; static bool s_hold; static uint32_t s_down_since; /* MQTT 从什么时候开始不通 */ static bool s_down; static bool s_host_prev; static bool s_on; static hub_why_t s_why; /* `a` 在 `b` 之前(回绕安全)。 */ static bool before(uint32_t a, uint32_t b) { return (int32_t)(a - b) < 0; } static void need(uint32_t now) { s_last_need = now; s_idle = true; } /* 开 = 放成输入,R716 拉高;关 = 配成输出、拉低。**不主动驱高**(hubctl.h)。 */ static hal_err_t apply(bool on) { if (on) { return hal_gpio_config(BOARD_PIN_HUB_RST_N, HAL_GPIO_INPUT); } hal_err_t e = hal_gpio_config(BOARD_PIN_HUB_RST_N, HAL_GPIO_OUTPUT); if (e == HAL_OK) { e = hal_gpio_write(BOARD_PIN_HUB_RST_N, 0); } return e; } void hubctl_init(hub_mode_t mode, hub_save_fn save, void *ctx, uint32_t now) { s_mode = (mode < HUB_MODE_COUNT) ? mode : HUB_MODE_AUTO; s_save = save; s_save_ctx = ctx; s_hold = false; s_hold_until = 0; s_down = false; s_down_since = now; s_host_prev = false; need(now); /* 启动算一次「需要」:刚复位完,最可能就是有人要用 USB */ s_on = (apply(true) == HAL_OK); s_why = HUB_WHY_IDLE; } hub_why_t hub_decide(const hub_in_t *in, uint32_t now) { if (in->host && !s_host_prev) { need(now); /* 电脑刚插上 */ } s_host_prev = in->host; if (in->link_up) { s_down = false; } else if (!s_down) { s_down = true; s_down_since = now; } if (s_hold && !before(now, s_hold_until)) { s_hold = false; } hub_why_t why = HUB_WHY_OFF; if (s_mode == HUB_MODE_ON) { why = HUB_WHY_MODE; } else if (in->vbus_any) { why = HUB_WHY_VBUS; } else if (in->flash_any) { why = HUB_WHY_FLASH; } else if (in->act_any) { why = HUB_WHY_ACT; } else if (s_hold) { why = HUB_WHY_HOLD; } else if (s_down && !before(now, s_down_since + HUB_LINK_LOST_MS)) { why = HUB_WHY_LINK; } if (why != HUB_WHY_OFF) { need(now); return why; } if (s_idle && before(now, s_last_need + HUB_IDLE_MS)) { return HUB_WHY_IDLE; } s_idle = false; return HUB_WHY_OFF; } void hubctl_tick(uint32_t now) { hub_in_t in; memset(&in, 0, sizeof(in)); for (int r = 1; r <= PHONERAIL_RAIL_COUNT; r++) { in.vbus_any = in.vbus_any || slot_vbus_is_on(r); in.act_any = in.act_any || t_actuator_busy(r); } in.flash_any = chgmode_count(now) > 0; net_status_t st; net_status_get(&st); in.link_up = (st.mqtt == NET_UP); int host = 0; in.host = (hal_gpio_read(BOARD_PIN_UP_VBUS_DET, &host) == HAL_OK) && host != 0; s_why = hub_decide(&in, now); const bool want = (s_why != HUB_WHY_OFF); if (want == s_on) { return; } if (apply(want) != HAL_OK) { return; /* 引脚号写错才会走到这 —— 下一拍再试,状态不假装变了 */ } s_on = want; evlog_put(0, LOG_EV_HUB, want ? HUB_EV_ON : HUB_EV_OFF, (int32_t)s_why, 0); } void hubctl_touch(uint32_t now) { need(now); } hal_err_t hubctl_set_mode(hub_mode_t mode) { if (mode >= HUB_MODE_COUNT) { return HAL_ERR_ARG; } if (mode != s_mode) { s_mode = mode; evlog_put(0, LOG_EV_HUB, (uint8_t)(HUB_EV_MODE | (uint32_t)mode), (int32_t)s_why, 0); } if (s_save != NULL) { s_save(s_save_ctx, mode); /* 没变也交给它:存盘函数自己判「没变不写」 */ } return HAL_OK; } hal_err_t hubctl_hold(uint32_t minutes, uint32_t now) { if (minutes > HUB_HOLD_MAX_MIN) { return HAL_ERR_ARG; } if (minutes == 0) { s_hold = false; s_idle = false; return HAL_OK; } s_hold_until = now + minutes * 60000u; s_hold = true; return HAL_OK; } hub_mode_t hubctl_mode(void) { return s_mode; } bool hubctl_is_on(void) { return s_on; } hub_why_t hubctl_why(void) { return s_why; } uint32_t hubctl_hold_left_s(uint32_t now) { if (!s_hold || !before(now, s_hold_until)) { return 0; } return (s_hold_until - now + 999u) / 1000u; } uint32_t hubctl_idle_left_s(uint32_t now) { if (s_why != HUB_WHY_IDLE || !s_idle || !before(now, s_last_need + HUB_IDLE_MS)) { return 0; } return (s_last_need + HUB_IDLE_MS - now + 999u) / 1000u; } static const char *const k_modes[HUB_MODE_COUNT] = { "auto", "on" }; static const char *const k_whys[HUB_WHY_COUNT] = { "off", "mode", "vbus", "flash", "act", "hold", "link", "idle", }; const char *hub_mode_str(hub_mode_t m) { return (m < HUB_MODE_COUNT) ? k_modes[m] : "?"; } const char *hub_why_str(hub_why_t w) { return (w < HUB_WHY_COUNT) ? k_whys[w] : "?"; } bool hub_mode_parse(const char *s, hub_mode_t *out) { for (int i = 0; i < HUB_MODE_COUNT; i++) { if (strcmp(s, k_modes[i]) == 0) { *out = (hub_mode_t)i; return true; } } return false; }