xuos-web/docs/doc/component/drvmodel.md

546 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 驱动模型
## 概述
多数嵌入式操作系统对驱动的抽象采用以物理外设实体为中心的方式,采用驱动(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为例
<img src="./imagesrc/spi_bus.png" width =100%/>
## 关键数据结构
* struct Bus结构
```c
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枚举结构
```c
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枚举结构
```c
enum BusState
{
BUS_INIT = 0,
BUS_INSTALL,
BUS_UNINSTALL,
};
```
* struct HardwareDev结构
```c
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结构
```c
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结构
```c
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为例完整的使用过程示例如下
```c
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注册函数
```c
/**
* @Description: support to register bus pointer with linklist
* @param bus - bus pointer
* @return successfulEOKfailedNONE
*/
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删除函数
```c
/**
* @Description: support to unregister bus pointer and delete its linklist node
* @param bus - bus pointer
* @return successfulEOKfailedNONE
*/
int BusUnregister(struct Bus *bus)
{
NULL_PARAM_CHECK(bus);
bus->bus_cnt--;
DoubleLinkListRmNode(&(bus->bus_link));
return EOK;
}
```
* DRIVER注册到BUS函数
```c
/**
* @Description: support to register driver pointer to bus pointer
* @param bus - bus pointer
* @param driver - driver pointer
* @return successfulEOKfailedNONE
*/
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中删除函数
```c
/**
* @Description: support to delete driver pointer from bus pointer
* @param bus - bus pointer
* @param driver - driver pointer
* @return successfulEOKfailedNONE
*/
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函数
```c
/**
* @Description: support to register dev pointer to bus pointer
* @param bus - bus pointer
* @param device - device pointer
* @return successfulEOKfailedNONE
*/
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中删除函数
```c
/**
* @Description: support to delete dev pointer from bus pointer
* @param bus - bus pointer
* @param device - device pointer
* @return successfulEOKfailedNONE
*/
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函数
```c
/**
* @Description: support to find bus pointer by bus name
* @param bus_name - bus name
* @return successfulbus pointerfailedNONE
*/
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函数
```c
/**
* @Description: support to find driver pointer of certain bus by driver name
* @param bus - bus pointer
* @param driver_name - driver name
* @return successfulEOKfailedNONE
*/
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函数
```c
/**
* @Description: support to find device pointer of certain bus by device name
* @param bus - bus pointer
* @param device_name - device name
* @return successfulEOKfailedNONE
*/
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函数
```c
/**
* @Description: support to open dev
* @param dev - dev pointer
* @return successfulEOKfailedERROR
*/
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函数
```c
/**
* @Description: support to close dev
* @param dev - dev pointer
* @return successfulEOKfailedERROR
*/
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写函数
```c
/**
* @Description: support to write data to dev
* @param dev - dev pointer
* @param write_param - BusBlockWriteParam
* @return successfulEOKfailedNONE
*/
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读函数
```c
/**
* @Description: support to read data from dev
* @param dev - dev pointer
* @param read_param - BusBlockReadParam
* @return successfulEOKfailedNONE
*/
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配置函数
```c
/**
* @Description: support to configure drv, include OPE_CFG and OPE_INT
* @param drv - drv pointer
* @param configure_info - BusConfigureInfo
* @return successfulEOKfailedNONE
*/
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;
}
```