#include #include #include #define MODULE mode_switch_module #include #include #include #include #include #include #include #include #include "module_lifecycle.h" #include "mode_switch_event.h" LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); #define MODE_SWITCH_ADC_NODE DT_NODELABEL(mode_switch_adc) #define MODE_SWITCH_SAMPLE_INTERVAL K_MSEC(500) #define MODE_SWITCH_USB_MAX_MV 825 #define MODE_SWITCH_24G_MAX_MV 2475 BUILD_ASSERT(DT_NODE_EXISTS(MODE_SWITCH_ADC_NODE), "Missing mode_switch_adc node in devicetree"); struct mode_switch_module_ctx { struct module_lifecycle_ctx lc; const struct device *mode_switch_adc_dev; struct k_work_delayable mode_switch_sample_work; enum mode_switch_mode last_mode; }; static int do_init(void); static int do_start(void); static int do_stop(void); static const struct module_lifecycle_cfg lifecycle_cfg = { .mode = ML_MODE_POWER, .stopped_state = MODULE_STATE_STANDBY, }; static const struct module_lifecycle_ops lifecycle_ops = { .do_init = do_init, .do_start = do_start, .do_stop = do_stop, }; static struct mode_switch_module_ctx ctx = { .lc = { .state = LC_UNINIT, .cfg = &lifecycle_cfg, .ops = &lifecycle_ops, }, .mode_switch_adc_dev = DEVICE_DT_GET(MODE_SWITCH_ADC_NODE), }; static enum mode_switch_mode detect_mode(uint16_t voltage_mv) { if (voltage_mv < MODE_SWITCH_USB_MAX_MV) { return MODE_SWITCH_USB; } if (voltage_mv < MODE_SWITCH_24G_MAX_MV) { return MODE_SWITCH_24G; } return MODE_SWITCH_BLE; } static int mode_switch_enable(bool enable) { enum pm_device_action action = enable ? PM_DEVICE_ACTION_RESUME : PM_DEVICE_ACTION_SUSPEND; int err = pm_device_action_run(ctx.mode_switch_adc_dev, action); if (err && (err != -EALREADY) && (err != -ENOTSUP)) { LOG_ERR("Cannot %s mode switch ADC (%d)", enable ? "resume" : "suspend", err); return err; } return 0; } static void mode_switch_sample_fn(struct k_work *work) { struct sensor_value voltage; int err; ARG_UNUSED(work); if (!module_lifecycle_is_running(&ctx.lc)) { return; } err = sensor_sample_fetch(ctx.mode_switch_adc_dev); if (err) { LOG_WRN("Mode switch ADC sample fetch failed (%d)", err); goto reschedule; } err = sensor_channel_get(ctx.mode_switch_adc_dev, SENSOR_CHAN_VOLTAGE, &voltage); if (err) { LOG_WRN("Mode switch ADC channel get failed (%d)", err); goto reschedule; } uint16_t sample_mv = (uint16_t)((voltage.val1 * 1000) + (voltage.val2 / 1000)); enum mode_switch_mode mode = detect_mode(sample_mv); if ((ctx.last_mode == MODE_SWITCH_INVALID) || (mode != ctx.last_mode)) { submit_mode_switch_event(mode, sample_mv); ctx.last_mode = mode; } reschedule: if (module_lifecycle_is_running(&ctx.lc)) { k_work_reschedule(&ctx.mode_switch_sample_work, MODE_SWITCH_SAMPLE_INTERVAL); } } static int do_init(void) { if (!device_is_ready(ctx.mode_switch_adc_dev)) { LOG_ERR("Mode switch ADC device not ready"); return -ENODEV; } k_work_init_delayable(&ctx.mode_switch_sample_work, mode_switch_sample_fn); ctx.last_mode = MODE_SWITCH_INVALID; return 0; } static int do_start(void) { int err; if (module_lifecycle_is_running(&ctx.lc)) { return 0; } err = mode_switch_enable(true); if (err) { return err; } k_work_reschedule(&ctx.mode_switch_sample_work, K_NO_WAIT); return 0; } static int do_stop(void) { if (!module_lifecycle_is_running(&ctx.lc)) { return 0; } (void)k_work_cancel_delayable(&ctx.mode_switch_sample_work); (void)mode_switch_enable(false); return 0; } static bool app_event_handler(const struct app_event_header *aeh) { if (is_module_state_event(aeh)) { const struct module_state_event *event = cast_module_state_event(aeh); if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) { (void)module_set_lifecycle(&ctx.lc, LC_RUNNING); return false; } if (check_state(event, MODULE_ID(mode_policy_module), MODULE_STATE_READY)) { if (ctx.last_mode != MODE_SWITCH_INVALID) { submit_mode_switch_event(ctx.last_mode, 0U); } return false; } return false; } if (is_power_down_event(aeh)) { if (module_lifecycle_is_initialized(&ctx.lc)) { (void)module_set_lifecycle(&ctx.lc, LC_STOPPED); } return false; } if (is_wake_up_event(aeh)) { if (module_lifecycle_is_initialized(&ctx.lc)) { (void)module_set_lifecycle(&ctx.lc, LC_RUNNING); } return false; } __ASSERT_NO_MSG(false); return false; } APP_EVENT_LISTENER(MODULE, app_event_handler); APP_EVENT_SUBSCRIBE(MODULE, module_state_event); APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);