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)