Add xteink browser bridge

This commit is contained in:
2026-07-20 00:20:51 -04:00
parent d58495104a
commit b0bce6c486
5 changed files with 101 additions and 5 deletions
+1 -1
View File
@@ -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];
+56
View File
@@ -9,6 +9,60 @@
#include "hw/irq.h"
#include "hw/display/xteink_x3_eink.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
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);
}
+14 -4
View File
@@ -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;
}
+28
View File
@@ -57,6 +57,10 @@
#include "hw/sd/sd.h"
#include "hw/net/can/esp32c3_twai.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#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. */
+2
View File
@@ -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);