feat: 添加模块生命周期管理框架并重构现有模块

添加了模块生命周期管理头文件 module_lifecycle.h,定义了完整的生命周期状态机,
包括初始化、运行、停止、挂起和错误状态。同时将电池模块、BLE BAS模块、BLE HID
模块和BLE NUS模块重构为使用新的生命周期框架进行状态管理。

提升日志缓冲区大小以支持更详细的调试信息记录。
This commit is contained in:
2026-04-17 19:12:57 +08:00
parent 8bfb8b540c
commit ceebaaa600
19 changed files with 1361 additions and 1079 deletions

204
inc/module_lifecycle.h Normal file
View File

@@ -0,0 +1,204 @@
#ifndef MODULE_LIFECYCLE_H_
#define MODULE_LIFECYCLE_H_
#include <errno.h>
#include <stdbool.h>
#include <caf/events/module_state_event.h>
enum module_lifecycle {
LC_UNINIT,
LC_RUNNING,
LC_STOPPED,
LC_SUSPENDED,
LC_ERROR,
};
enum module_lifecycle_mode {
ML_MODE_NONE,
ML_MODE_POWER,
ML_MODE_SUSPEND,
};
struct module_lifecycle_ops {
int (*do_init)(void);
int (*do_start)(void);
int (*do_stop)(void);
};
struct module_lifecycle_cfg {
enum module_lifecycle_mode mode;
enum module_state stopped_state;
};
struct module_lifecycle_ctx {
enum module_lifecycle state;
const struct module_lifecycle_cfg *cfg;
const struct module_lifecycle_ops *ops;
};
static inline bool module_lifecycle_is_running(
const struct module_lifecycle_ctx *ctx)
{
return ctx->state == LC_RUNNING;
}
static inline bool module_lifecycle_is_initialized(
const struct module_lifecycle_ctx *ctx)
{
return (ctx->state != LC_UNINIT) && (ctx->state != LC_ERROR);
}
static inline bool module_lifecycle_reports_stopped_state(
const struct module_lifecycle_ctx *ctx)
{
return ctx->cfg->mode != ML_MODE_NONE;
}
static inline int module_lifecycle_report_state(
struct module_lifecycle_ctx *ctx,
enum module_lifecycle state)
{
switch (state) {
case LC_RUNNING:
module_set_state(MODULE_STATE_READY);
return 0;
case LC_STOPPED:
if (!module_lifecycle_reports_stopped_state(ctx)) {
return 0;
}
module_set_state(ctx->cfg->stopped_state);
return 0;
case LC_SUSPENDED:
module_set_state(MODULE_STATE_SUSPENDED);
return 0;
case LC_ERROR:
module_set_state(MODULE_STATE_ERROR);
return 0;
case LC_UNINIT:
default:
return -EINVAL;
}
}
static inline int module_lifecycle_fail(struct module_lifecycle_ctx *ctx, int err)
{
ctx->state = LC_ERROR;
(void)module_lifecycle_report_state(ctx, LC_ERROR);
return err ? err : -EIO;
}
static inline int module_lifecycle_do_init(struct module_lifecycle_ctx *ctx)
{
int err = ctx->ops->do_init();
if (err) {
return module_lifecycle_fail(ctx, err);
}
return 0;
}
static inline int module_lifecycle_do_start(struct module_lifecycle_ctx *ctx,
enum module_lifecycle target)
{
int err = ctx->ops->do_start();
if (err) {
return module_lifecycle_fail(ctx, err);
}
ctx->state = target;
return module_lifecycle_report_state(ctx, target);
}
static inline int module_lifecycle_do_stop(struct module_lifecycle_ctx *ctx,
enum module_lifecycle target)
{
int err = ctx->ops->do_stop();
if (err) {
return module_lifecycle_fail(ctx, err);
}
ctx->state = target;
return module_lifecycle_report_state(ctx, target);
}
static inline int module_set_lifecycle(struct module_lifecycle_ctx *ctx,
enum module_lifecycle target)
{
if ((ctx == NULL) || (ctx->cfg == NULL) || (ctx->ops == NULL) ||
(ctx->ops->do_init == NULL) || (ctx->ops->do_start == NULL) ||
(ctx->ops->do_stop == NULL)) {
return -EINVAL;
}
if (ctx->state == LC_ERROR) {
return -EFAULT;
}
if (ctx->state == target) {
return 0;
}
if (target == LC_ERROR) {
return module_lifecycle_fail(ctx, -EIO);
}
switch (ctx->state) {
case LC_UNINIT:
if (target == LC_RUNNING) {
int err = module_lifecycle_do_init(ctx);
if (err) {
return err;
}
ctx->state = LC_STOPPED;
return module_lifecycle_do_start(ctx, LC_RUNNING);
}
if (target == LC_STOPPED) {
int err = module_lifecycle_do_init(ctx);
if (err) {
return err;
}
ctx->state = LC_STOPPED;
return module_lifecycle_report_state(ctx, LC_STOPPED);
}
break;
case LC_RUNNING:
if ((target == LC_STOPPED) || (target == LC_SUSPENDED)) {
return module_lifecycle_do_stop(ctx, target);
}
break;
case LC_STOPPED:
case LC_SUSPENDED:
if (target == LC_RUNNING) {
return module_lifecycle_do_start(ctx, LC_RUNNING);
}
break;
case LC_ERROR:
default:
break;
}
return -EPERM;
}
#endif /* MODULE_LIFECYCLE_H_ */

View File

@@ -26,7 +26,10 @@ CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_HEAP_MEM_POOL_SIZE=4096
CONFIG_LOG=y
CONFIG_LOG_BUFFER_SIZE=16384
CONFIG_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE=16384
CONFIG_ASSERT=y
CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=16384
CONFIG_APP_EVENT_MANAGER_MAX_EVENT_CNT=64
CONFIG_LED_STRIP=y
CONFIG_WS2812_STRIP_SPI=y

View File

