linux设备驱动模型二【转】
本文转载自:http://blog.csdn.net/u013904227/article/details/51167886
版权声明:本文为博主原创文章,未经博主允许不得转载。
1、总线概念
总线是内核与一个或者多个设备之间的通道。在设备模型中,所有的设备都是通过总线连接
2、sysfs
作用:
一个用于内核对象表述的文件系统
sysfs是一个基于ramfs的内存文件系统. 描述内核数据结构,属性以及与它们用户空间的链接关系sys文件夹内含的顶层文件夹(使用
ls -l /sys
可以查看)- block/
- bus/ :包含内核中所有总线类型的描述. 每一个总线文件夹下面都有下面两类文件夹
- devices/ :内核中所有注册的设备,内含的设备都是指向sys目录下面devices目录内设备的链接文件
- drivers/ :所有与注册设备相匹配的驱动程序
总线设备驱动总是会分出设备与驱动,也就是设备、驱动分离分层的概念,在注册的时候总会进行设备与驱动的匹配,内核总线会提供各自的match函数以供此类总线设备驱动调用 - class/
- devices/ : 设备树的文件系统表述. 映射了内核的设备结构层次,包含内核所有的注册设备
- firmware/
- net/
- fs/ : 包含一些文件系统的目录.想要表述其属性的文件系统都必须在fs目录下创建自己的层次
3、kobject
每个加载到内核的kobject结构体都具体化为一个sys或者其子目录下的一个文件夹
Kobject
一个基础结构,用于保持设备模型在一起,Kobject最初是用来简单的引用计数的,但是随着时间的发展已经增加了很多的功能
- 对象的引用计数
当一个内核对象被创建的时候,往往不能够确定这个对象的声明周期,Kobject被用作对象声明周期的跟踪,当对象计数完毕之后且没有内核进行引用,则该对象就可以被删除(正在使用的模块不能够删除应该与此有关) - sysfs的表示
在sys下面的每一个对象都对应一个Kobject结构,用来创建其可视化的表示 - 数据结构粘和
整体来看, 设备模型是一个极端复杂的由多级组成的数据结构, 各级之间有许多连接。kobject 实现这个结构并且保持它在一起 - 热拔插事件的处理
Kobject负责事件的产生,而事件则负责通知内核用户空间硬件的接入与卸载
Kobject结构体原型(不同版本的内核原型可能不一样)
struct kobject {
const char * k_name; //Kobject静态名字
char name[KOBJ_NAME_LEN]; //Kobject名字
struct kref kref;
struct list_head entry; //list_head结构体
struct kobject * parent; //父结构体,往往是指向容纳kobject的kset中的kobject结构体
struct kset * kset; //kset结构体
struct kobj_type * ktype; //ktype,内包含kobject的relase函数指针
struct dentry * dentry;
wait_queue_head_t poll; //等待队列的头部
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
使用Kobject
由于C语言没有继承这一特性,所以需要另一种方法实现,就是在结构体里面嵌入另一个结构体来实现对上一个对象的继承,要从嵌入的结构体找到被嵌入的结构体需要使用container_of
宏,这个是与list_head一样的原理,详见list_head结构体详解
- 初始化清零Kobject
使用memset函数对Kobject结构体进行初始化清零操作,否则会产生意想不到的程序崩溃 - 初始化一个Kobject
void kobject_init(struct kobject *kobj);
- 1
- 1
- 设置Kobject的名字(必须的,需要用在sysfs的入口)
int kobject_set_name(struct kobject *kobj, const char *fmt, ...);
va_start(args,fmt); //初始化一个char型指针,指向本函数最后一个参数的结尾处并且32字节对齐
(void)((args) = (((char *) &(fmt)) + (((sizeof (fmt)) + (31)) & (~(31)))))
- 1
- 2
- 3
- 1
- 2
- 3
- 设置其他应当设置的 kobject 成员
ktype, kset, parent.
Kobject引用计数
struct kobject *kobject_get(struct kobject *kobj); //增加一个计数
void kobject_put(struct kobject *kobj); //减掉一个计数
- 1
- 2
- 1
- 2
当一个Kobject计数为0时,代表此对象已经到达生命周期的尽头,此时就需要一个Kobject的释放函数,而这个释放函数必须存在到其被调用为止,但是又由于这个Kobject释放函数并不能嵌入kobject结构体本身,所以就有了kobj_type这个结构体,此结构体往往可能会在两个地方给出,一个是kobject内部的kobj_type,另一个是包含该kobject的kset给出,已知该kobject的话,可以使用struct kobj_type *get_ktype(struct kobject *kobj);
来获取其kobj_type结构体
4、kset
很多情况下,kset被看做是kobj_type的扩展,用来表示被嵌入到同一类型结构的kobject集合。但是kobj_type关注的是特定一个对象,而kset关注的是一整个集合
struct kset {
struct kobj_type * ktype; //内含的kobj_type,用于指示kobject的kobj_type,优先于kobject自身含有的kobj_type
struct list_head list; //用于串联kset的list_head双向循环链表
spinlock_t list_lock;
struct kobject kobj; //内含的kobject
struct kset_uevent_ops * uevent_ops; //
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
增加一个kobject到kset
此时该objetc计数会增加1,因为这是对kobject的一个引用
extern int kobject_register(struct kobject *kobj);
//此函数是kobject_init 和 kobject_add 的结合
- 1
- 2
- 1
- 2
从kset删掉一个kobject
void kobject_del(struct kobject *kobj); //或者
extern void kset_unregister(struct kset * k);
//后者是下述两个函数的结合
extern void kobject_del(struct kobject *);
extern void kobject_put(struct kobject *);
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
kset提供类似于kobject的接口函数
extern void kset_init(struct kset * k);
extern int __must_check kset_add(struct kset * k);
extern int __must_check kset_register(struct kset * k);
extern void kset_unregister(struct kset * k);
/* 减少一个计数 */
static inline struct kset * kset_get(struct kset * k)
{
return k ? to_kset(kobject_get(&k->kobj)) : NULL;
}
/* 增加一个计数 */
static inline void kset_put(struct kset * k)
{
kobject_put(&k->kobj);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
5、低级sysfs操作
kobjects 的 sysfs 入口一直为目录, 因此一个对 kobject_add 的调用导致在sysfs 中创建一个目录. 常常地, 这个目录包含一个或多个属性
分配给 kobject 的名字( 用
kobject_set_name
) 是给 sysfs 目录使用的名字. 因此, 出现在 sysfs 层次的相同部分的 kobjects必须有唯一的名字. 分配给 kobjects 的名字也应当是合理的文件名字: 它们不能包含斜线字符, 不能为空sysfs 入口位于对应 kobject 的 parent 指针的目录中. 如果 parent 是 NULL 当
kobject_add
被调用时, 它被设置为嵌在新 kobject 的kset 中的 kobject; 因此, sysfs 层级常常匹配使用 kset 创建的内部层次. 如果 parent 和 kset 都是 NULL, sysfs 目录将在顶级被创建
//每个kobject被创建的时候都会有一个缺省的属性,由kobj_type里面的struct attribute ** default_attrs;指定
/* struct attribute原型 */
struct attribute {
const char * name;
struct module * owner;
mode_t mode;
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
//属性类别
#define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO)
#define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH)
#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH)
#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
/* attrbute只是负责指明属性的类型,而实现这些属性的工作就交给了sysfs_ops */
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *,char *);
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
};
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
- 添加一个属性到kobject
int sysfs_create_file(struct kobject *kobj, struct attribute *attr);
- 1
- 1
- 删除属性
int sysfs_remove_file(struct kobject *kobj, struct attribute *attr);
- 1
- 1
- 创建一个符号链接
int sysfs_create_link(struct kobject *kobj, struct kobject *target, char *name);
- 1
- 1
- 删除一个符号链接
void sysfs_remove_link(struct kobject *kobj, char *name);
- 1
- 1
6、mybus的编写实现
头文件
#ifndef _MYBUS_H_
#define _MYBUS_H_
#include <linux/device.h>
/* 定义mybus的设备结构体 */
struct mybus_device {
const char * name;
struct device dev;
struct mybus_driver *drv;
};
/* 由mybus设备里面的设备结构体找到该mybus,详见list_head结构体注解 */
#define to_mybus_device(_dev) container_of(_dev, struct mybus_device, dev)
/* 定义mybus驱动 */
struct mybus_driver {
int (*probe)(struct mybus_device *);
int (*remove)(struct mybus_device *);
void (*shutdown)(struct mybus_device *);
int (*suspend)(struct mybus_device *, pm_message_t state);
int (*resume)(struct mybus_device *);
struct device_driver driver;
};
/* 同上面的 to_mybus_device */
#define to_mybus_driver(drv) container_of(drv, struct mybus_driver, driver)
extern int register_mybus_device(struct mybus_device *mybusdev);
extern void unregister_mybus_device(struct mybus_device *mybusdev);
extern int register_mybus_driver(struct mybus_driver *mybusdrv);
extern void unregister_mybus_driver(struct mybus_driver *mybusdrv);
#endif /* end _MYBUS_H_ */
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
C文件
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include "/work/testfile/ARM9_Pro/mybus/mybus.h"
/* mybus卸载时候会调用到 */
static void mybus_relase(struct device * dev)
{
printk("mybus relase \n");
}
/* 总线设备 */
struct device my_bus = {
.bus_id = "mybus",
.release = mybus_relase
};
/* 设备与设备驱动匹配函数 */
static int mybus_match(struct device * dev, struct device_driver * drv)
{
struct mybus_device *pdev = to_mybus_device(dev);
printk("mybus_match is called\n"); //输出打印信息,说明此函数被调用
return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
}
/* mybus总线热拔插事件 */
static int mybus_event(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
{
envp[0] = buffer;
snprintf(buffer, buffer_size, "LDDBUS_VERSION=%s", mybus_version);
return 0;
}
/* 定义一个总线结构体 */
struct bus_type mybus_type = {
.name = "mybus",
.match = mybus_match,
.uevent = mybus_event,
};
/* mybus 的设备与设备驱动操作函数 */
/* mybus 设备相关函数 */
static void mybus_device_release(struct device * dev)
{
printk("mybus_device relase \n");
}
int register_mybus_device(struct mybus_device *mybusdev)
{
mybusdev->dev.bus = &mybus_type; //设置该设备的总线为mybus总线类型
mybusdev->dev.parent = &my_bus; //设置该设备的父设备为my_bus设备
mybusdev->dev.release = mybus_device_release; //设备释放
strncpy(mybusdev->dev.bus_id, mybusdev->name, BUS_ID_SIZE); //拷贝名字
return device_register(&mybusdev->dev); //注册该设备
}
EXPORT_SYMBOL(register_mybus_device);
void unregister_mybus_device(struct mybus_device *mybusdev)
{
device_unregister(&mybusdev->dev); //卸载设备
}
EXPORT_SYMBOL(unregister_mybus_device);
/* mybus设备驱动相关的函数 */
static int mybus_driver_probe(struct device *_dev)
{
struct mybus_driver *drv = to_mybus_driver(_dev->driver);
struct mybus_device *dev = to_mybus_device(_dev);
return drv->probe(dev);
}
static int mybus_driver_remove(struct device *_dev)
{
struct mybus_driver *drv = to_mybus_driver(_dev->driver);
struct mybus_device *dev = to_mybus_device(_dev);
return drv->remove(dev);
}
static void mybus_driver_shutdown(struct device *_dev)
{
struct mybus_driver *drv = to_mybus_driver(_dev->driver);
struct mybus_device *dev = to_mybus_device(_dev);
drv->shutdown(dev);
}
static int mybus_driver_suspend(struct device *_dev, pm_message_t state)
{
struct mybus_driver *drv = to_mybus_driver(_dev->driver);
struct mybus_device *dev = to_mybus_device(_dev);
return drv->suspend(dev, state);
}
static int mybus_driver_resume(struct device *_dev)
{
struct mybus_driver *drv = to_mybus_driver(_dev->driver);
struct mybus_device *dev = to_mybus_device(_dev);
return drv->resume(dev);
}
int register_mybus_driver(struct mybus_driver *mybusdrv)
{
if(mybusdrv->probe)
mybusdrv->driver.probe = mybus_driver_probe;
if(mybusdrv->remove)
mybusdrv->driver.remove = mybus_driver_remove;
if(mybusdrv->shutdown)
mybusdrv->driver.shutdown = mybus_driver_shutdown;
if(mybusdrv->suspend)
mybusdrv->driver.suspend = mybus_driver_suspend;
if(mybusdrv->resume)
mybusdrv->driver.resume = mybus_driver_resume;
mybusdrv->driver.bus = &mybus_type; //设置设备驱动的总线为mybus总线
return driver_register(&mybusdrv->driver); //注册设备驱动
}
EXPORT_SYMBOL(register_mybus_driver);
void unregister_mybus_driver(struct mybus_driver *mybusdrv)
{
driver_unregister(&mybusdrv->driver); //卸载设备驱动
}
EXPORT_SYMBOL(unregister_mybus_driver);
/* mybus总线初始化 */
static int mybus_init(void)
{
int error;
error = bus_register(&mybus_type); //注册mybus总线
if(error){
bus_unregister(&mybus_type);
printk("Can't register mybus_type bus_type\n");
return error;
}
// 此处简化创建,没有使用属性,创建之后会自动被设置为默认属性
// if(bus_create_file(&mybus_type, &mybus_attribute)){
// printk("Can't create mybus version attribute\n");
// }
error = device_register(&my_bus); //mybus设备注册,所有的从属于mybus总线的设备都会被挂入此设备链表
if(error){
device_unregister(&my_bus);
printk("Can't register my_bus device\n");
return error;
}
return 0;
}
/* mybus总线卸载 */
static void mybus_exit(void)
{
device_unregister(&my_bus);
// bus_remove_file(&mybus_type, &mybus_attribute);
// 此处简化创建,没有使用属性,创建之后会自动被设置为默认属性
bus_unregister(&mybus_type);
}
module_init(mybus_init);
module_exit(mybus_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("YellowMax");
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
8、mybus与驱动的联合测试
led_dev测试程序
#include "/work/testfile/ARM9_Pro/mybus/mybus.h"
static void led_dev_release(struct device * dev)
{
printk("led_dev relase \n");
}
static struct mybus_device led_dev = {
.name = "by_led",
.dev = {
.release = led_dev_release,
},
};
static int led_dev_init(void)
{
int error;
error = register_mybus_device(&led_dev);
if(error){
printk("register led_dev failed\n");
unregister_mybus_device(&led_dev);
return error;
}
return 0;
}
static void led_dev_exit(void)
{
unregister_mybus_device(&led_dev);
}
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
led_drv测试程序
#include "/work/testfile/ARM9_Pro/mybus/mybus.h"
static int led_drv_probe(struct platform_device *p_dev)
{
/* 输出打印信息,说明两者成功匹配 */
printk("Found by_led, Connect success\n");
return 0;
}
static int led_drv_remove(struct platform_device *p_dev)
{
/* 输出打印信息,说明成功卸载设备 */
printk("Found by_led, Remove success\n");
return 0;
}
static struct mybus_driver led_drv = {
.probe = led_drv_probe,
.remove = led_drv_remove,
.driver = {
.name = "by_led",
},
};
static int led_drv_init(void)
{
register_mybus_driver(&led_drv);
return 0;
}
static void led_drv_exit(void)
{
unregister_mybus_driver(&led_drv);
}
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
联合测试
- 装载led_drv.ko查看现象(由于没有注册总线设备,所以会出现函数未定义错误)
insmod led_drv.ko led_drv: Unknown symbol register_mybus_driver led_drv: Unknown symbol unregister_mybus_driver insmod: cannot insert 'led_drv.ko': Unknown symbol in module (-1): No such file or directory |
- 装载mybus.ko并且查看/sys/bus/
insmod mybus.ko ls -l /sys/bus/ drwxr-xr-x 4 0 0 0 Jan 1 00:01 mybus |
- 查看设备
ls -l /sys/devices/ drwxr-xr-x 3 0 0 0 Jan 1 00:01 mybus |
- 再次装载led_drv.ko并查看mybus里面的驱动
insmod led_drv.ko ls -l /sys/bus/mybus/drivers drwxr-xr-x 2 0 0 0 Jan 1 00:12 by_led |
- 装载led_dev.ko
insmod led_dev.ko mybus_match is called //调用到了mybus.c里面的相关match函数 Found by_led, Connect success //调用到led_drv.c里面的probe相关函数 |
- 装载其他模块
insmod led_drv1.ko insmod led_drv2.ko insmod led_dev2.ko mybus_match is called mybus_match is called mybus_match is called Found by_led2, Connect success |
由此可见,mybus.c里面的match相关函数会被内核不断地调用,直到找到匹配的设备或者驱动(测试函数同上,只不过修改一下设备以及驱动名字)。我装载了3个驱动,一共找了三次,基本可以说明这些设备是挂载在mybus总线设备上面的,找的时候直接去mybus总线找,
insmod led_dev1.ko mybus_match is called mybus_match is called Found by_led1, Connect success |
- 全部装载完毕并且查看相关的设备以及设备驱动
ls -l /sys/bus/mybus/drivers drwxr-xr-x 2 0 0 0 Jan 1 00:12 by_led drwxr-xr-x 2 0 0 0 Jan 1 00:13 by_led1 drwxr-xr-x 2 0 0 0 Jan 1 00:13 by_led2 ls -l /sys/bus/mybus/devices/ lrwxrwxrwx 1 0 0 0 Jan 1 00:15 by_led -> ../../../devices/mybus/by_led lrwxrwxrwx 1 0 0 0 Jan 1 00:15 by_led1 -> ../../../devices/mybus/by_led1 lrwxrwxrwx 1 0 0 0 Jan 1 00:15 by_led2 -> ../../../devices/mybus/by_led2 |
- 尝试移除mybus
rmmod mybus rmmod: mybus: Resource temporarily unavailable rmmod led_dev1 Found by_led, Remove success mybus_device relase rmmod led_dev Found by_led, Remove success mybus_device relase rmmod led_dev2 Found by_led, Remove success mybus_device relase rmmod led_drv rmmod led_drv1 rmmod led_drv2 |
- 再次尝试移除mybus,调用到了mybus.c里面的
static void mybus_relase(struct device * dev)
函数,mybus设备引用计数达到0
rmmod mybus mybus relase |
9、分析mybus过程
/* 初始化注册一个bus总线 */
int bus_register(struct bus_type * bus)
retval = kobject_set_name(&bus->subsys.kobj, "%s", bus->name); //将总线对应为mybus子系统的Kobject结构
/* 将Kobject放入一个kset容器,并且注册 */
subsys_set_kset(bus, bus_subsys);
retval = subsystem_register(&bus->subsys);
/* 设置一个名字为devices的Kobject,然后将其父结构指向上面构建的mybus的Kobject */
kobject_set_name(&bus->devices.kobj, "devices");
bus->devices.kobj.parent = &bus->subsys.kobj;
/* 初始化bus总线的device链表与driver链表,device链表添加get(增加一个设备引用计数),put(减少一个设备引用计数)
* 之后在此bus总线里面加入设备以及驱动的时候就可以通过该bus总线的设备链与设备驱动链进行查找了
*/
klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&bus->klist_drivers, NULL, NULL);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
/* 提供该bus总线的设备注册 */
int register_mybus_device(struct mybus_device *mybusdev)
/* 结构体的填充 */
mybusdev->dev.bus = &mybus_type; //设置该设备的总线为mybus总线类型
mybusdev->dev.parent = &my_bus; //设置该设备的父设备为my_bus设备
mybusdev->dev.release = mybus_device_release; //设备释放
strncpy(mybusdev->dev.bus_id, mybusdev->name, BUS_ID_SIZE); //拷贝名字
return device_register(&mybusdev->dev); //注册该设备
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
/* 提供该bus总线的设备驱动注册 */
int register_mybus_driver(struct mybus_driver *mybusdrv)
if(mybusdrv->probe)
mybusdrv->driver.probe = mybus_driver_probe;
if(mybusdrv->remove)
mybusdrv->driver.remove = mybus_driver_remove;
//省略若干
mybusdrv->driver.bus = &mybus_type; //设置设备驱动的总线为mybus总线
return driver_register(&mybusdrv->driver); //注册设备驱动
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
10、总线设备以及总线设备驱动的注册
分别由device_register与driver_register实际完成注册
在两者的函数里面都会有试图获得该注册对象的bus的语句,如果获得成功的话,不仅在系统顶层的sys/devices里面创建对象Kobject,而且在对应的bus里面也创建Kobject用来计数以及以文件形式显示出来设备。同一个被注册对象可能会被不同的链表链接,在任意一个链表里面都可以找到该对象。就比如一个总线设备,在其bus总线的设备列表里面可以找得到,在系统的devices链表里面也可以找得到
linux设备驱动模型二【转】的更多相关文章
- LINUX设备驱动模型之class
转自 https://blog.csdn.net/qq_20678703/article/details/52754661 1.LINUX设备驱动模型中的bus.device.driver,.其中bu ...
- linux设备驱动模型之Kobject、kobj_type、kset【转】
本文转载自:http://blog.csdn.net/fengyuwuzu0519/article/details/74838165 版权声明:本文为博主原创文章,转载请注明http://blog.c ...
- Linux设备驱动模型之platform(平台)总线详解
/********************************************************/ 内核版本:2.6.35.7 运行平台:三星s5pv210 /*********** ...
- Linux设备驱动模型底层架构及组织方式
1.什么是设备驱动模型? 设备驱动模型,说实话这个概念真的不好解释,他是一个比较抽象的概念,我在网上也是没有找到关于设备驱动模型的一个定义,那么今天就我所学.所了解 到的,我对设备驱动模型的一个理解: ...
- 探究linux设备驱动模型之——platform虚拟总线(一)
说在前面的话 : 设备驱动模型系列的文章主要依据的内核版本是2.6.32的,因为我装的Linux系统差不多就是这个版本的(实际上我用的fedora 14的内核版本是2.6.35.13的.) ...
- Linux 设备驱动模型
Linux系统将设备和驱动归一到设备驱动模型中了来管理 设备驱动程序功能: 1,对硬件设备初始化和释放 2,对设备进行管理,包括实参设置,以及提供对设备的统一操作接口 3,读取应用程序传递给设备文件的 ...
- Linux设备驱动模型简述(源码剖析)
1. Linux设备驱动模型和sysfs文件系统 Linux内核在2.6版本中引入设备驱动模型,简化了驱动程序的编写.Linux设备驱动模型包含设备(device).总线(bus).类(class)和 ...
- linux设备驱动模型
尽管LDD3中说对多数程序员掌握设备驱动模型不是必要的,但对于嵌入式Linux的底层程序员而言,对设备驱动模型的学习非常重要. Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一个对系统 ...
- linux设备驱动模型(kobject与kset)
Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一个对系统结构的一般性抽象描述.换句话说,Linux设备模型提取了设备操作的共同属性,进行抽象,并将这部分共同的属性在内核中实现,而为需要 ...
随机推荐
- Vue课程思维导图
- Java线程处理
Java线程处理 创建线程 继承Thread类 public class TestThread extends Thread{ public void run(){ System.out.printl ...
- js继承的方式
深入理解继承的实现方式不仅仅有利于自己去造轮子,封装插件,更有利于我们去阅读一些框架的源码, 以下记录几种常见的继承方式 1. 原型链实现继承 function Father(){ this.name ...
- Android studio 开发一个用户登录界面
Android studio 开发一个用户登录界面 activity_main.xml <?xml version="1.0" encoding="utf-8&qu ...
- 40条常见的移动端Web页面问题解决方案
1.安卓浏览器看背景图片,有些设备会模糊.想让图片在手机里显示更为清晰,必须使用2x的背景图来代替img标签(一般情况都是用2倍).例如一个div的宽高是100100,背景图必须得200200,然后b ...
- FileWriter实现从一个文件中读取内容并写到另一个文件中
FileWriter和FileOutputStream都是向文件写内容,区别是前台一次写一个字符,后者一次写一个字节 package com.janson.day20180827; import ja ...
- CentOS 6磁盘配额
可以指定用户能超过其配额限制.如果不想拒绝用户对卷的访问但想跟踪每个用户的磁盘空间使用情况,启用配额而且不限制磁盘空间的使用是非常有用的.也可指定不管用户超过配额警告级别还是超过配额限制时是否要记录事 ...
- Python爬虫入门教程: 27270图片爬取
今天继续爬取一个网站,http://www.27270.com/ent/meinvtupian/ 这个网站具备反爬,so我们下载的代码有些地方处理的也不是很到位,大家重点学习思路,有啥建议可以在评论的 ...
- 洛谷 1894 [USACO4.2]完美的牛栏The Perfect Stall
[题解] 其实是个二分图最大匹配的模板题,直接上匈牙利算法就好了. #include<cstdio> #include<algorithm> #define N 1010 #d ...
- MySQL 乐观锁和悲观锁
前言 1)在数据库的锁机制中介绍过,数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和一致性以及数据库的一致性. 2)加锁是为了解决更新丢失问题 ...