设备模型六(bus, device, driver)

来源:互联网 发布:网络直播产业链 编辑:程序博客网 时间:2024/05/17 06:15
  • 前言
    前面讨论已经知道, kset, kobject相当于c++中的基类,会有上层容器去继承他们,c语言里面的继承也就是包含其数据结构的意思,而bus(总线), device(设备),驱动(驱动), 他们三个壮汉继承了他们,从而引出了设备模型的高级部分,这里我们要搞明白以下几点
    1.bus, device, driver的数据结构
    2.三者间的关系
    3.device与driver间的绑定
    开搞!

  • 1.bus, device, driver的数据结构

  • 1.1感性的认识什么是bus,device, driver
    bus,即总线, 根据<<设备模型一>>文章内容叙述,总线可以是真实总线的抽象,如i2c,也可以是非真实 的总线,为了一致统一, 非真实统一也要跟真实总线一样被抽象,即虚拟总线
    即 i2c(真实总线)—->抽象—>i2c_bus_type
    虚拟总线—>抽象—>platform
    所谓抽象,意思就是说把真实的总线(或虚拟总线)描述成一个数据结构
    对于加入系统中的硬件或者驱动(写代码时会把硬件或者驱动所依赖的总线提前写好), 都会挂靠到总线上
    那什么是挂靠到总线上呢?说白了就是建立三者之间的练习,不如device的数据结构中有一个指针指向bus跟driver,同理driver与bus也是一样的
  • 1.2 sysfs上的体现
  • 1.2.1在谈sysfs
    sysfs文件系统, 根据前面文章所讲的,体现给用户bus,device,driver的关系层(一目了然)
    大家都知道kset, kobject这些组成上层容器(bus, device, driver)的这些基类,他们的存在就是为了使这三者之间建立联系,并且通过sysfs向用户反映他们之间的联系,打个比方比如我插入Upan,内核检测到U盘插入后会调用device_add来添加设备,这里device_add会调用kobj_add会建立关系,所以你看。。
  • 1.2.2 bus的由来
    内核初始化的时候:
    start_kernel–>kernel_init(启动的一个内核线程)–>do_basic_setup–>driver_init
    在driver_init中:
    devices_init();    buses_init();    classes_init();

好的,上面就是这三座大山的初始化, 注意,这里还有个class_init,关于class的讲解,在下一篇文章会讨论
这里简单看一下devices_init()

    devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);    if (!devices_kset)        return -ENOMEM;    dev_kobj = kobject_create_and_add("dev", NULL);    if (!dev_kobj)        goto dev_kobj_err;    sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);    if (!sysfs_dev_block_kobj)        goto block_kobj_err;    sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);    if (!sysfs_dev_char_kobj)        goto char_kobj_err;

这里不做过多解释,如果前面的文章都看了的话,相信这里的代码还是比较简单的
对于kset_create_and_add函数的作用前面有讲解:
三个init函数执行完之后,sysfs上的关系如下图所示:
这里写图片描述
摁,设备模型的骨架子基本上是出来了,之后我们看一下bus的数据结构

  • 1.2.3 bus_type
    上面讲到,在linux中真实总线与虚拟总线会被抽象出数据结构,即bus_type,下图会讲述一下bus_type中比较重要的参数
    这里写图片描述
    这里说一下bus_type中的match函数,对于每一个总线对应的match函数不同,比较简单的是platform总线
    platform_bus_init–>bus_register(&platform_bus_type);
    其中platform_bus_type
