一 程序位置分布
1 2 3 4 5 6 7 8
| RTOS中DSMC代码,drv_dsmc_host.c是主机驱动代码,drv_dsmc_slave.c是从机驱动代码,dsmc_test.c是测试程序 SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/drivers/drv_dsmc_host.c SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/drivers/drv_dsmc_slave.c SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/tests/dsmc_test.c SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/hal/lib/hal/src/hal_dsmc_host.c SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/hal/lib/hal/src/hal_dsmc_slave.c SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/drivers/dma.c
|
二 主机驱动代码解析
drv_dsmc_host.c -> hal_dsmc_host.c -> drv_dsmc_host.c -> dsmc_test.c
2.1 系统如何启动驱动?
- 首先,在
drv_dsmc_host.c主机驱动中rockchip_dsmc_host_probe(void)函数下方,使用了INIT_DEVICE_EXPORT(rockchip_dsmc_host_probe)将rockchip_dsmc_host_probe注册到了设备自动初始化函数

- 这是RT-Thread自动初始化机制,听起来似乎很高大上,实际上只是将函数入口地址记录,在
board_base.c中的rt_hw_board_init()启动板卡,完成基础初始化后,会在调用rt_components_board_init(),
rt_components_board_init()内部实质上是执行函数指针,也就是执行对应的rockchip_dsmc_host_probe函数来启动驱动。
2.2 主机驱动初始化代码流向
1 2
| rockchip_dsmc_host_probe -> HAL_DSMC_HOST_Init -> DSMC_HOST_DataInit -> DSMC_HOST_MapInit -> DSMC_HOST_LbInit -> DSMC_HOST_LbCmnConfig -> DSMC_HOST_LbCmnRgnConfig -> DSMC_HOST_LbCsrConfig -> lbReadReg与lbWriteReg
|
- 在底层lbReadReg与lbWriteReg会去读写寄存器配置,然后逐级返回完成初始化打印HAL_DSMC_HOST_Init OK

2.3 主机驱动代码框架
drv_dsmc_host.c驱动主要做外设初始化与DMA数据拷贝搬运和直接读写指定地址的用户调用实现
在RTOS中drv_dsmc_host.c的rockchip_dsmc_host_probe初始化函数会从RTOS层调用HAL层hal_dsmc_host.c文件的HAL_DSMC_HOST_Init去写寄存器初始化DSMC外设
从下图drv_dsmc_host.c函数列表中可以看出来,除了初始化外设,主要就是DMA数据拷贝搬运的实现和直接读写指定地址的实现

并且驱动通过ops操作集将DMA数据拷贝搬运和直接读写指定地址函数用作上层调用函数接口

- 在
drv_dsmc_host.c中,除了初始化函数,还对dsmc_host_ops这个ops操作集合做读写与底层拷贝等函数的实现,本质上类似 Linux 的file_operations或 platform_driver 的 ops,统一抽象了对硬件的访问接口(read/write/copy_from/copy_to等),便于上层调用和适配不同实现,(参考Linux通用的驱动篇)给ops赋值函数指针之后,在上层应用函数,就可以直接使用一个dsmc_host_ops类型的结构体,调用其读写函数,拿到DSMC通信数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| struct dsmc_host_ops { rt_err_t (*read)(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs, uint32_t region, unsigned long addr, uint32_t *data); rt_err_t (*write)(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs, uint32_t region, unsigned long addr, uint32_t val); rt_err_t (*copy_from)(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs, uint32_t region, uint32_t from, uint32_t dst_phys, size_t size); rt_err_t (*copy_from_state)(struct rockchip_rt_dsmc_host *dsmc_host); rt_err_t (*copy_to)(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs, uint32_t region, uint32_t src_phys, uint32_t to, size_t size); rt_err_t (*copy_to_state)(struct rockchip_rt_dsmc_host *dsmc_host); };
static struct dsmc_host_ops rockchip_dsmc_ops = { .read = DSMC_HOST_ReadData, .write = DSMC_HOST_WriteData, .copy_from = DSMC_HOST_CopyFrom, .copy_from_state = DSMC_HOST_CopyFromState, .copy_to = DSMC_HOST_CopyTo, .copy_to_state = DSMC_HOST_CopyToState, };
|
三 从机驱动代码解析
drv_dsmc_slave.c -> hal_dsmc_slave.c -> drv_dsmc_slave.c -> dsmc_test.c
3.1 系统如何启动驱动?
- 与主机代码基本一致,
drv_dsmc_slave.c中rockchip_dsmc_slave_probe(void)函数下方,使用了INIT_DEVICE_EXPORT(rockchip_dsmc_slave_probe)将rockchip_dsmc_slave_probe注册到了设备自动初始化函数
3.2 从机驱动初始化代码流向
rockchip_dsmc_slave_probe -> HAL_DSMC_SLAVE_Init -> WRITE_REG -> rockchip_dsmc_slave_probe
- 在底层WriteReg会去读写寄存器配置,然后逐级返回完成初始化打印rockchip dsmc local bus slave driver initialized
3.3 从机驱动代码框架
- 从机代码比较简单,只需要初始化写寄存器,然后配置中断函数即可,主机发来的数据可以直接读取dma
HAL_DSMC_SLAVE_IrqHander是中断处理入口,判断是否要启动DMA,DSMC_SLAVE_DmaTrigger则负责具体执行通知硬件搬运数据的动作,它们一起实现了从机对主机搬运请求的响应和触发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| static HAL_Status DSMC_SLAVE_DmaTrigger(struct LBC_SLAVE_CSR_REG *pReg) { int timeOut = 1000;
/* wait interrupt register empty */ while (timeOut-- > 0) { if (!(READ_REG(pReg->LBC_S2H_INT_STA) & (0x1 << S2H_INT_FOR_DMA_NUM))) { break; } if (timeOut == 0) { HAL_DBG_ERR("Timeout waiting for s2h interrupt empty!\n");
return HAL_TIMEOUT; } HAL_CPUDelayUs(1); }
/* trigger a slave to host interrupt which will start dma hardware mode copy */ WRITE_REG(pReg->APP_CON15, 0x1);
return HAL_OK; }
/** * @brief DSMC_SLAVE irq hander. * @param slave: pointer to a DSMC_SLAVE structure. * @return HAL_Status */ HAL_Status HAL_DSMC_SLAVE_IrqHander(struct HAL_DSMC_SLAVE *slave) { struct LBC_SLAVE_CSR_REG *pReg;
HAL_ASSERT(IS_LBC_SLAVE_CSR_INSTANCE(slave->pReg)); pReg = slave->pReg;
if (READ_REG(pReg->LBC_CON15)) { DSMC_SLAVE_DmaTrigger(pReg); }
/* clear all h2s interrupt */ WRITE_REG(pReg->APP_H2S_INT_STA, LBC_SLAVE_CSR_APP_H2S_INT_STA_APP_H2S_INT_STA_MASK);
return HAL_OK; }
|