qemu源码解析【03】qom实例

发布于:2024-12-18 ⋅ 阅读:(16) ⋅ 点赞:(0)

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)