/* 一片刀片的 I/O 门面。契约见 slot.h。 */ #include "slot.h" #include #include #include "hal_gpio.h" #include "i2cbus.h" #include "rail_cfg.h" /* `PRSNT#` 的缓存(见 slot.h:读失败保留上一次的值)。 */ static bool s_present[PHONERAIL_RAIL_COUNT] = { true, true, true, true, true }; static bool rail_valid(int rail) { return rail >= 1 && rail <= PHONERAIL_RAIL_COUNT; } int slot_mux_ch(int rail) { assert(rail_valid(rail)); return (int)rail_cfg(rail)->mux_ch; } /* ---- PRSNT# -------------------------------------------------------------- */ hal_err_t slot_prsnt_init(void) { for (int rail = 1; rail <= PHONERAIL_RAIL_COUNT; rail++) { const hal_err_t err = hal_gpio_config(rail_cfg(rail)->prsnt_n, HAL_GPIO_INPUT_PULLUP); if (err != HAL_OK) { return err; } } slot_prsnt_poll(); return HAL_OK; } void slot_prsnt_poll(void) { for (int rail = 1; rail <= PHONERAIL_RAIL_COUNT; rail++) { int level = 0; if (hal_gpio_read(rail_cfg(rail)->prsnt_n, &level) == HAL_OK) { s_present[rail - 1] = (level == 0); /* 低 = 在位 */ } /* 读失败:保留上一次的值(slot.h 里的理由)。 */ } } bool slot_present(int rail) { return rail_valid(rail) && s_present[rail - 1]; } /* ---- 一次 xact 的正文们 -------------------------------------------------- 每个都是「通道已选好、锁已持有」的状态下被调一次。 */ typedef struct { int rail; uint8_t mask; uint8_t value; } out0_arg_t; static hal_err_t do_out0(void *arg) { const out0_arg_t *a = (const out0_arg_t *)arg; return pca9555_write_out0(a->rail - 1, a->mask, a->value); } static hal_err_t do_init(void *arg) { const int rail = *(const int *)arg; return pca9555_init(rail - 1); } static hal_err_t do_init_keep_vbus(void *arg) { const int rail = *(const int *)arg; return pca9555_init_keep(rail - 1, (uint8_t)(1u << PCA9555_BIT_VBUS_EN)); } /* ---- 输出 ---------------------------------------------------------------- */ /* 已持锁版本:影子下标与通道由调用方保证一致 —— 调用方只可能是 `t_safety`,而它是拿 `slot_mux_ch(rail)` 去开的 `xact`。 */ static hal_err_t out0_locked(int rail, uint8_t mask, uint8_t value) { if (!rail_valid(rail)) { return HAL_ERR_ARG; } return pca9555_write_out0(rail - 1, mask, value); } static hal_err_t out0_xact(int rail, uint8_t mask, uint8_t value) { if (!rail_valid(rail)) { return HAL_ERR_ARG; } out0_arg_t a = { rail, mask, value }; return i2cbus_xact(slot_mux_ch(rail), SLOT_LOCK_TIMEOUT_MS, do_out0, &a); } hal_err_t slot_init(int rail) { if (!rail_valid(rail)) { return HAL_ERR_ARG; } int r = rail; return i2cbus_xact(slot_mux_ch(rail), SLOT_LOCK_TIMEOUT_MS, do_init, &r); } hal_err_t slot_init_keep_vbus(int rail) { if (!rail_valid(rail)) { return HAL_ERR_ARG; } int r = rail; return i2cbus_xact(slot_mux_ch(rail), SLOT_LOCK_TIMEOUT_MS, do_init_keep_vbus, &r); } hal_err_t slot_out0_write(int rail, uint8_t mask, uint8_t value) { return out0_xact(rail, mask, value); } uint8_t slot_shadow(int rail) { assert(rail_valid(rail)); return pca9555_shadow(rail - 1); } /* VBUS 的极性:**高 = 通**(TPS22918 的 ON 脚,板上 R701 100 kΩ 下拉 ⇒ PCA9555 复位为高阻时 VBUS 必断)。极性只在这两个函数里各出现一次。 */ #define SLOT_VBUS_BIT ((uint8_t)(1u << PCA9555_BIT_VBUS_EN)) hal_err_t slot_vbus_enable(int rail, bool on) { return out0_xact(rail, SLOT_VBUS_BIT, on ? SLOT_VBUS_BIT : 0u); } hal_err_t slot_vbus_enable_locked(int rail, bool on) { return out0_locked(rail, SLOT_VBUS_BIT, on ? SLOT_VBUS_BIT : 0u); } bool slot_vbus_is_on(int rail) { /* 影子就在驱动里(写成功才更新),这里不另存一份 —— 两处影子迟早会分家。 */ return rail_valid(rail) && (pca9555_shadow(rail - 1) & SLOT_VBUS_BIT) != 0; } /* ---- 输入 ---------------------------------------------------------------- */ hal_err_t slot_read_inputs_locked(int rail, pca9555_in_t *out) { if (!rail_valid(rail) || out == NULL) { return HAL_ERR_ARG; } return pca9555_read_inputs(out); }