struct bus_type platform_bus_type = {    .name       = "platform",    .dev_attrs  = platform_dev_attrs,    .match      = platform_match, //总线的match函数    .uevent     = platform_uevent,    .pm     = &platform_dev_pm_ops,};

platform_match函数如下

static int platform_match(struct device *dev, struct device_driver *drv){    struct platform_device *pdev = to_platform_device(dev);    struct platform_driver *pdrv = to_platform_driver(drv);    /* Attempt an OF style match first */    if (of_driver_match_device(dev, drv))        return 1;    /* Then try to match against the id table */    if (pdrv->id_table)        return platform_match_id(pdrv->id_table, pdev) != NULL;    /* fall-back to driver name match */    return (strcmp(pdev->name, drv->name) == 0); //这里比较dev与drv的名字是否相等}

上图中,重要的数据结构以及其作用已经很好的表达出来,接下来讨论下注册一个总线时都发生了什么
其实注册一个总线就是形成创建一个上图的关系网

  • 1.2.4注册一个总线(虽然现在没有注册总线的机会,但是有必要小研究一下)→bus_register
    我们采用这个方式,从代码中表示生成的步骤,之后用图来描述代码的步骤
    bus_register(&platform_bus_type):
int bus_register(struct bus_type *bus){    int retval;    struct subsys_private *priv;    priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); //1.malloc ksubsys_private结构     if (!priv)        return -ENOMEM;    priv->bus = bus; //2.确立关系    bus->p = priv;  //2.与bus确立暧昧关系    BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);    retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);//3.设置obj名字之后会在sysfs显示,因为sysfs显示的是obj的名字,所以用的bus->name,如上面显示, bus->name为platform    if (retval)        goto out;    priv->subsys.kobj.kset = bus_kset;//4.确立与/sys/bus的关系    priv->subsys.kobj.ktype = &bus_ktype;    priv->drivers_autoprobe = 1;//5.为了加设备后自动添加驱动用    retval = kset_register(&priv->subsys); //6.前面的准备做好后,注册近kobj机制中    if (retval)        goto out;    retval = bus_create_file(bus, &bus_attr_uevent);//7.在/sys/bus/platform生成文件 uevent    if (retval)        goto bus_uevent_fail;    priv->devices_kset = kset_create_and_add("devices", NULL,                         &priv->subsys.kobj); //8.创建devices    if (!priv->devices_kset) {        retval = -ENOMEM;        goto bus_devices_fail;    }    priv->drivers_kset = kset_create_and_add("drivers", NULL,                         &priv->subsys.kobj);//9.创建 drivers    if (!priv->drivers_kset) {        retval = -ENOMEM;        goto bus_drivers_fail;    }    klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);    klist_init(&priv->klist_drivers, NULL, NULL);    retval = add_probe_files(bus);//10.创建drivers_probe, drivers_autoprobe文件    if (retval)        goto bus_probe_files_fail;    retval = bus_add_attrs(bus);//11.如果bus中还定义了其他的属性文件,则创建    if (retval)        goto bus_attrs_fail;    pr_debug("bus: '%s': registered\n", bus->name);    return 0;bus_attrs_fail:    remove_probe_files(bus);bus_probe_files_fail:    kset_unregister(bus->p->drivers_kset);bus_drivers_fail:    kset_unregister(bus->p->devices_kset);bus_devices_fail:    bus_remove_file(bus, &bus_attr_uevent);bus_uevent_fail:    kset_unregister(&bus->p->subsys);out:    kfree(bus->p);    bus->p = NULL;    return retval;}

10来个步骤,还是比较清晰,下面跟一个图。。。(本人喜欢用图来说明问题,理清思路)
这里写图片描述
好了,对于bus的注册就讲完了, 下面开讲device_add这个函数,即,往总线添加一个设备都干嘛了,对于加入一个驱动的话同加入设备大同小异了,就不用再细说,老规矩,先分析代码,后根据上面的图进行延伸

  • 1.2.5注册设备(device_add)
    当然不同的平台对应不同的device结构,但是里面都会嵌入一个struct device结构
    这里我们以platform_device为例子,给出数据结构,呜哈哈
    这里写图片描述
    上图中 dev结构体里面的platform_data是特殊针对,下面我们看一下如何注册一个platform device
static struct platform_device leds_gpio = {    .name   = "xxx_platDev_ma", //定义设备名字    .id = -1, //id初始化    .dev    = {        .platform_data  = NULL,//啥都不说了,dev的一个变量    },};

调用函数platform_device_register(&leds_gpio);
接下来,厉害了!重点介绍一下platform注册都发生了什么,想想都让人兴奋,GO!
当然,不能涉及到所有的细节,我们还是强调脉络的分析
这里写图片描述
大致的脉络已经体现出来了,至于其他的什么链接文件什么的,个人认为不是什么重点,所以在下面会从现象分析,东西实在太多,接下来我们还是跟据图还说明问题,形象的从图的角度看看都发生了什么,首先,来一个简单的例子

static struct platform_device leds_gpio = {    .name   = "xx_platDev_ma",    .id = -1,    .dev    = {        .platform_data  = NULL    },};struct class *ma_class = NULL;struct device *dev1;struct device *dev2;struct device *dev3;struct device *dev4;static int ma_class_init(void){    int err;    printk(KERN_ALERT "class_init\n");    err = platform_device_register(&leds_gpio); //非常简单,就是注册一个名字为xx_platDev_ma的platform设备    return 0;}static void ma_class_exit(void){    printk(KERN_ALERT "Goodbye, class_init\n");    platform_device_unregister(&leds_gpio);}module_init(ma_class_init);module_exit(ma_class_exit);

我们结合上面的代码逻辑,给出小例子的图解分析
这里写图片描述
我们将例子insmod后,出现
这里写图片描述
我们看到/sys下的结构,跟上面分析的一致,摁。。基本上加入一个设备的分析到位了
还差个驱动的绑定,还的继续努力!
1.2.6驱动的绑定,关于这个方面,留个悬念,这里不想说的太多,老生长谈,大家有兴趣可以看下代码,对应的接口函数是bus_probe_device
在上面的1.2.3 中的图已经很清晰的介绍了绑定的流程顺序

0 0
原创粉丝点击