From b0bce6c4860f338f49e7243e098eba961af0ce9d Mon Sep 17 00:00:00 2001 From: Evan Reichard Date: Mon, 20 Jul 2026 00:20:51 -0400 Subject: [PATCH] Add xteink browser bridge --- hw/adc/esp32c3_adc.c | 2 +- hw/display/xteink_x3_eink.c | 56 ++++++++++++++++++++++++++++++++++++ hw/gpio/esp32_gpio.c | 18 +++++++++--- hw/riscv/esp32c3.c | 28 ++++++++++++++++++ include/hw/gpio/esp32_gpio.h | 2 ++ 5 files changed, 101 insertions(+), 5 deletions(-) diff --git a/hw/adc/esp32c3_adc.c b/hw/adc/esp32c3_adc.c index 89087429c7..4d38b3e13c 100644 --- a/hw/adc/esp32c3_adc.c +++ b/hw/adc/esp32c3_adc.c @@ -27,7 +27,7 @@ static uint64_t esp32c3_adc_read(void *opaque, hwaddr addr, unsigned size) } if (addr == ADC_DATA1 || addr == ADC_DATA2) { return s->channel < ESP32C3_ADC_CHANNELS - ? MIN(s->input[s->channel], ADC_MAX) + ? MIN(qatomic_read(&s->input[s->channel]), ADC_MAX) : ADC_MAX; } return s->regs[addr / 4]; diff --git a/hw/display/xteink_x3_eink.c b/hw/display/xteink_x3_eink.c index b699abae4e..8a177e9159 100644 --- a/hw/display/xteink_x3_eink.c +++ b/hw/display/xteink_x3_eink.c @@ -9,6 +9,60 @@ #include "hw/irq.h" #include "hw/display/xteink_x3_eink.h" +#ifdef __EMSCRIPTEN__ +#include + +static QemuConsole *xteink_wasm_console; +static uint32_t xteink_wasm_generation; + +EMSCRIPTEN_KEEPALIVE uintptr_t xteink_wasm_framebuffer(void) +{ + DisplaySurface *surface = xteink_wasm_console + ? qemu_console_surface(xteink_wasm_console) + : NULL; + return surface ? (uintptr_t)surface_data(surface) : 0; +} + +EMSCRIPTEN_KEEPALIVE int xteink_wasm_width(void) +{ + DisplaySurface *surface = xteink_wasm_console + ? qemu_console_surface(xteink_wasm_console) + : NULL; + return surface ? surface_width(surface) : 0; +} + +EMSCRIPTEN_KEEPALIVE int xteink_wasm_height(void) +{ + DisplaySurface *surface = xteink_wasm_console + ? qemu_console_surface(xteink_wasm_console) + : NULL; + return surface ? surface_height(surface) : 0; +} + +EMSCRIPTEN_KEEPALIVE int xteink_wasm_stride(void) +{ + DisplaySurface *surface = xteink_wasm_console + ? qemu_console_surface(xteink_wasm_console) + : NULL; + return surface ? surface_stride(surface) : 0; +} + +EMSCRIPTEN_KEEPALIVE uint32_t xteink_wasm_frame_generation(void) +{ + return qatomic_read(&xteink_wasm_generation); +} + +static void xteink_wasm_frame_updated(QemuConsole *console) +{ + xteink_wasm_console = console; + qatomic_inc(&xteink_wasm_generation); +} +#else +static void xteink_wasm_frame_updated(QemuConsole *console) +{ +} +#endif + #define CMD_POWER_OFF 0x02 #define CMD_POWER_ON 0x04 #define CMD_DTM1 0x10 @@ -33,6 +87,7 @@ static void xteink_x3_eink_render(XteinkX3EinkState *s) } } + xteink_wasm_frame_updated(s->console); dpy_gfx_update(s->console, 0, 0, XTEINK_X3_HEIGHT, XTEINK_X3_WIDTH); } @@ -171,6 +226,7 @@ static void xteink_x4_eink_render(XteinkX4EinkState *s) DisplaySurface *surface = qemu_console_surface(s->console); memset(surface_data(surface), 0xff, surface_stride(surface) * surface_height(surface)); + xteink_wasm_frame_updated(s->console); dpy_gfx_update(s->console, 0, 0, 480, 800); } diff --git a/hw/gpio/esp32_gpio.c b/hw/gpio/esp32_gpio.c index 9ec8bd9244..754990968c 100644 --- a/hw/gpio/esp32_gpio.c +++ b/hw/gpio/esp32_gpio.c @@ -38,10 +38,20 @@ static void esp32_gpio_drive_outputs(Esp32GpioState *s) } } +void esp32_gpio_set_input_level(Esp32GpioState *s, int pin, bool level) +{ + uint32_t old_level; + uint32_t new_level; + + do { + old_level = qatomic_read(&s->input_level); + new_level = deposit32(old_level, pin, 1, level); + } while (qatomic_cmpxchg(&s->input_level, old_level, new_level) != old_level); +} + static void esp32_gpio_set_input(void *opaque, int pin, int level) { - Esp32GpioState *s = ESP32_GPIO(opaque); - s->input_level = deposit32(s->input_level, pin, 1, !!level); + esp32_gpio_set_input_level(ESP32_GPIO(opaque), pin, level); } static void esp32_gpio_get_input(Object *obj, Visitor *v, const char *name, @@ -49,7 +59,7 @@ static void esp32_gpio_get_input(Object *obj, Visitor *v, const char *name, { Esp32GpioState *s = ESP32_GPIO(obj); int pin = GPOINTER_TO_INT(opaque); - bool level = extract32(s->input_level, pin, 1); + bool level = extract32(qatomic_read(&s->input_level), pin, 1); visit_type_bool(v, name, &level, errp); } @@ -78,7 +88,7 @@ static uint64_t esp32_gpio_read(void *opaque, hwaddr addr, unsigned int size) case A_GPIO_STRAP: return s->strap_mode; case GPIO_IN: - return s->input_level; + return qatomic_read(&s->input_level); default: return 0; } diff --git a/hw/riscv/esp32c3.c b/hw/riscv/esp32c3.c index 55ab156533..febc96bfe3 100644 --- a/hw/riscv/esp32c3.c +++ b/hw/riscv/esp32c3.c @@ -57,6 +57,10 @@ #include "hw/sd/sd.h" #include "hw/net/can/esp32c3_twai.h" +#ifdef __EMSCRIPTEN__ +#include +#endif + #define ESP32C3_IO_WARNING 0 #define ESP32C3_RESET_ADDRESS 0x40000000 @@ -103,6 +107,27 @@ struct Esp32C3MachineState { Esp32C3TWAIState twai; }; +#ifdef __EMSCRIPTEN__ +static struct Esp32C3MachineState *xteink_wasm_machine; + +EMSCRIPTEN_KEEPALIVE void xteink_wasm_set_adc(int channel, uint32_t value) +{ + if (xteink_wasm_machine && channel >= 0 && + channel < ESP32C3_ADC_CHANNELS) { + qatomic_set(&xteink_wasm_machine->adc.input[channel], + MIN(value, UINT32_C(0xfff))); + } +} + +EMSCRIPTEN_KEEPALIVE void xteink_wasm_set_gpio(int pin, int level) +{ + if (xteink_wasm_machine && pin >= 0 && pin < ESP32_GPIO_COUNT) { + esp32_gpio_set_input_level(&xteink_wasm_machine->gpio.parent, + pin, level); + } +} +#endif + /* Fake register used by ESP-IDF application to determine whether the code is running on real hardware or on QEMU */ #define A_SYSCON_ORIGIN_REG 0x3F8 /* Temporary macro for generating a random value from register SYSCON_RND_DATA_REG */ @@ -722,6 +747,9 @@ static void xteink_machine_init(MachineState *machine) Esp32C3MachineState *ms = ESP32C3_MACHINE(machine); ms->xteink = true; esp32c3_machine_init(machine); +#ifdef __EMSCRIPTEN__ + xteink_wasm_machine = ms; +#endif /* X3 exposes the fingerprint chips; X4 omits them so stock firmware's * all-NAK probe selects X4. The X4 panel is a blank protocol stub. */ diff --git a/include/hw/gpio/esp32_gpio.h b/include/hw/gpio/esp32_gpio.h index e2f52ad702..721f5de5df 100644 --- a/include/hw/gpio/esp32_gpio.h +++ b/include/hw/gpio/esp32_gpio.h @@ -33,3 +33,5 @@ typedef struct Esp32GpioState { typedef struct Esp32GpioClass { SysBusDeviceClass parent_class; } Esp32GpioClass; + +void esp32_gpio_set_input_level(Esp32GpioState *s, int pin, bool level);