qemu源碼解析【03】qom實例
arm_sbcon_i2c實例
以hw/i2c/arm_sbcon_i2c.c代碼為例,這個實例很簡單,只用100行左右的代碼,調用qemu系統接口實現了一個i2c硬件模擬 先看include/hw/i2c/arm_sbcon_i2c.h頭文件定義
#ifndef HW_I2C_ARM_SBCON_I2C_H
#define HW_I2C_ARM_SBCON_I2C_H#include "hw/sysbus.h"
#include "hw/i2c/bitbang_i2c.h"
#include "qom/object.h"// 定義類型名稱,該名稱會用type_register_static
// 注冊到TypeInfo.name中
#define TYPE_ARM_SBCON_I2C "versatile_i2c"typedef struct ArmSbconI2CState ArmSbconI2CState;
DECLARE_INSTANCE_CHECKER(ArmSbconI2CState, ARM_SBCON_I2C, TYPE_ARM_SBCON_I2C)// 類型的私有數據結構
// 該數據結構的size會注冊到TypeInfo.instance_size中
// 系統會根據這個size為我們分配內存
struct ArmSbconI2CState {/*< private >*/SysBusDevice parent_obj;/*< public >*/MemoryRegion iomem;bitbang_i2c_interface bitbang;int out;int in;
};#endif /* HW_I2C_ARM_SBCON_I2C_H */
分析一下上面的:DECLARE_INSTANCE_CHECKER(ArmSbconI2CState, ARM_SBCON_I2C, TYPE_ARM_SBCON_I2C)
初步展開這個宏會變成:
static inline G_GNUC_UNUSED ArmSbconI2CState * \
ARM_SBCON_I2C(const void *obj) \
{ return OBJECT_CHECK(ArmSbconI2CState, obj, TYPE_ARM_SBCON_I2C); }
static inline G_GNUC_UNUSED ArmSbconI2CState *ARM_SBCON_I2C(const void *obj) \
{return ((ArmSbconI2CState *)object_dynamic_cast_assert(OBJECT(obj), (TYPE_ARM_SBCON_I2C), \__FILE__, __LINE__, __func__));
}
object_dynamic_cast_assert()函數就不繼續展開了,這個函數注釋里面這樣寫道:perform type safe dynamic_casts to this object type,看起來就是一個指針類型的動態轉換。所以這里定義了一個函數ARM_SBCON_I2C(),將給定的(void *)obj內存動態轉換成(ArmSbconI2CState *)類型 后面在arm_sbcon_i2c_init()函數中我們也可以看到,ArmSbconI2CState *s = ARM_SBCON_I2C(obj)這條語句正是使用了這個數據類型轉換的功能 再看hw/i2c/arm_sbcon_i2c.c具體實現,具體解析都寫在注釋中了
#include "qemu/osdep.h"
#include "hw/i2c/arm_sbcon_i2c.h"
#include "hw/registerfields.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "qom/object.h"// 聲明該i2c硬件的寄存器
REG32(CONTROL_GET, 0)
REG32(CONTROL_SET, 0)
REG32(CONTROL_CLR, 4)#define SCL BIT(0)
#define SDA BIT(1)// 聲明i2c read函數,注冊到后面的arm_sbcon_i2c_ops結構體中
static uint64_t arm_sbcon_i2c_read(void *opaque, hwaddr offset,unsigned size)
{ArmSbconI2CState *s = opaque;// 解析寄存器參數switch (offset) {case A_CONTROL_SET:return (s->out & 1) | (s->in << 1);default:qemu_log_mask(LOG_GUEST_ERROR,"%s: Bad offset 0x%x\n", __func__, (int)offset);return -1;}
}// 聲明i2c write函數,注冊到后面的arm_sbcon_i2c_ops結構體中
static void arm_sbcon_i2c_write(void *opaque, hwaddr offset,uint64_t value, unsigned size)
{ArmSbconI2CState *s = opaque;// 解析寄存器參數switch (offset) {case A_CONTROL_SET:s->out |= value & 3;break;case A_CONTROL_CLR:s->out &= ~value;break;default:qemu_log_mask(LOG_GUEST_ERROR,"%s: Bad offset 0x%x\n", __func__, (int)offset);}// 調用bitbang模擬I2C時序bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SCL, (s->out & SCL) != 0);s->in = bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SDA, (s->out & SDA) != 0);
}// 定義了memory region上面綁定的操作函數,后面使用memory_region_init_io,在聲明memory region的時候進行注冊
static const MemoryRegionOps arm_sbcon_i2c_ops = {.read = arm_sbcon_i2c_read,.write = arm_sbcon_i2c_write,.endianness = DEVICE_NATIVE_ENDIAN,
};static void arm_sbcon_i2c_init(Object *obj)
{DeviceState *dev = DEVICE(obj);// 在頭文件中用DECLARE_INSTANCE_CHECKER(ArmSbconI2CState, ARM_SBCON_I2C, TYPE_ARM_SBCON_I2C)// 已經聲明了ARM_SBCON_I2C()函數ArmSbconI2CState *s = ARM_SBCON_I2C(obj);SysBusDevice *sbd = SYS_BUS_DEVICE(obj);I2CBus *bus;// qemu系統的i2c函數,創建一個i2c總線bus = i2c_init_bus(dev, "i2c");// i2c bitbang,軟件層通過IO腳去模擬I2C的時序,從而實現I2C協議bitbang_i2c_init(&s->bitbang, bus);// 分配一塊memory空間,用來做io操作// 其實就是模擬硬件的寄存器地址空間// 注意這里將ArmSbconI2CState *s指針作為參數傳進去// 后面每次調用io操作的時候,都會作為void *opaque參數memory_region_init_io(&s->iomem, obj, &arm_sbcon_i2c_ops, s,"arm_sbcon_i2c", 0x1000);sysbus_init_mmio(sbd, &s->iomem);
}static const TypeInfo arm_sbcon_i2c_info = {// 類型名稱.name = TYPE_ARM_SBCON_I2C,// 類型的父類型.parent = TYPE_SYS_BUS_DEVICE,// 類型的數據size.instance_size = sizeof(ArmSbconI2CState),// 注冊類型的初始化函數.instance_init = arm_sbcon_i2c_init,
};static void arm_sbcon_i2c_register_types(void)
{// 調用系統接口注冊類型type_register_static(&arm_sbcon_i2c_info);
}type_init(arm_sbcon_i2c_register_types)