linux驱动-设备驱动模型(driver驱动)

文章详细介绍了Linux设备驱动模型中的数据结构,包括device_driver和driver_private,并阐述了driver的注册过程,包括在总线上查找和防止重复注册、调用bus_add_driver以及创建属性文件等步骤。此外,还提到了如何在driver目录下创建属性文件。最后,给出了一个注册自定义驱动的示例代码,并展示了验证结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

该系列文章阅读顺序:

  1. linux驱动-设备驱动模型(属性文件 kobject )
  2. linux驱动-设备驱动模型(kset)
  3. linux驱动-设备驱动模型(bus总线)
  4. linux驱动-设备驱动模型(device设备)
  5. linux驱动-设备驱动模型(driver驱动)
  6. linux驱动-设备驱动模型(class类)
  7. linux驱动-设备驱动模型(platform设备)

Linux设备模型用 Driver 抽象硬件设备的驱动程序,它包含设备初始化、电源管理相关的接口实现。而Linux内核中的驱动开发,基本都围绕该抽象进行(实现所规定的接口函数)。

作者: baron

1、数据结构

1) device_driver

struct device_driver {
    const char      *name;   //名称
    struct bus_type     *bus; //挂接的总线

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

    bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */
    enum probe_type probe_type;

    const struct of_device_id   *of_match_table; //用设备树匹配时,用于匹配设备
    const struct acpi_device_id *acpi_match_table;

    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);
    const struct attribute_group **groups;

    const struct dev_pm_ops *pm;

    struct driver_private *p; //保存相关链表,也保存了kobj
};

2) driver_private

struct driver_private {
    struct kobject kobj;          // 用于创建 driver 所在目录
    struct klist klist_devices;   // 用于链接匹配到的设备
    struct klist_node knode_bus;  // 链接到bus->p->klist_drivers
    struct module_kobject *mkobj; 
    struct device_driver *driver; // 指向该结构的拥有者
};

2、driver的注册

相较于device的注册,driver的注册就比较简单

  1. 在总线上查找drv,判断drv是否已经注册进bus,防止重复注册
  2. 调用bus_add_driver将drv注册进入bus
  3. 创建属性文件 drv->groups
  4. 向上层发送uevent事件,KOBJ_ADD
int driver_register(struct device_driver *drv)
{
    int ret;
    struct device_driver *other;

    BUG_ON(!drv->bus->p);

    if ((drv->bus->probe && drv->probe) ||
        (drv->bus->remove && drv->remove) ||
        (drv->bus->shutdown && drv->shutdown))
        printk(KERN_WARNING "Driver '%s' needs updating - please use "
            "bus_type methods\n", drv->name);
	
    //在总线上查找drv,判断drv是否已经注册进bus,防止重复注册
    other = driver_find(drv->name, drv->bus);
    if (other) {
        printk(KERN_ERR "Error: Driver '%s' is already registered, "
            "aborting...\n", drv->name);
        return -EBUSY;
    }
	
    //调用bus_add_driver将drv注册进入bus
    ret = bus_add_driver(drv);
    if (ret)
        return ret;

    //创建属性文件 drv->groups
    ret = driver_add_groups(drv, drv->groups);
    if (ret) {
        bus_remove_driver(drv);
        return ret;
    }

    //向上层发送uevent事件,KOBJ_ADD
    kobject_uevent(&drv->p->kobj, KOBJ_ADD);

    return ret;
}

bus_add_driver

int bus_add_driver(struct device_driver *drv)
{
    struct bus_type *bus;
    struct driver_private *priv;
    int error = 0;

    bus = bus_get(drv->bus);
    if (!bus)
        return -EINVAL;

    pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);

    priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    if (!priv) {
        error = -ENOMEM;
        goto out_put_bus;
    }
    //初始化klist_devices
    klist_init(&priv->klist_devices, NULL, NULL);
    priv->driver = drv; 
    drv->p = priv;

    priv->kobj.kset = bus->p->drivers_kset; //初始化kest

    // 由于没有设置 parent 因此使用 bus->p->drivers_kset 做父节点,并在这里创建drv目录,
    // 从这里可以知道drv的根目录为 /sys/bus/drv->bus/drivers/
    error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
                     "%s", drv->name);
    if (error)
        goto out_unregister;

    //将priv->knode_bus 链接进入priv->bus->p->klist_drivers
    klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);

    //判断是否可以自动probe,如果可以,则遍历klist_devices,对其中的每一个dev都调用drv->bus->match(dev, drv)函数
    if (drv->bus->p->drivers_autoprobe) { 
        if (driver_allows_async_probing(drv)) {
            pr_debug("bus: '%s': probing driver %s asynchronously\n",
                drv->bus->name, drv->name);
            async_schedule(driver_attach_async, drv);
        } else 
            error = driver_attach(drv);
            if (error)
                goto out_unregister;
        }
    }
    module_add_driver(drv->owner, drv);

    //在drv所在目录创建属性文件 uevent
    error = driver_create_file(drv, &driver_attr_uevent);
    if (error) {
        printk(KERN_ERR "%s: uevent attr (%s) failed\n",
            __func__, drv->name);
    }

    //在drv所在目录创建属性文件 drv_groups
    error = driver_add_groups(drv, bus->drv_groups);
    if (error) {
        /* How the hell do we get out of this pickle? Give up */
        printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
            __func__, drv->name);
    }
	
	//如果支持 bind 则在 drv 所在目录创建属性文件 bind 和 unbind
    if (!drv->suppress_bind_attrs) {
        error = add_bind_files(drv);
        if (error) {
            /* Ditto */
            printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
                __func__, drv->name);
        }
    }

    return 0;