@@ -18,6 +18,7 @@
#include <zephyr/pm/device.h>
#include "bat_state_event.h"
#include "module_lifecycle.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
@@ -32,17 +33,43 @@ BUILD_ASSERT(DT_NODE_HAS_STATUS(VBATT_NODE, okay),
BUILD_ASSERT(DT_NODE_HAS_STATUS(IP5306_NODE, okay),
"Missing ip5306 node in devicetree");
static const struct device *const vbatt_dev = DEVICE_DT_GET(VBATT_NODE);
static const struct device *const ip5306_dev = DEVICE_DT_GET(IP5306_NODE);
static struct k_work_delayable battery_sample_work;
static struct {
struct battery_module_ctx {
struct module_lifecycle_ctx lc;
const struct device *vbatt_dev;
const struct device *ip5306_dev;
struct k_work_delayable battery_sample_work;
struct {
bool valid;
uint8_t soc;
bool charging;
bool full;
} last_bat_state;
static bool initialized;
static bool running;
} last_bat_state;
};
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 battery_module_ctx ctx = {
.lc = {
.state = LC_UNINIT,
.cfg = &lifecycle_cfg,
.ops = &lifecycle_ops,
},
.vbatt_dev = DEVICE_DT_GET(VBATT_NODE),
.ip5306_dev = DEVICE_DT_GET(IP5306_NODE),
};
static int sensor_value_to_mv(const struct sensor_value *value)
{
@@ -53,7 +80,7 @@ static int measurement_enable(bool enable)
{
enum pm_device_action action = enable ? PM_DEVICE_ACTION_RESUME
: PM_DEVICE_ACTION_SUSPEND;
int err = pm_device_action_run(vbatt_dev, action);
int err = pm_device_action_run(ctx.vbatt_dev, action);
if (err && (err != -EALREADY) && (err != -ENOTSUP)) {
LOG_ERR("Cannot %s vbatt sensor (%d)", enable ? "resume" : "suspend", err);
@@ -90,23 +117,23 @@ static void battery_sample_fn(struct k_work *work)
ARG_UNUSED(work);
if (!running) {
if (!module_lifecycle_is_running(&ctx.lc)) {
return;
}
err = sensor_sample_fetch(vbatt_dev);
err = sensor_sample_fetch(ctx.vbatt_dev);
if (err) {
LOG_WRN("Battery sample fetch failed (%d)", err);
goto reschedule;
}
err = sensor_channel_get(vbatt_dev, SENSOR_CHAN_VOLTAGE, &voltage);
err = sensor_channel_get(ctx.vbatt_dev, SENSOR_CHAN_VOLTAGE, &voltage);
if (err) {
LOG_WRN("Battery channel get failed (%d)", err);
goto reschedule;
}
err = ip5306_get_status(ip5306_dev, &pmic_status);
err = ip5306_get_status(ctx.ip5306_dev, &pmic_status);
if (err) {
LOG_WRN("IP5306 status read failed (%d)", err);
goto reschedule;
@@ -115,53 +142,53 @@ static void battery_sample_fn(struct k_work *work)
voltage_mv = sensor_value_to_mv(&voltage);
uint8_t soc = battery_soc_from_mv(voltage_mv);
if (!last_bat_state.valid ||
(last_bat_state.soc != soc) ||
(last_bat_state.charging != pmic_status.charging) ||
(last_bat_state.full != pmic_status.full)) {
last_bat_state.valid = true;
last_bat_state.soc = soc;
last_bat_state.charging = pmic_status.charging;
last_bat_state.full = pmic_status.full;
if (!ctx.last_bat_state.valid ||
(ctx.last_bat_state.soc != soc) ||
(ctx.last_bat_state.charging != pmic_status.charging) ||
(ctx.last_bat_state.full != pmic_status.full)) {
ctx.last_bat_state.valid = true;
ctx.last_bat_state.soc = soc;
ctx.last_bat_state.charging = pmic_status.charging;
ctx.last_bat_state.full = pmic_status.full;
submit_bat_state_event(soc, pmic_status.charging, pmic_status.full);
}
reschedule:
if (running) {
k_work_reschedule(&battery_sample_work, BATTERY_SAMPLE_INTERVAL);
if (module_lifecycle_is_running(&ctx.lc)) {
k_work_reschedule(&ctx.battery_sample_work, BATTERY_SAMPLE_INTERVAL);
}
}
static int module_init(void)
static int do_init(void)
{
if (!device_is_ready(vbatt_dev)) {
if (!device_is_ready(ctx.vbatt_dev)) {
LOG_ERR("vbatt device not ready");
return -ENODEV;
}
if (!device_is_ready(ip5306_dev)) {
if (!device_is_ready(ctx.ip5306_dev)) {
LOG_ERR("ip5306 device not ready");
return -ENODEV;
}
int err = ip5306_init(ip5306_dev);
int err = ip5306_init(ctx.ip5306_dev);
if (err) {
LOG_ERR("ip5306 init failed (%d)", err);
return err;
}
k_work_init_delayable(&battery_sample_work, battery_sample_fn);
memset(&last_bat_state, 0, sizeof(last_bat_state));
k_work_init_delayable(&ctx.battery_sample_work, battery_sample_fn);
memset(&ctx.last_bat_state, 0, sizeof(ctx.last_bat_state));
power_manager_restrict(MODULE_IDX(MODULE), POWER_MANAGER_LEVEL_SUSPENDED);
return 0;
}
static int module_start(void)
static int do_start(void)
{
int err;
if (running) {
if (module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
@@ -170,21 +197,21 @@ static int module_start(void)
return err;
}
running = true;
k_work_reschedule(&battery_sample_work, K_NO_WAIT);
k_work_reschedule(&ctx.battery_sample_work, K_NO_WAIT);
return 0;
}
static void module_pause(void)
static int do_stop(void)
{
if (!running) {
return;
if (!module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
(void)k_work_cancel_delayable(&battery_sample_work);
(void)k_work_cancel_delayable(&ctx.battery_sample_work);
(void)measurement_enable(false);
running = false;
return 0;
}
static bool app_event_handler(const struct app_event_header *aeh)
@@ -193,47 +220,23 @@ static bool app_event_handler(const struct app_event_header *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);
}
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
module_pause();
module_set_state(MODULE_STATE_STANDBY);
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_STOPPED);
}
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);
}
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;

View File

@@ -11,34 +11,54 @@
#include <zephyr/logging/log.h>
#include "bat_state_event.h"
#include "module_lifecycle.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
static uint8_t current_soc = 100U;
static bool initialized;
static bool running;
static bool ble_ready;
struct ble_bas_module_ctx {
struct module_lifecycle_ctx lc;
uint8_t current_soc;
bool ble_ready;
};
static int module_init(void)
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_NONE,
.stopped_state = MODULE_STATE_OFF,
};
static const struct module_lifecycle_ops lifecycle_ops = {
.do_init = do_init,
.do_start = do_start,
.do_stop = do_stop,
};
static struct ble_bas_module_ctx ctx = {
.lc = {
.state = LC_UNINIT,
.cfg = &lifecycle_cfg,
.ops = &lifecycle_ops,
},
.current_soc = 100U,
};
static int do_init(void)
{
return 0;
}
static int module_start(void)
static int bas_update_level(void)
{
int err;
int err = bt_bas_set_battery_level(ctx.current_soc);
if (running) {
if ((err == -EAGAIN) || (err == -ENOTCONN)) {
LOG_INF("BAS notify deferred (%d)", err);
return 0;
}
running = true;
if (!ble_ready) {
return 0;
}
err = bt_bas_set_battery_level(current_soc);
if (err) {
LOG_WRN("bt_bas_set_battery_level failed (%d)", err);
return err;
@@ -47,21 +67,30 @@ static int module_start(void)
return 0;
}
static void module_pause(void)
static int do_start(void)
{
running = false;
if (module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
if (!ctx.ble_ready) {
return 0;
}
return bas_update_level();
}
static int do_stop(void)
{
return 0;
}
static bool handle_bat_state_event(const struct bat_state_event *event)
{
current_soc = event->soc;
ctx.current_soc = event->soc;
if (running && ble_ready) {
int err = bt_bas_set_battery_level(current_soc);
if (err) {
LOG_WRN("bt_bas_set_battery_level failed (%d)", err);
}
if (module_lifecycle_is_running(&ctx.lc) && ctx.ble_ready) {
(void)bas_update_level();
}
return false;
@@ -75,38 +104,22 @@ 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);
int err;
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
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);
}
(void)module_set_lifecycle(&ctx.lc,
ctx.ble_ready ? LC_RUNNING :
LC_STOPPED);
return false;
}
if (check_state(event, MODULE_ID(ble_state), MODULE_STATE_READY)) {
ble_ready = true;
ctx.ble_ready = true;
if (running) {
err = bt_bas_set_battery_level(current_soc);
if (err) {
LOG_WRN("bt_bas_set_battery_level failed (%d)", err);
if (ctx.lc.state == LC_STOPPED) {
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
} else if (module_lifecycle_is_running(&ctx.lc)) {
(void)bas_update_level();
}
}
module_set_state(MODULE_STATE_READY);
return false;
}

View File

@@ -18,6 +18,7 @@
#include "hid_report_sent_event.h"
#include "hid_tx_report_event.h"
#include "keyboard_core.h"
#include "module_lifecycle.h"
#include "set_protocol_event.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
@@ -40,15 +41,40 @@ BT_HIDS_DEF(hids_obj,
KEYBOARD_CONSUMER_REPORT_SIZE,
BLE_HID_KEYS_LED_REPORT_SIZE);
static struct bt_conn *active_conn;
static struct in_flight_report in_flight;
static enum keyboard_protocol_mode protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT;
static bool initialized;
static bool running;
static bool secured;
static bool keyboard_report_notify_enabled;
static bool consumer_report_notify_enabled;
static bool boot_keyboard_notify_enabled;
struct ble_hid_module_ctx {
struct module_lifecycle_ctx lc;
struct bt_conn *active_conn;
struct in_flight_report in_flight;
enum keyboard_protocol_mode protocol_mode;
bool secured;
bool keyboard_report_notify_enabled;
bool consumer_report_notify_enabled;
bool boot_keyboard_notify_enabled;
};
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 ble_hid_module_ctx ctx = {
.lc = {
.state = LC_UNINIT,
.cfg = &lifecycle_cfg,
.ops = &lifecycle_ops,
},
.protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT,
};
static const uint8_t hid_report_desc[] = {
0x05, 0x01, /* Usage Page (Generic Desktop) */
@@ -100,24 +126,25 @@ static const uint8_t hid_report_desc[] = {
static void submit_ble_transport_state_event(void)
{
bool ready = running && secured && (active_conn != NULL);
bool ready = module_lifecycle_is_running(&ctx.lc) &&
ctx.secured && (ctx.active_conn != NULL);
uint8_t report_ready_bm = 0U;
if (ready && ((protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT) ?
boot_keyboard_notify_enabled :
keyboard_report_notify_enabled)) {
if (ready && ((ctx.protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT) ?
ctx.boot_keyboard_notify_enabled :
ctx.keyboard_report_notify_enabled)) {
report_ready_bm |= BIT(KEYBOARD_REPORT_TYPE_KEYS);
}
if (ready && (protocol_mode == KEYBOARD_PROTOCOL_MODE_REPORT) &&
consumer_report_notify_enabled) {
if (ready && (ctx.protocol_mode == KEYBOARD_PROTOCOL_MODE_REPORT) &&
ctx.consumer_report_notify_enabled) {
report_ready_bm |= BIT(KEYBOARD_REPORT_TYPE_CONSUMER);
}
submit_hid_channel_state_event(
HID_SEND_CH_BLE_SHARED,
report_ready_bm,
protocol_mode);
ctx.protocol_mode);
}
static void input_report_notify_handler(uint8_t report_id, enum bt_hids_notify_evt evt)
@@ -125,9 +152,9 @@ static void input_report_notify_handler(uint8_t report_id, enum bt_hids_notify_e
bool enabled = (evt == BT_HIDS_CCCD_EVT_NOTIFY_ENABLED);
if (report_id == BLE_HID_KEYS_REPORT_ID) {
keyboard_report_notify_enabled = enabled;
ctx.keyboard_report_notify_enabled = enabled;
} else if (report_id == BLE_HID_CONSUMER_REPORT_ID) {
consumer_report_notify_enabled = enabled;
ctx.consumer_report_notify_enabled = enabled;
}
submit_ble_transport_state_event();
@@ -135,7 +162,7 @@ static void input_report_notify_handler(uint8_t report_id, enum bt_hids_notify_e
static void boot_keyboard_notify_handler(enum bt_hids_notify_evt evt)
{
boot_keyboard_notify_enabled = (evt == BT_HIDS_CCCD_EVT_NOTIFY_ENABLED);
ctx.boot_keyboard_notify_enabled = (evt == BT_HIDS_CCCD_EVT_NOTIFY_ENABLED);
submit_ble_transport_state_event();
}
@@ -144,14 +171,14 @@ static void hid_report_complete_cb(struct bt_conn *conn, void *user_data)
ARG_UNUSED(conn);
ARG_UNUSED(user_data);
if (!in_flight.active) {
if (!ctx.in_flight.active) {
return;
}
submit_hid_report_sent_event(HID_SEND_CH_BLE_SHARED,
in_flight.report_type,
in_flight.sequence, false);
in_flight.active = false;
ctx.in_flight.report_type,
ctx.in_flight.sequence, false);
ctx.in_flight.active = false;
}
static void keyboard_led_report_common(struct bt_hids_rep *rep, bool write)
@@ -187,22 +214,22 @@ static void pm_evt_handler(enum bt_hids_pm_evt evt, struct bt_conn *conn)
switch (evt) {
case BT_HIDS_PM_EVT_BOOT_MODE_ENTERED:
protocol_mode = KEYBOARD_PROTOCOL_MODE_BOOT;
ctx.protocol_mode = KEYBOARD_PROTOCOL_MODE_BOOT;
break;
case BT_HIDS_PM_EVT_REPORT_MODE_ENTERED:
protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT;
ctx.protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT;
break;
default:
return;
}
submit_set_protocol_event(HID_TRANSPORT_BLE, protocol_mode);
submit_set_protocol_event(HID_TRANSPORT_BLE, ctx.protocol_mode);
submit_ble_transport_state_event();
}
static int module_init(void)
static int do_init(void)
{
struct bt_hids_init_param hids_init_param = { 0 };
struct bt_hids_inp_rep *input_report;
@@ -239,38 +266,38 @@ static int module_init(void)
return bt_hids_init(&hids_obj, &hids_init_param);
}
static int module_start(void)
static int do_start(void)
{
if (running) {
if (module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
running = true;
submit_ble_transport_state_event();
return 0;
}
static void module_pause(void)
static int do_stop(void)
{
if (!running) {
return;
if (!module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
in_flight.active = false;
running = false;
ctx.in_flight.active = false;
submit_ble_transport_state_event();
return 0;
}
static void reset_connection_state(void)
{
active_conn = NULL;
secured = false;
keyboard_report_notify_enabled = false;
consumer_report_notify_enabled = false;
boot_keyboard_notify_enabled = false;
protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT;
in_flight.active = false;
ctx.active_conn = NULL;
ctx.secured = false;
ctx.keyboard_report_notify_enabled = false;
ctx.consumer_report_notify_enabled = false;
ctx.boot_keyboard_notify_enabled = false;
ctx.protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT;
ctx.in_flight.active = false;
}
static bool handle_ble_peer_event(const struct ble_peer_event *event)
@@ -279,13 +306,13 @@ static bool handle_ble_peer_event(const struct ble_peer_event *event)
switch (event->state) {
case PEER_STATE_CONNECTED:
if (active_conn != NULL) {
if (ctx.active_conn != NULL) {
return false;
}
active_conn = event->id;
protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT;
submit_set_protocol_event(HID_TRANSPORT_BLE, protocol_mode);
ctx.active_conn = event->id;
ctx.protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT;
submit_set_protocol_event(HID_TRANSPORT_BLE, ctx.protocol_mode);
err = bt_hids_connected(&hids_obj, event->id);
if (err) {
LOG_ERR("bt_hids_connected failed (%d)", err);
@@ -294,16 +321,16 @@ static bool handle_ble_peer_event(const struct ble_peer_event *event)
return false;
case PEER_STATE_SECURED:
if (active_conn != event->id) {
if (ctx.active_conn != event->id) {
return false;
}
secured = true;
ctx.secured = true;
submit_ble_transport_state_event();
return false;
case PEER_STATE_DISCONNECTED:
if (active_conn != event->id) {
if (ctx.active_conn != event->id) {
return false;
}
@@ -325,34 +352,35 @@ static bool handle_hid_tx_report_event(const struct hid_tx_report_event *event)
{
int err;
if (!running || (event->channel != HID_SEND_CH_BLE_SHARED) ||
in_flight.active) {
if (!module_lifecycle_is_running(&ctx.lc) ||
(event->channel != HID_SEND_CH_BLE_SHARED) ||
ctx.in_flight.active) {
return false;
}
if ((active_conn == NULL) || !secured) {
if ((ctx.active_conn == NULL) || !ctx.secured) {
return false;
}
if (event->report_type == KEYBOARD_REPORT_TYPE_KEYS) {
if (event->protocol_mode != protocol_mode) {
if (event->protocol_mode != ctx.protocol_mode) {
LOG_WRN("Drop BLE keys report due to protocol mismatch");
return false;
}
in_flight.active = true;
in_flight.report_type = event->report_type;
in_flight.sequence = event->sequence;
ctx.in_flight.active = true;
ctx.in_flight.report_type = event->report_type;
ctx.in_flight.sequence = event->sequence;
if (protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT) {
if (ctx.protocol_mode == KEYBOARD_PROTOCOL_MODE_BOOT) {
err = bt_hids_boot_kb_inp_rep_send(&hids_obj,
active_conn,
ctx.active_conn,
event->dyndata.data,
(uint8_t)event->dyndata.size,
hid_report_complete_cb);
} else {
err = bt_hids_inp_rep_send(&hids_obj,
active_conn,
ctx.active_conn,
BLE_HID_KEYS_REPORT_IDX,
event->dyndata.data,
(uint8_t)event->dyndata.size,
@@ -360,7 +388,7 @@ static bool handle_hid_tx_report_event(const struct hid_tx_report_event *event)
}
if (err) {
in_flight.active = false;
ctx.in_flight.active = false;
LOG_WRN("BLE keyboard report submit failed (%d)", err);
submit_hid_report_sent_event(HID_SEND_CH_BLE_SHARED,
KEYBOARD_REPORT_TYPE_KEYS,
@@ -371,23 +399,23 @@ static bool handle_hid_tx_report_event(const struct hid_tx_report_event *event)
}
if (event->report_type == KEYBOARD_REPORT_TYPE_CONSUMER) {
if (protocol_mode != KEYBOARD_PROTOCOL_MODE_REPORT) {
if (ctx.protocol_mode != KEYBOARD_PROTOCOL_MODE_REPORT) {
LOG_WRN("Drop BLE consumer report in boot mode");
return false;
}
in_flight.active = true;
in_flight.report_type = event->report_type;
in_flight.sequence = event->sequence;
ctx.in_flight.active = true;
ctx.in_flight.report_type = event->report_type;
ctx.in_flight.sequence = event->sequence;
err = bt_hids_inp_rep_send(&hids_obj,
active_conn,
ctx.active_conn,
BLE_HID_CONSUMER_REPORT_IDX,
event->dyndata.data,
(uint8_t)event->dyndata.size,
hid_report_complete_cb);
if (err) {
in_flight.active = false;
ctx.in_flight.active = false;
LOG_WRN("BLE consumer report submit failed (%d)", err);
submit_hid_report_sent_event(HID_SEND_CH_BLE_SHARED,
KEYBOARD_REPORT_TYPE_CONSUMER,
@@ -410,48 +438,25 @@ 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);
int err;
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
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);
}
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
module_pause();
module_set_state(MODULE_STATE_STANDBY);
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_STOPPED);
}
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);
}
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;

View File

@@ -13,6 +13,7 @@
#include <zephyr/bluetooth/services/nus.h>
#include <zephyr/logging/log.h>
#include "module_lifecycle.h"
#include "proto_rx_event.h"
#include "proto_transport_state_event.h"
#include "proto_tx_event.h"
@@ -27,20 +28,39 @@ enum ble_nus_business_state {
};
struct ble_nus_ctx {
enum module_state lifecycle;
struct module_lifecycle_ctx lc;
enum ble_nus_business_state business;
struct bt_conn *active_conn;
};
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 ble_nus_ctx ctx = {
.lifecycle = MODULE_STATE_OFF,
.lc = {
.state = LC_UNINIT,
.cfg = &lifecycle_cfg,
.ops = &lifecycle_ops,
},
.business = BLE_NUS_STACK_OFFLINE,
.active_conn = NULL,
};
static bool lifecycle_is_ready(void)
{
return ctx.lifecycle == MODULE_STATE_READY;
return module_lifecycle_is_running(&ctx.lc);
}
static enum proto_transport_link_state transport_link_state_get(void)
@@ -51,11 +71,11 @@ static enum proto_transport_link_state transport_link_state_get(void)
PROTO_TRANSPORT_LINK_DOWN;
}
static void state_reconcile(enum module_state old_lifecycle,
static void state_reconcile(enum module_lifecycle old_lifecycle,
enum ble_nus_business_state old_business)
{
enum proto_transport_link_state old_link =
((old_lifecycle == MODULE_STATE_READY) &&
((old_lifecycle == LC_RUNNING) &&
(old_business == BLE_NUS_SESSION_READY)) ?
PROTO_TRANSPORT_LINK_READY :
PROTO_TRANSPORT_LINK_DOWN;
@@ -67,23 +87,9 @@ static void state_reconcile(enum module_state old_lifecycle,
}
}
static void lifecycle_set(enum module_state new_state)
{
enum module_state old_lifecycle = ctx.lifecycle;
enum ble_nus_business_state old_business = ctx.business;
if (ctx.lifecycle == new_state) {
return;
}
ctx.lifecycle = new_state;
state_reconcile(old_lifecycle, old_business);
module_set_state(new_state);
}
static void business_state_set(enum ble_nus_business_state new_state)
{
enum module_state old_lifecycle = ctx.lifecycle;
enum module_lifecycle old_lifecycle = ctx.lc.state;
enum ble_nus_business_state old_business = ctx.business;
if (ctx.business == new_state) {
@@ -150,7 +156,7 @@ static void reset_connection_state(void)
}
}
static int module_init(void)
static int do_init(void)
{
int err;
@@ -160,23 +166,33 @@ static int module_init(void)
return err;
}
ctx = (struct ble_nus_ctx) {
.lifecycle = MODULE_STATE_OFF,
.business = BLE_NUS_STACK_OFFLINE,
.active_conn = NULL,
};
ctx.business = BLE_NUS_STACK_OFFLINE;
ctx.active_conn = NULL;
return 0;
}
static void module_start(void)
static int do_start(void)
{
lifecycle_set(MODULE_STATE_READY);
return 0;
}
static void module_pause(void)
static int do_stop(void)
{
lifecycle_set(MODULE_STATE_STANDBY);
return 0;
}
static int apply_lifecycle(enum module_lifecycle target)
{
enum module_lifecycle old_lifecycle = ctx.lc.state;
enum ble_nus_business_state old_business = ctx.business;
int err = module_set_lifecycle(&ctx.lc, target);
if (!err) {
state_reconcile(old_lifecycle, old_business);
}
return err;
}
static bool handle_ble_peer_event(const struct ble_peer_event *event)
@@ -243,27 +259,13 @@ static bool app_event_handler(const struct app_event_header *aeh)
cast_module_state_event(aeh);
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
if (ctx.lifecycle == MODULE_STATE_OFF) {
int err = module_init();
if (err) {
lifecycle_set(MODULE_STATE_ERROR);
return false;
}
}
module_start();
(void)apply_lifecycle(LC_RUNNING);
return false;
}
if (check_state(event, MODULE_ID(ble_state), MODULE_STATE_READY)) {
business_state_set_stack_ready();
if (lifecycle_is_ready()) {
lifecycle_set(MODULE_STATE_READY);
}
return false;
}
@@ -271,16 +273,16 @@ static bool app_event_handler(const struct app_event_header *aeh)
}
if (is_power_down_event(aeh)) {
if (ctx.lifecycle != MODULE_STATE_OFF) {
module_pause();
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)apply_lifecycle(LC_STOPPED);
}
return false;
}
if (is_wake_up_event(aeh)) {
if (ctx.lifecycle != MODULE_STATE_OFF) {
module_start();
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)apply_lifecycle(LC_RUNNING);
}
return false;

View File

@@ -16,6 +16,7 @@
#include "bat_state_event.h"
#include "datetime_event.h"
#include "hid_led_event.h"
#include "module_lifecycle.h"
#include "mode_switch_event.h"
#include "theme_rgb_update_event.h"
#include "theme_color.h"
@@ -27,46 +28,74 @@ BUILD_ASSERT(DT_HAS_CHOSEN(zephyr_display), "Missing zephyr,display chosen node"
BUILD_ASSERT(DT_NODE_HAS_STATUS(DT_ALIAS(backlight), okay),
"Missing backlight alias");
static const struct device *const display_dev =
DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
static const struct device *const backlight_dev =
DEVICE_DT_GET(DT_PARENT(DT_ALIAS(backlight)));
static const uint32_t backlight_idx = DT_NODE_CHILD_IDX(DT_ALIAS(backlight));
static struct ui_main_model ui_model = {
struct display_module_ctx {
struct module_lifecycle_ctx lc;
const struct device *display_dev;
const struct device *backlight_dev;
uint32_t backlight_idx;
struct ui_main_model ui_model;
bool lvgl_initialized;
char date_text[DATETIME_EVENT_DATE_TEXT_LEN];
char time_text[DATETIME_EVENT_TIME_TEXT_LEN];
};
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 display_module_ctx ctx = {
.lc = {
.state = LC_UNINIT,
.cfg = &lifecycle_cfg,
.ops = &lifecycle_ops,
},
.display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display)),
.backlight_dev = DEVICE_DT_GET(DT_PARENT(DT_ALIAS(backlight))),
.backlight_idx = DT_NODE_CHILD_IDX(DT_ALIAS(backlight)),
.ui_model = {
.theme_color = LV_COLOR_MAKE(BLINKY_THEME_DEFAULT_R,
BLINKY_THEME_DEFAULT_G,
BLINKY_THEME_DEFAULT_B),
.inactive_border_color = LV_COLOR_MAKE(0x3A, 0x44, 0x52),
.mode = MODE_SWITCH_BLE,
},
.date_text = "1970/01/01",
.time_text = "00:00:00",
};
static bool initialized;
static bool running;
static bool lvgl_initialized;
static char date_text[DATETIME_EVENT_DATE_TEXT_LEN] = "1970/01/01";
static char time_text[DATETIME_EVENT_TIME_TEXT_LEN] = "00:00:00";
static int backlight_set(bool on)
{
if (on) {
return led_on(backlight_dev, backlight_idx);
return led_on(ctx.backlight_dev, ctx.backlight_idx);
}
return led_off(backlight_dev, backlight_idx);
return led_off(ctx.backlight_dev, ctx.backlight_idx);
}
static int module_init(void)
static int do_init(void)
{
int err;
LOG_INF("Display init on %s", display_dev->name);
LOG_INF("Display init on %s", ctx.display_dev->name);
if (!device_is_ready(display_dev)) {
LOG_ERR("Display device %s not ready", display_dev->name);
if (!device_is_ready(ctx.display_dev)) {
LOG_ERR("Display device %s not ready", ctx.display_dev->name);
return -ENODEV;
}
if (!device_is_ready(backlight_dev)) {
LOG_ERR("Backlight device %s not ready", backlight_dev->name);
if (!device_is_ready(ctx.backlight_dev)) {
LOG_ERR("Backlight device %s not ready", ctx.backlight_dev->name);
return -ENODEV;
}
@@ -79,25 +108,25 @@ static int module_init(void)
return 0;
}
static int module_start(void)
static int do_start(void)
{
int err;
if (running) {
if (module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
if (!lvgl_initialized) {
if (!ctx.lvgl_initialized) {
err = lvgl_init();
if (err) {
LOG_ERR("lvgl_init failed (%d)", err);
return err;
}
lvgl_initialized = true;
ctx.lvgl_initialized = true;
lvgl_lock();
ui_main_init(&ui_model, date_text, time_text);
ui_main_init(&ctx.ui_model, ctx.date_text, ctx.time_text);
lvgl_unlock();
}
@@ -107,39 +136,39 @@ static int module_start(void)
return err;
}
err = display_blanking_off(display_dev);
err = display_blanking_off(ctx.display_dev);
if (err) {
LOG_ERR("display_blanking_off failed (%d)", err);
(void)backlight_set(false);
return err;
}
running = true;
LOG_INF("LVGL display started");
return 0;
}
static void module_pause(void)
static int do_stop(void)
{
if (!running) {
return;
if (!module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
(void)display_blanking_on(display_dev);
(void)display_blanking_on(ctx.display_dev);
(void)backlight_set(false);
running = false;
LOG_INF("LVGL display paused");
return 0;
}
static void refresh_ui(void)
{
if (!lvgl_initialized) {
if (!ctx.lvgl_initialized) {
return;
}
lvgl_lock();
ui_main_refresh_all(&ui_model, date_text, time_text);
ui_main_refresh_all(&ctx.ui_model, ctx.date_text, ctx.time_text);
lvgl_unlock();
}
@@ -148,9 +177,9 @@ static bool app_event_handler(const struct app_event_header *aeh)
if (is_bat_state_event(aeh)) {
const struct bat_state_event *event = cast_bat_state_event(aeh);
ui_model.battery_level = event->soc;
ui_model.charging = event->charging;
ui_model.full = event->full;
ctx.ui_model.battery_level = event->soc;
ctx.ui_model.charging = event->charging;
ctx.ui_model.full = event->full;
refresh_ui();
return false;
}
@@ -158,7 +187,7 @@ static bool app_event_handler(const struct app_event_header *aeh)
if (is_mode_switch_event(aeh)) {
const struct mode_switch_event *event = cast_mode_switch_event(aeh);
ui_model.mode = event->mode;
ctx.ui_model.mode = event->mode;
refresh_ui();
return false;
}
@@ -166,7 +195,7 @@ static bool app_event_handler(const struct app_event_header *aeh)
if (is_hid_led_event(aeh)) {
const struct hid_led_event *event = cast_hid_led_event(aeh);
ui_model.led_mask = event->led_bm;
ctx.ui_model.led_mask = event->led_bm;
refresh_ui();
return false;
}
@@ -175,7 +204,7 @@ static bool app_event_handler(const struct app_event_header *aeh)
const struct theme_rgb_update_event *event =
cast_theme_rgb_update_event(aeh);
ui_model.theme_color = (lv_color_t)LV_COLOR_MAKE(event->theme.r,
ctx.ui_model.theme_color = (lv_color_t)LV_COLOR_MAKE(event->theme.r,
event->theme.g,
event->theme.b);
refresh_ui();
@@ -185,58 +214,35 @@ static bool app_event_handler(const struct app_event_header *aeh)
if (is_datetime_event(aeh)) {
const struct datetime_event *event = cast_datetime_event(aeh);
strncpy(date_text, event->date_text, sizeof(date_text));
date_text[sizeof(date_text) - 1] = '\0';
strncpy(time_text, event->time_text, sizeof(time_text));
time_text[sizeof(time_text) - 1] = '\0';
strncpy(ctx.date_text, event->date_text, sizeof(ctx.date_text));
ctx.date_text[sizeof(ctx.date_text) - 1] = '\0';
strncpy(ctx.time_text, event->time_text, sizeof(ctx.time_text));
ctx.time_text[sizeof(ctx.time_text) - 1] = '\0';
refresh_ui();
return false;
}
if (is_module_state_event(aeh)) {
const struct module_state_event *event = cast_module_state_event(aeh);
int err;
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
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);
}
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
module_pause();
module_set_state(MODULE_STATE_STANDBY);
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_STOPPED);
}
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);
}
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;

View File

@@ -13,6 +13,7 @@
#include <zephyr/pm/device.h>
#include "encoder_event.h"
#include "module_lifecycle.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
@@ -22,17 +23,41 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
BUILD_ASSERT(DT_NODE_EXISTS(ENCODER_QDEC_NODE), "Missing qdec0 alias");
static const struct device *const qdec_dev = DEVICE_DT_GET(ENCODER_QDEC_NODE);
static struct k_work encoder_report_work;
static struct sensor_trigger encoder_trigger = {
.type = SENSOR_TRIG_DATA_READY,
.chan = SENSOR_CHAN_ROTATION,
struct encoder_module_ctx {
struct module_lifecycle_ctx lc;
const struct device *qdec_dev;
struct k_work encoder_report_work;
struct sensor_trigger encoder_trigger;
int64_t angle_remainder_udeg;
};
static bool initialized;
static bool running;
static int64_t angle_remainder_udeg;
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 encoder_module_ctx ctx = {
.lc = {
.state = LC_UNINIT,
.cfg = &lifecycle_cfg,
.ops = &lifecycle_ops,
},
.qdec_dev = DEVICE_DT_GET(ENCODER_QDEC_NODE),
.encoder_trigger = {
.type = SENSOR_TRIG_DATA_READY,
.chan = SENSOR_CHAN_ROTATION,
},
};
static int64_t sensor_value_to_udeg(const struct sensor_value *value)
{
@@ -68,25 +93,26 @@ static void encoder_report_work_handler(struct k_work *work)
ARG_UNUSED(work);
if (!running) {
if (!module_lifecycle_is_running(&ctx.lc)) {
return;
}
err = sensor_sample_fetch(qdec_dev);
err = sensor_sample_fetch(ctx.qdec_dev);
if (err) {
LOG_WRN("QDEC sample fetch failed (%d)", err);
return;
}
err = sensor_channel_get(qdec_dev, SENSOR_CHAN_ROTATION, &rotation);
err = sensor_channel_get(ctx.qdec_dev, SENSOR_CHAN_ROTATION, &rotation);
if (err) {
LOG_WRN("QDEC channel get failed (%d)", err);
return;
}
total_udeg = angle_remainder_udeg + sensor_value_to_udeg(&rotation);
total_udeg = ctx.angle_remainder_udeg + sensor_value_to_udeg(&rotation);
detents = (int32_t)(total_udeg / ENCODER_DETENT_UDEG);
angle_remainder_udeg = total_udeg - ((int64_t)detents * ENCODER_DETENT_UDEG);
ctx.angle_remainder_udeg =
total_udeg - ((int64_t)detents * ENCODER_DETENT_UDEG);
if (detents != 0) {
submit_detents_batched(detents);
@@ -99,26 +125,27 @@ static void encoder_trigger_handler(const struct device *dev,
ARG_UNUSED(dev);
ARG_UNUSED(trigger);
if (!running) {
if (!module_lifecycle_is_running(&ctx.lc)) {
return;
}
k_work_submit(&encoder_report_work);
k_work_submit(&ctx.encoder_report_work);
}
static int module_init(void)
static int do_init(void)
{
int err;
if (!device_is_ready(qdec_dev)) {
if (!device_is_ready(ctx.qdec_dev)) {
LOG_ERR("QDEC device not ready");
return -ENODEV;
}
k_work_init(&encoder_report_work, encoder_report_work_handler);
angle_remainder_udeg = 0;
k_work_init(&ctx.encoder_report_work, encoder_report_work_handler);
ctx.angle_remainder_udeg = 0;
err = sensor_trigger_set(qdec_dev, &encoder_trigger, encoder_trigger_handler);
err = sensor_trigger_set(ctx.qdec_dev, &ctx.encoder_trigger,
encoder_trigger_handler);
if (err) {
LOG_ERR("Cannot set QDEC trigger (%d)", err);
return err;
@@ -127,41 +154,41 @@ static int module_init(void)
return 0;
}
static int module_start(void)
static int do_start(void)
{
int err;
if (running) {
if (module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
err = pm_device_action_run(qdec_dev, PM_DEVICE_ACTION_RESUME);
err = pm_device_action_run(ctx.qdec_dev, PM_DEVICE_ACTION_RESUME);
if (err && (err != -EALREADY) && (err != -ENOTSUP)) {
LOG_ERR("Cannot resume QDEC device (%d)", err);
return err;
}
angle_remainder_udeg = 0;
running = true;
ctx.angle_remainder_udeg = 0;
return 0;
}
static void module_pause(void)
static int do_stop(void)
{
int err;
if (!running) {
return;
if (!module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
err = pm_device_action_run(qdec_dev, PM_DEVICE_ACTION_SUSPEND);
err = pm_device_action_run(ctx.qdec_dev, PM_DEVICE_ACTION_SUSPEND);
if (err && (err != -EALREADY) && (err != -ENOTSUP)) {
LOG_WRN("Cannot suspend QDEC device (%d)", err);
}
angle_remainder_udeg = 0;
running = false;
ctx.angle_remainder_udeg = 0;
return 0;
}
static bool app_event_handler(const struct app_event_header *aeh)
@@ -170,47 +197,23 @@ static bool app_event_handler(const struct app_event_header *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);
}
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
module_pause();
module_set_state(MODULE_STATE_STANDBY);
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_STOPPED);
}
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);
}
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;

View File

@@ -15,6 +15,7 @@
#include "hid_tx_report_event.h"
#include "keyboard_core.h"
#include "keyboard_hid_report_event.h"
#include "module_lifecycle.h"
#include "mode_switch_event.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
@@ -49,7 +50,42 @@ struct in_flight_report {
uint16_t sequence;
};
static struct hid_channel_state_data channel_state[HID_SEND_CH_COUNT] = {
struct hid_flowctrl_module_ctx {
struct module_lifecycle_ctx lc;
struct hid_channel_state_data channel_state[HID_SEND_CH_COUNT];
struct pending_report pending_keys;
struct pending_report pending_consumer_latest;
struct queued_report consumer_fifo[HID_FLOWCTRL_FIFO_DEPTH];
uint8_t consumer_fifo_head;
uint8_t consumer_fifo_tail;
uint8_t consumer_fifo_count;
struct in_flight_report in_flight[HID_SEND_CH_COUNT];
enum mode_switch_mode current_mode;
uint16_t next_sequence;
};
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 hid_flowctrl_module_ctx ctx = {
.lc = {
.state = LC_UNINIT,
.cfg = &lifecycle_cfg,
.ops = &lifecycle_ops,
},
.channel_state = {
[HID_SEND_CH_USB_KEYS] = {
.protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT,
},
@@ -59,18 +95,21 @@ static struct hid_channel_state_data channel_state[HID_SEND_CH_COUNT] = {
[HID_SEND_CH_BLE_SHARED] = {
.protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT,
},
},
};
static struct pending_report pending_keys;
static struct pending_report pending_consumer_latest;
static struct queued_report consumer_fifo[HID_FLOWCTRL_FIFO_DEPTH];
static uint8_t consumer_fifo_head;
static uint8_t consumer_fifo_tail;
static uint8_t consumer_fifo_count;
static struct in_flight_report in_flight[HID_SEND_CH_COUNT];
static enum mode_switch_mode current_mode;
static uint16_t next_sequence;
static bool initialized;
static bool running;
#define lifecycle ctx.lc
#define channel_state ctx.channel_state
#define pending_keys ctx.pending_keys
#define pending_consumer_latest ctx.pending_consumer_latest
#define consumer_fifo ctx.consumer_fifo
#define consumer_fifo_head ctx.consumer_fifo_head
#define consumer_fifo_tail ctx.consumer_fifo_tail
#define consumer_fifo_count ctx.consumer_fifo_count
#define in_flight ctx.in_flight
#define current_mode ctx.current_mode
#define next_sequence ctx.next_sequence
#define running module_lifecycle_is_running(&ctx.lc)
static bool current_mode_to_channel(enum keyboard_report_type report_type,
enum hid_send_channel *channel)
@@ -359,7 +398,7 @@ static bool handle_mode_switch_event(const struct mode_switch_event *event)
return false;
}
static int module_init(void)
static int do_init(void)
{
clear_pending_reports();
current_mode = MODE_SWITCH_USB;
@@ -374,25 +413,25 @@ static int module_init(void)
return 0;
}
static int module_start(void)
static int do_start(void)
{
if (running) {
return 0;
}
running = true;
try_send_next();
return 0;
}
static void module_pause(void)
static int do_stop(void)
{
if (!running) {
return;
return 0;
}
clear_pending_reports();
running = false;
return 0;
}
static bool app_event_handler(const struct app_event_header *aeh)
@@ -421,47 +460,23 @@ static bool app_event_handler(const struct app_event_header *aeh)
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);
}
(void)module_set_lifecycle(&lifecycle, LC_RUNNING);
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
module_pause();
module_set_state(MODULE_STATE_STANDBY);
if (module_lifecycle_is_initialized(&lifecycle)) {
(void)module_set_lifecycle(&lifecycle, LC_STOPPED);
}
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);
}
if (module_lifecycle_is_initialized(&lifecycle)) {
(void)module_set_lifecycle(&lifecycle, LC_RUNNING);
}
return false;

View File

@@ -19,6 +19,7 @@
#include "function_bitmap_update_event.h"
#include "keyboard_core.h"
#include "keyboard_hid_report_event.h"
#include "module_lifecycle.h"
#include "mode_switch_event.h"
#include "set_protocol_event.h"
@@ -85,17 +86,51 @@ static const uint16_t consumer_usage_map[KEYBOARD_CONSUMER_CTRL_COUNT] = {
[KEYBOARD_CONSUMER_CTRL_PREV_TRACK] = 0x00B6,
};
static struct keyboard_state keyboard_state;
static struct keyboard_reports_cache reports_cache;
static uint8_t function_usage_mask[KEYBOARD_PROTOCOL_BITMAP_BYTES];
static enum keyboard_protocol_mode transport_protocol_modes[HID_TRANSPORT_COUNT] = {
struct keyboard_core_module_ctx {
struct module_lifecycle_ctx lc;
struct keyboard_state keyboard_state;
struct keyboard_reports_cache reports_cache;
uint8_t function_usage_mask[KEYBOARD_PROTOCOL_BITMAP_BYTES];
enum keyboard_protocol_mode transport_protocol_modes[HID_TRANSPORT_COUNT];
enum mode_switch_mode current_mode;
bool mode_valid;
};
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 keyboard_core_module_ctx ctx = {
.lc = {
.state = LC_UNINIT,
.cfg = &lifecycle_cfg,
.ops = &lifecycle_ops,
},
.transport_protocol_modes = {
[HID_TRANSPORT_USB] = KEYBOARD_PROTOCOL_MODE_REPORT,
[HID_TRANSPORT_BLE] = KEYBOARD_PROTOCOL_MODE_REPORT,
},
};
static enum mode_switch_mode current_mode;
static bool initialized;
static bool running;
static bool mode_valid;
#define lifecycle ctx.lc
#define keyboard_state ctx.keyboard_state
#define reports_cache ctx.reports_cache
#define function_usage_mask ctx.function_usage_mask
#define transport_protocol_modes ctx.transport_protocol_modes
#define current_mode ctx.current_mode
#define mode_valid ctx.mode_valid
#define running module_lifecycle_is_running(&ctx.lc)
static bool mode_to_transport(enum mode_switch_mode mode, enum hid_transport *transport)
{
@@ -440,7 +475,7 @@ static void emit_release_reports(enum mode_switch_mode mode)
}
}
static int module_init(void)
static int do_init(void)
{
keyboard_state_clear();
reports_cache_invalidate();
@@ -454,20 +489,19 @@ static int module_init(void)
return 0;
}
static int module_start(void)
static int do_start(void)
{
if (running) {
return 0;
}
running = true;
return 0;
}
static void module_pause(void)
static int do_stop(void)
{
if (!running) {
return;
return 0;
}
if (mode_valid) {
@@ -478,7 +512,8 @@ static void module_pause(void)
keyboard_state_clear();
reports_cache_invalidate();
mode_valid = false;
running = false;
return 0;
}
static bool handle_button_event(const struct button_event *event)
@@ -630,47 +665,23 @@ static bool app_event_handler(const struct app_event_header *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);
}
(void)module_set_lifecycle(&lifecycle, LC_RUNNING);
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
module_pause();
module_set_state(MODULE_STATE_STANDBY);
if (module_lifecycle_is_initialized(&lifecycle)) {
(void)module_set_lifecycle(&lifecycle, LC_STOPPED);
}
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);
}
if (module_lifecycle_is_initialized(&lifecycle)) {
(void)module_set_lifecycle(&lifecycle, LC_RUNNING);
}
return false;

View File

@@ -18,6 +18,7 @@
#include "led_effect/led_effect.h"
#include "led_strip_en_event.h"
#include "module_lifecycle.h"
#include "theme_rgb_update_event.h"
#include "theme_color.h"
@@ -34,9 +35,16 @@ BUILD_ASSERT(DT_NODE_HAS_PROP(LED_STRIP_NODE, supply_gpios),
BUILD_ASSERT(LED_STRIP_NUM_PIXELS == 17U,
"LED strip key map expects 17 pixels");
static const struct device *const strip = DEVICE_DT_GET(LED_STRIP_NODE);
static const struct gpio_dt_spec strip_en =
GPIO_DT_SPEC_GET(LED_STRIP_NODE, supply_gpios);
struct led_strip_module_ctx {
struct module_lifecycle_ctx lc;
const struct device *strip;
struct gpio_dt_spec strip_en;
struct led_rgb pixels[LED_STRIP_NUM_PIXELS];
struct k_work_delayable effect_work;
struct led_effect *effect;
struct theme_rgb current_theme;
bool enabled;
};
static const struct led_key_map led_key_map[] = {
{ KEY_ID(0, 1), 0U },
@@ -65,28 +73,47 @@ static const struct led_effect_config effect_cfg = {
.default_brightness = 0xFFU,
};
static struct led_rgb pixels[LED_STRIP_NUM_PIXELS];
static struct k_work_delayable effect_work;
static struct led_effect *effect;
static struct theme_rgb current_theme = {
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 led_strip_module_ctx ctx = {
.lc = {
.state = LC_UNINIT,
.cfg = &lifecycle_cfg,
.ops = &lifecycle_ops,
},
.strip = DEVICE_DT_GET(LED_STRIP_NODE),
.strip_en = GPIO_DT_SPEC_GET(LED_STRIP_NODE, supply_gpios),
.current_theme = {
.r = BLINKY_THEME_DEFAULT_R,
.g = BLINKY_THEME_DEFAULT_G,
.b = BLINKY_THEME_DEFAULT_B,
},
.enabled = true,
};
static bool initialized;
static bool running;
static bool enabled = true;
static void clear_pixels(void)
{
memset(pixels, 0, sizeof(pixels));
memset(ctx.pixels, 0, sizeof(ctx.pixels));
}
static int submit_frame(void)
{
int err;
err = led_strip_update_rgb(strip, pixels, ARRAY_SIZE(pixels));
err = led_strip_update_rgb(ctx.strip, ctx.pixels, ARRAY_SIZE(ctx.pixels));
if (err) {
LOG_WRN("led_strip_update_rgb failed (%d)", err);
}
@@ -98,7 +125,7 @@ static int set_strip_power(bool on)
{
int err;
err = gpio_pin_set_dt(&strip_en, on ? 1 : 0);
err = gpio_pin_set_dt(&ctx.strip_en, on ? 1 : 0);
if (err) {
LOG_WRN("LED strip EN set failed (%d)", err);
}
@@ -114,12 +141,12 @@ static int refresh_idle_frame(void)
static void stop_effect_work(void)
{
k_work_cancel_delayable(&effect_work);
k_work_cancel_delayable(&ctx.effect_work);
}
static void schedule_effect_tick(k_timeout_t delay)
{
k_work_reschedule(&effect_work, delay);
k_work_reschedule(&ctx.effect_work, delay);
}
static int enable_strip_output(void)
@@ -148,13 +175,15 @@ static void effect_work_handler(struct k_work *work)
ARG_UNUSED(work);
if (!running || !enabled || (effect == NULL)) {
if (!module_lifecycle_is_running(&ctx.lc) || !ctx.enabled ||
(ctx.effect == NULL)) {
return;
}
keep_running = effect->ops->tick(effect, k_ticks_to_ms_floor32(
keep_running = ctx.effect->ops->tick(ctx.effect,
k_ticks_to_ms_floor32(
LED_STRIP_TICK_INTERVAL.ticks),
pixels, ARRAY_SIZE(pixels));
ctx.pixels, ARRAY_SIZE(ctx.pixels));
(void)submit_frame();
if (keep_running) {
@@ -162,90 +191,85 @@ static void effect_work_handler(struct k_work *work)
}
}
static int module_init(void)
static int do_init(void)
{
int err;
if (!device_is_ready(strip)) {
LOG_ERR("LED strip device %s not ready", strip->name);
if (!device_is_ready(ctx.strip)) {
LOG_ERR("LED strip device %s not ready", ctx.strip->name);
return -ENODEV;
}
if (!gpio_is_ready_dt(&strip_en)) {
if (!gpio_is_ready_dt(&ctx.strip_en)) {
LOG_ERR("LED strip supply GPIO not ready");
return -ENODEV;
}
err = gpio_pin_configure_dt(&strip_en, GPIO_OUTPUT_INACTIVE);
err = gpio_pin_configure_dt(&ctx.strip_en, GPIO_OUTPUT_INACTIVE);
if (err) {
LOG_ERR("Failed to configure LED strip supply GPIO (%d)", err);
return err;
}
k_work_init_delayable(&effect_work, effect_work_handler);
k_work_init_delayable(&ctx.effect_work, effect_work_handler);
clear_pixels();
effect = led_effect_get_mutable(LED_EFFECT_ID_KEY_FADE);
if ((effect == NULL) || (effect->ops == NULL)) {
ctx.effect = led_effect_get_mutable(LED_EFFECT_ID_KEY_FADE);
if ((ctx.effect == NULL) || (ctx.effect->ops == NULL)) {
LOG_ERR("LED effect not available");
return -ENOENT;
}
err = effect->ops->init(effect, &effect_cfg);
err = ctx.effect->ops->init(ctx.effect, &effect_cfg);
if (err) {
LOG_ERR("LED effect init failed (%d)", err);
return err;
}
effect->ops->set_theme(effect, &current_theme);
ctx.effect->ops->set_theme(ctx.effect, &ctx.current_theme);
LOG_INF("LED strip ready len:%u", (uint32_t)led_strip_length(strip));
LOG_INF("LED strip ready len:%u", (uint32_t)led_strip_length(ctx.strip));
return 0;
}
static int module_start(void)
static int do_start(void)
{
int err;
if (running) {
if (module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
running = true;
if (!enabled) {
if (!ctx.enabled) {
return 0;
}
err = enable_strip_output();
if (err) {
running = false;
}
return err;
}
static void module_pause(void)
static int do_stop(void)
{
if (!running) {
return;
if (!module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
if ((effect != NULL) && (effect->ops != NULL)) {
effect->ops->reset(effect);
if ((ctx.effect != NULL) && (ctx.effect->ops != NULL)) {
ctx.effect->ops->reset(ctx.effect);
}
disable_strip_output();
running = false;
return 0;
}
static bool handle_button_event(const struct button_event *event)
{
if (!running || !enabled || (effect == NULL) || !event->pressed) {
if (!module_lifecycle_is_running(&ctx.lc) || !ctx.enabled ||
(ctx.effect == NULL) || !event->pressed) {
return false;
}
effect->ops->on_key_press(effect, event->key_id);
ctx.effect->ops->on_key_press(ctx.effect, event->key_id);
schedule_effect_tick(K_NO_WAIT);
return false;
@@ -253,22 +277,23 @@ static bool handle_button_event(const struct button_event *event)
static bool handle_led_strip_en_event(const struct led_strip_en_event *event)
{
enabled = event->enabled;
ctx.enabled = event->enabled;
if (!running) {
if (!module_lifecycle_is_running(&ctx.lc)) {
return false;
}
if (enabled) {
if (ctx.enabled) {
int err = enable_strip_output();
if (err) {
module_set_state(MODULE_STATE_ERROR);
LOG_WRN("LED strip enable request failed (%d)", err);
ctx.enabled = false;
return false;
}
} else {
if ((effect != NULL) && (effect->ops != NULL)) {
effect->ops->reset(effect);
if ((ctx.effect != NULL) && (ctx.effect->ops != NULL)) {
ctx.effect->ops->reset(ctx.effect);
}
disable_strip_output();
@@ -279,13 +304,14 @@ static bool handle_led_strip_en_event(const struct led_strip_en_event *event)
static bool handle_theme_rgb_update_event(const struct theme_rgb_update_event *event)
{
current_theme = event->theme;
ctx.current_theme = event->theme;
if ((effect != NULL) && (effect->ops != NULL)) {
effect->ops->set_theme(effect, &current_theme);
if ((ctx.effect != NULL) && (ctx.effect->ops != NULL)) {
ctx.effect->ops->set_theme(ctx.effect, &ctx.current_theme);
}
if (running && enabled && (effect != NULL) && effect->ops->is_active(effect)) {
if (module_lifecycle_is_running(&ctx.lc) && ctx.enabled &&
(ctx.effect != NULL) && ctx.effect->ops->is_active(ctx.effect)) {
schedule_effect_tick(K_NO_WAIT);
}
@@ -310,47 +336,23 @@ static bool app_event_handler(const struct app_event_header *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);
}
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
module_pause();
module_set_state(MODULE_STATE_STANDBY);
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_STOPPED);
}
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);
}
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;

View File

@@ -9,14 +9,40 @@
#include <zephyr/logging/log.h>
#include "module_lifecycle.h"
#include "mode_switch_event.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
static bool initialized;
static bool running;
static bool ble_adv_suspended = true;
static bool usb_enabled;
struct mode_policy_module_ctx {
struct module_lifecycle_ctx lc;
bool ble_adv_suspended;
bool usb_enabled;
};
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_policy_module_ctx ctx = {
.lc = {
.state = LC_UNINIT,
.cfg = &lifecycle_cfg,
.ops = &lifecycle_ops,
},
.ble_adv_suspended = true,
};
static void broadcast_ble_adv_req(bool suspend)
{
@@ -40,14 +66,14 @@ static void apply_mode_policy(enum mode_switch_mode mode)
bool should_suspend_ble_adv = (mode != MODE_SWITCH_BLE);
bool should_enable_usb = (mode == MODE_SWITCH_USB);
if (should_suspend_ble_adv != ble_adv_suspended) {
ble_adv_suspended = should_suspend_ble_adv;
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);
}
if (should_enable_usb != usb_enabled) {
usb_enabled = should_enable_usb;
if (usb_enabled) {
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();
@@ -65,26 +91,25 @@ static void apply_mode_policy(enum mode_switch_mode mode)
}
}
static int module_init(void)
static int do_init(void)
{
ble_adv_suspended = true;
usb_enabled = false;
ctx.ble_adv_suspended = true;
ctx.usb_enabled = false;
return 0;
}
static int module_start(void)
static int do_start(void)
{
if (running) {
if (module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
running = true;
return 0;
}
static void module_pause(void)
static int do_stop(void)
{
running = false;
return 0;
}
static bool app_event_handler(const struct app_event_header *aeh)
@@ -92,7 +117,7 @@ static bool app_event_handler(const struct app_event_header *aeh)
if (is_mode_switch_event(aeh)) {
const struct mode_switch_event *event = cast_mode_switch_event(aeh);
if (running) {
if (module_lifecycle_is_running(&ctx.lc)) {
apply_mode_policy(event->mode);
}
@@ -103,47 +128,23 @@ static bool app_event_handler(const struct app_event_header *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);
}
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
module_pause();
module_set_state(MODULE_STATE_STANDBY);
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_STOPPED);
}
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);
}
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;

View File

@@ -14,6 +14,7 @@
#include <zephyr/logging/log.h>
#include <zephyr/pm/device.h>
#include "module_lifecycle.h"
#include "mode_switch_event.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
@@ -26,15 +27,38 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
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);
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;
};
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 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)
{
@@ -53,7 +77,7 @@ 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);
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)",
@@ -71,17 +95,18 @@ static void mode_switch_sample_fn(struct k_work *work)
ARG_UNUSED(work);
if (!running) {
if (!module_lifecycle_is_running(&ctx.lc)) {
return;
}
err = sensor_sample_fetch(mode_switch_adc_dev);
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(mode_switch_adc_dev, SENSOR_CHAN_VOLTAGE, &voltage);
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;
@@ -90,39 +115,40 @@ 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 (force_report || !mode_valid || (mode != last_mode)) {
if (ctx.force_report || !ctx.mode_valid || (mode != ctx.last_mode)) {
submit_mode_switch_event(mode, sample_mv);
last_mode = mode;
mode_valid = true;
force_report = false;
ctx.last_mode = mode;
ctx.mode_valid = true;
ctx.force_report = false;
}
reschedule:
if (running) {
k_work_reschedule(&mode_switch_sample_work, MODE_SWITCH_SAMPLE_INTERVAL);
if (module_lifecycle_is_running(&ctx.lc)) {
k_work_reschedule(&ctx.mode_switch_sample_work,
MODE_SWITCH_SAMPLE_INTERVAL);
}
}
static int module_init(void)
static int do_init(void)
{
if (!device_is_ready(mode_switch_adc_dev)) {
if (!device_is_ready(ctx.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;
k_work_init_delayable(&ctx.mode_switch_sample_work, mode_switch_sample_fn);
ctx.mode_valid = false;
ctx.force_report = false;
return 0;
}
static int module_start(void)
static int do_start(void)
{
int err;
if (running) {
if (module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
@@ -131,22 +157,22 @@ static int module_start(void)
return err;
}
running = true;
force_report = true;
k_work_reschedule(&mode_switch_sample_work, K_NO_WAIT);
ctx.force_report = true;
k_work_reschedule(&ctx.mode_switch_sample_work, K_NO_WAIT);
return 0;
}
static void module_pause(void)
static int do_stop(void)
{
if (!running) {
return;
if (!module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
(void)k_work_cancel_delayable(&mode_switch_sample_work);
(void)k_work_cancel_delayable(&ctx.mode_switch_sample_work);
(void)mode_switch_enable(false);
running = false;
return 0;
}
static bool app_event_handler(const struct app_event_header *aeh)
@@ -155,47 +181,23 @@ static bool app_event_handler(const struct app_event_header *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);
}
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
module_pause();
module_set_state(MODULE_STATE_STANDBY);
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_STOPPED);
}
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);
}
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;

View File

@@ -21,6 +21,7 @@
#include "function_bitmap_state_event.h"
#include "function_bitmap_update_event.h"
#include "hid_led_event.h"
#include "module_lifecycle.h"
#include "proto_rx_event.h"
#include "proto_transport_state_event.h"
#include "proto_tx_event.h"
@@ -38,16 +39,41 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
#define PROTOCOL_CAPABILITY_FLAGS (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4))
#define PROTOCOL_MAX_MSG_LEN 128U
static bool initialized;
static bool running;
enum proto_session_state {
PROTO_SESSION_DOWN = 0,
PROTO_SESSION_WAIT_HELLO,
PROTO_SESSION_ACTIVE,
};
static enum proto_session_state session_state[PROTO_TRANSPORT_COUNT];
struct protocol_module_ctx {
struct module_lifecycle_ctx lc;
enum proto_session_state session_state[PROTO_TRANSPORT_COUNT];
};
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 protocol_module_ctx ctx = {
.lc = {
.state = LC_UNINIT,
.cfg = &lifecycle_cfg,
.ops = &lifecycle_ops,
},
};
#define session_state ctx.session_state
static int decode_body(const uint8_t *payload, size_t payload_len,
CdcPacketBody *body)
@@ -157,7 +183,7 @@ static int encode_function_bitmap_state(const uint8_t *bitmap, uint8_t *payload,
return encode_body(&body, payload, payload_buf_size, payload_len);
}
static int module_init(void)
static int do_init(void)
{
for (size_t i = 0; i < ARRAY_SIZE(session_state); i++) {
session_state[i] = PROTO_SESSION_DOWN;
@@ -166,27 +192,26 @@ static int module_init(void)
return 0;
}
static int module_start(void)
static int do_start(void)
{
if (running) {
if (module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
running = true;
return 0;
}
static void module_pause(void)
static int do_stop(void)
{
if (!running) {
return;
if (!module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
for (size_t i = 0; i < ARRAY_SIZE(session_state); i++) {
session_state[i] = PROTO_SESSION_DOWN;
}
running = false;
return 0;
}
int protocol_module_process_message(enum proto_transport transport,
@@ -204,7 +229,7 @@ int protocol_module_process_message(enum proto_transport transport,
return -EINVAL;
}
if (!running) {
if (!module_lifecycle_is_running(&ctx.lc)) {
return -EAGAIN;
}
@@ -317,7 +342,7 @@ static bool handle_proto_rx_event(const struct proto_rx_event *event)
size_t rsp_payload_len = 0U;
int err;
if (!running) {
if (!module_lifecycle_is_running(&ctx.lc)) {
return false;
}
@@ -373,7 +398,7 @@ static bool handle_function_bitmap_state_event(
size_t payload_len;
int err;
if (!running) {
if (!module_lifecycle_is_running(&ctx.lc)) {
return false;
}
@@ -406,7 +431,7 @@ static bool handle_hid_led_event(const struct hid_led_event *event)
int err;
enum proto_transport transport;
if (!running) {
if (!module_lifecycle_is_running(&ctx.lc)) {
return false;
}
@@ -454,25 +479,9 @@ 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);
int err;
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
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);
}
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
return false;
}
@@ -481,23 +490,16 @@ static bool app_event_handler(const struct app_event_header *aeh)
}
if (is_power_down_event(aeh)) {
if (initialized) {
module_pause();
module_set_state(MODULE_STATE_STANDBY);
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_STOPPED);
}
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);
}
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;

View File

@@ -13,18 +13,43 @@
#include <zephyr/sys/printk.h>
#include "datetime_event.h"
#include "module_lifecycle.h"
#include "time_sync_event.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
#define TIME_SYNC_REFRESH_PERIOD K_SECONDS(1)
static struct k_work_delayable refresh_work;
static bool initialized;
static bool running;
static int32_t timezone_min;
static uint64_t utc_ms_base;
static int64_t uptime_ms_base;
struct time_sync_module_ctx {
struct module_lifecycle_ctx lc;
struct k_work_delayable refresh_work;
int32_t timezone_min;
uint64_t utc_ms_base;
int64_t uptime_ms_base;
};
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 time_sync_module_ctx ctx = {
.lc = {
.state = LC_UNINIT,
.cfg = &lifecycle_cfg,
.ops = &lifecycle_ops,
},
};
static void publish_datetime_event(void)
{
@@ -34,9 +59,9 @@ static void publish_datetime_event(void)
char date_text[DATETIME_EVENT_DATE_TEXT_LEN];
char time_text[DATETIME_EVENT_TIME_TEXT_LEN];
local_ms = (int64_t)utc_ms_base +
(k_uptime_get() - uptime_ms_base) +
((int64_t)timezone_min * 60LL * 1000LL);
local_ms = (int64_t)ctx.utc_ms_base +
(k_uptime_get() - ctx.uptime_ms_base) +
((int64_t)ctx.timezone_min * 60LL * 1000LL);
if (local_ms < 0) {
local_ms = 0;
}
@@ -67,43 +92,43 @@ static void refresh_work_handler(struct k_work *work)
{
ARG_UNUSED(work);
if (!running) {
if (!module_lifecycle_is_running(&ctx.lc)) {
return;
}
publish_datetime_event();
k_work_reschedule(&refresh_work, TIME_SYNC_REFRESH_PERIOD);
k_work_reschedule(&ctx.refresh_work, TIME_SYNC_REFRESH_PERIOD);
}
static int module_init(void)
static int do_init(void)
{
timezone_min = 0;
utc_ms_base = 0U;
uptime_ms_base = k_uptime_get();
k_work_init_delayable(&refresh_work, refresh_work_handler);
ctx.timezone_min = 0;
ctx.utc_ms_base = 0U;
ctx.uptime_ms_base = k_uptime_get();
k_work_init_delayable(&ctx.refresh_work, refresh_work_handler);
return 0;
}
static int module_start(void)
static int do_start(void)
{
if (running) {
if (module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
running = true;
k_work_reschedule(&refresh_work, K_NO_WAIT);
k_work_reschedule(&ctx.refresh_work, K_NO_WAIT);
return 0;
}
static void module_pause(void)
static int do_stop(void)
{
if (!running) {
return;
if (!module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
k_work_cancel_delayable(&refresh_work);
running = false;
k_work_cancel_delayable(&ctx.refresh_work);
return 0;
}
static bool handle_time_sync_event(const struct time_sync_event *event)
@@ -113,12 +138,12 @@ static bool handle_time_sync_event(const struct time_sync_event *event)
return false;
}
timezone_min = event->timezone_min;
utc_ms_base = event->utc_ms;
uptime_ms_base = k_uptime_get();
ctx.timezone_min = event->timezone_min;
ctx.utc_ms_base = event->utc_ms;
ctx.uptime_ms_base = k_uptime_get();
if (running) {
k_work_reschedule(&refresh_work, K_NO_WAIT);
if (module_lifecycle_is_running(&ctx.lc)) {
k_work_reschedule(&ctx.refresh_work, K_NO_WAIT);
}
return false;
@@ -132,48 +157,25 @@ 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);
int err;
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
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);
}
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
module_pause();
module_set_state(MODULE_STATE_STANDBY);
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_STOPPED);
}
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);
}
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;

View File

@@ -16,6 +16,7 @@
#include <zephyr/sys/ring_buffer.h>
#include <zephyr/sys/util.h>
#include "module_lifecycle.h"
#include "proto_rx_event.h"
#include "proto_transport_state_event.h"
#include "proto_tx_event.h"
@@ -38,33 +39,53 @@ enum usb_cdc_business_state {
};
struct usb_cdc_ctx {
enum module_state lifecycle;
struct module_lifecycle_ctx lc;
enum usb_cdc_business_state business;
const struct device *cdc_dev;
uint8_t rx_ring_buffer[USB_CDC_RX_RING_BUF_SIZE];
uint8_t tx_ring_buffer[USB_CDC_TX_RING_BUF_SIZE];
struct ring_buf rx_ringbuf;
struct ring_buf tx_ringbuf;
struct k_work rx_work;
struct k_work_delayable rx_flush_work;
uint8_t proto_rx_buf[USB_CDC_PROTO_RX_BUF_SIZE];
bool usb_active;
size_t proto_rx_len;
};
static int do_init(void);
static int do_start(void);
static int do_stop(void);
static const struct device *const cdc_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart);
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 uint8_t rx_ring_buffer[USB_CDC_RX_RING_BUF_SIZE];
static uint8_t tx_ring_buffer[USB_CDC_TX_RING_BUF_SIZE];
static struct ring_buf rx_ringbuf;
static struct ring_buf tx_ringbuf;
static struct k_work rx_work;
static struct k_work_delayable rx_flush_work;
static uint8_t proto_rx_buf[USB_CDC_PROTO_RX_BUF_SIZE];
static struct usb_cdc_ctx ctx = {
.lifecycle = MODULE_STATE_OFF,
.lc = {
.state = LC_UNINIT,
.cfg = &lifecycle_cfg,
.ops = &lifecycle_ops,
},
.business = USB_CDC_BUS_OFFLINE,
.cdc_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart),
.usb_active = false,
.proto_rx_len = 0U,
};
#define proto_rx_buf ctx.proto_rx_buf
static void validate_line_coding(void);
static bool lifecycle_is_ready(void)
{
return ctx.lifecycle == MODULE_STATE_READY;
return module_lifecycle_is_running(&ctx.lc);
}
static enum proto_transport_link_state transport_link_state_get(void)
@@ -75,44 +96,36 @@ static enum proto_transport_link_state transport_link_state_get(void)
PROTO_TRANSPORT_LINK_DOWN;
}
static bool control_poll_needed(enum module_state lifecycle,
enum usb_cdc_business_state business)
{
ARG_UNUSED(lifecycle);
ARG_UNUSED(business);
return false;
}
static void reset_ring_buffers(void)
{
unsigned int key = irq_lock();
ring_buf_init(&rx_ringbuf, sizeof(rx_ring_buffer), rx_ring_buffer);
ring_buf_init(&tx_ringbuf, sizeof(tx_ring_buffer), tx_ring_buffer);
ring_buf_init(&ctx.rx_ringbuf, sizeof(ctx.rx_ring_buffer), ctx.rx_ring_buffer);
ring_buf_init(&ctx.tx_ringbuf, sizeof(ctx.tx_ring_buffer), ctx.tx_ring_buffer);
irq_unlock(key);
}
static void disable_uart_io(void)
{
uart_irq_rx_disable(cdc_dev);
uart_irq_tx_disable(cdc_dev);
uart_irq_rx_disable(ctx.cdc_dev);
uart_irq_tx_disable(ctx.cdc_dev);
ctx.proto_rx_len = 0U;
k_work_cancel_delayable(&rx_flush_work);
k_work_cancel_delayable(&ctx.rx_flush_work);
reset_ring_buffers();
}
static void state_reconcile(enum module_state old_lifecycle,
static void state_reconcile(enum module_lifecycle old_lifecycle,
enum usb_cdc_business_state old_business)
{
enum proto_transport_link_state old_link =
((old_lifecycle == MODULE_STATE_READY) &&
((old_lifecycle == LC_RUNNING) &&
(old_business == USB_CDC_SESSION_READY)) ?
PROTO_TRANSPORT_LINK_READY :
PROTO_TRANSPORT_LINK_DOWN;
enum proto_transport_link_state new_link = transport_link_state_get();
if ((old_lifecycle == MODULE_STATE_READY) &&
if ((old_lifecycle == LC_RUNNING) &&
(old_business == USB_CDC_SESSION_READY) &&
(new_link == PROTO_TRANSPORT_LINK_DOWN)) {
disable_uart_io();
@@ -124,17 +137,17 @@ static void state_reconcile(enum module_state old_lifecycle,
validate_line_coding();
err = uart_line_ctrl_set(cdc_dev, UART_LINE_CTRL_DCD, 1);
err = uart_line_ctrl_set(ctx.cdc_dev, UART_LINE_CTRL_DCD, 1);
if (err) {
LOG_WRN("Failed to set DCD (%d)", err);
}
err = uart_line_ctrl_set(cdc_dev, UART_LINE_CTRL_DSR, 1);
err = uart_line_ctrl_set(ctx.cdc_dev, UART_LINE_CTRL_DSR, 1);
if (err) {
LOG_WRN("Failed to set DSR (%d)", err);
}
uart_irq_rx_enable(cdc_dev);
uart_irq_rx_enable(ctx.cdc_dev);
}
if (old_link != new_link) {
@@ -143,23 +156,9 @@ static void state_reconcile(enum module_state old_lifecycle,
}
}
static void lifecycle_set(enum module_state new_state)
{
enum module_state old_lifecycle = ctx.lifecycle;
enum usb_cdc_business_state old_business = ctx.business;
if (ctx.lifecycle == new_state) {
return;
}
ctx.lifecycle = new_state;
state_reconcile(old_lifecycle, old_business);
module_set_state(new_state);
}
static void business_state_set(enum usb_cdc_business_state new_state)
{
enum module_state old_lifecycle = ctx.lifecycle;
enum module_lifecycle old_lifecycle = ctx.lc.state;
enum usb_cdc_business_state old_business = ctx.business;
if (ctx.business == new_state) {
@@ -192,7 +191,7 @@ static void kick_tx(void)
return;
}
uart_irq_tx_enable(cdc_dev);
uart_irq_tx_enable(ctx.cdc_dev);
}
static void validate_line_coding(void)
@@ -200,7 +199,7 @@ static void validate_line_coding(void)
uint32_t baudrate = 0U;
int err;
err = uart_line_ctrl_get(cdc_dev, UART_LINE_CTRL_BAUD_RATE, &baudrate);
err = uart_line_ctrl_get(ctx.cdc_dev, UART_LINE_CTRL_BAUD_RATE, &baudrate);
if (err) {
LOG_WRN("Failed to get CDC baudrate (%d)", err);
} else {
@@ -215,7 +214,7 @@ static void validate_line_coding(void)
{
struct uart_config cfg;
err = uart_config_get(cdc_dev, &cfg);
err = uart_config_get(ctx.cdc_dev, &cfg);
if (err) {
LOG_WRN("uart_config_get failed (%d)", err);
} else {
@@ -243,7 +242,7 @@ static void rx_work_handler(struct k_work *work)
uint32_t len;
unsigned int key = irq_lock();
len = ring_buf_get(&rx_ringbuf, buffer, sizeof(buffer));
len = ring_buf_get(&ctx.rx_ringbuf, buffer, sizeof(buffer));
irq_unlock(key);
if (len == 0U) {
@@ -259,7 +258,7 @@ static void rx_work_handler(struct k_work *work)
if (len > 0U) {
memcpy(&proto_rx_buf[ctx.proto_rx_len], buffer, len);
ctx.proto_rx_len += len;
k_work_reschedule(&rx_flush_work, USB_CDC_PROTO_RX_FLUSH_DELAY);
k_work_reschedule(&ctx.rx_flush_work, USB_CDC_PROTO_RX_FLUSH_DELAY);
}
}
}
@@ -296,7 +295,7 @@ static void cdc_interrupt_handler(const struct device *dev, void *user_data)
uint32_t written;
unsigned int key = irq_lock();
written = ring_buf_put(&rx_ringbuf, buffer,
written = ring_buf_put(&ctx.rx_ringbuf, buffer,
(uint32_t)recv_len);
irq_unlock(key);
@@ -305,7 +304,7 @@ static void cdc_interrupt_handler(const struct device *dev, void *user_data)
recv_len - (int)written);
}
k_work_submit(&rx_work);
k_work_submit(&ctx.rx_work);
}
}
@@ -315,7 +314,7 @@ static void cdc_interrupt_handler(const struct device *dev, void *user_data)
int sent_len;
unsigned int key = irq_lock();
len = ring_buf_get(&tx_ringbuf, buffer, sizeof(buffer));
len = ring_buf_get(&ctx.tx_ringbuf, buffer, sizeof(buffer));
irq_unlock(key);
if (len == 0U) {
@@ -335,37 +334,52 @@ static void cdc_interrupt_handler(const struct device *dev, void *user_data)
}
}
static int module_init(void)
static int do_init(void)
{
if (!device_is_ready(cdc_dev)) {
if (!device_is_ready(ctx.cdc_dev)) {
LOG_ERR("CDC ACM device not ready");
return -ENODEV;
}
reset_ring_buffers();
k_work_init(&rx_work, rx_work_handler);
k_work_init_delayable(&rx_flush_work, rx_flush_work_handler);
uart_irq_callback_set(cdc_dev, cdc_interrupt_handler);
ctx = (struct usb_cdc_ctx) {
.lifecycle = MODULE_STATE_OFF,
.business = USB_CDC_BUS_OFFLINE,
.usb_active = false,
.proto_rx_len = 0U,
};
k_work_init(&ctx.rx_work, rx_work_handler);
k_work_init_delayable(&ctx.rx_flush_work, rx_flush_work_handler);
uart_irq_callback_set(ctx.cdc_dev, cdc_interrupt_handler);
ctx.business = USB_CDC_BUS_OFFLINE;
ctx.usb_active = false;
ctx.proto_rx_len = 0U;
return 0;
}
static void module_start(void)
static int do_start(void)
{
lifecycle_set(MODULE_STATE_READY);
business_state_sync_from_usb();
return 0;
}
static void module_pause(void)
static int do_stop(void)
{
business_state_set(USB_CDC_BUS_OFFLINE);
lifecycle_set(MODULE_STATE_STANDBY);
ctx.business = USB_CDC_BUS_OFFLINE;
return 0;
}
static int apply_lifecycle(enum module_lifecycle target)
{
enum module_lifecycle old_lifecycle = ctx.lc.state;
enum usb_cdc_business_state old_business = ctx.business;
int err = module_set_lifecycle(&ctx.lc, target);
if (err) {
return err;
}
state_reconcile(old_lifecycle, old_business);
if (target == LC_RUNNING) {
business_state_sync_from_usb();
}
return 0;
}
static bool handle_usb_state_event(const struct usb_state_event *event)
@@ -384,7 +398,7 @@ static bool handle_usb_state_event(const struct usb_state_event *event)
static bool handle_usb_control_event(const struct usb_control_event *event)
{
if (event->dev != cdc_dev) {
if (event->dev != ctx.cdc_dev) {
return false;
}
@@ -449,7 +463,7 @@ static bool handle_proto_tx_event(const struct proto_tx_event *event)
}
key = irq_lock();
written = ring_buf_put(&tx_ringbuf, event->dyndata.data,
written = ring_buf_put(&ctx.tx_ringbuf, event->dyndata.data,
(uint32_t)event->dyndata.size);
irq_unlock(key);
@@ -483,32 +497,23 @@ static bool app_event_handler(const struct app_event_header *aeh)
cast_module_state_event(aeh);
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
if (ctx.lifecycle == MODULE_STATE_OFF) {
int err = module_init();
if (err) {
lifecycle_set(MODULE_STATE_ERROR);
return false;
}
}
module_start();
(void)apply_lifecycle(LC_RUNNING);
}
return false;
}
if (is_power_down_event(aeh)) {
if (ctx.lifecycle != MODULE_STATE_OFF) {
module_pause();
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)apply_lifecycle(LC_STOPPED);
}
return false;
}
if (is_wake_up_event(aeh)) {
if (ctx.lifecycle != MODULE_STATE_OFF) {
module_start();
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)apply_lifecycle(LC_RUNNING);
}
return false;

View File

@@ -15,6 +15,7 @@
#include <caf/events/power_manager_event.h>
#include "module_lifecycle.h"
#include "usb_function_hook.h"
#include "usb_control_event.h"
#include "usb_state_event.h"
@@ -40,9 +41,6 @@ static const char *const class_blocklist[] = {
NULL,
};
static bool initialized;
static bool running;
enum usb_stack_state {
USB_STACK_UNINITIALIZED = 0,
USB_STACK_DISABLED,
@@ -58,11 +56,35 @@ enum usb_bus_state {
};
struct usb_owner_ctx {
struct module_lifecycle_ctx lc;
enum usb_stack_state stack;
enum usb_bus_state bus;
};
static int do_init(void);
static int do_start(void);
static int do_stop(void);
/* Project exception: mode policy suspend/resume controls USB availability,
* while global power events still map to standby lifecycle transitions.
*/
static const struct module_lifecycle_cfg lifecycle_cfg = {
.mode = ML_MODE_SUSPEND,
.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 usb_owner_ctx usb_ctx = {
.lc = {
.state = LC_UNINIT,
.cfg = &lifecycle_cfg,
.ops = &lifecycle_ops,
},
.stack = USB_STACK_UNINITIALIZED,
.bus = USB_BUS_DISCONNECTED,
};
@@ -363,7 +385,7 @@ static int usb_stack_disable_if_needed(void)
return 0;
}
static int module_init(void)
static int do_init(void)
{
usb_ctx.stack = USB_STACK_UNINITIALIZED;
usb_ctx.bus = USB_BUS_DISCONNECTED;
@@ -372,25 +394,25 @@ static int module_init(void)
return 0;
}
static int module_start(void)
static int do_start(void)
{
if (running) {
if (module_lifecycle_is_running(&usb_ctx.lc)) {
return 0;
}
running = true;
submit_usb_state(usb_public_state_get());
return 0;
}
static void module_pause(void)
static int do_stop(void)
{
if (!running) {
return;
if (!module_lifecycle_is_running(&usb_ctx.lc)) {
return 0;
}
running = false;
submit_usb_state(usb_public_state_get());
return 0;
}
static bool handle_module_state_event(const struct module_state_event *event)
@@ -399,26 +421,7 @@ static bool handle_module_state_event(const struct module_state_event *event)
return false;
}
if (!initialized) {
int err = module_init();
if (err) {
module_set_state(MODULE_STATE_ERROR);
return false;
}
initialized = true;
}
if (!running) {
int err = module_start();
if (err) {
module_set_state(MODULE_STATE_ERROR);
} else if (usb_stack_was_initialized()) {
module_set_state(MODULE_STATE_READY);
}
}
(void)module_set_lifecycle(&usb_ctx.lc, LC_RUNNING);
return false;
}
@@ -426,38 +429,38 @@ static bool handle_module_state_event(const struct module_state_event *event)
static bool handle_module_suspend_req_event(
const struct module_suspend_req_event *event)
{
if ((event->sink_module_id != MODULE_ID(MODULE)) || !running) {
if ((event->sink_module_id != MODULE_ID(MODULE)) ||
(usb_ctx.lc.state != LC_RUNNING)) {
return false;
}
int err = usb_stack_disable_if_needed();
if (err) {
module_set_state(MODULE_STATE_ERROR);
LOG_WRN("USB suspend request failed (%d)", err);
return false;
}
module_set_state(MODULE_STATE_READY);
(void)module_set_lifecycle(&usb_ctx.lc, LC_SUSPENDED);
return false;
}
static bool handle_module_resume_req_event(
const struct module_resume_req_event *event)
{
if ((event->sink_module_id != MODULE_ID(MODULE)) || !running) {
if ((event->sink_module_id != MODULE_ID(MODULE)) ||
(usb_ctx.lc.state != LC_SUSPENDED)) {
return false;
}
int err = usb_stack_enable_if_needed();
if (err) {
module_set_state(MODULE_STATE_ERROR);
LOG_WRN("USB resume request failed (%d)", err);
return false;
}
if (usb_stack_was_initialized()) {
module_set_state(MODULE_STATE_READY);
}
(void)module_set_lifecycle(&usb_ctx.lc, LC_RUNNING);
return false;
}
@@ -479,23 +482,16 @@ static bool app_event_handler(const struct app_event_header *aeh)
}
if (is_power_down_event(aeh)) {
if (initialized) {
module_pause();
module_set_state(MODULE_STATE_STANDBY);
if (module_lifecycle_is_initialized(&usb_ctx.lc)) {
(void)module_set_lifecycle(&usb_ctx.lc, LC_STOPPED);
}
return false;
}
if (is_wake_up_event(aeh)) {
if (initialized) {
int err = module_start();
if (err) {
module_set_state(MODULE_STATE_ERROR);
} else if (usb_stack_was_initialized()) {
module_set_state(MODULE_STATE_READY);
}
if (module_lifecycle_is_initialized(&usb_ctx.lc)) {
(void)module_set_lifecycle(&usb_ctx.lc, LC_RUNNING);
}
return false;

View File

@@ -18,32 +18,57 @@
#include "hid_report_sent_event.h"
#include "hid_tx_report_event.h"
#include "keyboard_core.h"
#include "module_lifecycle.h"
#include "usb_function_hook.h"
#include "usb_state_event.h"
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
static const struct device *const hid_dev =
DEVICE_DT_GET(DT_NODELABEL(hid_consumer));
static const uint8_t consumer_report_desc[] = {
0x05, 0x0C, 0x09, 0x01, 0xA1, 0x01, 0x15, 0x00,
0x26, 0xFF, 0x03, 0x19, 0x00, 0x2A, 0xFF, 0x03,
0x75, 0x10, 0x95, 0x01, 0x81, 0x00, 0xC0
};
static bool initialized;
static bool running;
static bool usb_active;
static bool iface_ready;
static bool report_in_flight;
static uint16_t in_flight_sequence;
struct usb_hid_consumer_module_ctx {
struct module_lifecycle_ctx lc;
const struct device *hid_dev;
bool usb_active;
bool iface_ready;
bool report_in_flight;
uint16_t in_flight_sequence;
};
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 usb_hid_consumer_module_ctx ctx = {
.lc = {
.state = LC_UNINIT,
.cfg = &lifecycle_cfg,
.ops = &lifecycle_ops,
},
.hid_dev = DEVICE_DT_GET(DT_NODELABEL(hid_consumer)),
};
UDC_STATIC_BUF_DEFINE(consumer_tx_buf, KEYBOARD_CONSUMER_REPORT_SIZE);
static void publish_consumer_state(void)
{
bool ready = running && usb_active && iface_ready;
bool ready = module_lifecycle_is_running(&ctx.lc) &&
ctx.usb_active && ctx.iface_ready;
submit_hid_channel_state_event(HID_SEND_CH_USB_CONSUMER,
ready ? BIT(KEYBOARD_REPORT_TYPE_CONSUMER) : 0U,
@@ -54,12 +79,13 @@ static void consumer_iface_ready(const struct device *dev, const bool ready)
{
ARG_UNUSED(dev);
iface_ready = ready;
ctx.iface_ready = ready;
if (!ready) {
report_in_flight = false;
ctx.report_in_flight = false;
}
LOG_INF("%s interface %s", hid_dev->name, ready ? "ready" : "not ready");
LOG_INF("%s interface %s", ctx.hid_dev->name,
ready ? "ready" : "not ready");
publish_consumer_state();
}
@@ -117,10 +143,10 @@ static void consumer_input_report_done(const struct device *dev,
ARG_UNUSED(dev);
ARG_UNUSED(report);
report_in_flight = false;
ctx.report_in_flight = false;
submit_hid_report_sent_event(HID_SEND_CH_USB_CONSUMER,
KEYBOARD_REPORT_TYPE_CONSUMER,
in_flight_sequence, false);
ctx.in_flight_sequence, false);
}
static void consumer_output_report(const struct device *dev,
@@ -145,59 +171,59 @@ static const struct hid_device_ops consumer_ops = {
static int usb_hid_consumer_register_device(void)
{
if (!device_is_ready(hid_dev)) {
LOG_ERR("HID device %s not ready", hid_dev->name);
if (!device_is_ready(ctx.hid_dev)) {
LOG_ERR("HID device %s not ready", ctx.hid_dev->name);
return -ENODEV;
}
return hid_device_register(hid_dev, consumer_report_desc,
return hid_device_register(ctx.hid_dev, consumer_report_desc,
sizeof(consumer_report_desc), &consumer_ops);
}
USB_FUNCTION_HOOK_DEFINE(usb_hid_consumer_hook, usb_hid_consumer_register_device);
static int module_init(void)
static int do_init(void)
{
usb_active = false;
iface_ready = false;
report_in_flight = false;
ctx.usb_active = false;
ctx.iface_ready = false;
ctx.report_in_flight = false;
return 0;
}
static int module_start(void)
static int do_start(void)
{
if (running) {
if (module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
running = true;
publish_consumer_state();
return 0;
}
static void module_pause(void)
static int do_stop(void)
{
if (!running) {
return;
if (!module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
running = false;
report_in_flight = false;
ctx.report_in_flight = false;
publish_consumer_state();
return 0;
}
static bool handle_usb_state_event(const struct usb_state_event *event)
{
bool new_usb_active = (event->state == USB_STATE_ACTIVE);
if (new_usb_active == usb_active) {
if (new_usb_active == ctx.usb_active) {
return false;
}
usb_active = new_usb_active;
if (!usb_active) {
iface_ready = false;
report_in_flight = false;
ctx.usb_active = new_usb_active;
if (!ctx.usb_active) {
ctx.iface_ready = false;
ctx.report_in_flight = false;
}
publish_consumer_state();
@@ -208,7 +234,7 @@ static bool handle_hid_tx_report_event(const struct hid_tx_report_event *event)
{
int err;
if (!running || !usb_active ||
if (!module_lifecycle_is_running(&ctx.lc) || !ctx.usb_active ||
(event->channel != HID_SEND_CH_USB_CONSUMER)) {
return false;
}
@@ -217,17 +243,17 @@ static bool handle_hid_tx_report_event(const struct hid_tx_report_event *event)
return false;
}
if (!iface_ready) {
if (!ctx.iface_ready) {
return false;
}
if (report_in_flight) {
if (ctx.report_in_flight) {
LOG_WRN("Drop USB consumer report while previous report is in flight");
return false;
}
memcpy(consumer_tx_buf, event->dyndata.data, event->dyndata.size);
err = hid_device_submit_report(hid_dev, (uint16_t)event->dyndata.size,
err = hid_device_submit_report(ctx.hid_dev, (uint16_t)event->dyndata.size,
consumer_tx_buf);
if (err) {
LOG_WRN("USB consumer report submit failed (%d)", err);
@@ -235,8 +261,8 @@ static bool handle_hid_tx_report_event(const struct hid_tx_report_event *event)
KEYBOARD_REPORT_TYPE_CONSUMER,
event->sequence, true);
} else {
report_in_flight = true;
in_flight_sequence = event->sequence;
ctx.report_in_flight = true;
ctx.in_flight_sequence = event->sequence;
}
return false;
@@ -254,48 +280,25 @@ 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);
int err;
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
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);
}
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
module_pause();
module_set_state(MODULE_STATE_STANDBY);
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_STOPPED);
}
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);
}
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;

View File

@@ -19,6 +19,7 @@
#include "hid_report_sent_event.h"
#include "hid_tx_report_event.h"
#include "keyboard_core.h"
#include "module_lifecycle.h"
#include "set_protocol_event.h"
#include "usb_function_hook.h"
#include "usb_state_event.h"
@@ -28,8 +29,6 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
#define KBD_LED_REPORT_SIZE 1U
#define KBD_LED_REPORT_WITH_ID_SIZE (KBD_LED_REPORT_SIZE + 1U)
static const struct device *const hid_dev = DEVICE_DT_GET(DT_NODELABEL(hid_kbd));
static const uint8_t keyboard_report_desc[] = {
0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07,
0x19, 0xE0, 0x29, 0xE7, 0x15, 0x00, 0x25, 0x01,
@@ -41,36 +40,64 @@ static const uint8_t keyboard_report_desc[] = {
0x75, 0x03, 0x95, 0x01, 0x91, 0x01, 0xC0
};
static enum keyboard_protocol_mode keyboard_protocol_mode =
KEYBOARD_PROTOCOL_MODE_REPORT;
static bool initialized;
static bool running;
static bool usb_active;
static bool iface_ready;
static bool report_in_flight;
static uint16_t in_flight_sequence;
struct usb_hid_keyboard_module_ctx {
struct module_lifecycle_ctx lc;
const struct device *hid_dev;
enum keyboard_protocol_mode keyboard_protocol_mode;
bool usb_active;
bool iface_ready;
bool report_in_flight;
uint16_t in_flight_sequence;
};
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 usb_hid_keyboard_module_ctx ctx = {
.lc = {
.state = LC_UNINIT,
.cfg = &lifecycle_cfg,
.ops = &lifecycle_ops,
},
.hid_dev = DEVICE_DT_GET(DT_NODELABEL(hid_kbd)),
.keyboard_protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT,
};
UDC_STATIC_BUF_DEFINE(keyboard_tx_buf, KEYBOARD_NKRO_REPORT_SIZE);
static void publish_keyboard_state(void)
{
bool ready = running && usb_active && iface_ready;
bool ready = module_lifecycle_is_running(&ctx.lc) &&
ctx.usb_active && ctx.iface_ready;
submit_hid_channel_state_event(HID_SEND_CH_USB_KEYS,
ready ? BIT(KEYBOARD_REPORT_TYPE_KEYS) : 0U,
keyboard_protocol_mode);
ctx.keyboard_protocol_mode);
}
static void keyboard_iface_ready(const struct device *dev, const bool ready)
{
ARG_UNUSED(dev);
iface_ready = ready;
ctx.iface_ready = ready;
if (!ready) {
report_in_flight = false;
ctx.report_in_flight = false;
}
LOG_INF("%s interface %s", hid_dev->name, ready ? "ready" : "not ready");
LOG_INF("%s interface %s", ctx.hid_dev->name,
ready ? "ready" : "not ready");
publish_keyboard_state();
}
@@ -164,11 +191,11 @@ static void keyboard_set_protocol(const struct device *dev, const uint8_t proto)
enum keyboard_protocol_mode new_mode = usb_proto_to_keyboard_proto(proto);
if (keyboard_protocol_mode == new_mode) {
if (ctx.keyboard_protocol_mode == new_mode) {
return;
}
keyboard_protocol_mode = new_mode;
ctx.keyboard_protocol_mode = new_mode;
LOG_INF("USB keyboard protocol -> %s",
(new_mode == KEYBOARD_PROTOCOL_MODE_BOOT) ? "boot" : "report");
submit_set_protocol_event(HID_TRANSPORT_USB, new_mode);
@@ -181,10 +208,10 @@ static void keyboard_input_report_done(const struct device *dev,
ARG_UNUSED(dev);
ARG_UNUSED(report);
report_in_flight = false;
ctx.report_in_flight = false;
submit_hid_report_sent_event(HID_SEND_CH_USB_KEYS,
KEYBOARD_REPORT_TYPE_KEYS,
in_flight_sequence, false);
ctx.in_flight_sequence, false);
}
static void keyboard_output_report(const struct device *dev,
@@ -211,60 +238,60 @@ static const struct hid_device_ops keyboard_ops = {
static int usb_hid_keyboard_register_device(void)
{
if (!device_is_ready(hid_dev)) {
LOG_ERR("HID device %s not ready", hid_dev->name);
if (!device_is_ready(ctx.hid_dev)) {
LOG_ERR("HID device %s not ready", ctx.hid_dev->name);
return -ENODEV;
}
return hid_device_register(hid_dev, keyboard_report_desc,
return hid_device_register(ctx.hid_dev, keyboard_report_desc,
sizeof(keyboard_report_desc), &keyboard_ops);
}
USB_FUNCTION_HOOK_DEFINE(usb_hid_keyboard_hook, usb_hid_keyboard_register_device);
static int module_init(void)
static int do_init(void)
{
keyboard_protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT;
usb_active = false;
iface_ready = false;
report_in_flight = false;
ctx.keyboard_protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT;
ctx.usb_active = false;
ctx.iface_ready = false;
ctx.report_in_flight = false;
return 0;
}
static int module_start(void)
static int do_start(void)
{
if (running) {
if (module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
running = true;
publish_keyboard_state();
return 0;
}
static void module_pause(void)
static int do_stop(void)
{
if (!running) {
return;
if (!module_lifecycle_is_running(&ctx.lc)) {
return 0;
}
running = false;
report_in_flight = false;
ctx.report_in_flight = false;
publish_keyboard_state();
return 0;
}
static bool handle_usb_state_event(const struct usb_state_event *event)
{
bool new_usb_active = (event->state == USB_STATE_ACTIVE);
if (new_usb_active == usb_active) {
if (new_usb_active == ctx.usb_active) {
return false;
}
usb_active = new_usb_active;
if (!usb_active) {
iface_ready = false;
report_in_flight = false;
ctx.usb_active = new_usb_active;
if (!ctx.usb_active) {
ctx.iface_ready = false;
ctx.report_in_flight = false;
}
publish_keyboard_state();
@@ -275,7 +302,7 @@ static bool handle_hid_tx_report_event(const struct hid_tx_report_event *event)
{
int err;
if (!running || !usb_active ||
if (!module_lifecycle_is_running(&ctx.lc) || !ctx.usb_active ||
(event->channel != HID_SEND_CH_USB_KEYS)) {
return false;
}
@@ -284,22 +311,22 @@ static bool handle_hid_tx_report_event(const struct hid_tx_report_event *event)
return false;
}
if (event->protocol_mode != keyboard_protocol_mode) {
if (event->protocol_mode != ctx.keyboard_protocol_mode) {
LOG_WRN("Drop USB keys report due to protocol mismatch");
return false;
}
if (!iface_ready) {
if (!ctx.iface_ready) {
return false;
}
if (report_in_flight) {
if (ctx.report_in_flight) {
LOG_WRN("Drop USB keyboard report while previous report is in flight");
return false;
}
memcpy(keyboard_tx_buf, event->dyndata.data, event->dyndata.size);
err = hid_device_submit_report(hid_dev, (uint16_t)event->dyndata.size,
err = hid_device_submit_report(ctx.hid_dev, (uint16_t)event->dyndata.size,
keyboard_tx_buf);
if (err) {
LOG_WRN("USB keyboard report submit failed (%d)", err);
@@ -307,8 +334,8 @@ static bool handle_hid_tx_report_event(const struct hid_tx_report_event *event)
KEYBOARD_REPORT_TYPE_KEYS,
event->sequence, true);
} else {
report_in_flight = true;
in_flight_sequence = event->sequence;
ctx.report_in_flight = true;
ctx.in_flight_sequence = event->sequence;
}
return false;
@@ -326,48 +353,25 @@ 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);
int err;
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
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);
}
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;
}
if (is_power_down_event(aeh)) {
if (initialized) {
module_pause();
module_set_state(MODULE_STATE_STANDBY);
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_STOPPED);
}
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);
}
if (module_lifecycle_is_initialized(&ctx.lc)) {
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
}
return false;