当前位置: 首页 > 工具软件 > barebox > 使用案例 >

barebox学习笔记之二 -- 设备驱动

劳韬
2023-12-01
1. @Documentation/device_drivers.txt文档译稿

barebox中的设备和驱动 
---------------------------------------------------------------------------------------------------
   这里,我们遵循相当简化的驱动模型.有一个描述系统中出现的特定设备的device结构,一个表示系统中出现的驱动的driver结构.
两个结构通过成员'type'(int)和成员'name'(char*)找到对方.如果这两个成员都匹配,驱动的probe函数会被调用,并以device
结构作为参数.熟悉Linux platform总线的童鞋将会认出这个行为,并且实际上,很多东西都是从Linux 里面"偷"来的.结构的某些
成员将在文档中给出描述.
   目前所有的设备和驱动都用简单的链表处理,当涉及到USB和PCI的时候,可能希望有个树结构,但是这也可能会是一笔不必要的开销.
---------------------------------------------------------------------------------------------------

struct device
-------------
char name[MAX_DRIVER_NAME];
这个成员('type'在下面描述)用于和驱动进行匹配.这是一个描述性的名字,可以是MPC5XXX_ether或者imx_serial.

char id[MAX_DRIVER_NAME];
id用于唯一的表示系统中的一个设备.id会在/dev/目录下作为设备的名称出现.通常这是一些类似eth0或者nor0之类.

void *type_data;
某种特殊类型的设备通常需要存储比device结构所能够容纳的更多的信息.这个entry保存了一个指向特定类型的结构的指针

void *priv;
被设备驱动用来存储私有信息.

void *platform_data;
用于从板级代码到设备驱动携带板子专用数据的信息.

struct param_d *param;
设备的参数,参考Documentation/parameters.txt获取更多信息.

struct driver_d
---------------
char name[MAX_DRIVER_NAME];
unsigned long type;
参考上文.

int     (*probe) (struct device_d *);
int     (*remove)(struct device_d *);

这两个函数在发现一个设备实例或者设备实例丢失的时候调用

ssize_t (*read)  (struct device_d*, void* buf, size_t count, ulong offset, ulong flags);
ssize_t (*write) (struct device_d*, const void* buf, size_t count, ulong offset, ulong flags);

标准的read/write函数.这些作为read/write系统调用的响应来实现.驱动不需要实现这些.

void *type_data;

某种程度上因为device结构中已经有了type数据,这个指针有些多余.目前文件系统的实现使用这个域(driver中的type_data)
而以太网驱动则使用device中相同的域(device中的type). 可能二者之一应该被去掉.
struct device_d {
        //! This member (and 'type' described below) is used to match with a driver. This is a descriptive 
        //name and could be MPC5XXX_ether or imx_serial.
        char name[MAX_DRIVER_NAME];

        //! The id is used to uniquely identify a device in the system. The id will show up under /dev/ as 
        //  the device's name. Usually this is something like eth0 or nor0. 
        int id;

        struct resource *resource;
        int num_resources;

        void *platform_data; /*! board specific information about this device */

        //! Devices of a particular class normaly need to store more
        //  information than struct device holds.
        void *priv;
        void *type_data;                       //! In case this device is a specific device, this pointer
                                               //points to the type specific device, i.e. eth_device
        struct driver_d *driver; /*! The driver for this device */

        //设备注册的时候将会通过这个list链接到全局设备链表device_list当中.
        struct list_head list;     /* The list of all devices */
        struct list_head children; /* our children            */
        struct list_head sibling;
        struct list_head active;   /* The list of all devices which have a driver */

        struct device_d *parent;   /* our parent, NULL if not present */

        struct bus_type *bus;

        //! The parameters for this device. This is used to carry information of board specific data from 
        //  the board code to the device driver.
        struct list_head parameters;

        struct list_head cdevs;
};



struct driver_d {
        /*! The name of this driver. Used to match to
         * the corresponding device. */
        const char *name;

        //驱动注册的时候将通过list链接到全局驱动链表driver_list中
        struct list_head list;

        /*! Called if an instance of a device is found */
        int     (*probe) (struct device_d *);

        /*! Called if an instance of a device is gone. */
        void     (*remove)(struct device_d *);

        void    (*info) (struct device_d *);
        void    (*shortinfo) (struct device_d *);

        struct bus_type *bus;
};



struct bus_type {
        char *name;
        int (*match)(struct device_d *dev, struct driver_d *drv);
        int (*probe)(struct device_d *dev);
        void (*remove)(struct device_d *dev);
        
        //总线在注册的时候将通过这个list将自己链接到全局总线链表bus_list中
        struct list_head list;
};
2.设备、驱动 和 总线 的注册
//设备的注册
register_device(struct device_d *new_device)
+-- list_add_tail(&new_device->list, &device_list);
+-- match()
    +-- dev->bus->match(dev, drv);
    +-- dev->bus->probe(dev);
    +-- list_add(&dev->active, &active); 

