Due to issues with the Internet.ee domain registry, our main domain, paste.ee, is currently disabled due to abuse reports. We are looking into alternative domains to continue operation, but for now the pastee.dev domain is the primary domain.
If you wish to blame someone, blame the scum using this site as a malware host.
Submitted on November 7, 2025 at 04:56 AM
Expires on November 7, 2026 at 04:56 AM (4 months from now)

/*
 * ESP32 GPIO emulation
 *
 * Copyright (c) 2019 Espressif Systems (Shanghai) Co. Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 or
 * (at your option) any later version.
 *
	Modifed by cpu.
 */

#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "hw/registerfields.h"
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "hw/gpio/esp32_gpio.h"



//base address is 0x3FF44000



#define GPIO_OUT_REG_RW                   0x0004
#define GPIO_OUT_W1TS_REG_WO              0x0008
#define GPIO_OUT_W1TC_REG_WO              0x000C

#define GPIO_OUT1_REG_RW                  0x0010
#define GPIO_OUT1_W1TS_REG_WO             0x0014
#define GPIO_OUT1_W1TC_REG_WO             0x0018

#define GPIO_ENABLE_REG_RW                0x0020
#define GPIO_ENABLE_W1TS_REG_WO           0x0024
#define GPIO_ENABLE_W1TC_REG_WO           0x0028

#define GPIO_ENABLE1_REG_RW               0x002C
#define GPIO_ENABLE1_W1TS_REG_WO          0x0030
#define GPIO_ENABLE1_W1TC_REG_WO          0x0034

#define GPIO_STRAP_REG_RO                 0x0038

#define GPIO_IN_REG_RO                    0x003C
#define GPIO_IN1_REG_RO                   0x0040

#define GPIO_STATUS_REG_RW                0x0044
#define GPIO_STATUS_W1TS_REG_WO           0x0048
#define GPIO_STATUS_W1TC_REG_WO           0x004C

#define GPIO_STATUS1_REG_RW               0x0050
#define GPIO_STATUS1_W1TS_REG_WO          0x0054
#define GPIO_STATUS1_W1TC_REG_WO          0x0058

#define GPIO_ACPU_INT_REG_RO              0x0060
#define GPIO_ACPU_NMI_INT_REG_RO          0x0064

#define GPIO_PCPU_INT_REG_RO              0x0068
#define GPIO_PCPU_NMI_INT_REG_RO          0x006C

#define GPIO_ACPU_INT1_REG_RO             0x0074
#define GPIO_ACPU_NMI_INT1_REG_RO         0x0078

#define GPIO_PCPU_INT1_REG_RO             0x007C
#define GPIO_PCPU_NMI_INT1_REG_RO         0x0080

#define GPIO_PIN_REG_START_RW             0x0088
#define GPIO_PIN_REG_END_RW               0x0124

#define GPIO_FUNC_IN_SEL_CFG_REG_START_RW 0x0130
#define GPIO_FUNC_IN_SEL_CFG_REG_END_RW   0x052C

#define GPIO_FUNC_OUT_SEL_CFG_START_RW    0x0530
#define GPIO_FUNC_OUT_SEL_CFG_END_RW      0x092C






#define GPIO_INT_TYPE_DISABLED     0
#define GPIO_INT_TYPE_RISING_EDGE  1
#define GPIO_INT_TYPE_FALLING_EDGE 2
#define GPIO_INT_TYPE_ANY_EDGE     3
#define GPIO_INT_TYPE_LOW_LEVEL    4
#define GPIO_INT_TYPE_HIGH_LEVEL   5




/*****************************************
*          GPIO IN IRQ CALLBACK          *
*****************************************/

