#include #include #include #define MODULE mode_switch_module #include #include #include #include #include #include #include #include #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"); static const struct device *const mode_switch_adc_dev = DEVICE_DT_GET(MODE_SWITCH_ADC_NODE); static struct k_work_delayable mode_switch_sample_work; static bool initialized; static bool running; static bool force_report; static bool mode_valid; static enum mode_switch_mode last_mode; 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(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 (!running) { return; } err = sensor_sample_fetch(mode_switch_adc_dev); if (err) { LOG_WRN("Mode switch ADC sample fetch failed (%d)", err); goto reschedule; } err = sensor_channel_get(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 (force_report || !mode_valid || (mode != last_mode)) { submit_mode_switch_event(mode, sample_mv); last_mode = mode; mode_valid = true; force_report = false; } reschedule: if (running) { k_work_reschedule(&mode_switch_sample_work, MODE_SWITCH_SAMPLE_INTERVAL); } } static int module_init(void) { if (!device_is_ready(mode_switch_adc_dev)) { LOG_ERR("Mode switch ADC device not ready"); return -ENODEV; } k_work_init_delayable(&mode_switch_sample_work, mode_switch_sample_fn); mode_valid = false; force_report = false; return 0; } static int module_start(void) { int err; if (running) { return 0; } err = mode_switch_enable(true); if (err) { return err; } running = true; force_report = true; k_work_reschedule(&mode_switch_sample_work, K_NO_WAIT); return 0; } static void module_pause(void) { if (!running) { return; } (void)k_work_cancel_delayable(&mode_switch_sample_work); (void)mode_switch_enable(false); running = false; } 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)) { int err; if (!initialized) { err = module_init(); if (err) { module_set_state(MODULE_STATE_ERROR); return false; } initialized = true; } err = module_start(); if (err) { module_set_state(MODULE_STATE_ERROR); } else { module_set_state(MODULE_STATE_READY); } } return false; } if (is_power_down_event(aeh)) { if (initialized) { module_pause(); module_set_state(MODULE_STATE_STANDBY); } return false; } if (is_wake_up_event(aeh)) { if (initialized) { int err = module_start(); if (err) { module_set_state(MODULE_STATE_ERROR); } else { module_set_state(MODULE_STATE_READY); } } 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);