zoukankan      html  css  js  c++  java
  • linux驱动之设备模型

    linux 设备驱动模型

    inux2.6提供了新的设备模型:总线、驱动、设备。基本关系简要的概括如下:
    驱动核心可以注册多种类型的总线。
    每种总线下面可以挂载许多设备。(通过kset devices)
    每种总线下可以用很多设备驱动。(通过包含一个kset drivers)}
    每个驱动可以处理一组设备。按照我的理解就是所有的设备都挂载到总线上,当加载驱动时,驱动就支总线上找到自己对应的设备。或者先把驱动加载上,来了一个设备就去总线找驱动。

    一:总线

    总线是处理器与设备之间通道,在设备模型中,所有的设备都通过总线相连。

    (1)总线结构

    bus_type:

     struct bus_type {
     const char  * name;//设备名称
     struct subsystem subsys;//代表自身
     struct kset  drivers;   //当前总线的设备驱动集合
     struct kset  devices; //所有设备集合
     struct klist  klist_devices;
     struct klist  klist_drivers;
     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, char **envp,  
           int num_envp, char *buffer, int buffer_size);//热拔插事件
     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);
    };
    

    在后面的实例当中用到了里面的两个成员

    1:const char *name;
    2:int  (*match)(struct device * dev, struct device_driver * drv);
    

    设备驱动匹配函数,这个匹配函数是很关键的东西,这是建立总线上设备与驱动的桥梁,当一个新的设备或驱动被添加到一个总线上时被调用。

    (2)总线的操作:

    注册:int bus_register(struct bus_type * bus)
    注销:void bus_unregister(struct bus_type *bus);

    (3)总线属性 bus_attribute

    struct bus_attribute {
    struct attribute attr;
    ssize_t (*show)(struct bus_type *bus, char *buf);
    ssize_t (*store)(struct bus_type *bus, const char *buf,size_t count);
    };
    BUS_ATTR(name, mode, show, store);
    

    这个宏声明一个结构, 产生它的名子通过前缀字符串 bus_attr_ 到给定的名子(bus_attr_name),然后利用bus_create_file来创建总线属性

    int bus_create_file(struct bus_type *bus, struct bus_attribute *attr);
    

    参数中的attr,即为bus_attr_name。
    另外, 就是参数中的 show 方法,设置方法如下

    static ssize_t show_bus_version(struct bus_type *bus, char *buf) {
    return snprintf(buf, PAGE_SIZE, "%s
    ", Version);
    }
    

    总线属性的删除, 使用:

    void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr);
    

    (4)总线实例:

    1:首先是要准备一个总线bus_type.也就是定义一个bus_type,然后给它填上一些成员。定义如下:
    struct bus_type my_bus_type = {
    .name = "my_bus",
    .match = my_match,
    };
    这里就对其两个成员赋值了。一个是名称。另一个则是匹配函数:
    2,总线本身也是要对应一个设备的。还要为总线创建设备。
    struct device my_bus = {
    .bus_id = "my_bus0",
    .release = my_bus_release
    };
    源代码:

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/device.h>
    #include <linux/string.h>
    static char *version = "version 1.0";
    //用于判断指定的驱动程序是否能处理指定的设备。
    static int my_match(struct device *dev, struct device_driver *driver) {
    	return !strncmp(dev->bus_id, driver->name, strlen(driver->name));
    }
    static int my_bus_release(struct device *dev) {
    	return 0;
    }
    static ssize_t show_bus_version(struct bus_type *bus, char *buf) {
    	return sprintf(buf, PAGE_SIZE, "%s
    ", version);
    }
    static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
    
    struct device my_bus = {//定义总线设备
    	.bus_id = "my_bus0",
    	.release = my_bus_release,
    };
    EXPORT_SYMBOL(my_bus);
    struct bus_type my_bus_type = {  //定义总线类型
    	.name = "my_bus",
    	.match = my_match,
    };
    EXPORT_SYMBOL(my_bus_type);
    static int __init my_bus_init(void){
    	int ret;
    	ret = bus_register(&my_bus_type);//注册总线
    	if(ret)	
    		printk("bus_register failed!
    ");
    	if(bus_create_file(&my_bus_type, &bus_attr_version))//创建总线属性	
    		printk("Creat bus failed!
    ");
    	ret = device_register(&my_bus);//注册总线设备
    	if (ret)
    		printk("device_register failed!
    ");
    	return ret;
    }
    static void __exit my_bus_exit(void) {
       bus_unregister(&my_bus_type);//删除总线属性
    	device_unregister(&my_bus);//删除总线设备
    }
    module_init(my_bus_init);
    module_exit(my_bus_exit);
    MODULE_AUTHOR("Fany");
    MODULE_LICENSE("GPL");
    

    (5)测试

    将bus.c以动态加载的方式加载到内核,insmod bus.ko,在/sys/bus/目录下会有一个my_bus目录,这就是我们添加的总线。该目录下有devices,drivers目录,因为该总线上没有挂载任何设备和驱动,所以这两个目录都为空;同时,在/sy/devices目录下,还可看到my_bus0设备(总线本身也是一种设备)。

    二:设备:

    设备结构体

    关于设备的一些常用结构体:device,
    1:struct device {
    struct device * parent; //父设备,一般一个bus也对应一个设备。
    struct kobject kobj;//代表自身
    char bus_id[BUS_ID_SIZE];
    struct bus_type * bus; /* 所属的总线 /
    struct device_driver driver; / 匹配的驱动
    /
    void driver_data; / data private to the driver 指向驱动 /
    void platform_data; / Platform specific data,由驱动定义并使用
    /
    ...........更多字段忽略了
    };
    注册设备:int device_register(sruct device *dev)
    注销设备:void device_unregister(struct device *dev);

    2:设备属性:

    sysfs 中的设备入口可有属性. 相关的结构是:

    struct device_attribute {
    struct attribute attr;
    ssize_t (*show)(struct device *dev, char *buf);
    ssize_t (*store)(struct device *dev, const char *buf,
    size_t count);
    };
    

    这些属性结构可在编译时建立, 使用这些宏:
    DEVICE_ATTR(name, mode, show, store);
    结果结构通过前缀 dev_attr_ 到给定名子上来命名. 属性文件的实际管理使用通常的函数对来处理:

    int device_create_file(struct device *device, struct device_attribute *entry);
    void device_remove_file(struct device *dev, struct device_attribute *attr);
    

    3:创建设备实例:

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/device.h>
    #include <linux/string.h>
    extern struct device my_bus; //这里用到了总线设备中定义的结构体
    extern struct bus_type my_bus_type;
    static int my_device_release() {
    	return 0;	
    }
    struct device my_dev={ //创建设备属性
    	.bus = &my_bus_type,//定义总线类型
    	.parent = &my_bus,//定义my_dev的父设备。
    	.release = my_device_release,	
    };
    static ssize_t mydev_show(struct device *dev, char *buf) {
    	return sprintf(buf, "%s
    ", "This is my device!");
    }
    static DEVICE_ATTR(dev, S_IRUGO, mydev_show, NULL);
    static int __init my_device_init(void){
    	int ret;
    	strncpy(my_dev.bus_id, "my_dev", BUS_ID_SIZE); //初始化设备
    	ret = device_register(&my_dev); //注册设备
    	if (ret)
    		printk("device register!
    ");
    	device_create_file(&my_dev, &dev_attr_dev); //创建设备文件
    	return ret;
    }
    static void __exit my_device_exit(void) {
    		device_unregister(&my_dev);//卸载设备
    }
    module_init(my_device_init);
    module_exit(my_device_exit);
    MODULE_AUTHOR("Fany");
    MODULE_LICENSE("GPL");
    

    4:测试

    将设备device.c,编译成模块,以动态加载的方式加载到内核。会发现在sys/bus/my_bus/devices/目录下有一个my_dev设备,查看属性,它是挂在/sys/devices/my_bus0/my_dev目录下,至此添加设备成功。

    三:设备驱动:

    1:数据结构

    关于驱动的常用结构体:device_driver
    struct device_driver {
    const char name; /驱动程序的名字( 在 sysfs 中出现 )/
    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 pm_ops *pm;
    struct driver_private *p;
    };

    2:驱动程序的注册和注销

    /注册device_driver 结构的函数是:/

    int driver_register(struct device_driver *drv);
    void driver_unregister(struct device_driver *drv);
    

    3:驱动程序的属性

    /driver的属性结构在:/

    struct driver_attribute {
         struct attribute attr;
         ssize_t (*show)(struct device_driver *drv, char *buf);
         ssize_t (*store)(struct device_driver *drv, const char *buf, size_t count);
    };
    DRIVER_ATTR(_name,_mode,_show,_store) /*属性文件创建的方法:*/
    int driver_create_file(struct device_driver * drv, struct driver_attribute * attr);//创建设备驱动的属性
    void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr);//删除设备驱动的属性
    

    4:驱动实例

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/device.h>
    #include <linux/string.h>
    extern struct bus_type my_bus_type;
    static int my_probe(struct device *dev) {
    	printk("Driver found device!
    ");
    	return 0;
    };
    static int my_remove(struct device *dev) {
    	printk("Driver unpluged!
    ");
    	return 0;
    };
    struct device_driver my_driver = {
    	.name = "my_dev",
    	.bus = &my_bus_type,
    	.probe = my_probe,
    	.remove = my_remove,
    };
    //定义设备驱动属性
    static ssize_t my_driver_show(struct device_driver *driver, char *buf) {
    	return sprintf(buf, "%s
    ", "This is my driver!");
    };
    static DRIVER_ATTR(drv, S_IRUGO, my_driver_show, NULL);
    static int __init my_driver_init(void){
    	int ret;
    	//注册设备驱动
    	ret = driver_register(&my_driver);
    	if(ret)
    		printk("driver_register failed!
    ");
    	//创建设备驱动属性
    	ret = driver_create_file(&my_driver, &driver_attr_drv);
    	if(ret)
    		printk("create_driver_file failed!
    ");
    	
    	return ret;
    }
    static void __exit my_driver_exit(void){
    	driver_unregister(&my_driver);//注销设备驱动
    }
    module_init(my_driver_init);
    module_exit(my_driver_exit);
    MODULE_AUTHOR("Fany");
    MODULE_LICENSE("GPL");
    

    5:测试

    当加载驱动程序时,终端界面上打印
    Driver found device!
    说明驱动找到了匹配的设备,看到打印这个时,想到在windows下插U盘,立马弹出“发现可移动设备”,有点相像!
    再看看相应的目录:/sys/bus/my_bus/drivers/,多了一个my_dev。
    说明添加驱动成功!

    欢迎评论交流
  • 相关阅读:
    WCF开发入门的实例
    转:SQLHelper类,泛型+反射
    设置ASP缓存的几个语句
    ADO.NET总结
    通过反射来实现:DataTable 和List 相互转换
    十步完成windows服务的安装
    dhl:The request for security token could not be satisfied because authentication failed. 调用方未由服务进行身份验证。
    C#中的readonly(运行时常量)与const(编译时常量)的区别
    用do{}创建内联子程序
    用autodie简化错误处理
  • 原文地址:https://www.cnblogs.com/linengier/p/9441704.html
Copyright © 2011-2022 走看看