static void inputGpioHandler( void* opaque, int gpioNo, int level ){
	
	int oldLevel;
	int bankNo;
	unsigned char flagIrq;
	unsigned char flagNmiIrq;
	uint32_t mask;
	Esp32GpioState* gpioState;
	
	
	//just in case
	if( gpioNo < 0 ) return;
	if( gpioNo >= ESP32_GPIO_COUNT ) return;
	
	gpioState = ESP32_GPIO( opaque );
	
	//prepage vars for operate with registers
	if( gpioNo < 32 ){
		bankNo = 0;
		mask = 1ull << gpioNo;
	}else{
		bankNo = 1;
		mask = 1ull << ( gpioNo - 32 );
	}
	
	//get current gpio level
	oldLevel = ( mask & gpioState->inputRegister[bankNo] ) ? 1 : 0;
	
	//set new gpio level
	if( level ) gpioState->inputRegister[bankNo] |=  mask;
	else        gpioState->inputRegister[bankNo] &= ~mask;
	
	//printf( "[%s] GPIO %i old value: %i, new value: %i\n", __FUNCTION__, gpioNo, oldLevel, level );
	
	//TODO: gpio inversion function
	
	//interrupt trigger check
	switch( gpioState->gpioConfig[gpioNo].interruptType ){
		
		case GPIO_INT_TYPE_DISABLED:
			return;
		
		case GPIO_INT_TYPE_RISING_EDGE:
			if( oldLevel < level ) goto l_interruptTriggered;
			return;
		
		case GPIO_INT_TYPE_FALLING_EDGE:
			if( oldLevel > level ) goto l_interruptTriggered;
			return;
		
		case GPIO_INT_TYPE_ANY_EDGE:
			if( oldLevel != level ) goto l_interruptTriggered;
			return;
			
		case GPIO_INT_TYPE_LOW_LEVEL:
			if( !level ) goto l_interruptTriggered;
			return;
			
		case GPIO_INT_TYPE_HIGH_LEVEL:
			if( level ) goto l_interruptTriggered;
			return;
		
		default:
			qemu_log_mask(
				LOG_GUEST_ERROR,  
				"[%s] Unknown interrupt type (%" PRIu32 ") has been ignored\n", 
				__FUNCTION__, 
				gpioState->gpioConfig[gpioNo].interruptType 
			);
			return;
	}
	
l_interruptTriggered:
	
	flagIrq    = 0;
	flagNmiIrq = 0;
	
	//update interrupt status register
	gpioState->interruptStatusRegister[bankNo] |= mask;
	
	//update app cpu interrupt register
	if( gpioState->gpioConfig[gpioNo].appCpuIntEnable ){
		gpioState->appCpuInterruptStatus[bankNo] |= mask;
		flagIrq = 1;
	}
	
	//update app cpu non-maskable interrupt register
	if( gpioState->gpioConfig[gpioNo].appCpuNmiEnable ){
		gpioState->appCpuNmiInterruptStatus[bankNo] |= mask;
		flagNmiIrq = 1;
	}
	
	//update pro cpu interrupt register
	if( gpioState->gpioConfig[gpioNo].proCpuIntEnable ){
		gpioState->proCpuInterruptStatus[bankNo] |= mask;
		flagIrq = 1;
	}
	
	//update pro cpu non-maskable interrupt register
	if( gpioState->gpioConfig[gpioNo].proCpuNmiEnable ){
		gpioState->proCpuNmiInterruptStatus[bankNo] |= mask;
		flagNmiIrq = 1;
	}
	
	//apply irq flags to irq lines
	if( flagIrq    ) qemu_set_irq( gpioState->gpioIrq,    1 );
	if( flagNmiIrq ) qemu_set_irq( gpioState->gpioNmiIrq, 1 );
}

/***************************************
*          GPIO READ CALLBACK          *
***************************************/

