!Warning:请确保能够访问图床Imgur,以正常显示图片
源码基线:linux-4.0-rc1 & OpenWrt 12.09 branch (Attitude Adjustment)
硬件:Atheros AR9331
1 设备模型概述
设备模型主要完成以下工作:
- 设备分类,以分层的架构对设备进行描述,隐藏设备内部的连接细节,对外清晰地展示可用的设备。
- 创建和管理设备的生命周期。
- 通过sysfs虚拟文件系统,向用户空间提供对设备的读写操作,获取设备的信息、改变设备的运行状态。
设备模型的结构组成:
1.总线。
所有的设备都通过总线相连,包括内部的虚拟总线。在内核中,用struct bus_type
结构体来表示总线。
struct bus_type {
const char * name;
struct kset drivers;
struct kset devices;
}
-
name
是总线的名字。 -
kset drivers
与kset 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_head
与hlist_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)
函数,可以把新节点添加到链表中。
-
空节点
-
添加第一个节点
-
添加第二个节点
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-- */