the linux device model bus device driver学习笔记

mangogeek 2019-10-07 PM 23℃ 0条

Linux中,分别使用bus_type,device_driver,device来描述总线,驱动和设备,在设备模型中,所有的设备都通过总线来连接。即使有些设备没有连接到一根物理总线上,Linux为其设置了一个内部的、虚拟的platform总线,来维持总线、驱动、设备的关系。
总线:

struct bus_type {
    const char        * name;//总线名字
    struct module        * owner;
    struct kset        subsys;//子系统,连接到一个全局变量kset bus_subsys。这样每一根总线系统都会通过bus_subsys结构连接起来。
    struct kset        drivers;//总线驱动
    struct kset        devices;//插入总线的所有设备
    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);//参数与kset里的uevent相同。
    int        (*probe)(struct device * dev);
    int        (*remove)(struct device * dev);
    void        (*shutdown)(struct device * dev);
....
}

总线注册:bus_register(struct bus_type *bus);
总线删除:bus_unregister(struct bus_type *bus);
总线属性:

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

创建和初始化bus_attribute结构:

#define BUS_ATTR(_name,_mode,_show,_store)
创建总线属性:
int bus_create_file(struct bus_type *,struct bus_attribute *);
删除属性:
void bus_remove_file(struct bus_type *, struct bus_attribute *);
Bus.c
#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>
#include <linux/mm.h>


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

static int virtual_bus_match(struct device *dev,struct device_driver *drv) {
Printk(KERN_DEBUG”\n!!!!!!match!!!!!!\n”);
     return !strcmp(dev_name(dev),drv->name);
    }

void my_bus_release(struct device *dev) {
        printk(KERN_DEBUG"my bus release\n");
    }

struct bus_type virtual_bus={
        .name        =    "virtual_bus",
        .match    =    virtual_bus_match,
    };

struct device my_busdev={
        .init_name    =    "my_busdev",
        .release    =    my_bus_release,
    };
EXPORT_SYMBOL(virtual_bus);
EXPORT_SYMBOL(my_busdev);

static BUS_ATTR(c,S_IRUGO,show_bus_author,NULL);

static int __init bus_init(void) {
    int ret;
    ret=bus_register(&virtual_bus);    //register virtual_bus
    if(ret){
        return ret;
    }
    if(bus_create_file(&virtual_bus,&bus_attr_c)) { //创建总线属性文件
        printk(KERN_NOTICE"unable to create bus attr\n");
    }
    
    ret =    device_register(&my_busdev);
    if(ret) {
    printk(KERN_NOTICE"unable to register device\n");
    }
    printk("bus register success!\n");
    return 0;
}

static void __exit bus_exit(void) {
    bus_unregister(&virtual_bus);
    device_unregister(&my_busdev);
}

module_init(bus_init);
module_exit(bus_exit);
MODULE_AUTHOR("MANGO");
MODULE_LICENSE("Dual BSD/GPL")

编译后,会在/sys/bus下建立virtual_bus文件夹。
22547469_1414804854k5J2.png
执行cat c,得到如下输出:
22547469_1414804880OUuA.png

设备驱动程序:

struct device_driver {
    const char        * name;
    struct bus_type        * bus;
    struct kobject        kobj;
    struct klist        klist_devices;
    struct klist_node    knode_bus;
    struct module        * owner;
    const char         * mod_name;    /* used for built-in modules */
    struct module_kobject    * mkobj;
    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);
}

Driver.c

#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>
#include <linux/mm.h>

extern struct device my_busdev;
extern struct bus_type virtual_bus;
extern struct device virtual_device;

static ssize_t show_driver_author(struct device_driver *driver,char *buf) {
    return snprintf(buf,PAGE_SIZE,"%s\n","mango");
}

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

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

struct device_driver virtual_driver={
        .name        =    "mytest",
        .bus        =    &virtual_bus,
        .probe    =        my_driver_probe,
        .remove    =        my_driver_remove,
    };

static DRIVER_ATTR(c,S_IRUGO,show_driver_author,NULL);