static uint64_t esp32GpioRead( void* opaque, hwaddr address, unsigned int size ){
	
	size_t index;
	Esp32GpioState* gpioState;
	
	gpioState = ESP32_GPIO( opaque );
	
	//unaligned access
	if( address % 4 ){
		qemu_log_mask( LOG_GUEST_ERROR, "[%s] Unaligned access: 0x%04lX\n", __FUNCTION__, address );
		return 0;
	}
	
	//unexpected size
	if( size != 4 ){
		qemu_log_mask( LOG_GUEST_ERROR, "[%s] Unexpected size: %u\n", __FUNCTION__, size );
		return 0;
	}
	
	
	//output register
	if( address == GPIO_OUT_REG_RW           ) return gpioState->outputRegister[0];
	if( address == GPIO_OUT1_REG_RW          ) return gpioState->outputRegister[1];
	
	//enable output register
	if( address == GPIO_ENABLE_REG_RW        ) return gpioState->outputEnableRegister[0];
	if( address == GPIO_ENABLE1_REG_RW       ) return gpioState->outputEnableRegister[1];
	
	//bootstrap pin value
	if( address == GPIO_STRAP_REG_RO         ) return gpioState->strap_mode;
	
	//input register
	if( address == GPIO_IN_REG_RO            ) return gpioState->inputRegister[0];
	if( address == GPIO_IN1_REG_RO           ) return gpioState->inputRegister[1];
	
	//interrupt register
	if( address == GPIO_STATUS_REG_RW        ) return gpioState->interruptStatusRegister[0];
	if( address == GPIO_STATUS1_REG_RW       ) return gpioState->interruptStatusRegister[1];
	
	//application cpu interrupt 0-31
	if( address == GPIO_ACPU_INT_REG_RO      ) return gpioState->appCpuInterruptStatus[0];
	if( address == GPIO_ACPU_NMI_INT_REG_RO  ) return gpioState->appCpuNmiInterruptStatus[0];
	
	//pro cpu interrupt 0-31
	if( address == GPIO_PCPU_INT_REG_RO      ) return gpioState->proCpuInterruptStatus[0];
	if( address == GPIO_PCPU_NMI_INT_REG_RO  ) return gpioState->proCpuNmiInterruptStatus[0];
	
	//application cpu interrupt 32-39
	if( address == GPIO_ACPU_INT1_REG_RO     ) return gpioState->appCpuInterruptStatus[1];
	if( address == GPIO_ACPU_NMI_INT1_REG_RO ) return gpioState->appCpuNmiInterruptStatus[1];
	
	//pro cpu interrupt 32-39
	if( address == GPIO_PCPU_INT1_REG_RO     ) return gpioState->proCpuInterruptStatus[1];
	if( address == GPIO_PCPU_NMI_INT1_REG_RO ) return gpioState->proCpuNmiInterruptStatus[1];
	
	
	//pins configuration
	if( address >= GPIO_PIN_REG_START_RW && address <= GPIO_PIN_REG_END_RW ){
		
		index = ( address - GPIO_PIN_REG_START_RW ) / 4;
		return gpioState->gpioConfig[index].mem;
	}
	
	//peripheral input function selection
	if( address >= GPIO_FUNC_IN_SEL_CFG_REG_START_RW && address <= GPIO_FUNC_IN_SEL_CFG_REG_END_RW ){
		
		index = ( address - GPIO_FUNC_IN_SEL_CFG_REG_START_RW ) / 4;
		return gpioState->peripheralFunctionInput[index];
	}
	
	//peripheral output function selection
	if( address >= GPIO_FUNC_OUT_SEL_CFG_START_RW && address <= GPIO_FUNC_OUT_SEL_CFG_END_RW ){
		
		index = ( address - GPIO_FUNC_OUT_SEL_CFG_START_RW ) / 4;
		return gpioState->peripheralFunctionOutput[index];
	}
	
	
	qemu_log_mask( LOG_GUEST_ERROR, "[%s] Unexpected address: 0x%04lX\n", __FUNCTION__, address );
	return 0;
}

/****************************************
*          GPIO WRITE CALLBACK          *
****************************************/

//write output register logic
static void writeOutputRegister( Esp32GpioState* gpioState, unsigned int index, uint32_t value ){
	gpioState->outputRegister[index] = value;
}

static void setBitOutputRegister( Esp32GpioState* gpioState, unsigned int index, uint32_t mask ){
	gpioState->outputRegister[index] |= mask;
}

static void clearBitOutputRegister( Esp32GpioState* gpioState, unsigned int index, uint32_t mask ){
	gpioState->outputRegister[index] &= ~mask;
}


//write output enable register logic
static void writeOutputEnableRegister( Esp32GpioState* gpioState, unsigned int index, uint32_t value ){
	gpioState->outputEnableRegister[index] = value;
}

static void setBitOutputEnableRegister( Esp32GpioState* gpioState, unsigned int index, uint32_t mask ){
	gpioState->outputEnableRegister[index] |= mask;
}

static void clearBitOutputEnableRegister( Esp32GpioState* gpioState, unsigned int index, uint32_t mask ){
	gpioState->outputEnableRegister[index] &= ~mask;
}


