diff --git a/inc/events/mode_switch_event.h b/inc/events/mode_switch_event.h index 5dcbaab..d1972c9 100644 --- a/inc/events/mode_switch_event.h +++ b/inc/events/mode_switch_event.h @@ -9,6 +9,7 @@ extern "C" { #endif enum mode_switch_mode { + MODE_SWITCH_INVALID, MODE_SWITCH_USB, MODE_SWITCH_24G, MODE_SWITCH_BLE, diff --git a/src/ble_nus_module.c b/src/ble_nus_module.c index 00518b6..be349f0 100644 --- a/src/ble_nus_module.c +++ b/src/ble_nus_module.c @@ -58,6 +58,52 @@ static struct ble_nus_ctx ctx = { .active_conn = NULL, }; +static const char *lifecycle_name(enum module_lifecycle state) +{ + switch (state) { + case LC_UNINIT: + return "UNINIT"; + case LC_RUNNING: + return "RUNNING"; + case LC_STOPPED: + return "STOPPED"; + case LC_SUSPENDED: + return "SUSPENDED"; + case LC_ERROR: + return "ERROR"; + default: + return "?"; + } +} + +static const char *business_state_name(enum ble_nus_business_state state) +{ + switch (state) { + case BLE_NUS_STACK_OFFLINE: + return "STACK_OFFLINE"; + case BLE_NUS_IDLE: + return "IDLE"; + case BLE_NUS_WAIT_NOTIFY: + return "WAIT_NOTIFY"; + case BLE_NUS_SESSION_READY: + return "SESSION_READY"; + default: + return "?"; + } +} + +static const char *link_state_name(enum proto_transport_link_state state) +{ + switch (state) { + case PROTO_TRANSPORT_LINK_DOWN: + return "DOWN"; + case PROTO_TRANSPORT_LINK_READY: + return "READY"; + default: + return "?"; + } +} + static bool lifecycle_is_ready(void) { return module_lifecycle_is_running(&ctx.lc); @@ -96,6 +142,9 @@ static void business_state_set(enum ble_nus_business_state new_state) return; } + LOG_INF("BLE NUS business %s -> %s", + business_state_name(ctx.business), + business_state_name(new_state)); ctx.business = new_state; state_reconcile(old_lifecycle, old_business); } @@ -136,6 +185,11 @@ static void received(struct bt_conn *conn, const void *data, uint16_t len, if (!lifecycle_is_ready() || (ctx.business == BLE_NUS_STACK_OFFLINE) || (conn != ctx.active_conn)) { + LOG_WRN("BLE NUS drop RX len:%u lc:%s business:%s active_conn:%p conn:%p link:%s", + len, lifecycle_name(ctx.lc.state), + business_state_name(ctx.business), + (void *)ctx.active_conn, (void *)conn, + link_state_name(transport_link_state_get())); return; } @@ -190,6 +244,9 @@ static int apply_lifecycle(enum module_lifecycle target) if (!err) { state_reconcile(old_lifecycle, old_business); + } else { + LOG_WRN("BLE NUS lifecycle change failed target:%s err:%d", + lifecycle_name(target), err); } return err; diff --git a/src/events/keyboard_hid_report_event.c b/src/events/keyboard_hid_report_event.c index 2415fec..b9097dd 100644 --- a/src/events/keyboard_hid_report_event.c +++ b/src/events/keyboard_hid_report_event.c @@ -7,6 +7,8 @@ static const char *mode_name(enum mode_switch_mode mode) { switch (mode) { + case MODE_SWITCH_INVALID: + return "INVALID"; case MODE_SWITCH_USB: return "USB"; case MODE_SWITCH_24G: diff --git a/src/events/mode_switch_event.c b/src/events/mode_switch_event.c index ce17ee7..ee7f6d0 100644 --- a/src/events/mode_switch_event.c +++ b/src/events/mode_switch_event.c @@ -5,6 +5,8 @@ static const char *mode_name(enum mode_switch_mode mode) { switch (mode) { + case MODE_SWITCH_INVALID: + return "INVALID"; case MODE_SWITCH_USB: return "USB"; case MODE_SWITCH_24G: diff --git a/src/mode_policy_module.c b/src/mode_policy_module.c index 8cc3733..e2ebb95 100644 --- a/src/mode_policy_module.c +++ b/src/mode_policy_module.c @@ -16,8 +16,7 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); struct mode_policy_module_ctx { struct module_lifecycle_ctx lc; - bool ble_adv_suspended; - bool usb_enabled; + enum mode_switch_mode active_mode; }; static int do_init(void); @@ -41,69 +40,115 @@ static struct mode_policy_module_ctx ctx = { .cfg = &lifecycle_cfg, .ops = &lifecycle_ops, }, - .ble_adv_suspended = true, + .active_mode = MODE_SWITCH_USB, }; -static void broadcast_ble_adv_req(bool suspend) +static void mode_policy_set_ble(bool enable) { - if (suspend) { - struct module_suspend_req_event *event = new_module_suspend_req_event(); - - event->sink_module_id = MODULE_ID(ble_adv); - event->src_module_id = MODULE_ID(MODULE); - APP_EVENT_SUBMIT(event); - } else { + if (enable) { struct module_resume_req_event *event = new_module_resume_req_event(); + event->sink_module_id = MODULE_ID(ble_adv); + event->src_module_id = MODULE_ID(MODULE); + APP_EVENT_SUBMIT(event); + } else { + struct module_suspend_req_event *event = new_module_suspend_req_event(); + event->sink_module_id = MODULE_ID(ble_adv); event->src_module_id = MODULE_ID(MODULE); APP_EVENT_SUBMIT(event); } } -static void apply_mode_policy(enum mode_switch_mode mode) +static void mode_policy_set_usb(bool enable) { - bool should_suspend_ble_adv = (mode != MODE_SWITCH_BLE); - bool should_enable_usb = (mode == MODE_SWITCH_USB); + if (enable) { + struct module_resume_req_event *event = new_module_resume_req_event(); - if (should_suspend_ble_adv != ctx.ble_adv_suspended) { - ctx.ble_adv_suspended = should_suspend_ble_adv; - broadcast_ble_adv_req(should_suspend_ble_adv); + event->sink_module_id = MODULE_ID(usb_device_module); + event->src_module_id = MODULE_ID(MODULE); + APP_EVENT_SUBMIT(event); + } else { + struct module_suspend_req_event *event = new_module_suspend_req_event(); + + event->sink_module_id = MODULE_ID(usb_device_module); + event->src_module_id = MODULE_ID(MODULE); + APP_EVENT_SUBMIT(event); } +} - if (should_enable_usb != ctx.usb_enabled) { - ctx.usb_enabled = should_enable_usb; - if (ctx.usb_enabled) { - struct module_resume_req_event *event = - new_module_resume_req_event(); +static void apply_active_mode(void) +{ + switch (ctx.active_mode) { + case MODE_SWITCH_BLE: + mode_policy_set_ble(true); + mode_policy_set_usb(false); + break; - event->sink_module_id = MODULE_ID(usb_device_module); - event->src_module_id = MODULE_ID(MODULE); - APP_EVENT_SUBMIT(event); - } else { - struct module_suspend_req_event *event = - new_module_suspend_req_event(); + case MODE_SWITCH_USB: + mode_policy_set_ble(false); + mode_policy_set_usb(true); + break; - event->sink_module_id = MODULE_ID(usb_device_module); - event->src_module_id = MODULE_ID(MODULE); - APP_EVENT_SUBMIT(event); - } + case MODE_SWITCH_24G: + mode_policy_set_ble(false); + mode_policy_set_usb(false); + break; + + default: + break; + } +} + +static void apply_ble_mode_policy(void) +{ + switch (ctx.active_mode) { + case MODE_SWITCH_BLE: + mode_policy_set_ble(true); + break; + + case MODE_SWITCH_USB: + mode_policy_set_ble(false); + break; + + case MODE_SWITCH_24G: + mode_policy_set_ble(false); + break; + + default: + break; + } +} + +static void apply_usb_mode_policy(void) +{ + switch (ctx.active_mode) { + case MODE_SWITCH_BLE: + mode_policy_set_usb(false); + break; + + case MODE_SWITCH_USB: + mode_policy_set_usb(true); + break; + + case MODE_SWITCH_24G: + mode_policy_set_usb(false); + break; + + default: + break; } } static int do_init(void) { - ctx.ble_adv_suspended = true; - ctx.usb_enabled = false; + ctx.active_mode = MODE_SWITCH_USB; return 0; } static int do_start(void) { - if (module_lifecycle_is_running(&ctx.lc)) { - return 0; - } - + apply_active_mode(); return 0; } @@ -118,7 +163,8 @@ static bool app_event_handler(const struct app_event_header *aeh) const struct mode_switch_event *event = cast_mode_switch_event(aeh); if (module_lifecycle_is_running(&ctx.lc)) { - apply_mode_policy(event->mode); + ctx.active_mode = event->mode; + apply_active_mode(); } return false; @@ -129,6 +175,21 @@ static bool app_event_handler(const struct app_event_header *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(ble_adv), MODULE_STATE_READY)) { + if (module_lifecycle_is_running(&ctx.lc)) { + apply_ble_mode_policy(); + } + return false; + } + + if (check_state(event, MODULE_ID(usb_device_module), + MODULE_STATE_READY)) { + if (module_lifecycle_is_running(&ctx.lc)) { + apply_usb_mode_policy(); + } } return false; diff --git a/src/mode_switch_module.c b/src/mode_switch_module.c index 0afc280..dbd97c3 100644 --- a/src/mode_switch_module.c +++ b/src/mode_switch_module.c @@ -31,8 +31,6 @@ 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; - bool force_report; - bool mode_valid; enum mode_switch_mode last_mode; }; @@ -115,12 +113,10 @@ static void mode_switch_sample_fn(struct k_work *work) uint16_t sample_mv = (uint16_t)((voltage.val1 * 1000) + (voltage.val2 / 1000)); enum mode_switch_mode mode = detect_mode(sample_mv); - if (ctx.force_report || !ctx.mode_valid || (mode != ctx.last_mode)) { + if ((ctx.last_mode == MODE_SWITCH_INVALID) || (mode != ctx.last_mode)) { submit_mode_switch_event(mode, sample_mv); ctx.last_mode = mode; - ctx.mode_valid = true; - ctx.force_report = false; } reschedule: @@ -138,8 +134,7 @@ static int do_init(void) } k_work_init_delayable(&ctx.mode_switch_sample_work, mode_switch_sample_fn); - ctx.mode_valid = false; - ctx.force_report = false; + ctx.last_mode = MODE_SWITCH_INVALID; return 0; } @@ -157,7 +152,6 @@ static int do_start(void) return err; } - ctx.force_report = true; k_work_reschedule(&ctx.mode_switch_sample_work, K_NO_WAIT); return 0; @@ -182,6 +176,15 @@ static bool app_event_handler(const struct app_event_header *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; diff --git a/src/protocol_module.c b/src/protocol_module.c index c441eee..fa9d108 100644 --- a/src/protocol_module.c +++ b/src/protocol_module.c @@ -75,6 +75,50 @@ static struct protocol_module_ctx ctx = { #define session_state ctx.session_state +static const char *lifecycle_name(enum module_lifecycle state) +{ + switch (state) { + case LC_UNINIT: + return "UNINIT"; + case LC_RUNNING: + return "RUNNING"; + case LC_STOPPED: + return "STOPPED"; + case LC_SUSPENDED: + return "SUSPENDED"; + case LC_ERROR: + return "ERROR"; + default: + return "?"; + } +} + +static const char *proto_session_name(enum proto_session_state state) +{ + switch (state) { + case PROTO_SESSION_DOWN: + return "DOWN"; + case PROTO_SESSION_WAIT_HELLO: + return "WAIT_HELLO"; + case PROTO_SESSION_ACTIVE: + return "ACTIVE"; + default: + return "?"; + } +} + +static const char *proto_transport_name(enum proto_transport transport) +{ + switch (transport) { + case PROTO_TRANSPORT_USB_CDC: + return "usb_cdc"; + case PROTO_TRANSPORT_BLE_NUS: + return "ble_nus"; + default: + return "?"; + } +} + static int decode_body(const uint8_t *payload, size_t payload_len, CdcPacketBody *body) { @@ -230,6 +274,9 @@ int protocol_module_process_message(enum proto_transport transport, } if (!module_lifecycle_is_running(&ctx.lc)) { + LOG_WRN("Reject proto msg transport:%s len:%u lc:%s", + proto_transport_name(transport), (uint32_t)req_payload_len, + lifecycle_name(ctx.lc.state)); return -EAGAIN; } @@ -241,6 +288,10 @@ int protocol_module_process_message(enum proto_transport transport, switch (body.which_body) { case CdcPacketBody_hello_req_tag: if (session_state[transport] == PROTO_SESSION_DOWN) { + LOG_WRN("Reject HelloReq transport:%s session:%s lc:%s", + proto_transport_name(transport), + proto_session_name(session_state[transport]), + lifecycle_name(ctx.lc.state)); return -EAGAIN; } @@ -354,7 +405,11 @@ static bool handle_proto_rx_event(const struct proto_rx_event *event) &rsp_payload_len); if (err) { if (err != -ENOTSUP) { - LOG_WRN("Protocol processing failed (%d)", err); + LOG_WRN("Protocol processing failed (%d) transport:%s session:%s lc:%s len:%u", + err, proto_transport_name(event->transport), + proto_session_name(session_state[event->transport]), + lifecycle_name(ctx.lc.state), + (uint32_t)event->dyndata.size); } return false; diff --git a/src/usb_device_module.c b/src/usb_device_module.c index 4956915..425b614 100644 --- a/src/usb_device_module.c +++ b/src/usb_device_module.c @@ -43,7 +43,6 @@ static const char *const class_blocklist[] = { enum usb_stack_state { USB_STACK_UNINITIALIZED = 0, - USB_STACK_DISABLED, USB_STACK_READY, USB_STACK_ENABLED, }; @@ -91,8 +90,8 @@ static struct usb_owner_ctx usb_ctx = { static inline enum usb_state usb_public_state_get(void) { - if ((usb_ctx.stack == USB_STACK_UNINITIALIZED) || - (usb_ctx.stack == USB_STACK_DISABLED)) { + if (!module_lifecycle_is_running(&usb_ctx.lc) || + (usb_ctx.stack == USB_STACK_UNINITIALIZED)) { return USB_STATE_DISABLED; } @@ -110,11 +109,6 @@ static inline enum usb_state usb_public_state_get(void) } } -static inline bool usb_stack_was_initialized(void) -{ - return (usb_ctx.stack != USB_STACK_UNINITIALIZED); -} - static inline void usb_bus_set(enum usb_bus_state state) { if (usb_ctx.bus == state) { @@ -122,7 +116,10 @@ static inline void usb_bus_set(enum usb_bus_state state) } usb_ctx.bus = state; - submit_usb_state(usb_public_state_get()); + + if (module_lifecycle_is_initialized(&usb_ctx.lc)) { + submit_usb_state(usb_public_state_get()); + } } static void update_power_manager_restriction(bool vbus_present) @@ -222,31 +219,30 @@ static void usbd_msg_cb(struct usbd_context *const usbd_ctx, switch (msg->type) { case USBD_MSG_VBUS_READY: update_power_manager_restriction(true); + usb_bus_set(USB_BUS_POWERED); - if (usb_ctx.stack == USB_STACK_READY) { - int err = usbd_enable(&blinky_usbd); + if (module_lifecycle_is_running(&usb_ctx.lc) && + (usb_ctx.stack == USB_STACK_READY)) { + int err = do_start(); if (err) { - LOG_ERR("usbd_enable failed (%d)", err); - } else { - usb_ctx.stack = USB_STACK_ENABLED; - usb_bus_set(USB_BUS_POWERED); + LOG_ERR("USB start on VBUS ready failed (%d)", err); } } break; case USBD_MSG_VBUS_REMOVED: - update_power_manager_restriction(false); - if (usb_ctx.stack == USB_STACK_ENABLED) { - int err = usbd_disable(&blinky_usbd); + int err = do_stop(); if (err) { - LOG_ERR("usbd_disable failed (%d)", err); + LOG_ERR("USB stop on VBUS removed failed (%d)", err); } + } else { + update_power_manager_restriction(false); } - if (usb_ctx.stack != USB_STACK_DISABLED) { + if (usb_ctx.stack != USB_STACK_UNINITIALIZED) { usb_ctx.stack = USB_STACK_READY; } @@ -278,16 +274,13 @@ static void usbd_msg_cb(struct usbd_context *const usbd_ctx, } } -static int usb_stack_init_once(void) +static int do_init(void) { int err; - if (usb_stack_was_initialized()) { - if (usb_ctx.stack == USB_STACK_DISABLED) { - usb_ctx.stack = USB_STACK_READY; - } - return 0; - } + usb_ctx.stack = USB_STACK_UNINITIALIZED; + usb_ctx.bus = USB_BUS_DISCONNECTED; + update_power_manager_restriction(false); STRUCT_SECTION_FOREACH(usb_function_hook, hook) { if (hook->pre_stack_init == NULL) { @@ -321,6 +314,17 @@ static int usb_stack_init_once(void) usb_ctx.stack = USB_STACK_READY; + return 0; +} + +static int do_start(void) +{ + int err; + + if (usb_ctx.stack == USB_STACK_ENABLED) { + return 0; + } + if (!usbd_can_detect_vbus(&blinky_usbd)) { err = usbd_enable(&blinky_usbd); if (err) { @@ -329,89 +333,40 @@ static int usb_stack_init_once(void) } usb_ctx.stack = USB_STACK_ENABLED; - usb_bus_set(USB_BUS_POWERED); - } - - return 0; -} - -static int usb_stack_enable_if_needed(void) -{ - int err; - - err = usb_stack_init_once(); - if (err) { - return err; - } - - if ((usb_ctx.stack == USB_STACK_READY) && - (!usbd_can_detect_vbus(&blinky_usbd) || - (usb_ctx.bus != USB_BUS_DISCONNECTED))) { - err = usbd_enable(&blinky_usbd); - if (err) { - LOG_ERR("usbd_enable failed (%d)", err); - return err; - } - - usb_ctx.stack = USB_STACK_ENABLED; - - if (usb_ctx.bus == USB_BUS_DISCONNECTED) { - usb_bus_set(USB_BUS_POWERED); - } else { - submit_usb_state(usb_public_state_get()); - } - + usb_ctx.bus = USB_BUS_POWERED; + update_power_manager_restriction(true); return 0; } - submit_usb_state(usb_public_state_get()); + if (usb_ctx.bus == USB_BUS_DISCONNECTED) { + return 0; + } + + err = usbd_enable(&blinky_usbd); + if (err) { + LOG_ERR("usbd_enable failed (%d)", err); + return err; + } + + usb_ctx.stack = USB_STACK_ENABLED; + update_power_manager_restriction(true); return 0; } -static int usb_stack_disable_if_needed(void) +static int do_stop(void) { - if (usb_ctx.stack == USB_STACK_ENABLED) { - int err = usbd_disable(&blinky_usbd); + int err; + if (usb_ctx.stack == USB_STACK_ENABLED) { + err = usbd_disable(&blinky_usbd); if (err) { LOG_ERR("usbd_disable failed (%d)", err); return err; } } - usb_ctx.stack = USB_STACK_DISABLED; + usb_ctx.stack = USB_STACK_READY; update_power_manager_restriction(false); - submit_usb_state(usb_public_state_get()); - return 0; -} - -static int do_init(void) -{ - usb_ctx.stack = USB_STACK_UNINITIALIZED; - usb_ctx.bus = USB_BUS_DISCONNECTED; - update_power_manager_restriction(false); - - return 0; -} - -static int do_start(void) -{ - if (module_lifecycle_is_running(&usb_ctx.lc)) { - return 0; - } - - submit_usb_state(usb_public_state_get()); - return 0; -} - -static int do_stop(void) -{ - if (!module_lifecycle_is_running(&usb_ctx.lc)) { - return 0; - } - - submit_usb_state(usb_public_state_get()); - return 0; } @@ -421,7 +376,9 @@ static bool handle_module_state_event(const struct module_state_event *event) return false; } - (void)module_set_lifecycle(&usb_ctx.lc, LC_RUNNING); + if (module_set_lifecycle(&usb_ctx.lc, LC_RUNNING) == 0) { + submit_usb_state(usb_public_state_get()); + } return false; } @@ -434,14 +391,10 @@ static bool handle_module_suspend_req_event( return false; } - int err = usb_stack_disable_if_needed(); - - if (err) { - LOG_WRN("USB suspend request failed (%d)", err); - return false; + if (module_set_lifecycle(&usb_ctx.lc, LC_SUSPENDED) == 0) { + submit_usb_state(usb_public_state_get()); } - (void)module_set_lifecycle(&usb_ctx.lc, LC_SUSPENDED); return false; } @@ -453,15 +406,10 @@ static bool handle_module_resume_req_event( return false; } - int err = usb_stack_enable_if_needed(); - - if (err) { - LOG_WRN("USB resume request failed (%d)", err); - return false; + if (module_set_lifecycle(&usb_ctx.lc, LC_RUNNING) == 0) { + submit_usb_state(usb_public_state_get()); } - (void)module_set_lifecycle(&usb_ctx.lc, LC_RUNNING); - return false; } @@ -483,7 +431,9 @@ static bool app_event_handler(const struct app_event_header *aeh) if (is_power_down_event(aeh)) { if (module_lifecycle_is_initialized(&usb_ctx.lc)) { - (void)module_set_lifecycle(&usb_ctx.lc, LC_STOPPED); + if (module_set_lifecycle(&usb_ctx.lc, LC_STOPPED) == 0) { + submit_usb_state(usb_public_state_get()); + } } return false; @@ -491,7 +441,9 @@ static bool app_event_handler(const struct app_event_header *aeh) if (is_wake_up_event(aeh)) { if (module_lifecycle_is_initialized(&usb_ctx.lc)) { - (void)module_set_lifecycle(&usb_ctx.lc, LC_RUNNING); + if (module_set_lifecycle(&usb_ctx.lc, LC_RUNNING) == 0) { + submit_usb_state(usb_public_state_get()); + } } return false;