• 20150225 IMX257 总线设备驱动模型编程之总线篇


    20150225 IMX257 总线设备驱动模型编程之总线篇

    2015-02-25 19:40 李海沿

    从现在开始,我们开始来实现 总线-设备-驱动模型中的总线。、

    我们这个程序的目标是在 sysfs文件系统的/sys/bus/ 目录下面建立一个文件夹。

    一、总线介绍

    1. 总线数据结构bus_type

    struct bus_type 结构体的定义如下:

    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);

    --实现设备与驱动的匹配。不同的总线实现匹配的方法不同,如platform总线采用name匹配,而usb_bus采用id匹配

    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);

    int (*probe)(struct device *dev);

    --在2.6的内核中实现一个设备与驱动的探测。主要是因为热插拔的设备的增多。

    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; --bus_type私有成员,

    这个结构体中主要包括了kset以及klist,用于管理其挂载其总线下的设备和驱动

    };

    下面我们来看看bus_type_private结构体

    struct bus_type_private {

    struct kset subsys; /*代表该bus子系统,里面的kobj是该bus的主kobj,最顶层*/

    struct kset *drivers_kset; /* 挂接到该总线上所有的驱动的集合 */

    struct kset *devices_kset; /* 挂接到该总线上所有的设备的集合 */

    struct klist klist_devices; /* 总线上所有设备的的列表 */

    struct klist klist_drivers; /* 总线上所有驱动的的列表 */

    struct blocking_notifier_head bus_notifier;

    unsigned int drivers_autoprobe:1; /*设置是否在注册驱动时,自动探测(probe)设备*/

    struct bus_type *bus; /*回指包含自己的总线*/

    };

    2. 注册总线

    内核中采用bus_register来注册一个新的总线。

    其使用如:ret = bus_register(&my_bus_type);

    bus_register函数的源码如下(剔除一些错误处理):

    int bus_register(struct bus_type *bus)

    {

    int retval;

    struct bus_type_private *priv;

    priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);

    priv->bus = bus;

    bus->p = priv;

    BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

    retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);

    //设置kobject名称,有时会使用snprintf来设置,不过会出现bug,最好使用该函数。

    priv->subsys.kobj.kset = bus_kset; //kobj中的kset指向父kset

    priv->subsys.kobj.ktype = &bus_ktype; //kobj中的ktype指向父ktype

    priv->drivers_autoprobe = 1; //设置总线下的设备和驱动自动探测

    retval = kset_register(&priv->subsys);

    //注册kset,关于kset,kobject,以及ktype的关系以后在学习

    retval = bus_create_file(bus, &bus_attr_uevent); //创建bus的文件属性

    priv->devices_kset = kset_create_and_add("devices", NULL,&priv->subsys.kobj);

    //在父kset下创建和加入名为devices和drivers的kset

    priv->drivers_kset = kset_create_and_add("drivers", NULL,&priv->subsys.kobj);

    klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);

    //初始化klist,这个函数主要是实现一些赋值及初始化的功能

    klist_init(&priv->klist_drivers, NULL, NULL);

    retval = add_probe_files(bus);

    //主要是创建该bus下probe的属性文件,可以通过cat file 和cat x > file 向属性文件读出和写入数据。

     

    retval = bus_add_attrs(bus); --添加bus自带的属性文件

    return 0;

    }

    从上面的程序得知,总线注册主要工作如下

    创建bus_type_ private,并申请相应的内存,通过赋值bus_type的一些属性,然后添加进入list

    下面函数其实是实现一个从链表的节点,查询到该节点所属的设备,并且减少dev的计数。

    static void klist_devices_get(struct klist_node *n)

    {

    struct device *dev = container_of(n, struct device, knode_bus);

    get_device(dev);

    }

    其实bus的注册比较简单,说到底还是kset和kobject控制着这一切,抛开kset和kobject的具体实现,bus还是很好理解的。

    3. 创建属性文件

    static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);

    通过BUS_ATTR宏定义定义一个属性文件,其参数依次是属性文件名,属性文件的mode,显示属性文件函数和存储属性文件函数。

    显示属性文件的函数如下所示:

    static ssize_t show_bus_version(struct bus_type *bus, char *buf){

    return snprintf(buf, PAGE_SIZE, "%s ", Version);

    }

    创建属性文件:

    bus_create_file(&my_bus_type, &bus_attr_version)

    其实原理上,它就是调用sysfs_create_file函数来实现的。

    4. bus的注销

    bus的卸载函数如下所示:

    bus_unregister(&my_bus_type);

    二、总线实例介绍

    1. 定义bus_type结构体

    以及实现.match的my_match函数,用于检测驱动与设备是否匹配

    2. 定义bus属性文件结构体

    上面的version是我们前面定义的一个字符串

    3. 在init函数中注册总线

    如图所示,

    bus_register(&my_bus_type); 用于注册我们的总线,加入总线列表list,具体功能看前面

    bus_create_file(&my_bus_type, &bus_attr_version); 创建我们my_bus_type 的属性文件

    &bus_attr_version 这个结构体就是前面我们

    static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL); 得到的。

    查看linux源码如下:

    4. 在exit函数中卸载总线

    至止,我们已经实现了 总线-驱动-设备 模型中的总线了

    5. 编译测试

    明天我们来实现 总线-设备-驱动模型 之中的 设备程序。

    附上源代码:

     1 #include <linux/device.h>
     2 #include <linux/module.h>
     3 #include <linux/kernel.h>
     4 #include <linux/init.h>
     5 #include <linux/string.h>
     6 
     7 static char *Version = "$LoverXueEr : 1.0 $";
     8 
     9 //检测驱动是否匹配设备,dev->bus_id 和 driver->name相等的
    10 static int my_match(struct device *dev ,struct device_driver *driver){
    11     return !strncmp(dev_name(dev),driver->name,strlen(driver->name));
    12 }
    13 
    14 struct bus_type my_bus_type = {
    15     .name = "my_bus",
    16     .match = my_match,
    17 };
    18 //显示总线版本号
    19 static ssize_t show_bus_version(struct bus_type *bus,char *buf){
    20     return snprintf(buf,PAGE_SIZE,"%s
    ",Version);
    21 }
    22 
    23 //产生后面的 bus_attr_version 结构体
    24 static BUS_ATTR(version,S_IRUGO, show_bus_version, NULL);
    25 
    26 static int __init my_bus_init(void){
    27     int ret;
    28     /* 注册总线 */
    29     ret = bus_register(&my_bus_type);
    30     if(ret)
    31         return ret;
    32     /*  创建属性文件 */
    33     if(bus_create_file(&my_bus_type, &bus_attr_version))
    34         printk("<0>Fail to create version attribute! 
    ");
    35     return ret;
    36 }
    37 
    38 static void my_bus_exit(void){
    39     bus_unregister(&my_bus_type);
    40 }
    41 
    42 module_init(my_bus_init);
    43 module_exit(my_bus_exit);
    44 
    45 
    46 MODULE_AUTHOR("Lover雪儿");
    47 MODULE_LICENSE("GPL");
    View bus.c
  • 相关阅读:
    《图解HTTP》读书笔记
    【译】关于vertical-align你应知道的一切
    【移动端debug-5】可恶的1px万能实现方案
    《编写高质量代码改善JavaScript程序的188个建议》读书笔记
    【移动端debug-4】iOS下setTimeout无法触发focus事件的解决方案
    一张图看懂Function和Object的关系及简述instanceof运算符
    三张图搞懂JavaScript的原型对象与原型链
    一张图看懂encodeURI、encodeURIComponent、decodeURI、decodeURIComponent的区别
    图解call、apply、bind的异同及各种实战应用演示
    centos vm 桥接 --网络配置
  • 原文地址:https://www.cnblogs.com/lihaiyan/p/4300062.html
Copyright © 2020-2023  润新知