Compare commits
2 Commits
8bfb8b540c
...
caf8d5acc6
| Author | SHA1 | Date | |
|---|---|---|---|
| caf8d5acc6 | |||
| ceebaaa600 |
@@ -9,6 +9,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
enum mode_switch_mode {
|
||||
MODE_SWITCH_INVALID,
|
||||
MODE_SWITCH_USB,
|
||||
MODE_SWITCH_24G,
|
||||
MODE_SWITCH_BLE,
|
||||
|
||||
204
inc/module_lifecycle.h
Normal file
204
inc/module_lifecycle.h
Normal 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_ */
|
||||
3
prj.conf
3
prj.conf
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
bool valid;
|
||||
uint8_t soc;
|
||||
bool charging;
|
||||
bool full;
|
||||
} last_bat_state;
|
||||
static bool initialized;
|
||||
static bool running;
|
||||
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 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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,85 @@ 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 const char *lifecycle_name(enum module_lifecycle state)
|
||||
{
|
||||
switch (state) {
|
||||
case LC_UNINIT:
|
||||
return "UNINIT";
|
||||
case LC_RUNNING:
|
||||
return "RUNNING";
|
||||
case LC_STOPPED:
|
||||
return "STOPPED";
|
||||
case LC_SUSPENDED:
|
||||
return "SUSPENDED";
|
||||
case LC_ERROR:
|
||||
return "ERROR";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *business_state_name(enum ble_nus_business_state state)
|
||||
{
|
||||
switch (state) {
|
||||
case BLE_NUS_STACK_OFFLINE:
|
||||
return "STACK_OFFLINE";
|
||||
case BLE_NUS_IDLE:
|
||||
return "IDLE";
|
||||
case BLE_NUS_WAIT_NOTIFY:
|
||||
return "WAIT_NOTIFY";
|
||||
case BLE_NUS_SESSION_READY:
|
||||
return "SESSION_READY";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *link_state_name(enum proto_transport_link_state state)
|
||||
{
|
||||
switch (state) {
|
||||
case PROTO_TRANSPORT_LINK_DOWN:
|
||||
return "DOWN";
|
||||
case PROTO_TRANSPORT_LINK_READY:
|
||||
return "READY";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
static bool lifecycle_is_ready(void)
|
||||
{
|
||||
return 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 +117,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,29 +133,18 @@ 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) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INF("BLE NUS business %s -> %s",
|
||||
business_state_name(ctx.business),
|
||||
business_state_name(new_state));
|
||||
ctx.business = new_state;
|
||||
state_reconcile(old_lifecycle, old_business);
|
||||
}
|
||||
@@ -130,6 +185,11 @@ static void received(struct bt_conn *conn, const void *data, uint16_t len,
|
||||
|
||||
if (!lifecycle_is_ready() || (ctx.business == BLE_NUS_STACK_OFFLINE) ||
|
||||
(conn != ctx.active_conn)) {
|
||||
LOG_WRN("BLE NUS drop RX len:%u lc:%s business:%s active_conn:%p conn:%p link:%s",
|
||||
len, lifecycle_name(ctx.lc.state),
|
||||
business_state_name(ctx.business),
|
||||
(void *)ctx.active_conn, (void *)conn,
|
||||
link_state_name(transport_link_state_get()));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -150,7 +210,7 @@ static void reset_connection_state(void)
|
||||
}
|
||||
}
|
||||
|
||||
static int module_init(void)
|
||||
static int do_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
@@ -160,23 +220,36 @@ 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);
|
||||
} else {
|
||||
LOG_WRN("BLE NUS lifecycle change failed target:%s err:%d",
|
||||
lifecycle_name(target), err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool handle_ble_peer_event(const struct ble_peer_event *event)
|
||||
@@ -243,27 +316,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 +330,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;
|
||||
|
||||
@@ -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 = {
|
||||
.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,
|
||||
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,9 +204,9 @@ 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,
|
||||
event->theme.g,
|
||||
event->theme.b);
|
||||
ctx.ui_model.theme_color = (lv_color_t)LV_COLOR_MAKE(event->theme.r,
|
||||
event->theme.g,
|
||||
event->theme.b);
|
||||
refresh_ui();
|
||||
return false;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
static const char *mode_name(enum mode_switch_mode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case MODE_SWITCH_INVALID:
|
||||
return "INVALID";
|
||||
case MODE_SWITCH_USB:
|
||||
return "USB";
|
||||
case MODE_SWITCH_24G:
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
static const char *mode_name(enum mode_switch_mode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case MODE_SWITCH_INVALID:
|
||||
return "INVALID";
|
||||
case MODE_SWITCH_USB:
|
||||
return "USB";
|
||||
case MODE_SWITCH_24G:
|
||||
|
||||
@@ -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,28 +50,66 @@ struct in_flight_report {
|
||||
uint16_t sequence;
|
||||
};
|
||||
|
||||
static struct hid_channel_state_data channel_state[HID_SEND_CH_COUNT] = {
|
||||
[HID_SEND_CH_USB_KEYS] = {
|
||||
.protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT,
|
||||
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,
|
||||
},
|
||||
[HID_SEND_CH_USB_CONSUMER] = {
|
||||
.protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT,
|
||||
},
|
||||
[HID_SEND_CH_BLE_SHARED] = {
|
||||
.protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT,
|
||||
.channel_state = {
|
||||
[HID_SEND_CH_USB_KEYS] = {
|
||||
.protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT,
|
||||
},
|
||||
[HID_SEND_CH_USB_CONSUMER] = {
|
||||
.protocol_mode = KEYBOARD_PROTOCOL_MODE_REPORT,
|
||||
},
|
||||
[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;
|
||||
|
||||
@@ -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] = {
|
||||
[HID_TRANSPORT_USB] = KEYBOARD_PROTOCOL_MODE_REPORT,
|
||||
[HID_TRANSPORT_BLE] = KEYBOARD_PROTOCOL_MODE_REPORT,
|
||||
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 enum mode_switch_mode current_mode;
|
||||
static bool initialized;
|
||||
static bool running;
|
||||
static 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,
|
||||
},
|
||||
};
|
||||
|
||||
#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;
|
||||
|
||||
@@ -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 = {
|
||||
.r = BLINKY_THEME_DEFAULT_R,
|
||||
.g = BLINKY_THEME_DEFAULT_G,
|
||||
.b = BLINKY_THEME_DEFAULT_B,
|
||||
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(
|
||||
LED_STRIP_TICK_INTERVAL.ticks),
|
||||
pixels, ARRAY_SIZE(pixels));
|
||||
keep_running = ctx.effect->ops->tick(ctx.effect,
|
||||
k_ticks_to_ms_floor32(
|
||||
LED_STRIP_TICK_INTERVAL.ticks),
|
||||
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, ¤t_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, ¤t_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;
|
||||
|
||||
@@ -9,82 +9,152 @@
|
||||
|
||||
#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;
|
||||
enum mode_switch_mode active_mode;
|
||||
};
|
||||
|
||||
static void broadcast_ble_adv_req(bool suspend)
|
||||
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,
|
||||
},
|
||||
.active_mode = MODE_SWITCH_USB,
|
||||
};
|
||||
|
||||
static void mode_policy_set_ble(bool enable)
|
||||
{
|
||||
if (suspend) {
|
||||
struct module_suspend_req_event *event = new_module_suspend_req_event();
|
||||
|
||||
event->sink_module_id = MODULE_ID(ble_adv);
|
||||
event->src_module_id = MODULE_ID(MODULE);
|
||||
APP_EVENT_SUBMIT(event);
|
||||
} else {
|
||||
if (enable) {
|
||||
struct module_resume_req_event *event = new_module_resume_req_event();
|
||||
|
||||
event->sink_module_id = MODULE_ID(ble_adv);
|
||||
event->src_module_id = MODULE_ID(MODULE);
|
||||
APP_EVENT_SUBMIT(event);
|
||||
} else {
|
||||
struct module_suspend_req_event *event = new_module_suspend_req_event();
|
||||
|
||||
event->sink_module_id = MODULE_ID(ble_adv);
|
||||
event->src_module_id = MODULE_ID(MODULE);
|
||||
APP_EVENT_SUBMIT(event);
|
||||
}
|
||||
}
|
||||
|
||||
static void apply_mode_policy(enum mode_switch_mode mode)
|
||||
static void mode_policy_set_usb(bool enable)
|
||||
{
|
||||
bool should_suspend_ble_adv = (mode != MODE_SWITCH_BLE);
|
||||
bool should_enable_usb = (mode == MODE_SWITCH_USB);
|
||||
if (enable) {
|
||||
struct module_resume_req_event *event = new_module_resume_req_event();
|
||||
|
||||
if (should_suspend_ble_adv != ble_adv_suspended) {
|
||||
ble_adv_suspended = should_suspend_ble_adv;
|
||||
broadcast_ble_adv_req(should_suspend_ble_adv);
|
||||
}
|
||||
event->sink_module_id = MODULE_ID(usb_device_module);
|
||||
event->src_module_id = MODULE_ID(MODULE);
|
||||
APP_EVENT_SUBMIT(event);
|
||||
} else {
|
||||
struct module_suspend_req_event *event = new_module_suspend_req_event();
|
||||
|
||||
if (should_enable_usb != usb_enabled) {
|
||||
usb_enabled = should_enable_usb;
|
||||
if (usb_enabled) {
|
||||
struct module_resume_req_event *event =
|
||||
new_module_resume_req_event();
|
||||
|
||||
event->sink_module_id = MODULE_ID(usb_device_module);
|
||||
event->src_module_id = MODULE_ID(MODULE);
|
||||
APP_EVENT_SUBMIT(event);
|
||||
} else {
|
||||
struct module_suspend_req_event *event =
|
||||
new_module_suspend_req_event();
|
||||
|
||||
event->sink_module_id = MODULE_ID(usb_device_module);
|
||||
event->src_module_id = MODULE_ID(MODULE);
|
||||
APP_EVENT_SUBMIT(event);
|
||||
}
|
||||
event->sink_module_id = MODULE_ID(usb_device_module);
|
||||
event->src_module_id = MODULE_ID(MODULE);
|
||||
APP_EVENT_SUBMIT(event);
|
||||
}
|
||||
}
|
||||
|
||||
static int module_init(void)
|
||||
static void apply_active_mode(void)
|
||||
{
|
||||
ble_adv_suspended = true;
|
||||
usb_enabled = false;
|
||||
switch (ctx.active_mode) {
|
||||
case MODE_SWITCH_BLE:
|
||||
mode_policy_set_ble(true);
|
||||
mode_policy_set_usb(false);
|
||||
break;
|
||||
|
||||
case MODE_SWITCH_USB:
|
||||
mode_policy_set_ble(false);
|
||||
mode_policy_set_usb(true);
|
||||
break;
|
||||
|
||||
case MODE_SWITCH_24G:
|
||||
mode_policy_set_ble(false);
|
||||
mode_policy_set_usb(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void apply_ble_mode_policy(void)
|
||||
{
|
||||
switch (ctx.active_mode) {
|
||||
case MODE_SWITCH_BLE:
|
||||
mode_policy_set_ble(true);
|
||||
break;
|
||||
|
||||
case MODE_SWITCH_USB:
|
||||
mode_policy_set_ble(false);
|
||||
break;
|
||||
|
||||
case MODE_SWITCH_24G:
|
||||
mode_policy_set_ble(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void apply_usb_mode_policy(void)
|
||||
{
|
||||
switch (ctx.active_mode) {
|
||||
case MODE_SWITCH_BLE:
|
||||
mode_policy_set_usb(false);
|
||||
break;
|
||||
|
||||
case MODE_SWITCH_USB:
|
||||
mode_policy_set_usb(true);
|
||||
break;
|
||||
|
||||
case MODE_SWITCH_24G:
|
||||
mode_policy_set_usb(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int do_init(void)
|
||||
{
|
||||
ctx.active_mode = MODE_SWITCH_USB;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int module_start(void)
|
||||
static int do_start(void)
|
||||
{
|
||||
if (running) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
running = true;
|
||||
apply_active_mode();
|
||||
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,8 +162,9 @@ 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) {
|
||||
apply_mode_policy(event->mode);
|
||||
if (module_lifecycle_is_running(&ctx.lc)) {
|
||||
ctx.active_mode = event->mode;
|
||||
apply_active_mode();
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -103,23 +174,21 @@ 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;
|
||||
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initialized) {
|
||||
err = module_init();
|
||||
if (err) {
|
||||
module_set_state(MODULE_STATE_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
if (check_state(event, MODULE_ID(ble_adv), MODULE_STATE_READY)) {
|
||||
if (module_lifecycle_is_running(&ctx.lc)) {
|
||||
apply_ble_mode_policy();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
err = module_start();
|
||||
if (err) {
|
||||
module_set_state(MODULE_STATE_ERROR);
|
||||
} else {
|
||||
module_set_state(MODULE_STATE_READY);
|
||||
if (check_state(event, MODULE_ID(usb_device_module),
|
||||
MODULE_STATE_READY)) {
|
||||
if (module_lifecycle_is_running(&ctx.lc)) {
|
||||
apply_usb_mode_policy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,23 +196,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;
|
||||
|
||||
@@ -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,36 @@ 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;
|
||||
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 +75,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 +93,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 +113,37 @@ 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.last_mode == MODE_SWITCH_INVALID) || (mode != ctx.last_mode)) {
|
||||
submit_mode_switch_event(mode, sample_mv);
|
||||
|
||||
last_mode = mode;
|
||||
mode_valid = true;
|
||||
force_report = false;
|
||||
ctx.last_mode = mode;
|
||||
}
|
||||
|
||||
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.last_mode = MODE_SWITCH_INVALID;
|
||||
|
||||
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 +152,21 @@ static int module_start(void)
|
||||
return err;
|
||||
}
|
||||
|
||||
running = true;
|
||||
force_report = true;
|
||||
k_work_reschedule(&mode_switch_sample_work, K_NO_WAIT);
|
||||
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 +175,32 @@ 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;
|
||||
(void)module_set_lifecycle(&ctx.lc, LC_RUNNING);
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
if (check_state(event, MODULE_ID(mode_policy_module),
|
||||
MODULE_STATE_READY)) {
|
||||
if (ctx.last_mode != MODE_SWITCH_INVALID) {
|
||||
submit_mode_switch_event(ctx.last_mode, 0U);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_power_down_event(aeh)) {
|
||||
if (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;
|
||||
|
||||
@@ -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,85 @@ 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 const char *lifecycle_name(enum module_lifecycle state)
|
||||
{
|
||||
switch (state) {
|
||||
case LC_UNINIT:
|
||||
return "UNINIT";
|
||||
case LC_RUNNING:
|
||||
return "RUNNING";
|
||||
case LC_STOPPED:
|
||||
return "STOPPED";
|
||||
case LC_SUSPENDED:
|
||||
return "SUSPENDED";
|
||||
case LC_ERROR:
|
||||
return "ERROR";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *proto_session_name(enum proto_session_state state)
|
||||
{
|
||||
switch (state) {
|
||||
case PROTO_SESSION_DOWN:
|
||||
return "DOWN";
|
||||
case PROTO_SESSION_WAIT_HELLO:
|
||||
return "WAIT_HELLO";
|
||||
case PROTO_SESSION_ACTIVE:
|
||||
return "ACTIVE";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *proto_transport_name(enum proto_transport transport)
|
||||
{
|
||||
switch (transport) {
|
||||
case PROTO_TRANSPORT_USB_CDC:
|
||||
return "usb_cdc";
|
||||
case PROTO_TRANSPORT_BLE_NUS:
|
||||
return "ble_nus";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
static int decode_body(const uint8_t *payload, size_t payload_len,
|
||||
CdcPacketBody *body)
|
||||
@@ -157,7 +227,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 +236,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 +273,10 @@ int protocol_module_process_message(enum proto_transport transport,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!running) {
|
||||
if (!module_lifecycle_is_running(&ctx.lc)) {
|
||||
LOG_WRN("Reject proto msg transport:%s len:%u lc:%s",
|
||||
proto_transport_name(transport), (uint32_t)req_payload_len,
|
||||
lifecycle_name(ctx.lc.state));
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
@@ -216,6 +288,10 @@ int protocol_module_process_message(enum proto_transport transport,
|
||||
switch (body.which_body) {
|
||||
case CdcPacketBody_hello_req_tag:
|
||||
if (session_state[transport] == PROTO_SESSION_DOWN) {
|
||||
LOG_WRN("Reject HelloReq transport:%s session:%s lc:%s",
|
||||
proto_transport_name(transport),
|
||||
proto_session_name(session_state[transport]),
|
||||
lifecycle_name(ctx.lc.state));
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
@@ -317,7 +393,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;
|
||||
}
|
||||
|
||||
@@ -329,7 +405,11 @@ static bool handle_proto_rx_event(const struct proto_rx_event *event)
|
||||
&rsp_payload_len);
|
||||
if (err) {
|
||||
if (err != -ENOTSUP) {
|
||||
LOG_WRN("Protocol processing failed (%d)", err);
|
||||
LOG_WRN("Protocol processing failed (%d) transport:%s session:%s lc:%s len:%u",
|
||||
err, proto_transport_name(event->transport),
|
||||
proto_session_name(session_state[event->transport]),
|
||||
lifecycle_name(ctx.lc.state),
|
||||
(uint32_t)event->dyndata.size);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -373,7 +453,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 +486,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 +534,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 +545,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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,12 +41,8 @@ static const char *const class_blocklist[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static bool initialized;
|
||||
static bool running;
|
||||
|
||||
enum usb_stack_state {
|
||||
USB_STACK_UNINITIALIZED = 0,
|
||||
USB_STACK_DISABLED,
|
||||
USB_STACK_READY,
|
||||
USB_STACK_ENABLED,
|
||||
};
|
||||
@@ -58,19 +55,43 @@ 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,
|
||||
};
|
||||
|
||||
static inline enum usb_state usb_public_state_get(void)
|
||||
{
|
||||
if ((usb_ctx.stack == USB_STACK_UNINITIALIZED) ||
|
||||
(usb_ctx.stack == USB_STACK_DISABLED)) {
|
||||
if (!module_lifecycle_is_running(&usb_ctx.lc) ||
|
||||
(usb_ctx.stack == USB_STACK_UNINITIALIZED)) {
|
||||
return USB_STATE_DISABLED;
|
||||
}
|
||||
|
||||
@@ -88,11 +109,6 @@ static inline enum usb_state usb_public_state_get(void)
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool usb_stack_was_initialized(void)
|
||||
{
|
||||
return (usb_ctx.stack != USB_STACK_UNINITIALIZED);
|
||||
}
|
||||
|
||||
static inline void usb_bus_set(enum usb_bus_state state)
|
||||
{
|
||||
if (usb_ctx.bus == state) {
|
||||
@@ -100,7 +116,10 @@ static inline void usb_bus_set(enum usb_bus_state state)
|
||||
}
|
||||
|
||||
usb_ctx.bus = state;
|
||||
submit_usb_state(usb_public_state_get());
|
||||
|
||||
if (module_lifecycle_is_initialized(&usb_ctx.lc)) {
|
||||
submit_usb_state(usb_public_state_get());
|
||||
}
|
||||
}
|
||||
|
||||
static void update_power_manager_restriction(bool vbus_present)
|
||||
@@ -200,31 +219,30 @@ static void usbd_msg_cb(struct usbd_context *const usbd_ctx,
|
||||
switch (msg->type) {
|
||||
case USBD_MSG_VBUS_READY:
|
||||
update_power_manager_restriction(true);
|
||||
usb_bus_set(USB_BUS_POWERED);
|
||||
|
||||
if (usb_ctx.stack == USB_STACK_READY) {
|
||||
int err = usbd_enable(&blinky_usbd);
|
||||
if (module_lifecycle_is_running(&usb_ctx.lc) &&
|
||||
(usb_ctx.stack == USB_STACK_READY)) {
|
||||
int err = do_start();
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("usbd_enable failed (%d)", err);
|
||||
} else {
|
||||
usb_ctx.stack = USB_STACK_ENABLED;
|
||||
usb_bus_set(USB_BUS_POWERED);
|
||||
LOG_ERR("USB start on VBUS ready failed (%d)", err);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case USBD_MSG_VBUS_REMOVED:
|
||||
update_power_manager_restriction(false);
|
||||
|
||||
if (usb_ctx.stack == USB_STACK_ENABLED) {
|
||||
int err = usbd_disable(&blinky_usbd);
|
||||
int err = do_stop();
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("usbd_disable failed (%d)", err);
|
||||
LOG_ERR("USB stop on VBUS removed failed (%d)", err);
|
||||
}
|
||||
} else {
|
||||
update_power_manager_restriction(false);
|
||||
}
|
||||
|
||||
if (usb_ctx.stack != USB_STACK_DISABLED) {
|
||||
if (usb_ctx.stack != USB_STACK_UNINITIALIZED) {
|
||||
usb_ctx.stack = USB_STACK_READY;
|
||||
}
|
||||
|
||||
@@ -256,16 +274,13 @@ static void usbd_msg_cb(struct usbd_context *const usbd_ctx,
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_stack_init_once(void)
|
||||
static int do_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (usb_stack_was_initialized()) {
|
||||
if (usb_ctx.stack == USB_STACK_DISABLED) {
|
||||
usb_ctx.stack = USB_STACK_READY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
usb_ctx.stack = USB_STACK_UNINITIALIZED;
|
||||
usb_ctx.bus = USB_BUS_DISCONNECTED;
|
||||
update_power_manager_restriction(false);
|
||||
|
||||
STRUCT_SECTION_FOREACH(usb_function_hook, hook) {
|
||||
if (hook->pre_stack_init == NULL) {
|
||||
@@ -299,6 +314,17 @@ static int usb_stack_init_once(void)
|
||||
|
||||
usb_ctx.stack = USB_STACK_READY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_start(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (usb_ctx.stack == USB_STACK_ENABLED) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!usbd_can_detect_vbus(&blinky_usbd)) {
|
||||
err = usbd_enable(&blinky_usbd);
|
||||
if (err) {
|
||||
@@ -307,117 +333,51 @@ static int usb_stack_init_once(void)
|
||||
}
|
||||
|
||||
usb_ctx.stack = USB_STACK_ENABLED;
|
||||
usb_bus_set(USB_BUS_POWERED);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_stack_enable_if_needed(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = usb_stack_init_once();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((usb_ctx.stack == USB_STACK_READY) &&
|
||||
(!usbd_can_detect_vbus(&blinky_usbd) ||
|
||||
(usb_ctx.bus != USB_BUS_DISCONNECTED))) {
|
||||
err = usbd_enable(&blinky_usbd);
|
||||
if (err) {
|
||||
LOG_ERR("usbd_enable failed (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
usb_ctx.stack = USB_STACK_ENABLED;
|
||||
|
||||
if (usb_ctx.bus == USB_BUS_DISCONNECTED) {
|
||||
usb_bus_set(USB_BUS_POWERED);
|
||||
} else {
|
||||
submit_usb_state(usb_public_state_get());
|
||||
}
|
||||
|
||||
usb_ctx.bus = USB_BUS_POWERED;
|
||||
update_power_manager_restriction(true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
submit_usb_state(usb_public_state_get());
|
||||
if (usb_ctx.bus == USB_BUS_DISCONNECTED) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = usbd_enable(&blinky_usbd);
|
||||
if (err) {
|
||||
LOG_ERR("usbd_enable failed (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
usb_ctx.stack = USB_STACK_ENABLED;
|
||||
update_power_manager_restriction(true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_stack_disable_if_needed(void)
|
||||
static int do_stop(void)
|
||||
{
|
||||
if (usb_ctx.stack == USB_STACK_ENABLED) {
|
||||
int err = usbd_disable(&blinky_usbd);
|
||||
int err;
|
||||
|
||||
if (usb_ctx.stack == USB_STACK_ENABLED) {
|
||||
err = usbd_disable(&blinky_usbd);
|
||||
if (err) {
|
||||
LOG_ERR("usbd_disable failed (%d)", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
usb_ctx.stack = USB_STACK_DISABLED;
|
||||
usb_ctx.stack = USB_STACK_READY;
|
||||
update_power_manager_restriction(false);
|
||||
submit_usb_state(usb_public_state_get());
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int module_init(void)
|
||||
{
|
||||
usb_ctx.stack = USB_STACK_UNINITIALIZED;
|
||||
usb_ctx.bus = USB_BUS_DISCONNECTED;
|
||||
update_power_manager_restriction(false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int module_start(void)
|
||||
{
|
||||
if (running) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
running = true;
|
||||
submit_usb_state(usb_public_state_get());
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void module_pause(void)
|
||||
{
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
|
||||
running = false;
|
||||
submit_usb_state(usb_public_state_get());
|
||||
}
|
||||
|
||||
static bool handle_module_state_event(const struct module_state_event *event)
|
||||
{
|
||||
if (!check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
|
||||
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);
|
||||
}
|
||||
if (module_set_lifecycle(&usb_ctx.lc, LC_RUNNING) == 0) {
|
||||
submit_usb_state(usb_public_state_get());
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -426,37 +386,28 @@ 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);
|
||||
return false;
|
||||
if (module_set_lifecycle(&usb_ctx.lc, LC_SUSPENDED) == 0) {
|
||||
submit_usb_state(usb_public_state_get());
|
||||
}
|
||||
|
||||
module_set_state(MODULE_STATE_READY);
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (usb_stack_was_initialized()) {
|
||||
module_set_state(MODULE_STATE_READY);
|
||||
if (module_set_lifecycle(&usb_ctx.lc, LC_RUNNING) == 0) {
|
||||
submit_usb_state(usb_public_state_get());
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -479,22 +430,19 @@ 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)) {
|
||||
if (module_set_lifecycle(&usb_ctx.lc, LC_STOPPED) == 0) {
|
||||
submit_usb_state(usb_public_state_get());
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
if (module_set_lifecycle(&usb_ctx.lc, LC_RUNNING) == 0) {
|
||||
submit_usb_state(usb_public_state_get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user