int register_device(struct device_d *new_device)
{
        struct driver_d *drv;

        if (new_device->id == DEVICE_ID_DYNAMIC) {
                new_device->id = get_free_deviceid(new_device->name);
        } else {
                if (get_device_by_name_id(new_device->name, new_device->id)) {
                        eprintf("register_device: already registered %s\n",
                                dev_name(new_device));
                        return -EINVAL;
                }
        }

        debug ("register_device: %s\n", dev_name(new_device));

        if (!new_device->bus) {
//              dev_err(new_device, "no bus type associated. Needs fixup\n");
                new_device->bus = &platform_bus;
        }

        list_add_tail(&new_device->list, &device_list);
        INIT_LIST_HEAD(&new_device->children);
        INIT_LIST_HEAD(&new_device->cdevs);
        INIT_LIST_HEAD(&new_device->parameters);
        INIT_LIST_HEAD(&new_device->active);

        for_each_driver(drv) {
                if (!match(drv, new_device))
                        break;
        }

        return 0;
}
EXPORT_SYMBOL(register_device);

static int match(struct driver_d *drv, struct device_d *dev)
{
        if (dev->driver)
                return -1;

        dev->driver = drv;

        if (dev->bus != drv->bus)       //设备和驱动必须是相同的总线
                goto err_out;
        if (dev->bus->match(dev, drv))  //如果总线的match()函数存在,则先调用之
                goto err_out;
        if (dev->bus->probe(dev))       //如果总线的probe()函数存在,则调用之
                goto err_out;

        list_add(&dev->active, &active); //将设备加入active链表.

        return 0;
err_out:
        dev->driver = NULL;
        return -1;
}
//驱动的注册
register_driver(struct driver_d *drv)
+-- list_add_tail(&drv->list, &driver_list);
+-- match(drv, dev);
    +-- dev->bus->match(dev, drv);
    +-- dev->bus->probe(dev);
    +-- list_add(&dev->active, &active); 

int register_driver(struct driver_d *drv)
{
        struct device_d *dev = NULL;

        debug("register_driver: %s\n", drv->name);

        if (!drv->bus) {
//              pr_err("driver %s has no bus type associated. Needs fixup\n", drv->name);
                drv->bus = &platform_bus;
        }

        list_add_tail(&drv->list, &driver_list);

        if (!drv->info)
                drv->info = noinfo;
        if (!drv->shortinfo)
                drv->shortinfo = noshortinfo;

        for_each_device(dev)
                match(drv, dev);

        return 0;
}
EXPORT_SYMBOL(register_driver);
//总线的注册
//总线本来是由链表bus_list来维护的,但是现在这部分已经被注释掉了,总线是通过内嵌到device_d结构中,之后,
//在遍历driver_list或者device_list的时候,会通过device找到bus,进而找到bus的match()和probe()
//因此,也就没有必要使用bus_list来维护所有的bus_type实例.
##if 0
#LIST_HEAD(bus_list);
#EXPORT_SYMBOL(bus_list);
#int bus_register(struct bus_type *bus){
#        list_add_tail(&bus->list, &bus_list);
#        return 0;
#}
##endif
//以USB总线为例:
struct bus_type usb_bus_type = {
        .name   = "usb",
        .match  = usb_match,
        .probe  = usb_probe,
        .remove = usb_remove,
};

static struct usb_device *usb_alloc_new_device(void)
{
        struct usb_device *usbdev = xzalloc(sizeof (*usbdev));

        if (!usbdev)
                return NULL;

        usbdev->devnum = dev_index + 1;
        usbdev->maxchild = 0;
        usbdev->dev.bus = &usb_bus_type;
        usbdev->setup_packet = dma_alloc(sizeof(*usbdev->setup_packet));
        usbdev->descriptor = dma_alloc(sizeof(*usbdev->descriptor));

        dev_index++;

        return usbdev;
}

//如果new_device->bus已经被初始化,则调用bus本身的match()和probe(). 否则初始化为platform_bus.
register_device(struct device_d *new_device)
+-- if (!new_device->bus) {
                dev_err(new_device, "no bus type associated. Needs fixup\n");
                new_device->bus = &platform_bus;
    }
+-- for_each_driver(drv) 
        if (!match(drv, new_device)) break;
            //+-- dev->driver = drv;              //设备在这里拿到驱动的指针
            //+-- dev->bus->match(dev, drv);
            //+-- dev->bus->probe(dev);
            //+-- list_add(&dev->active, &active);
 
//如果drv->bus已经被初始化,则调用bus本身的match()和probe().否则初始化为platform_bus. 
usb_driver_register(struct usb_driver *drv)
+-- drv->driver.name = drv->name;
+-- drv->driver.bus = &usb_bus_type;
+-- register_driver(&drv->driver);
    +-- if (!drv->bus) {
                //pr_err("driver %s has no bus type associated. Needs fixup\n", drv->name);
                drv->bus = &platform_bus;
        }
    +-- for_each_device(dev)
            match(drv, dev);
          //+-- dev->driver = drv;              //设备在这里拿到驱动的指针
          //+-- dev->bus->match(dev, drv);
          //+-- dev->bus->probe(dev);
          //+-- list_add(&dev->active, &active);

//在具体设备的总线驱动代码的bus->probe(struct device_d dev)的实现中,一般都会首先调用dev->driver->probe().
//如果成功则将设备和驱动关联起来,否则直接返回.

3. 具体的设备、驱动和总线
(1). mtd或flash等块设备
(2). platform设备
(3). cdev
 类似资料: