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

14 KiB
Raw Blame History

驱动模型

概述

多数嵌入式操作系统对驱动的抽象采用以物理外设实体为中心的方式,采用驱动(driver)-设备(device)的模式开发驱动。这种抽象带来的缺陷在面对一些兼容多平台的操作系统开发驱动时尤为突出。

对于实时复杂的操作系统,代码的重用性非常重要,否则的话就会在操作系统内核中存在大量无意义的重复代码。尤其是驱动程序,因为驱动程序占用了操作系统代码量的绝大部分,如果不对驱动程序加以管理,任由重复的代码肆意增加,那么随着操作系统的发展,其文件数量就庞大到无法接受的地步,进而增加了驱动开发难度和周期。

以I2C接口的ATH10的温湿度传感器驱动为例每种平台下都有一个主机驱动和设备驱动主机驱动是必要的它随着每个平台的IIC控制器不同而不同。但是对于不同平台下的同一个设备ATH10(温湿度传感器),没必要每个平台都写一个设备驱动,因为不管对于哪个架构的 SOC 来说, ATH10温湿度传感器都是一样通过I2C接口读写数据就行了只需要公用一个ATH10的驱动程序即可。基于上述思想XiuOS将驱动进行分隔设计以求简化驱动的开发采用驱动(driver)-总线(bus)-设备(device)的架构对驱动进行管理和组织。对于同一类设备,每个平台的同种外设控制器都提供一个统一的接口,即主机驱动。每个设备的话也只提供一个驱动程序(设备驱动),每个设备通过统一的 I2C接口驱动来访问这样就可以大大简化驱动文件。

对于ATH10的温湿度传感器我们只需要提供设备信息即可比如设备连接到了哪个I2C 接口上I2C的速度是多少等等。相当于将设备信息从设备驱动中剥离开来驱动使用标准方法去获取到设备信息然后根据获取到的设备信息来初始化设备。这样就相当于驱动只负责驱动设备只负责设备想办法将两者进行匹配即可总线负责将驱动和设备信息链接起来。

其中 xs_Bus 结构体被设计为可以采用类似面向对象的方法,它集合了所有总线的共有特征,为总线的基类。具体的总线以它为基类进行派生,如 xs_IIC_bus 、xs_USB_bus 、xs_SPI_bus 、xs_CAN_bus等。从而为众多不同性质的总线架构实现统一的管理架构。在应用开发的过程中只需要使用对应外设的总线实例无需关心传感器的硬件细节从而实现数据采样功能与底层硬件的解耦。

xs_Device 结构体同样采用类似面向对象的方法,它集合了所有设备的共有特征,为设备的基类。平台上接入的具体设备以它为基类进行派生,如 IICHardwareDevice 、USBHardwareDevice 、SPIHardwareDevice 、CANHardwareDevice等。从而为众多不同性质的设备实现统一的管理架构。

关键数据结构

  • struct xs_Bus结构
struct xs_Bus {
    char Bus_name[XS_NAME_MAX];       /* name of bus */
    enum xs_BusType type;             /* type of bus */
    enum xs_BUSSTATE flag;
    list_head device;
    list_head driver;
    int (*match)(const struct xs_DeviceDriver * drv,const struct xs_Device dev); 
    int (*probe)(void * bus);        
    int (*remove)(void * bus);
    int (*shutdown)(void * bus);
    int (*resume)(void * bus);

    int (bus_register)(void * bus);
    int (bus_unregister)(void * bus);
    void * private_data;
};

Bus_name成员是一个可读的名字用于唯一标识一个xs_Bus结构 type成员表示该xs_Bus为什么类型的总线用一个枚举变量表示。

enum xs_BusType {
    XS_IIC_BUS = 0, /* IIC */
    XS_SPI_BUS,     /* SPI */
    XS_USB_BUS,     /* USB */
    XS_CAN_BUS,     /* CAN */
    /* ...... */
    XS_BUS_END,
};

xs_BusType成员表示不同的总线类型其具体定义在上文给出。

  • enum Bus_priority枚举结构
enum Bus_priority{
    PriorityLevelOne = 1,
    PriorityLevelTwo,
    PriorityLevelThree,
    PriorityLevelforth,
    PriorityLevelFive,
    PriorityLevelSix,
    PriorityLevelSeven
};

Bus_priority变量表示总线优先级数值越低优先级越高

  • struct xs_IIC_bus结构
struct xs_I2CBus {
    struct xs_Bus bus;    /* Basic properties of the bus */
    unsigned int speed;   /* IIC  communication rate*/
};

speed成员记录I2C总线与传感器设备通信的速率

  • struct xs_SPIBus结构
struct xs_SPIBus {
    struct xs_Bus bus;            /* Basic properties of the bus */
    enum Bus_priority  priority   /* Bus priority */
    unsigned char CPHA;           /* SPI clock phase*/
    unsigned char CPOL;           /* SPI clock polarity*/
};

CPHA成员记录SPI总线的时钟相位CPOL成员记录SPI总线的时钟极性

  • struct xs_CANBus结构
