# 驱动模型
# 概述
多数嵌入式操作系统对驱动的抽象采用以物理外设实体为中心的方式,采用驱动(DRIVER)-设备(DEVICE)的模式开发驱动。这种抽象带来的缺陷在面对一些兼容多平台的操作系统开发驱动时尤为突出。
对于实时复杂的操作系统,代码的重用性非常重要,否则的话就会在操作系统内核中存在大量无意义的重复代码。尤其是驱动程序,因为驱动程序占用了操作系统代码量的绝大部分,如果不对驱动程序加以管理,任由重复的代码肆意增加,那么随着操作系统的发展,其文件数量就庞大到无法接受的地步,进而增加了驱动开发难度和周期。
针对这种情况,我们了解到对于嵌入式系统外设来说,不外乎几类常用的外设,如I2C外设、SPI外设、字符设备外设、块设备外设等,同一类外设使用的外设接口相同,这就意味着这类外设可以复用同一套DRIVER驱动代码和DEVICE操作代码。而对于上层应用来说,最理想的情况下是这类外设的接口控制器在不同SOC平台上可以复用,即应用程序通过统一的API接口操作外设,可以大大减少系统开发和维护工作量。
因此,XiUOS集成了一套BUS驱动模型,即利用一套BUS总线管理DEVICE设备和DRIVER驱动的框架,以BUS为基类,分发多种BUS子类对应一类外设,覆盖目前常使用的外设接口,如SPI_BUS、I2C_BUS、SERIAL_BUS等。以SPI_BUS总线为例,SPI_BUS总线创建后,首先会注册SPI_DRIVER驱动相关实现,之后系统接入的所有SPI_DEVICE都会注册挂载到SPI_BUS总线上,DRIVER驱动和DEVICE设备通过bus_name绑定上,实现设备和驱动相互对应的配置,这样即可实现用一套BUS数据结构管理其挂载的DEVICE设备和DRIVER驱动的目的。对于上层应用来说,通过获取SPI_BUS数据结构即可知道当前系统中挂载的SPI_DEVICE个数,利用BUS回调函数便可满足配置、数据交互需求,而不用关心具体的硬件细节,且底层新增或删减外设的操作由框架层统一管理,上层应用模块依旧可以复用,这套框架层次化管理界限清晰,驱动和应用解耦、复用功能凸显,应用层开发友好。
# 目录结构和框图
- 驱动框架resources/xxx/文件目录结构
文件名 | 描述 |
---|---|
bus.c | bus驱动框架实现,提供给上层应用调用 |
bus_xxx.c | xxx_bus子类实现,如spi_bus、i2c_bus、serial_bus等 |
dev_xxx.c | xxx_dev子类实现,如spi_dev、i2c_dev、serial_dev等 |
drv_xxx.c | xxx_drv子类实现,如spi_drv、i2c_drv、serial_drv等 |
- 驱动框架框图(以SPI_Bus为例)
# 关键数据结构
- struct Bus结构
struct Bus
{
int8 bus_name[NAME_NUM_MAX];
enum BusType bus_type;
enum BusState bus_state;
int32 (*match)(struct Driver *driver, struct HardwareDev *device);
int bus_lock;
struct HardwareDev *owner_haldev;
struct Driver *owner_driver;
void *private_data;
/*manage the drv of the bus*/
uint8 driver_cnt;
uint8 bus_drvlink_flag;
DoubleLinklistType bus_drvlink;
/*manage the dev of the bus*/
uint8 haldev_cnt;
uint8 bus_devlink_flag;
DoubleLinklistType bus_devlink;
uint8 bus_cnt;
uint8 bus_link_flag;
DoubleLinklistType bus_link;
};
- enum BusType枚举结构
enum BusType
{
TYPE_I2C_BUS = 0,
TYPE_SPI_BUS,
TYPE_HWTIMER_BUS,
TYPE_USB_BUS,
TYPE_CAN_BUS,
TYPE_WDT_BUS,
TYPE_SDIO_BUS,
TYPE_TOUCH_BUS,
TYPE_LCD_BUS,
TYPE_PIN_BUS,
TYPE_RTC_BUS,
TYPE_SERIAL_BUS,
TYPE_BUS_END,
};
- enum BusState枚举结构
enum BusState
{
BUS_INIT = 0,
BUS_INSTALL,
BUS_UNINSTALL,
};
- struct HardwareDev结构
struct HardwareDev
{
int8 dev_name[NAME_NUM_MAX];
enum DevType dev_type;
enum DevState dev_state;
const struct HalDevDone *dev_done;
int (*dev_recv_callback) (void *dev, x_size_t length);
int (*dev_block_control) (struct HardwareDev *dev, struct HalDevBlockParam *block_param);
struct Bus *owner_bus;
void *private_data;
int32 dev_sem;
DoubleLinklistType dev_link;
};
- struct HalDevDone结构
struct HalDevDone
{
uint32 (*open) (void *dev);
uint32 (*close) (void *dev);
uint32 (*write) (void *dev, struct BusBlockWriteParam *write_param);
uint32 (*read) (void *dev, struct BusBlockReadParam *read_param);
};
HalDevDone包含统一的、类似文件系统的API,用于对具体外设进行实际的开关和数据读写。如在使用一个外设前后需要打开(open)/关闭(close)该外设,read、write分别用与从外设接收数据与向外设发送数据。
- struct Driver结构
struct Driver
{
int8 drv_name[NAME_NUM_MAX];
enum DriverType driver_type;
enum DriverState driver_state;
uint32 (*configure)(void *drv, struct BusConfigureInfo *configure_info);
struct Bus *owner_bus;
void *private_data;
DoubleLinklistType driver_link;
};
回调函数configure用于对具体外设进行实际的配置。
# 使用场景
在获取外设数据前,要先获取外设bus,匹配driver和device数据结构,若有需要应配置外设driver,在外设device打开后对外设进行读写,使用完毕后,关闭设备。
以SERIAL为例,完整的使用过程示例如下:
int main(int argc, char *argv[])
{
int ret = EOK;
char test_str[] = "Hello AIIT!\r\n";
struct Bus *bus;
struct HardwareDev *dev;
struct Driver *drv;
/* find the serial bus pointer */
bus = BusFind(SERIAL_BUS_NAME);
if (NONE == bus) {
KPrintf("BusFind %s failed\n", SERIAL_BUS_NAME);
return ERROR;
}
/* find the serial driver pointer */
drv = BusFindDriver(bus, SERIAL_DRV_NAME);
if (NONE == drv) {
KPrintf("BusFindDriver %s failed\n", SERIAL_DRV_NAME);
return ERROR;
}
/* find the serial device pointer */
dev = BusFindDevice(bus, SERIAL_DEVICE_NAME);
if (NONE == dev) {
KPrintf("BusFindDevice %s failed\n", SERIAL_DEVICE_NAME);
return ERROR;
}
/*step 1: init bus_driver, change struct SerialCfgParam if necessary*/
struct SerialCfgParam serial_cfg;
memset(&serial_cfg, 0, sizeof(struct SerialCfgParam));
configure_info.configure_cmd = OPE_INT;
configure_info.private_data = &serial_cfg;
ret = BusDrvConfigure(drv, &configure_info);
if (EOK != ret) {
KPrintf("BusDrvConfigure OPE_INT failed error code %d\n", ret);
return ret;
}
/*step 2: match serial bus_driver with bus_device*/
bus->match(drv, dev);
/*step 3: open bus_device, configure struct SerialDevParam if necessary*/
serial_dev_param->serial_set_mode = SIGN_OPER_INT_RX;
serial_dev_param->serial_stream_mode = SIGN_OPER_STREAM;
ret = BusDevOpen(dev);
if (EOK != ret) {
KPrintf("BusDevOpen failed error code %d\n", ret);
return ret;
}
/*step 4: write serial data, configure struct BusBlockWriteParam*/
struct BusBlockWriteParam write_param;
write_param.pos = 0;
write_param.buffer = (void *)test_str;
write_param.size = sizeof(test_str) - 1;
BusDevWriteData(bus_device, &write_param);
/*step 5: close bus_device*/
BusDevClose(bus_device);
return EOK;
}
# 函数接口
- BUS注册函数
/**
* @Description: support to register bus pointer with linklist
* @param bus - bus pointer
* @return successful:EOK,failed:NONE
*/
int BusRegister(struct Bus *bus)
{
x_err_t ret = EOK;
NULL_PARAM_CHECK(bus);
bus->match = BusMatchDrvDev;
BusLinkInit(bus);
bus->bus_lock = KMutexCreate();
DoubleLinkListInsertNodeAfter(&bus_linklist, &(bus->bus_link));
return ret;
}
- BUS删除函数
/**
* @Description: support to unregister bus pointer and delete its linklist node
* @param bus - bus pointer
* @return successful:EOK,failed:NONE
*/
int BusUnregister(struct Bus *bus)
{
NULL_PARAM_CHECK(bus);
bus->bus_cnt--;
DoubleLinkListRmNode(&(bus->bus_link));
return EOK;
}
- DRIVER注册并挂接到BUS函数
/**
* @Description: support to register driver pointer to bus pointer
* @param bus - bus pointer
* @param driver - driver pointer
* @return successful:EOK,failed:NONE
*/
int DriverRegisterToBus(struct Bus *bus, struct Driver *driver)
{
NULL_PARAM_CHECK(bus);
NULL_PARAM_CHECK(driver);
driver->owner_bus = bus;
bus->driver_cnt++;
DoubleLinkListInsertNodeAfter(&bus->bus_drvlink, &(driver->driver_link));
return EOK;
}
- 将DRIVER从BUS中删除函数
/**
* @Description: support to delete driver pointer from bus pointer
* @param bus - bus pointer
* @param driver - driver pointer
* @return successful:EOK,failed:NONE
*/
int DriverDeleteFromBus(struct Bus *bus, struct Driver *driver)
{
NULL_PARAM_CHECK(bus);
NULL_PARAM_CHECK(driver);
bus->driver_cnt--;
DoubleLinkListRmNode(&(driver->driver_link));
free(driver);
return EOK;
}
- DEVICE注册并挂接到BUS函数
/**
* @Description: support to register dev pointer to bus pointer
* @param bus - bus pointer
* @param device - device pointer
* @return successful:EOK,failed:NONE
*/
int DeviceRegisterToBus(struct Bus *bus, struct HardwareDev *device)
{
NULL_PARAM_CHECK(bus);
NULL_PARAM_CHECK(device);
device->owner_bus = bus;
bus->haldev_cnt++;
DoubleLinkListInsertNodeAfter(&bus->bus_devlink, &(device->dev_link));
return EOK;
}
- 将DEVICE从BUS中删除函数
/**
* @Description: support to delete dev pointer from bus pointer
* @param bus - bus pointer
* @param device - device pointer
* @return successful:EOK,failed:NONE
*/
int DeviceDeleteFromBus(struct Bus *bus, struct HardwareDev *device)
{
NULL_PARAM_CHECK(bus);
NULL_PARAM_CHECK(device);
bus->haldev_cnt--;
DoubleLinkListRmNode(&(device->dev_link));
free(device);
return EOK;
}
- 查找BUS函数
/**
* @Description: support to find bus pointer by bus name
* @param bus_name - bus name
* @return successful:bus pointer,failed:NONE
*/
BusType BusFind(const char *bus_name)
{
struct Bus *bus = NONE;
DoubleLinklistType *node = NONE;
DoubleLinklistType *head = &bus_linklist;
for (node = head->node_next; node != head; node = node->node_next)
{
bus = SYS_DOUBLE_LINKLIST_ENTRY(node, struct Bus, bus_link);
if(!strcmp(bus->bus_name, bus_name)) {
return bus;
}
}
KPrintf("BusFind cannot find the %s bus.return NULL\n", bus_name);
return NONE;
}
- 查找DRIVER函数
/**
* @Description: support to find driver pointer of certain bus by driver name
* @param bus - bus pointer
* @param driver_name - driver name
* @return successful:EOK,failed:NONE
*/
DriverType BusFindDriver(struct Bus *bus, const char *driver_name)
{
NULL_PARAM_CHECK(bus);
struct Driver *driver = NONE;
DoubleLinklistType *node = NONE;
DoubleLinklistType *head = &bus->bus_drvlink;
for (node = head->node_next; node != head; node = node->node_next)
{
driver = SYS_DOUBLE_LINKLIST_ENTRY(node, struct Driver, driver_link);
if(!strcmp(driver->drv_name, driver_name)) {
return driver;
}
}
KPrintf("BusFindDriver cannot find the %s driver.return NULL\n", driver_name);
return NONE;
}
- 查找DEVICE函数
/**
* @Description: support to find device pointer of certain bus by device name
* @param bus - bus pointer
* @param device_name - device name
* @return successful:EOK,failed:NONE
*/
HardwareDevType BusFindDevice(struct Bus *bus, const char *device_name)
{
NULL_PARAM_CHECK(bus);
struct HardwareDev *device = NONE;
DoubleLinklistType *node = NONE;
DoubleLinklistType *head = &bus->bus_devlink;
for (node = head->node_next; node != head; node = node->node_next)
{
device = SYS_DOUBLE_LINKLIST_ENTRY(node, struct HardwareDev, dev_link);
if(!strcmp(device->dev_name, device_name)) {
return device;
}
}
KPrintf("BusFindDevice cannot find the %s device.return NULL\n", device_name);
return NONE;
}
- 打开DEVICE函数
/**
* @Description: support to open dev
* @param dev - dev pointer
* @return successful:EOK,failed:ERROR
*/
uint32 BusDevOpen(struct HardwareDev *dev)
{
NULL_PARAM_CHECK(dev);
x_err_t ret = EOK;
if (dev->dev_done->open) {
ret = dev->dev_done->open(dev);
if(ret) {
KPrintf("BusDevOpen error ret %u\n", ret);
return ERROR;
}
}
return ret;
}
- 关闭DEVICE函数
/**
* @Description: support to close dev
* @param dev - dev pointer
* @return successful:EOK,failed:ERROR
*/
uint32 BusDevClose(struct HardwareDev *dev)
{
NULL_PARAM_CHECK(dev);
x_err_t ret = EOK;
if (dev->dev_done->close) {
ret = dev->dev_done->close(dev);
if(ret) {
KPrintf("BusDevClose error ret %u\n", ret);
return ERROR;
}
}
return ret;
}
- DEVICE写函数
/**
* @Description: support to write data to dev
* @param dev - dev pointer
* @param write_param - BusBlockWriteParam
* @return successful:EOK,failed:NONE
*/
uint32 BusDevWriteData(struct HardwareDev *dev, struct BusBlockWriteParam *write_param)
{
NULL_PARAM_CHECK(dev);
if (dev->dev_done->write) {
return dev->dev_done->write(dev, write_param);
}
return EOK;
}
- DEVICE读函数
/**
* @Description: support to read data from dev
* @param dev - dev pointer
* @param read_param - BusBlockReadParam
* @return successful:EOK,failed:NONE
*/
uint32 BusDevReadData(struct HardwareDev *dev, struct BusBlockReadParam *read_param)
{
NULL_PARAM_CHECK(dev);
if (dev->dev_done->read) {
return dev->dev_done->read(dev, read_param);
}
return EOK;
}
- DRIVER配置函数
/**
* @Description: support to configure drv, include OPE_CFG and OPE_INT
* @param drv - drv pointer
* @param configure_info - BusConfigureInfo
* @return successful:EOK,failed:NONE
*/
uint32 BusDrvConfigure(struct Driver *drv, struct BusConfigureInfo *configure_info)
{
NULL_PARAM_CHECK(drv);
NULL_PARAM_CHECK(configure_info);
x_err_t ret = EOK;
if (drv->configure) {
ret = drv->configure(drv, configure_info);
if(ret) {
KPrintf("BusDrvCfg error, ret %u\n", ret);
return ERROR;
}
}
return ret;
}