27#include "esp_heap_caps.h"
28#include "freertos/FreeRTOS.h"
29#include "freertos/semphr.h"
40const char*
TAG =
"PLG_FEAT";
42constexpr uint8_t MAX_HANDLERS = 4;
44SemaphoreHandle_t s_lock =
nullptr;
45void lock_init() {
if (!s_lock) s_lock = xSemaphoreCreateMutex(); }
48 Guard() {
if (s_lock) held = (xSemaphoreTake(s_lock, portMAX_DELAY) == pdTRUE); }
49 ~Guard() {
if (held) xSemaphoreGive(s_lock); }
55 void* plugin =
nullptr;
57 uint32_t action_id = 0;
59Handler s_handlers[MAX_HANDLERS];
63 enum class St : uint8_t {
Idle, Stashed, Delivered };
66 std::string provider_id;
67 void* caller =
nullptr;
68 std::string caller_id;
69 uint32_t status_action_id = 0;
70 uint8_t* data =
nullptr;
78uint32_t s_cur_len = 0;
79uint8_t* s_cur_data =
nullptr;
81void job_free_locked() {
82 if (s_job.data) { heap_caps_free(s_job.data); s_job.data =
nullptr; }
84 s_job.st = FeatureJob::St::Idle;
85 s_job.caller =
nullptr;
86 s_job.caller_id.clear();
87 s_job.provider_id.clear();
88 s_job.status_action_id = 0;
89 s_job.feature[0] =
'\0';
92int find_handler(
void* plugin,
const char* feature) {
93 for (
int i = 0; i < MAX_HANDLERS; ++i) {
94 if (s_handlers[i].used && s_handlers[i].plugin == plugin &&
102int find_handler_by_feature(
const char* feature) {
103 for (
int i = 0; i < MAX_HANDLERS; ++i) {
104 if (s_handlers[i].used &&
114void report_to_caller(
void* caller,
const std::string& caller_id,
115 uint32_t action_id, int32_t status) {
116 if (!caller || !action_id || caller_id.empty())
return;
121 static_cast<pm::Plugin*
>(caller), action_id, 0,
122 static_cast<uint32_t
>(status));
136 uint32_t status_action_id) {
145 if (provider.empty()) {
152 uint8_t* copy =
nullptr;
154 copy =
static_cast<uint8_t*
>(heap_caps_malloc(len, MALLOC_CAP_SPIRAM));
156 std::memcpy(copy, data, len);
161 if (s_job.st != FeatureJob::St::Idle) {
162 if (copy) heap_caps_free(copy);
165 s_job.st = FeatureJob::St::Stashed;
166 std::strncpy(s_job.feature, feature,
sizeof(s_job.feature) - 1);
167 s_job.feature[
sizeof(s_job.feature) - 1] =
'\0';
168 s_job.provider_id = std::move(provider);
169 s_job.caller = caller;
170 s_job.caller_id = caller->id();
171 s_job.status_action_id = status_action_id;
173 s_job.len =
static_cast<uint32_t
>(len);
182 bool declared =
false;
183 for (
const auto&
name : p->manifest().capabilities.provides) {
184 if (
name == feature) { declared =
true;
break; }
188 int idx = find_handler(p, feature);
190 for (
int i = 0; i < MAX_HANDLERS; ++i) {
191 if (!s_handlers[i].used) { idx = i;
break; }
196 Handler& h = s_handlers[idx];
199 std::strncpy(h.feature, feature,
sizeof(h.feature) - 1);
200 h.feature[
sizeof(h.feature) - 1] =
'\0';
201 h.action_id = action_id;
206 char* feature_out,
size_t feature_size) {
208 uint32_t n = s_cur_len;
209 if (n > buf_size) n =
static_cast<uint32_t
>(buf_size);
210 if (n > 0 && s_cur_data) std::memcpy(buf, s_cur_data, n);
211 if (feature_out && feature_size) {
212 std::strncpy(feature_out, s_cur_feature, feature_size - 1);
213 feature_out[feature_size - 1] =
'\0';
215 return static_cast<int>(n);
222 void* caller =
nullptr;
223 std::string caller_id;
228 int idx = find_handler_by_feature(s_job.feature);
230 caller = s_job.caller;
231 caller_id = s_job.caller_id;
232 aid = s_job.status_action_id;
235 report_to_caller(caller, caller_id, aid, status_code);
242 std::string provider_id;
245 if (s_job.st != FeatureJob::St::Stashed)
return;
246 std::memcpy(feature, s_job.feature,
sizeof(feature));
247 provider_id = s_job.provider_id;
251 bool started = mgr.activePluginId() == provider_id;
254 int idx = started ? find_handler_by_feature(feature) : -1;
258 void* caller =
nullptr;
259 std::string caller_id;
263 if (s_job.st != FeatureJob::St::Stashed)
return;
264 caller = s_job.caller;
265 caller_id = s_job.caller_id;
266 aid = s_job.status_action_id;
269 LOG_W(
TAG,
"feature '%s': provider %s unavailable", feature, provider_id.c_str());
277 if (s_job.st != FeatureJob::St::Stashed)
return;
278 s_job.st = FeatureJob::St::Delivered;
279 s_cur_data = s_job.data;
280 s_cur_len = s_job.len;
281 std::memcpy(s_cur_feature, s_job.feature,
sizeof(s_cur_feature));
284 mgr.dispatchActionTo(
static_cast<pm::Plugin*
>(s_handlers[idx].plugin),
285 s_handlers[idx].action_id, 0, len);
290 s_cur_data =
nullptr;
291 s_cur_feature[0] =
'\0';
296 for (
int i = 0; i < MAX_HANDLERS; ++i) {
297 if (s_handlers[i].used && s_handlers[i].plugin == plugin) {
298 s_handlers[i] = Handler{};
303 void* caller =
nullptr;
304 std::string caller_id;
306 bool provider_died =
false;
309 if (s_job.st == FeatureJob::St::Idle)
return;
310 if (s_job.caller == plugin) {
313 s_job.caller =
nullptr;
314 s_job.caller_id.clear();
315 s_job.status_action_id = 0;
318 if (s_job.st == FeatureJob::St::Delivered) {
319 int idx = find_handler_by_feature(s_job.feature);
322 provider_died = (idx < 0);
324 if (!provider_died)
return;
325 caller = s_job.caller;
326 caller_id = s_job.caller_id;
327 aid = s_job.status_action_id;
Validation for external-feature names declared under the manifest provides capability and passed to t...
Internationalization with English fallbacks in code and overlay translations loaded at runtime from a...
Discovers, loads, runs and unloads WASM plugins on the badge.
Owned WAMR module instance + per-plugin state.
char name[cdc::hal::ISecureElement::RMEM_NAME_LEN]
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
static PluginManager & instance() noexcept
void dispatchActionTo(Plugin *plugin, uint32_t action_id, uint32_t idx, uint32_t user_data)
bool featureInstalled(const char *feature) const
True if any installed plugin's manifest provides this external feature. Reads the cached index (same ...
std::string featureProviderId(const char *feature) const
Installed plugin id providing this external feature, or empty.
int host_ext_feature_register_handler(const char *feature, uint32_t action_id)
Register this plugin as the live handler for a feature it provides.
#define HOST_EXT_FEATURE_NAME_MAX
Maximum external-feature name length including the NUL.
int host_ext_feature_result(int32_t status_code)
Report the outcome of the job this provider is currently handling.
#define HOST_EXT_FEATURE_PAYLOAD_MAX
Maximum payload a caller may hand to a provider in one job.
int host_ext_feature_use(const char *feature, const uint8_t *data, size_t len, uint32_t status_action_id)
Invoke feature with a payload; the provider runs in the foreground.
#define HOST_EXT_FEATURE_STATUS_ERROR
Provider status: generic failure. Provider-defined codes are >= 1.
int host_ext_feature_consume(uint8_t *buf, size_t buf_size, char *feature_out, size_t feature_size)
Pull the payload of the job that fired the current handler action.
int host_ext_feature_available(const char *feature)
Check whether an installed plugin provides feature.
CDC Badge OS plugin host API - canonical C ABI contract.
#define HOST_ERR_NO_CAPABILITY
#define HOST_ERR_INVALID_ARG
#define HOST_ERR_NO_MEMORY
#define HOST_ERR_NOT_FOUND
void plg_ext_feature_pump(void)
void * plg_get_active_plugin(void)
void plg_ext_feature_on_unload(void *plugin)
bool isValidExtFeatureName(const char *name)
True if name is a well-formed external-feature name.
const char * tr(const char *key)
Look up a translation by string key.
void showMessage(const char *message, MessageIcon icon=MessageIcon::NONE, uint32_t timeoutMs=0, MessageBox::CloseCallback onClose=nullptr)
Shows the shared modal message box.