static int __init my_driver_init(void) {
        int ret;
        ret    =    driver_register(&virtual_driver);
        if(ret)
            return ret;
        if(driver_create_file(&virtual_driver,&driver_attr_c))
            printk(KERN_NOTICE"unable to create author attr\n");
        printk("driver register success\n");
        return ret;
    }

static void __exit my_driver_exit(void) {
        driver_unregister(&virtual_driver);
        printk("driver unregister success\n");
}

module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_AUTHOR("MANGO");
MODULE_LICENSE("Dual BSD/GPL")

编译后driver.ko

设备:

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;//设备的父设备,大多数情况下,一个父设备是某种总线的宿主控制器。如果是NULL,则表示该设备是顶层设备。
    struct kobject kobj;//device->kobj->parent与&device->parent->kobj是相同的。
    char    bus_id[BUS_ID_SIZE];    /* position on parent bus */
    struct device_type    *type;
......
    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    (*release)(struct device * dev);//当指向设备的最后一个引用被删除时,内核调用该方法。
}

设备注册:int device_register(struct device *dev);
设备删除:int device_unregister(struct device *dev);
设备属性:

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

宏:#define DEVICE_ATTR(_name,_mode,_show,_store)
创建设备属性:
int device_create_file(struct device device,struct device_attribute entry);
删除设备属性:
void device_remove_file(struct device dev,struct device_attribute attr);
设置设备的名字:
dev_set_name(&dev, "name");

Device.c

#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>
#include <linux/mm.h>

extern struct device my_bus;
extern struct bus_type virtual_bus;
extern struct device virtual_device;

static ssize_t show_driver_author(struct device_driver *driver,char *buf) {
    return snprintf(buf,PAGE_SIZE,"%s\n","mango");
}

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

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

struct device_driver virtual_driver={
        .name        =    "mytest",
        .bus        =    &virtual_bus,
        .probe    =        my_driver_probe,
        .remove    =        my_driver_remove,
    };
static char *c="mango_attr";

static DRIVER_ATTR(c,S_IRUGO,show_driver_author,NULL);

static int __init my_driver_init(void) {
        int ret;
        ret    =    driver_register(&virtual_driver);
        if(ret)
            return ret;
        if(driver_create_file(&virtual_driver,&driver_attr_c))
            printk(KERN_NOTICE"unable to create author attr\n");
        printk("driver register success\n");
        return ret;
    }

static void __exit my_driver_exit(void) {
        driver_unregister(&virtual_driver);
        printk("driver unregister success\n");
}

module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_AUTHOR("MANGO");
MODULE_LICENSE("Dual BSD/GPL")

编译完后生成device.ko,加载后得到如下输出:
22547469_1414805094aB85.png
该device指定的父设备是my_busdev,则应该对应目录为:/sys/devices/my_busdev/mytest

执行cat c,可以看到如下输出:
22547469_14148051193523.png
三个都加载,由于driver和device名字一样,会匹配上,如下图:
22547469_1414805141omf9.png
此时再看bus下,文件结构如下:
22547469_1414805170iAda.png
会发现依附于virtual_bus总线的device和driver通过总线match起来,且都建立了相应的连接文件。
22547469_1414805200YGFg.png
device_driver和device分别表示驱动和设备,这两者必须依附于一条总线,因此都包含了struct bus_type指针。在Linux内核中,设备和驱动程序是分开注册的。bus_type的match成员函数可以将两者捆绑在一起。
总线、驱动和设备最终会落实为sysfs中的1个目录,总线、设备和驱动中的各个attribute则直接落实为sysfs中的1个文件,attribute会伴随着show和store函数,分别用于读和写该attribute对应的sysfs文件结点。
事实上,udev规则中个信息的来源实际上就是bus_type,device_driver,device以及attribute等所对应的文件节点。

一个设备类描述了一种设备类型,类是一个设备的高层视图,几乎所有的类都显示在/sys/class目录中。一个例外是块设备。出于历史原因,它们出现在/sys/block下。
每个类都需要一个唯一的名字,它将显示在/sys/class中。
注册函数:int class_register(struct class *cls);
删除函数:void class_unregister(struct class *cls);

标签: none

非特殊说明,本博所有文章均为博主原创。

评论啦~