struct xs_CAN_bus {
    struct xs_Bus bus;    /* Basic properties of the bus */
    enum Bus_priority  priority   /* Bus priority */
    unsigned int speed;   /* CAN  communication rate*/
};
  • struct xs_USBBus结构
struct xs_USB_bus {
    struct xs_Bus bus;    /* Basic properties of the bus */
    enum Bus_priority  priority   /* Bus priority */
    unsigned int speed;   /* USB  communication rate*/
};
  • struct xs_Device结构
typedef struct xs_Device {
    char dev_name[XS_NAME_MAX];   /* name of device */
    enum xs_DeviceType type;
    struct mutex  mut;            /* Mutually Exclusive Semaphore*/
    xs_uint32 ref_count; /* the number of open */ 
    void * driver;                 /* Mounted driver*/
    struct device_ops ops;        /* Device operation function set */
    struct xs_Device * prev;
    struct xs_Device * next;
    void * data;
    };
};
typedef struct xs_Device * xs_DevicePointer ;
  • struct device_ops结构
 struct device_ops{
    int (*init)(struct xs_Device * dev);
    int (*open)(struct xs_Device * dev);
    int (*close)(struct xs_Device * dev);
    int (*read)(struct xs_Device * dev, void * buf, int unm);
    int (*write)(struct xs_Device * dev, void *buf, int num);
    int (*ioctl)(struct xs_Device * dev, int cmd);
 }

device_ops包含统一的、类似文件系统的API用于对具体外设进行实际的数据读写。如在使用一个传感器前后需要打开open/关闭close该传感器read、write分别用与从传感器接收数据与向传感器发送数据ioctl用于配置传感器属性如波特率

  • struct xs_HardwareDev结构
struct xs_HardwareDev{
    struct xs_Device dev;
    struct device_ops ops;

    int (*probe)(struct xs_Device * dev);
	int (*remove)(struct xs_Device * dev);
	void (*shutdown)(struct xs_Device * dev);
	int (*suspend)(struct xs_Device * dev, int state);
	int (*resume)(struct xs_Device * dev);
};
  • struct IICHardwareDevice结构
 struct IICHardwareDevice{
     struct xs_HardwareDev hardware;
     unsigned short IIC_addr;
     void * PrivData;
 }
  • struct SPIHardwareDevice结构
 struct SPIHardwareDevice{
     struct xs_HardwareDev hardware;
     unsigned short SPI_addr;
     void * PrivData;
 }
  • struct CANHardwareDevice结构
 struct CANHardwareDevice{
     struct xs_HardwareDev hardware;
     unsigned short CAN_addr;
     void * PrivData;
 }
  • struct USBHardwareDevice结构
 struct USBHardwareDevice{
     struct xs_HardwareDev hardware;
     unsigned short USB_addr;
     void * PrivData;
 }
  • struct xs_DeviceDriver结构
struct xs_DeviceDriver{
   char driver_name[XS_NAME_MAX];
   struct bus_type  *  bus;
   enum driver_state falg;
   
   int (*add)(struct xs_DeviceDriver *dev);
   int (*remove)(struct xs_DeviceDriver *dev);
   int (*probe)(struct xs_HardwareDev *dev);
   void (*shutdown)(struct xs_HardwareDev *dev);
   struct list_drv{
       struct xs_DeviceDriver * prev;
       struct xs_DeviceDriver * next;
       void * data;
   };
} Device_driver_head;

使用场景

在获取i2c数据前要匹配设备驱动在设备驱动打开后对设备进行读写。使用完毕后关闭设备驱动。

完整的使用过程示例如下:

int main(int argc, char *argv[])
{
    int ret;
    struct xs_I2cDevice *dev;

    /* find the i2c device instance */
    dev = (struct xs_I2cDevice*)xs_DeviceFind("i2c_temp",XS_IIC_BUS);         //获取设备句柄
    if(dev == NULL)
    {
        xs_kprintf("find iic device error\n");
    }
   
    /* open the device instance */
    ret = xs_DeviceOpen(dev);                //打开设备
    XS_ASSERT(ret == XS_EOK);

    /* read temperature sensor data for 5 times */
    for (int i = 0; i < 5; i++)
        xs_kprintf("Current CO2 concentration is %u ppm\n", xs_DeviceRead(dev));   //读取设备数据
    xs_DeviceClose(dev);                                                           //关闭数据

    return 0;
}

函数接口

设备初始化函数

/**
 * This function will initialize the specified device
 *
 * @param dev the pointer of device driver structure
 *
 * @return the result
 */
xs_err_t xs_DeviceInit(xs_DevicePointer dev)
{
    xs_err_t result = XS_EOK;

    XS_ASSERT(dev != XS_NULL);

    /*driver init*/
    if(dev->driver && dev->driver->flag & DRIVER_FLAG_MOUNTED){
        device_register(dev)
        if (!(dev->flag & DEVICE_FLAG_ACTIVATED))
        {
            result = device_init(dev);/* Macro */
            if (result != XS_EOK)
            {
                xs_kprintf("Initialize device:%s failed. The error code is %d\n",
                           dev->name, result);
            }
            else
            {
                dev->flag |= XS_DEVICE_FLAG_ACTIVATED;
            }
        }

    }else{
        return DRIVER_ERROR;
    }

    return result;
}