//write interrupt status register logic
static void writeInterruptStatusRegister( Esp32GpioState* gpioState, unsigned int index, uint32_t value ){
	qemu_log_mask( LOG_GUEST_ERROR, "[%s] In not fully implemented!\n", __FUNCTION__ );
	gpioState->interruptStatusRegister[index] = value;
}

static void setBitInterruptStatusRegister( Esp32GpioState* gpioState, unsigned int index, uint32_t mask ){
	qemu_log_mask( LOG_GUEST_ERROR, "[%s] In not fully implemented!\n", __FUNCTION__ );
	gpioState->interruptStatusRegister[index] |= mask;
}

static void clearBitInterruptStatusRegister( Esp32GpioState* gpioState, unsigned int index, uint32_t mask ){
	
	gpioState->interruptStatusRegister[index]  &= ~mask;
	gpioState->appCpuInterruptStatus[index]    &= ~mask;
	gpioState->appCpuNmiInterruptStatus[index] &= ~mask;
	gpioState->proCpuInterruptStatus[index]    &= ~mask;
	gpioState->proCpuNmiInterruptStatus[index] &= ~mask;
	
	
	if( 
		!gpioState->appCpuInterruptStatus[0] && 
		!gpioState->appCpuInterruptStatus[1] &&
		!gpioState->proCpuInterruptStatus[0] &&
		!gpioState->proCpuInterruptStatus[1] 
	) qemu_set_irq( gpioState->gpioIrq, 0 );
	
	
	if( 
		!gpioState->appCpuNmiInterruptStatus[0] && 
		!gpioState->appCpuNmiInterruptStatus[1] &&
		!gpioState->proCpuNmiInterruptStatus[0] &&
		!gpioState->proCpuNmiInterruptStatus[1] 
	) qemu_set_irq( gpioState->gpioNmiIrq, 0 );
	
}


//gpio set config logic
static void setGpioConfig( Esp32GpioState* gpioState, unsigned int index, uint32_t value ){
	gpioState->gpioConfig[index].mem = value;
}


//set peripheral input function logic
static void setPeripheralInputFunction( Esp32GpioState* gpioState, unsigned int index, uint32_t value ){
	gpioState->peripheralFunctionInput[index] = value;
}


//set peripheral output function logic
static void setPeripheralOutputFunction( Esp32GpioState* gpioState, unsigned int index, uint32_t value ){
	gpioState->peripheralFunctionOutput[index] = value;
}