out_unregister:
    kobject_put(&priv->kobj);
    /* drv->p is freed in driver_release()  */
    drv->p = NULL;
out_put_bus:
    bus_put(bus);
    return error;
}

3、driver_register 总结

1) 在sys/创建对应节点

bus_add_driver 会创建下面节点

/sys/bus/xxx/drivers/drv->name
/sys/bus/xxx/drivers/drv->name/uevent

/* 支持 suppress_bind_attrs */
/sys/bus/xxx/drivers/drv->name/unbind
/sys/bus/xxx/drivers/drv->name/bind

2) 匹配 bus 总线上的设备

除了创建节点之外,如果可以自动 probe ,如果可以,则遍历 klist_devices,对其中的每一个 dev 都调用 drv->bus->match(dev, drv)函数,如果匹配成功则调用 really_probe(drv) ,在这个函数中默认先调用函数 dev->bus->probe(dev),如果没有设置 dev->bus->probe函数,则调用该函数,调用流程如下

bus_add_driver---->
	driver_attach----> 无论如何最终都会调用这个函数
		bus_for_each_dev---->
			__driver_attach---->
				driver_match_device---->
					drv->bus->match(dev, drv) //如果匹配成功则调用 really_probe
					really_probe---->
						dev->bus->probe(dev) //默认调用这个
						drv->probe(dev) //如果没有设置 dev->bus->probe 函数,则调用这个

4、注册我们自己的驱动

在我们创建的 bus 上注册我们自己的驱动

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

MODULE_AUTHOR("baron");
MODULE_LICENSE("GPL");

extern struct bus_type my_bus;

int my_drv_probe(struct device *dev)
{
    printk("my_drv_probe\n");
    return 0;
}

int my_drv_remove(struct device *dev)
{
    printk("my_drv_remove\n");
    return 0;
}

struct device_driver my_drv = {
    .name = "my_drv",
    .bus = &my_bus,
    .probe = my_drv_probe,
    .remove = my_drv_remove,
};

static int my_drv_init(void)
{
    driver_register(&my_drv);
    return 0;
}

static void my_drv_exit(void)
{
    driver_unregister(&my_drv);
}

module_init(my_drv_init);
module_exit(my_drv_exit);

验证结果

XF-X2:/sys/bus/my_bus/drivers # ls
XF-X2:/sys/bus/my_bus/drivers #
XF-X2:/sys/bus/my_bus/drivers # insmod /cache/my
my_bus.ko       my_device.ko    my_driver.ko
XF-X2:/sys/bus/my_bus/drivers # insmod /cache/my_driver.ko
XF-X2:/sys/bus/my_bus/drivers # ls
my_drv
XF-X2:/sys/bus/my_bus/drivers #
XF-X2:/sys/bus/my_bus/drivers # cd my_drv/
XF-X2:/sys/bus/my_bus/drivers/my_drv # ls
bind uevent unbind

5、在driver目录下创建属性文件

和前面的 device 下创建属性文件相同,这里只给接口不再赘述

struct driver_attribute {
    struct attribute attr;
    ssize_t (*show)(struct device_driver *driver, char *buf);
    ssize_t (*store)(struct device_driver *driver, const char *buf,
             size_t count);
};

#define DRIVER_ATTR(_name, _mode, _show, _store) \
    struct driver_attribute driver_attr_##_name = __ATTR(_name, _mode, _show, _store)
#define DRIVER_ATTR_RW(_name) \
    struct driver_attribute driver_attr_##_name = __ATTR_RW(_name)
#define DRIVER_ATTR_RO(_name) \
    struct driver_attribute driver_attr_##_name = __ATTR_RO(_name)
#define DRIVER_ATTR_WO(_name) \
    struct driver_attribute driver_attr_##_name = __ATTR_WO(_name)

//用于在当前 driver 下创建属性文件
extern int __must_check driver_create_file(struct device_driver *driver, const struct driver_attribute *attr);

//删除属性文件
extern void driver_remove_file(struct device_driver *driver, const struct driver_attribute *attr);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值