设备查找函数

/**
 * This function finds a device driver by specified name.
 */
xs_DevicePointer xs_DeviceFind(const char *name)
{
     enum xs_BusType e;
     xs_DevicePointer dev;
     for(e=0;e !=XS_BUS_END; ++e){
        if(dev = __xs_DeviceFind_by_type(name,e) && dev !=BUS_ERROR)
            return dev; 
     }
     return NULL;
}

xs_DevicePointer xs_DeviceFind(const char *name,enum xs_BusType bus_type){
    return __xs_DeviceFind_by_type(name,bus_type);
};

xs_DevicePointer __xs_DeviceFind_by_type(const char *name,enum xs_BusType bus_type)
{
    struct xs_Device *device;
    struct xs_ListNode *node;

    xs_Bus bus = get_bus_from_type(bus_type);
    if(!(bus && bus->falg = BUS_FLAG_ACTIVED))
        return BUS_ERROR;/*a point to speical area */

    /* enter critical */
    if (xs_get_kthread_descriptor() != XS_NULL)
        xs_critical_enter();

    /* try to find device */
    for (node  = bus->device.next;
         node != &(bus->device);
         node  = node->next)
    {
        device = xs_list_entry(node, struct xs_Device, list);
        if (xs_strncmp(device->name, name, XS_NAME_MAX) == 0)
        {
            /* leave critical */
            if (xs_get_kthread_descriptor() != XS_NULL)
                xs_critical_exit();

            return (xs_DevicePointer)device;
        }
    }

    /* leave critical */
    if (xs_get_kthread_descriptor() != XS_NULL)
        xs_critical_exit();

    /* not found */
    return XS_NULL;
}

设备打开函数


/*
* This function open a device
*/
xs_err_t xs_DeviceOpen(xs_DevicePointer dev)
{
    xs_err_t result = XS_EOK;

    XS_ASSERT(dev != XS_NULL);
    if(!(dev->bus))
        return BUS_ERROR;
    if(!(dev->driver && dev->bus->match(dev->driver,dev)))
        return DRIVER_ERROR;

    /* if device is not initialized, initialize it. */
    if (!(dev->flag & DEVICE_FLAG_ACTIVATED))
    {
        xs_DeviceInit(dev);
    }

    /* call device_open interface */
    if (device_open != XS_NULL)
    {
        result = device_open(dev, oflag);
        dev->ref_count++;
        XS_ASSERT(dev->ref_count != 0);
    }
    return result;
}

设备关闭函数

xs_err_t xs_DeviceClose(xs_DevicePointer dev)
{
    xs_err_t result = XS_EOK;

    XS_ASSERT(dev != XS_NULL);

    if (dev->ref_count == 0)
        return XS_ERROR;

    dev->ref_count--;

    if (dev->ref_count != 0)
        return XS_EOK;

    /* call device_close interface */
    if (device_close != XS_NULL)
    {
        result = device_close(dev);
        
    }


    return result;
}

i2c设备注册与卸载

int int xs_I2cDeviceRegister(struct xs_I2cDevice *dev,const char *name, xs_uint16 flags);
int xs_I2cDeviceunRegister(struct xs_I2cDevice *dev);

xs_I2cDeviceRegister函数具体实现

int xs_I2cDeviceRegister(struct xs_I2cDevice *dev,const char *name, xs_uint16 flags)
{
   if(dev != NULL)
   {
       return xs_DeviceRegister(dev,name,0);
   }
    return  -XS_ERROR;
}

xs_err_t xs_DeviceRegister(void * dev,const char *name, xs_uint16 flags)
{

    if (dev == XS_NULL)
        return -XS_ERROR;

    if (xs_DeviceFind(name) != XS_NULL)
        return -XS_ERROR;

    xs_CriticalEnter();
    for (node  = Device_driver_head.NodeNext;
            node != &(xiaoshan_device_head);
            node  = node->NodeNext)
    {
        struct xs_Device *device;

        device = XS_DOUBLE_LINKLIST_ENTRY(node, struct xs_Device, link);
        if (device)
        {
            XS_ASSERT(device != dev);
        }
    }
    xs_CriticalExit();
    xs_strncpy(dev->dev_string, name, XS_NAME_MAX);
    register xs_base temp = xs_DisableHwInterrupt();
    {
        xs_DoubleLinkListInsertNodeAfter(&xiaoshan_device_head, &(dev->link));
    }
    xs_EnableHwInterrupt(temp);
    dev->sign = flags;
    dev->call_cnt = 0;
    dev->status = 0;
    xs_InitWqueue(&(dev->wait_queue));
    return XS_EOK;
}