static void esp32GpioWrite( void* opaque, hwaddr address, uint64_t value, unsigned int size ){
	
	size_t index;
	Esp32GpioState* gpioState;
	
	gpioState = ESP32_GPIO( opaque );
	
	
	//unaligned access
	if( address % 4 ){
		qemu_log_mask( LOG_GUEST_ERROR, "[%s] Unaligned access: 0x%04lX\n", __FUNCTION__, address );
		return;
	}
	
	//unexpected size
	if( size != 4 ){
		qemu_log_mask( LOG_GUEST_ERROR, "[%s] Unexpected size: %u\n", __FUNCTION__, size );
		return;
	}
	
	//output register 0-31
	if( address == GPIO_OUT_REG_RW          ) {    writeOutputRegister( gpioState, 0, value ); return; }
	if( address == GPIO_OUT_W1TS_REG_WO     ) {   setBitOutputRegister( gpioState, 0, value ); return; }
	if( address == GPIO_OUT_W1TC_REG_WO     ) { clearBitOutputRegister( gpioState, 0, value ); return; }
	
	//output register 32-39
	if( address == GPIO_OUT1_REG_RW         ) {    writeOutputRegister( gpioState, 1, value ); return; }
	if( address == GPIO_OUT1_W1TS_REG_WO    ) {   setBitOutputRegister( gpioState, 1, value ); return; }
	if( address == GPIO_OUT1_W1TC_REG_WO    ) { clearBitOutputRegister( gpioState, 1, value ); return; }
	
	//output enable register 0-31
	if( address == GPIO_ENABLE_REG_RW       ) {    writeOutputEnableRegister( gpioState, 0, value ); return; }
	if( address == GPIO_ENABLE_W1TS_REG_WO  ) {   setBitOutputEnableRegister( gpioState, 0, value ); return; }
	if( address == GPIO_ENABLE_W1TC_REG_WO  ) { clearBitOutputEnableRegister( gpioState, 0, value ); return; }
	
	//output enable register 32-39
	if( address == GPIO_ENABLE1_REG_RW      ) {    writeOutputEnableRegister( gpioState, 1, value ); return; }
	if( address == GPIO_ENABLE1_W1TS_REG_WO ) {   setBitOutputEnableRegister( gpioState, 1, value ); return; }
	if( address == GPIO_ENABLE1_W1TC_REG_WO ) { clearBitOutputEnableRegister( gpioState, 1, value ); return; }
	
	//interrupt status register 0-31
	if( address == GPIO_STATUS_REG_RW       ) {    writeInterruptStatusRegister( gpioState, 0, value ); return; }
	if( address == GPIO_STATUS_W1TS_REG_WO  ) {   setBitInterruptStatusRegister( gpioState, 0, value ); return; }
	if( address == GPIO_STATUS_W1TC_REG_WO  ) { clearBitInterruptStatusRegister( gpioState, 0, value ); return; }
	
	//interrupt status register 32-39
	if( address == GPIO_STATUS1_REG_RW      ) {    writeInterruptStatusRegister( gpioState, 1, value ); return; }
	if( address == GPIO_STATUS1_W1TS_REG_WO ) {   setBitInterruptStatusRegister( gpioState, 1, value ); return; }
	if( address == GPIO_STATUS1_W1TC_REG_WO ) { clearBitInterruptStatusRegister( gpioState, 1, value ); return; }
	
	
	//pins configuration
	if( address >= GPIO_PIN_REG_START_RW && address <= GPIO_PIN_REG_END_RW ){
		index = ( address - GPIO_PIN_REG_START_RW ) / 4;
		setGpioConfig( gpioState, index, value );
		return;
	}
	
	//peripheral input function selection
	if( address >= GPIO_FUNC_IN_SEL_CFG_REG_START_RW && address <= GPIO_FUNC_IN_SEL_CFG_REG_END_RW ){
		index = ( address - GPIO_FUNC_IN_SEL_CFG_REG_START_RW ) / 4;
		setPeripheralInputFunction( gpioState, index, value );
		return;
	}
	
	//peripheral output function selection
	if( address >= GPIO_FUNC_OUT_SEL_CFG_START_RW && address <= GPIO_FUNC_OUT_SEL_CFG_END_RW ){
		index = ( address - GPIO_FUNC_OUT_SEL_CFG_START_RW ) / 4;
		setPeripheralOutputFunction( gpioState, index, value );
		return;
	}
	
	qemu_log_mask( LOG_GUEST_ERROR, "[%s] Unexpected address: 0x%04lX\n", __FUNCTION__, address );
}


/*******************************
*          CLASS INIT          *
*******************************/

static const MemoryRegionOps uart_ops = {
    .read =  esp32GpioRead,
    .write = esp32GpioWrite,
    .endianness = DEVICE_LITTLE_ENDIAN,
};

static void esp32_gpio_reset_hold(Object *obj, ResetType type)
{
}

static void esp32_gpio_realize(DeviceState *dev, Error **errp)
{
}

static void esp32_gpio_init(Object *obj)
{
    Esp32GpioState *s = ESP32_GPIO(obj);
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    
    //deviceState added by cpu
    DeviceState* deviceState;

    /* Set the default value for the strap_mode property */
    object_property_set_int(obj, "strap_mode", ESP32_STRAP_MODE_FLASH_BOOT, &error_fatal);

    memory_region_init_io(&s->iomem, obj, &uart_ops, s,
                          TYPE_ESP32_GPIO, 0x1000);
    sysbus_init_mmio(sbd, &s->iomem);
    sysbus_init_irq(sbd, &s->irq);
    
    //added by cpu
    deviceState = DEVICE( s );
    qdev_init_gpio_in_named_with_opaque( deviceState, inputGpioHandler, s, "gpio_in", ESP32_GPIO_COUNT );
    qdev_init_gpio_out_named( deviceState, &s->gpioIrq,    ESP32_GPIO_IRQ_GPIO,     1 );
    qdev_init_gpio_out_named( deviceState, &s->gpioNmiIrq, ESP32_GPIO_NMI_IRQ_GPIO, 1 );
}

