美文网首页
基于Atheros无线芯片的platform总线、设备、驱动、虚

基于Atheros无线芯片的platform总线、设备、驱动、虚

作者: moyushion | 来源:发表于2016-11-14 08:43 被阅读0次

    !Warning:请确保能够访问图床Imgur,以正常显示图片


    源码基线:linux-4.0-rc1 & OpenWrt 12.09 branch (Attitude Adjustment)

    硬件:Atheros AR9331

    1 设备模型概述

    设备模型主要完成以下工作:

    1. 设备分类,以分层的架构对设备进行描述,隐藏设备内部的连接细节,对外清晰地展示可用的设备。
    2. 创建和管理设备的生命周期。
    3. 通过sysfs虚拟文件系统,向用户空间提供对设备的读写操作,获取设备的信息、改变设备的运行状态。

    设备模型的结构组成:

    1.总线。

    所有的设备都通过总线相连,包括内部的虚拟总线。在内核中,用struct bus_type结构体来表示总线。

    struct bus_type {  
        const char      * name;  
        struct kset     drivers;  
        struct kset     devices;  
    }  
    
    • name是总线的名字。
    • kset driverskset devices,分别代表了总线的驱动程序及插入总线的所有设备集合。

    2.设备。

    每个设备实例用struct device结构体来表示。

    struct device {  
        struct device       *parent;  
        struct kobject kobj;  
            char bus_id[BUS_ID_SIZE];  
        struct bus_type * bus;  
        struct device_driver *driver;  
    }  
    
    • parent指向设备的所属父设备,通常是某种总线。
    • kobj表示本设备对象。
    • bus_id是总线上标识设备的ID信息,通常由字符串"<域编号>:<总线编号>:<设备编号>:<功能编号>"定义
    • bus标识了设备连接在哪个总线上。
    • driver管理设备的驱动程序。

    3.驱动程序。

    设备驱动程序设备模型可以跟踪所有注册的设备,驱动程序为设备提供服务,完成设备的工作。设备的驱动程序由结构体struct device_driver定义。

    struct device_driver {  
        const char      * name;  
        struct bus_type     * bus;  
        struct kobject      kobj;  
            struct klist            klist_devices;  
    }  
    
    • name是驱动程序的名字。
    • bus是指驱动程序所操作的总线类型。
    • kobj表示驱动程序服务的设备对象。
    • klist_devices是驱动程序当前能操作的设备链表。

    主要数据结构

    • struct net_device:表示网络设备实例,存储了网络设备的一些关键信息:
    • 设备名称。
    • 设备mac地址。由软件随机生成。
    • 广播地址。
    • 以太网类型、帧长度、帧头长度、mtu大小。
    • 设备模型对象。
    • struct net_bridge:网桥的默认vid,默认优先级,生成树使能状态。
    • struct net_port_vlans:网桥管理的所有vlan列表。
    • struct net_bridge_fdb_entry:网桥管理的所有mac转发表。
    • netdev_queue,netdev_rx_queue:网桥的收发包队列。
    • struct net:linux网络命名空间
    struct net_port_vlans {
        u16                port_idx;
        u16                pvid;
        union {
            struct net_bridge_port        *port;
            struct net_bridge        *br; /* vlan所属网桥,即vlan对哪个网桥生效 */
        }                parent;
        struct rcu_head            rcu;
        unsigned long        vlan_bitmap[BR_VLAN_BITMAP_LEN]; /* 已创建的vlan列表 */
        unsigned long        untagged_bitmap[BR_VLAN_BITMAP_LEN]; /* 哪些vlan出口时
                                        去掉tag */
        u16                num_vlans;
    };
    
    struct net_bridge_fdb_entry
    {
        struct hlist_node        hlist;
        struct net_bridge_port        *dst; /* mac地址绑定的端口 */
    
        unsigned long            updated;
        unsigned long            used;
        mac_addr            addr; /* mac地址 */
        __u16                vlan_id;
        unsigned char            is_local:1, /* 是否本机mac地址 */
                        is_static:1, /* 是否静态mac地址 */
                        added_by_user:1,
                        added_by_external_learn:1;
        struct rcu_head            rcu;
    };
    
    struct net_bridge
    {
        spinlock_t            lock;
        struct list_head        port_list; /* 网桥的虚拟端口struct net_bridge_port */
        struct net_device        *dev; /* 网桥设备br0信息 */
    
        struct pcpu_sw_netstats        __percpu *stats;
        spinlock_t            hash_lock;
        struct hlist_head        hash[BR_HASH_SIZE]; /* mac地址hash表,
                                    每个mac地址由struct net_bridge_fdb_entry表示 */
    }
    
    struct net_device {
        char            name[IFNAMSIZ]; /* 设备名称 */
        struct hlist_node    name_hlist; /* 设备名称链表 */
        struct netdev_hw_addr_list    dev_addrs; /* 设备mac地址 */
        struct hlist_node    index_hlist; /* ifindex链表 */
    
        unsigned long        mem_end; /* 设备共享内存的起始地址 */
        unsigned long        mem_start; /* 设备共享内存的结束地址 */
        unsigned long        base_addr; /* 网络设备I/O基地址 */
        int            irq; /* 设备使用的中断号 */
    
        const struct net_device_ops *netdev_ops;
        const struct ethtool_ops *ethtool_ops;
        const struct forwarding_accel_ops *fwd_ops;
    }
    

    主要链表结构

    主要使用的链表数据结构是hlist_headhlist_node,其工作原理与list_head类似。

    struct hlist_head {
        struct hlist_node *first;
    };
    
    struct hlist_node {
        struct hlist_node *next, **pprev;
    };
    

    通过hlist_add_head_rcu(struct hlist_node *n, struct hlist_head *h)函数,可以把新节点添加到链表中。

    1. 空节点

    2. 添加第一个节点

    3. 添加第二个节点

    2 platform总线

    struct kobject与kset结构体

    kobject与kset是组成设备模型的基本结构,为来表示设备模型的设备实例与设备层次关系。这两个结构体作为面向对象概念的基类,嵌套在其他结构体里使用。在sysfs中显示的每一个实例,都对应一个kobject。

    /* 设备模型的基本结构。 
     * 在sysfs中显示的每一个对象,都对应一个kobject,它被用来与内核交互并创建它的可见表述。 
     * 内核用kobject结构将各个对象连接起来组成一个分层的结构体系 */  
    struct kobject {  
        const char      *name;  
        struct kref     kref; /* 对象的引用计数。一个内核对象被创建时,需要知道对象存活
                    的时间,跟踪生命周期的一个方法是使用引用计数,当内核中没有该对象的引用
                    时,表示对象的生命周期结束,可以被删除 */  
        struct list_head    entry; /* 连接到kset建立层次结构 */  
        struct kobject      * parent; /* parent保存了分层结构中上一层节点kobject结构
                    的指针。比如一个kobject结构表示了一个USB设备,它的parent指针可能指向
                    了表示USB集线器的对象,而USB设备是插在USB集线器上的。parent指针最重
                    要的用途是在sysfs分层结构中定位对象 */   
        struct kset     * kset; /* 一个kset是嵌入相同类型结构的kobject集合。每个kset
                    内部,包含了自己的kobject。kset总是在sysfs中出现,一旦设置了kset并
                    把它添加到系统中,将在sysfs中创建一个目录。kobject不必在sysfs中表示,
                    但kset中每一个kobject成员都将在sysfs中得到表述 */  
        struct kobj_type    * ktype; /* 属性结构。kset中也有一个ktype,其使用优先于
                    kobject的此处ktype,因此在典型应用中,kobject中的ktype成员被设置
                    为NULL */  
        struct sysfs_dirent * sd; /* 指向sysfs下以kobj.name所命名生成的目录 */  
    };  
      
    struct kset {  
        struct kobj_type    *ktype;  
        struct list_head    list;  
        spinlock_t      list_lock;  
        struct kobject      kobj;  
        struct kset_uevent_ops  *uevent_ops;  
    };  
    

    假设有个总线X-bus,以及3个设备驱动A-driver、B-driver、C-driver,且这3个设备都属于同一类X-bus总线。那么kobject与kset在描述设备模型时,通常这样使用。在X-bus的描述结构体中包含kset结构体作为同一类型设备的集合;在描述设备驱动A、B、C的driver结构体中包含kset指针,指向所属的类型集合。每个driver都有一个kobject来表示驱动实例自身,并把所有集合按照加入的先后顺序通过list链表(kset.list与kobject.entry)连接起来。

    注册platform总线

    函数入口driver_init()

    void __init driver_init(void)  
    {  
        devices_init();  
        buses_init();  
        platform_bus_init();  
    }  
    

    devices_init()

    devices_init()创建sysfs模型如下:

    /sys
    ├── dev
    │ ├── block
    │ └── char
    └── devices

    
    ```c
    struct kset *devices_kset; /* /sys/devices/ */
    static struct kobject *dev_kobj;
    struct kobject *sysfs_dev_char_kobj;
    struct kobject *sysfs_dev_block_kobj;
    
    int __init devices_init(void)
    {
        devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
    
        dev_kobj = kobject_create_and_add("dev", NULL);
    
        sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
    
        sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
    }
    

    kset_create_and_add()

    /* 动态创建kset结构体,在sysfs中创建以@name命名的目录,如果kset->kobj.ktype有属性值,
       则在目录下创建以属性名命名的文件 */
    struct kset *kset_create_and_add(const char *name,
                     const struct kset_uevent_ops *uevent_ops,
                     struct kobject *parent_kobj)
    {
        struct kset *kset;
    
        kset = kset_create(name, uevent_ops, parent_kobj); /* 动态创建kset结构体 */
        kset_register(kset); /* 初始化kset,并加入到sysfs */
    }
    
    /* 动态创建kset结构体 */
    static struct kset *kset_create(const char *name,
                    const struct kset_uevent_ops *uevent_ops,
                    struct kobject *parent_kobj)
    {
        struct kset *kset;
        int retval;
    
        kset = kzalloc(sizeof(*kset), GFP_KERNEL);
        kobject_set_name(&kset->kobj, "%s", name);
        kset->uevent_ops = uevent_ops;
        kset->kobj.parent = parent_kobj;
        kset->kobj.ktype = &kset_ktype;
        kset->kobj.kset = NULL;
    
        return kset;
    }
      
    /* 初始化kset,并加入到sysfs */
    int kset_register(struct kset * k)  
    {  
            kset_init(k);  
            kobject_add_internal(&k->kobj);
            kobject_uevent(&k->kobj, KOBJ_ADD);  
    }          
    
    void kset_init(struct kset * k)  
    {  
        kobject_init_internal(&k->kobj);
        INIT_LIST_HEAD(&k->list);
        spin_lock_init(&k->list_lock);
    }  
    
    static void kobject_init_internal(struct kobject *kobj)
    {  
            kref_init(&kobj->kref); /* 初始化kobject的引用计数为1 */  
            INIT_LIST_HEAD(&kobj->entry); /* 初始化entry链表 */  
            kobj->state_in_sysfs = 0; /* 设备注册标志,0表示未注册 */
            kobj->state_add_uevent_sent = 0;
            kobj->state_remove_uevent_sent = 0;
            kobj->state_initialized = 1;
    }
    
    /* kobject_add_internal()在sysfs下建立以kobj.name命名的目录 */
    static int kobject_add_internal(struct kobject *kobj)
    {
        struct kobject *parent;
    
        parent = kobject_get(kobj->parent);
    
        /* join kset if set, use it as parent if we do not already have one */
        if (kobj->kset) {
            if (!parent)
                parent = kobject_get(&kobj->kset->kobj);
            kobj_kset_join(kobj);
            kobj->parent = parent;
        }
    
        create_dir(kobj);
        kobj->state_in_sysfs = 1; /* 标记设备已注册 */
    }
    
    /* 创建kobject对象的目录及属性文件 */
    static int create_dir(struct kobject *kobj)
    {
        const struct kobj_ns_type_operations *ops;
    
        sysfs_create_dir_ns(kobj, kobject_namespace(kobj)); /* 创建目录 */ 
        populate_dir(kobj); /* 创建属性文件 */
    
        sysfs_get(kobj->sd);
    
        ops = kobj_child_ns_ops(kobj);
        if (ops) {
            sysfs_enable_ns(kobj->sd);
        }
    }  
    
    /* 以kobj.name命名,在sysfs下创建一个目录 */
    int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
    {
        struct kernfs_node *parent, *kn;
    
        /* 指定创建的kobj目录所属的的父目录,默认为sysfs根目录 */
        if (kobj->parent)
            parent = kobj->parent->sd;
        else
            parent = sysfs_root_kn;
    
        /* 在parent目录下,创建以kobj.name命名的目录 */
        kn = kernfs_create_dir_ns(parent, kobject_name(kobj),
                      S_IRWXU | S_IRUGO | S_IXUGO, kobj, ns);
        kobj->sd = kn;
    }
    
    /* 对象可能是带有属性的,在kobject目录下创建以属性名命名的文件 */
    static int populate_dir(struct kobject *kobj)
    {
        struct kobj_type *t = get_ktype(kobj);
        struct attribute *attr;
        int i;
    
        if (t && t->default_attrs) {
            for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {
                sysfs_create_file(kobj, attr);
            }
        }
    }
    

    devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL)创建的模型:

    kobject_create_and_add()

    /* 动态创建一个kobject对象,并且加入到sysfs */
    struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
    {
        struct kobject *kobj;
    
        kobj = kobject_create();
        kobject_add(kobj, parent, "%s", name);
    
        return kobj;
    }
    
    struct kobject *kobject_create(void)
    {
        struct kobject *kobj;
    
        kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
        kobject_init(kobj, &dynamic_kobj_ktype);
        return kobj;
    }
    
    /* 初始化kobject结构体 */
    void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
    {
        kobject_init_internal(kobj);
        kobj->ktype = ktype;
    }
    
    int kobject_add(struct kobject *kobj, struct kobject *parent,
            const char *fmt, ...)
    {
        va_list args;
    
        va_start(args, fmt);
        kobject_add_varg(kobj, parent, fmt, args);
        va_end(args);
    }
    
    static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
                    const char *fmt, va_list vargs)
    {
        kobject_set_name_vargs(kobj, fmt, vargs);
        kobj->parent = parent;
        return kobject_add_internal(kobj);
    }
    
    /* 设置@kobj的name */
    int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
                      va_list vargs)
    {
        char *s;
    
        kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
    
        /* 名字中不能包含有'/'的字符,如果有,则直接截短 */
        while ((s = strchr(kobj->name, '/')))
            s[0] = '!';
    }
    

    dev_kobj = kobject_create_and_add("dev", NULL)创建的模型:

    sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj)
    sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj)创建的模型

    buses_init()

    buses_init()创建sysfs模型如下:

    /sys
    ├── bus
    └── devices
    └── system

    
    ```c
    static struct kset *bus_kset;
    static struct kset *system_kset; /* /sys/devices/system */
    
    int __init buses_init(void)
    {
        bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
        system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
    }
    

    bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL)创建的模型:

    system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj)创建的模型:

    platform_bus_init()

    platform_bus_init()完成虚拟设备platform,及虚拟总线platform的创建。

    /sys
    └── devices
    └── platform
    └── uevent /* -rw-r--r-- */

    
    ```c
    /* platform设备 */
    struct device platform_bus = {
        .init_name    = "platform",
    };
    
    /* platform总线 */
    struct bus_type platform_bus_type = {
        .name        = "platform",
        .dev_groups    = platform_dev_groups,
        .match        = platform_match,
        .uevent        = platform_uevent,
        .pm        = &platform_dev_pm_ops,
    };
    
    int __init platform_bus_init(void)
    {
        early_platform_cleanup();
        device_register(&platform_bus); /* 注册到devices目录中 */  
        bus_register(&platform_bus_type); /* 注册到bus目录中 */  
        of_platform_register_reconfig_notifier();
    }
    

    device_register()

    /* 注册设备:初始化设备的数据结构,将其加入到数据结构的网络中。 
       完成设备注册后,可以在/sys/devices目录中看到 */  
    int device_register(struct device *dev)  
    {  
        device_initialize(dev); /* 初始化dev结构 */  
        device_add(dev); /* 添加dev至sysfs */  
    }  
      
    void device_initialize(struct device *dev)  
    {  
        /* 将设备kobject的kset集合指向devices_kset */  
        dev->kobj.kset = devices_kset; 
        kobject_init(&dev->kobj, &device_ktype);
    }  
    
    #define DEVICE_ATTR_RW(_name) \
        struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
    
    #define __ATTR_RW(_name) __ATTR(_name, (S_IWUSR | S_IRUGO),        \
                 _name##_show, _name##_store)
    
    #define __ATTR(_name, _mode, _show, _store) {                \
        .attr = {.name = __stringify(_name),                \
             .mode = VERIFY_OCTAL_PERMISSIONS(_mode) },        \
        .show    = _show,                        \
        .store    = _store,                        \
    }
    
    static DEVICE_ATTR_RW(uevent); /* 定义dev_attr_uevent,其展开宏如下:
        struct device_attribute dev_attr_uevent {
            .attr = {
                .name = "uevent",
                .mode = (S_IWUSR | S_IRUGO),
            },
            .show = uevent_show,
            .store = uevent_store,
        }; */
      
    int device_add(struct device *dev)  
    {  
        struct device *parent = NULL;  
        struct kobject *kobj;
        struct class_interface *class_intf;  
        /* 将设备的引用计数加1 */
        dev = get_device(dev);
        /* 创建私有数据结构体,并与主结构体进行关联 */
        if (!dev->p) {
            device_private_init(dev);
        }
    
        /* 使用dev.init_name设置dev.kobj.name,并置空dev.init_name */
        if (dev->init_name) {
            dev_set_name(dev, "%s", dev->init_name);
            dev->init_name = NULL;
        }
      
        /* 如果kobject_add()的第二个参数parent为空,则以dev->kobj.kset.kobj作为父目录 */  
        kobject_add(&dev->kobj, dev->kobj.parent, NULL);  
    
        /* dev_attr_uevent由宏DEVICE_ATTR_RW()定义,在dev.kobj目录下创建以
           dev_attr_uevent.attr.name命名,权限为dev_attr_uevent.attr.mode的文件 */
        device_create_file(dev, &dev_attr_uevent); 
        /* 创建设备类型软链接 */
        device_add_class_symlinks(dev);
        bus_add_device(dev);
    }
    
    int bus_add_device(struct device *dev)
    {
        struct bus_type *bus = bus_get(dev->bus);
    
        if (bus) {
            device_add_attrs(bus, dev);
            device_add_groups(dev, bus->dev_groups);
            sysfs_create_link(&bus->p->devices_kset->kobj,
                            &dev->kobj, dev_name(dev));
            sysfs_create_link(&dev->kobj,
                    &dev->bus->p->subsys.kobj, "subsystem");
    
            klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
        }
    }
    

    bus_register()

    bus_register(&platform_bus_type)向系统添加一个新总线。

    /sys
    └── bus
    └── platform
    ├── devices
    ├── drivers
    ├── drivers_autoprobe /* -rw-r--r-- /
    ├── drivers_probe /
    --w------- /
    └── uevent /
    --w------- */

    
    ```c
    struct bus_type {  
        const char      * name; /* 总线的文本名称,用于在sysfs文件系统中标识总线 */  
      
        /* 试图查找与给定设备匹配的驱动程序 */  
        int     (*match)(struct device * dev, struct device_driver * drv);  
        int     (*uevent)(struct device *dev, struct kobj_uevent_env *env);  
        /* 在有必要将驱动程序关联到设备时,会调用probe。该函数检测设备在系统中是否 
           真正存在 */  
        int     (*probe)(struct device * dev);  
        /* 删除驱动程序和设备之间的关联。例如,在将可热挺拔的设备从系统中移除时,会 
           调用该函数 */  
        int     (*remove)(struct device * dev);  
    
        struct subsys_private *p;
    };  
      
    struct bus_type platform_bus_type = {
        .name        = "platform",
        .dev_groups    = platform_dev_groups,
        .match        = platform_match,
        .uevent        = platform_uevent,
        .pm        = &platform_dev_pm_ops,
    };
    
    static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store); /* 定义bus_attr_uevent
        ,其展开宏如下:
        struct bus_attribute bus_attr_uevent {
            .attr = {
                .name = "uevent",
                .mode = S_IWUSR,
            },
            .show = NULL,
            .store = bus_uevent_store,
        }; */
      
    /* 注册新的总线 */  
    int bus_register(struct bus_type *bus)
    {
        int retval;
        struct subsys_private *priv;
        struct lock_class_key *key = &bus->lock_key;
      
        /* 框架设计机制:虽然可以直接用单个结构体来表示一种设备,但是更好的做法是把设备的私
           有数据分离出来,每个设备用两个结构体表示,主结构体表示大家都存在的公共属性,并把
           成员指针指向另一个表示私有属性的结构体。这样分离的另一个好处是,代码函数设计上更
           加高内聚、低耦合。 */
        priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
        priv->bus = bus;
        bus->p = priv;
    
        /*  */
        kobject_set_name(&priv->subsys.kobj, "%s", bus->name);  
    
        priv->subsys.kobj.kset = bus_kset; /* 父目录指向bus_kset.kobj */
        priv->subsys.kobj.ktype = &bus_ktype;
        priv->drivers_autoprobe = 1;
    
        /* 初始化&priv->subsys,并加入到sysfs */
        kset_register(&priv->subsys);
    
        /* bus_attr_uevent由宏BUS_ATTR()定义,在priv.subsys.kobj目录下创建以
           bus_attr_uevent.attr.name命名,权限为bus_attr_uevent.attr.mode的文件 */
        bus_create_file(bus, &bus_attr_uevent);
    
        priv->devices_kset = kset_create_and_add("devices", NULL,
                             &priv->subsys.kobj);
    
        priv->drivers_kset = kset_create_and_add("drivers", NULL,
                             &priv->subsys.kobj);
    
        add_probe_files(bus); /* 创建drivers_probe与drivers_autoprobe文件 */
    
        bus_add_groups(bus, bus->bus_groups);
    }  
    

    内核设备模型框架

    driver_init()最终形成的模型框架,如下所示:

    /sys  
    ├── bus
    │     └── platform
    │            ├── devices
    │            ├── drivers
    │            ├── drivers_autoprobe /* -rw-r--r-- */
    │            ├── drivers_probe /* --w------- */
    │             └── uevent /* --w------- */    
    ├── dev
    │     ├── block 
    │     └── char               
    └── devices
          └── platform
                  └── uevent /* -rw-r--r-- */  
    

    相关文章

      网友评论

          本文标题:基于Atheros无线芯片的platform总线、设备、驱动、虚

          本文链接:https://www.haomeiwen.com/subject/auchpttx.html