ESP-NOW bridge for P4 (#573)

This commit is contained in:
Shadowtrance
2026-07-19 17:23:20 +10:00
committed by GitHub
parent bd30aa046a
commit 2d768ef3a1
40 changed files with 1343 additions and 182 deletions
@@ -5,6 +5,7 @@
#include <stdint.h>
#include <tactility/error.h>
#include <tactility/firmware/firmware.h>
#ifdef __cplusplus
extern "C" {
@@ -196,6 +197,15 @@ struct WifiApi {
* @return ERROR_NONE on success
*/
error_t (*remove_event_callback)(struct Device* device, WifiEventCallback callback);
/**
* Get this device's co-processor firmware update interface, if it has one.
* @param[in] device the wifi device
* @param[out] ops filled in with the FirmwareOps vtable and its ctx, if supported
* @return ERROR_NONE on success, ERROR_NOT_SUPPORTED if this device has no updatable
* co-processor (ops is left untouched in that case)
*/
error_t (*get_firmware_ops)(struct Device* device, const struct FirmwareOps** ops, void** ctx);
};
extern const struct DeviceType WIFI_TYPE;
@@ -216,6 +226,7 @@ error_t wifi_station_disconnect(struct Device* device);
error_t wifi_station_get_rssi(struct Device* device, int32_t* rssi);
error_t wifi_add_event_callback(struct Device* device, void* callback_context, WifiEventCallback callback);
error_t wifi_remove_event_callback(struct Device* device, WifiEventCallback callback);
error_t wifi_get_firmware_ops(struct Device* device, const struct FirmwareOps** ops, void** ctx);
#ifdef __cplusplus
}
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <tactility/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Generic co-processor/companion-radio firmware info & update interface.
*
* Not tied to any particular driver: some devices are backed by a separate co-processor with its
* own updatable firmware (e.g. esp-hosted-mcu's P4+C6/C5 pairing, reachable via
* WifiApi::get_firmware_ops() in tactility/drivers/wifi.h); most aren't. A driver with no such
* co-processor returns ERROR_NOT_SUPPORTED from whatever function exposes this interface
* (leaving its output params untouched) - callers must check the result before using any
* FirmwareOps function.
*/
struct FirmwareInfo {
char name[32]; // human-readable target name, e.g. "esp32c6" - empty if unavailable
uint32_t hw_id; // raw hardware/chip id, implementation-defined
uint32_t fw_major;
uint32_t fw_minor;
uint32_t fw_patch;
};
struct FirmwareUpdateRequest {
size_t image_size;
uint32_t flags;
};
struct FirmwareUpdateHandle;
struct FirmwareOps {
/**
* Wait for the co-processor to be ready to accept firmware info/update calls.
* @param[in] ctx opaque context, passed to every other FirmwareOps function
* @param[in] timeout_ms how long to wait
* @return true if ready, false on timeout
*/
bool (*wait_ready)(void* ctx, uint32_t timeout_ms);
/**
* Get the co-processor's current firmware/hardware info.
* @return ERROR_NONE on success, or an error code (e.g. not ready)
*/
error_t (*get_info)(void* ctx, struct FirmwareInfo* info);
/**
* Begin a firmware update, filling in *handle for use with write()/finish()/abort().
* @return ERROR_NONE on success, or an error code
*/
error_t (*begin)(void* ctx, const struct FirmwareUpdateRequest* req, struct FirmwareUpdateHandle** handle);
/** Write a chunk of firmware image data. @return ERROR_NONE on success, or an error code */
error_t (*write)(struct FirmwareUpdateHandle* handle, const void* data, size_t len);
/** Finalize a successful update. @return ERROR_NONE on success, or an error code */
error_t (*finish)(struct FirmwareUpdateHandle* handle);
/** Abort an in-progress update (e.g. after a write() failure). */
error_t (*abort)(struct FirmwareUpdateHandle* handle);
/**
* Activate the most recently finish()ed firmware, causing the co-processor to reboot into it.
* @note Not guaranteed to be supported by every co-processor firmware version - callers should
* check get_info()'s reported version against their own known-good threshold before calling
* this, and fall back to treating the update as complete after finish() otherwise.
*/
error_t (*activate)(void* ctx);
};
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Suspend or resume WifiService's periodic background auto-connect scan.
*
* Not a device operation - see tactility/drivers/wifi.h for the actual radio API. This exists
* for narrow, short-lived windows where any WiFi/co-processor traffic would be unsafe (e.g. a
* known co-processor reboot in progress during an OTA update) - callers must resume when done.
* Independent of WifiService's own internal connect()/disconnect() pause bookkeeping: it is
* never cleared implicitly by a connection succeeding/failing or the radio being enabled, only a
* matching wifi_auto_scan_set_paused(false) clears it.
*
* The real implementation lives in the Tactility WiFi service
* (Tactility/Source/service/wifi/Wifi.cpp), a layer above TactilityKernel - TactilityKernel
* can't call up into it directly (and mustn't link against it: TactilityKernelTests links
* TactilityKernel alone, without Tactility). Tactility registers its implementation at startup
* via wifi_auto_scan_set_paused_function(); until then (or on a build that never links Tactility, e.g. a
* bare-kernel target) this is a no-op, same as WifiApi::get_firmware_ops() returning
* ERROR_NOT_SUPPORTED when nothing is registered.
*
* @param[in] paused when true, the auto-connect timer stops issuing scans until resumed
*/
void wifi_auto_scan_set_paused(bool paused);
/** @brief Register the real implementation. Called once by the Tactility WiFi service. */
void wifi_auto_scan_set_paused_function(void (*set_paused)(bool paused));
#ifdef __cplusplus
}
#endif
+8
View File
@@ -68,6 +68,14 @@ error_t wifi_remove_event_callback(struct Device* device, WifiEventCallback call
return WIFI_API(device)->remove_event_callback(device, callback);
}
error_t wifi_get_firmware_ops(struct Device* device, const struct FirmwareOps** ops, void** ctx) {
auto* api = WIFI_API(device);
if (api->get_firmware_ops == nullptr) {
return ERROR_NOT_SUPPORTED;
}
return api->get_firmware_ops(device, ops, ctx);
}
const struct DeviceType WIFI_TYPE = {
.name = "wifi"
};
+4
View File
@@ -34,6 +34,7 @@
#include <tactility/error.h>
#include <tactility/filesystem/file_system.h>
#include <tactility/module.h>
#include <tactility/wifi_auto_scan.h>
#include <tactility/service/service_instance.h>
#include <tactility/service/service_manager.h>
#include <tactility/service/service_paths.h>
@@ -326,7 +327,10 @@ const struct ModuleSymbol KERNEL_SYMBOLS[] = {
DEFINE_MODULE_SYMBOL(wifi_station_get_rssi),
DEFINE_MODULE_SYMBOL(wifi_add_event_callback),
DEFINE_MODULE_SYMBOL(wifi_remove_event_callback),
DEFINE_MODULE_SYMBOL(wifi_get_firmware_ops),
DEFINE_MODULE_SYMBOL(WIFI_TYPE),
// wifi_auto_scan
DEFINE_MODULE_SYMBOL(wifi_auto_scan_set_paused),
// drivers/usb_host_hid
DEFINE_MODULE_SYMBOL(usb_host_hid_is_connected),
DEFINE_MODULE_SYMBOL(usb_host_hid_subscribe),
+15
View File
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
#include <tactility/wifi_auto_scan.h>
#include <stddef.h>
static void (*registeredSetPaused)(bool paused) = NULL;
void wifi_auto_scan_set_paused_function(void (*set_paused)(bool paused)) {
registeredSetPaused = set_paused;
}
void wifi_auto_scan_set_paused(bool paused) {
if (registeredSetPaused != NULL) {
registeredSetPaused(paused);
}
}