Linux总线设备驱动模型

时间:2022-04-11 17:32:44

1. Linux2.6内核引入总线、设备、驱动模型来描述各种总线(PCI、USB、I2C、SPI)与外围设备及其驱动之间的关系。

 

2. 在Linux内核中,总线用bus_type结构来描述,定义于文件:include/linux/Device.h

struct bus_type {
    const char        *name;
    struct bus_attribute    *bus_attrs;
    struct device_attribute    *dev_attrs;
    struct driver_attribute    *drv_attrs;

    int (*match)(struct device *dev, struct device_driver *drv);
    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    void (*shutdown)(struct device *dev);

    int (*suspend)(struct device *dev, pm_message_t state);
    int (*suspend_late)(struct device *dev, pm_message_t state);
    int (*resume_early)(struct device *dev);
    int (*resume)(struct device *dev);

    struct dev_pm_ops *pm;

    struct bus_type_private *p;
};

① name:总线名字,如PCI

bus_attrs:总线属性

③ match:当一个新设备或者新驱动被添加到这个总线时,该函数被调用。用于判断指定的驱动程序是否能处理指定的设备。若可以,则返回非零。

④ uevent:

⑤ probe:

⑥ remove:

(1) 总线的注册:int us_register(struct bus_type *bus)(若注册成功,新的总线将被添加进系统,可在/sys/bus 下看到相应的目录)

(2) 总线的注销:void bus_unregister(struct bus_type *bus)

 

3. 在Linux内核中, 驱动由device_driver结构描述

struct device_driver {
    const char        *name;
    struct bus_type        *bus;

    struct module        *owner;
    const char         *mod_name;    /* used for built-in modules */

    int (*probe) (struct device *dev);
    int (*remove) (struct device *dev);
    void (*shutdown) (struct device *dev);
    int (*suspend) (struct device *dev, pm_message_t state);
    int (*resume) (struct device *dev);
    struct attribute_group **groups;

    struct dev_pm_ops *pm;

    struct driver_private *p;
};

① probe:

② remove:

(1)驱动的注册:int driver_register(struct device_driver *drv)

(2)驱动的注销:void driver_unregister(struct device_driver *drv)

 

4. 在Linux内核中, 设备由struct device结构描述

struct device {
    struct klist        klist_children;
    struct klist_node    knode_parent;    /* node in sibling list */
    struct klist_node    knode_driver;
    struct klist_node    knode_bus;
    struct device        *parent;

    struct kobject kobj;
    char    bus_id[BUS_ID_SIZE];    /* position on parent bus */
    unsigned        uevent_suppress:1;
    const char        *init_name; /* initial name of the device */
    struct device_type    *type;

    struct semaphore    sem;    /* semaphore to synchronize calls to
                     * its driver.
                     */

    struct bus_type    *bus;        /* type of bus device is on */
    struct device_driver *driver;    /* which driver has allocated this
                       device */
    void        *driver_data;    /* data private to the driver */
    void        *platform_data;    /* Platform specific data, device
                       core doesn't touch it */
    struct dev_pm_info    power;

#ifdef CONFIG_NUMA
    int        numa_node;    /* NUMA node this device is close to */
#endif
    u64        *dma_mask;    /* dma mask (if dma'able device) */
    u64        coherent_dma_mask;/* Like dma_mask, but for
                         alloc_coherent mappings as
                         not all hardware supports
                         64 bit addresses for consistent
                         allocations such descriptors. */

    struct device_dma_parameters *dma_parms;

    struct list_head    dma_pools;    /* dma pools (if dma'ble) */

    struct dma_coherent_mem    *dma_mem; /* internal for coherent mem
                         override */
    /* arch specific additions */
    struct dev_archdata    archdata;

    dev_t            devt;    /* dev_t, creates the sysfs "dev" */

    spinlock_t        devres_lock;
    struct list_head    devres_head;

    struct klist_node    knode_class;
    struct class        *class;
    struct attribute_group    **groups;    /* optional groups */

    void    (*release)(struct device *dev);
};

(1)设备的注册:int device_register(struct device *dev)

(2)设备的注销:void device_unregister(struct device *dev)

 

5. 简单示例

(1)Bus.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>

MODULE_LICENSE("GPL");

int my_match(struct device *dev, struct device_driver *drv)
{
    printk("Bus: my_match\n");
    return !strncmp(dev->kobj.name, drv->name, strlen(drv->name));
}

int my_remove(struct device *dev)
{
    printk("Bus: Device %s removed from bus\n", dev->init_name);
}

struct bus_type my_bus_type = {
    .name = "my_bus",
    .match = my_match,
    .remove = my_remove,
};
    
EXPORT_SYMBOL(my_bus_type);

int my_bus_init(void)
{
    int ret;
    
    ret = bus_register(&my_bus_type);
    
    return ret;
}

void my_bus_exit(void)
{
    bus_unregister(&my_bus_type);
}

module_init(my_bus_init);
module_exit(my_bus_exit);

 

(2)Device.c

#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

MODULE_LICENSE("GPL");

extern struct bus_type my_bus_type;

void my_release(struct device *dev)
{
    printk("Device: Device %s's release function\n", dev->init_name);
}

struct device my_dev = {
    .init_name = "my_dev",
    .bus = &my_bus_type,
    .release = my_release,
};

int my_device_init(void)
{
    int ret;
    ret = device_register(&my_dev);
    return ret;
}

void my_device_exit(void)
{
    device_unregister(&my_dev);
}

module_init(my_device_init);
module_exit(my_device_exit);

 

(3)Driver.c

#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

MODULE_LICENSE("GPL");

extern struct bus_type my_bus_type;

int my_probe(struct device *dev)
{
    printk("Driver: driver found the device it can handle!\n");
    return 0;
}

struct device_driver my_driver = {
    .name = "my_dev",
    .bus = &my_bus_type,    
    .probe = my_probe,
};

int my_driver_init(void)
{
    int ret;
    
    ret = driver_register(&my_driver);
    
    return ret;
}

void my_driver_exit(void)
{
    driver_unregister(&my_driver);    
}

module_init(my_driver_init);
module_exit(my_driver_exit);