static Property esp32_gpio_properties[] = {
    /* The strap_mode needs to be explicitly set in the instance init, thus, set
     * the default value to 0. */
    DEFINE_PROP_UINT32("strap_mode", Esp32GpioState, strap_mode, 0),
    DEFINE_PROP_END_OF_LIST(),
};

static void esp32_gpio_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);
    ResettableClass *rc = RESETTABLE_CLASS(klass);

    rc->phases.hold = esp32_gpio_reset_hold;
    dc->realize = esp32_gpio_realize;
    device_class_set_props(dc, esp32_gpio_properties);
}

/******************************
*          TYPE INFO          *
******************************/

static const TypeInfo esp32_gpio_info = {
    .name = TYPE_ESP32_GPIO,
    .parent = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(Esp32GpioState),
    .instance_init = esp32_gpio_init,
    .class_init = esp32_gpio_class_init,
    .class_size = sizeof(Esp32GpioClass),
};

static void esp32_gpio_register_types(void)
{
    type_register_static(&esp32_gpio_info);
}

type_init(esp32_gpio_register_types)
#pragma once

#include "hw/sysbus.h"
#include "hw/hw.h"
#include "hw/registerfields.h"

#define TYPE_ESP32_GPIO "esp32.gpio"
#define ESP32_GPIO(obj)             OBJECT_CHECK(Esp32GpioState, (obj), TYPE_ESP32_GPIO)
#define ESP32_GPIO_GET_CLASS(obj)   OBJECT_GET_CLASS(Esp32GpioClass, obj, TYPE_ESP32_GPIO)
#define ESP32_GPIO_CLASS(klass)     OBJECT_CLASS_CHECK(Esp32GpioClass, klass, TYPE_ESP32_GPIO)

REG32(GPIO_STRAP, 0x0038)

#define ESP32_STRAP_MODE_FLASH_BOOT 0x12
#define ESP32_STRAP_MODE_UART_BOOT  0x0f

//added by cpu
#define ESP32_GPIO_COUNT 40
#define ESP32_PERIPHERAL_CONFIG_COUNT 256

#define ESP32_GPIO_IRQ_GPIO     "gpio_irq"
#define ESP32_GPIO_NMI_IRQ_GPIO "gpio_nmi_irq"

union GpioConfig{
	struct{
		uint32_t _reserved0_     :2;
		uint32_t padDriver       :1;
		uint32_t _reserved1_     :4;
		uint32_t interruptType   :3;
		uint32_t wakeupEnable    :1;
		uint32_t _reserved2_     :2;
		uint32_t appCpuIntEnable :1;
		uint32_t appCpuNmiEnable :1;
		uint32_t proCpuIntEnable :1;
		uint32_t proCpuNmiEnable :1;
		uint32_t _unknownField_  :1;
		uint32_t _reserved3_     :14;
	};
	
	uint32_t mem;
};

typedef struct Esp32GpioState {
    SysBusDevice parent_obj;

    MemoryRegion iomem;
    qemu_irq irq;
    uint32_t strap_mode;
    
    //added by cpu
    uint32_t outputRegister[2];
    uint32_t outputEnableRegister[2];
    uint32_t inputRegister[2];
    uint32_t interruptStatusRegister[2];
    uint32_t appCpuInterruptStatus[2];
    uint32_t appCpuNmiInterruptStatus[2]; //NMI means Non-Maskable Interrupt
    uint32_t proCpuInterruptStatus[2];
    uint32_t proCpuNmiInterruptStatus[2];
    
    union GpioConfig gpioConfig[ESP32_GPIO_COUNT];
    uint32_t peripheralFunctionInput[ESP32_PERIPHERAL_CONFIG_COUNT];
    uint32_t peripheralFunctionOutput[ESP32_PERIPHERAL_CONFIG_COUNT];
    
    qemu_irq gpioIrq;
    qemu_irq gpioNmiIrq;
    
} Esp32GpioState;

typedef struct Esp32GpioClass {
    SysBusDeviceClass parent_class;
} Esp32GpioClass;