linux device注册
来源:互联网 发布:皮卡刻字机端口设置 编辑:程序博客网 时间:2024/06/01 10:07
device注册
谨以此文纪念过往的岁月。
device和driver是设备管理系统的核心,作为驱动工程师有必要去一探究竟。
1.device
对于上层而言device的注册很简单只要简单的去device_register就ok了,但是对于内核而言,他要去完成很多事情。
int device_register(struct device *dev)
{
device_initialize(dev); --负责设备初始化
return device_add(dev); --负责将设备加入设备链表
}
在设备管理中kset,kobject,以及ktype占据了很重要的地位。linux内核就是通过kset以及kobject将一个个设备关联起来。
1.1 device_initialize
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset; --设置设备的父kset为devices_kset,
kobject_init(&dev->kobj, &device_ktype); --这个函数在下面解释
klist_init(&dev->klist_children, klist_children_get,klist_children_put); --初始化子设备klist
INIT_LIST_HEAD(&dev->dma_pools); --初始化链表
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
下面的主要是对设备电源的管理,暂时不去理解
device_init_wakeup(dev, 0);
device_pm_init(dev);
set_dev_node(dev, -1);
}
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
kobject_init_internal(kobj);
kobj->ktype = ktype; --指向对象类型描述符的指针为device_ktype ,设置ktype
return;
}
static void kobject_init_internal(struct kobject *kobj)
{
if (!kobj)
return;
kref_init(&kobj->kref); --初始化kobject计数
INIT_LIST_HEAD(&kobj->entry);
kobj->state_in_sysfs = 0;
kobj->state_add_uevent_sent = 0;
kobj->state_remove_uevent_sent = 0;
kobj->state_initialized = 1; --将初始化状态置1
}
对于上层而言device的注册很简单只要简单的去device_register就ok了,但是对于内核而言,他要去完成很多事情。
int device_register(struct device *dev)
{
device_initialize(dev); --负责设备初始化
return device_add(dev); --负责将设备加入设备链表
}
在设备管理中kset,kobject,以及ktype占据了很重要的地位。linux内核就是通过kset以及kobject将一个个设备关联起来。
1.1 device_initialize
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset; --设置设备的父kset为devices_kset,
kobject_init(&dev->kobj, &device_ktype); --这个函数在下面解释
klist_init(&dev->klist_children, klist_children_get,klist_children_put); --初始化子设备klist
INIT_LIST_HEAD(&dev->dma_pools); --初始化链表
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
下面的主要是对设备电源的管理,暂时不去理解
device_init_wakeup(dev, 0);
device_pm_init(dev);
set_dev_node(dev, -1);
}
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
kobject_init_internal(kobj);
kobj->ktype = ktype; --指向对象类型描述符的指针为device_ktype ,设置ktype
return;
}
static void kobject_init_internal(struct kobject *kobj)
{
if (!kobj)
return;
kref_init(&kobj->kref); --初始化kobject计数
INIT_LIST_HEAD(&kobj->entry);
kobj->state_in_sysfs = 0;
kobj->state_add_uevent_sent = 0;
kobj->state_remove_uevent_sent = 0;
kobj->state_initialized = 1; --将初始化状态置1
}
上面是设备初始化,说白了就是将device中的kobject以及kset等成员初始化。对于kobject,kset,这些都是设备驱动的根本,linux用这些结构体来屏蔽各个设备的不同。
1.2 device_add
device_add函数比较长,但是其核心依然是kobject和kset,将一些错误处理以及创建文件删掉就看到了device_add的本质。
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error = -EINVAL;
dev = get_device(dev); --增加dev的计数,说到底还是增加dev->kobj->kref的计数
parent = get_device(dev->parent); --这个函数增加dev->parent的计数,同上
setup_parent(dev, parent); --设置dev->kobj.parent,对于这个device的parent还是比较麻烦的,其实device->kobj.parent不一定就是parent->kobj,其原因主要是
如果dev还属于某一个类,那他的parent应该是class的kobj,而不是parent,这时的parent其实升级为祖父了。不过在
一般情况下,dev->kobj.parent = &parent->kobj。具体是如何设置的,在下面仔细看。
error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev_name(dev)); 将设备的kobject加入dev->kobj.parent,至于如何加,下面看。
error = device_create_file(dev, &uevent_attr); --以后关于属性文件的创建,咱们就不管了,包括在sysfs下创建文件,因为首先需要了解的是设备在内核中
怎么去运转的。
error = bus_add_device(dev); --从函数名上理解是在总线下添加设备,其实仅仅是在/sys/bus下创建一些属性文件以及软连接。
device_pm_add(dev); --电源管理,不管
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_ADD_DEVICE, dev); --这个函数不太理解做什么用的。
kobject_uevent(&dev->kobj, KOBJ_ADD); --这个函数也是一个大的函数,在以后学习中来分析它,他的作用是调用kobject加入时所产生的事件。
bus_attach_device(dev); --这个就是用于设备探测驱动,实现设备与驱动的连接。怎么实现的下面看。
if (parent)
klist_add_tail(&dev->knode_parent, &parent->klist_children); --将节点连接到父设备的子链表上,这里的用词描述可能不准确。
if (dev->class) { --这个是针对设备有存在的类的时候使用。
mutex_lock(&dev->class->p->class_mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,&dev->class->p->class_devices);
1.2 device_add
device_add函数比较长,但是其核心依然是kobject和kset,将一些错误处理以及创建文件删掉就看到了device_add的本质。
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error = -EINVAL;
dev = get_device(dev); --增加dev的计数,说到底还是增加dev->kobj->kref的计数
parent = get_device(dev->parent); --这个函数增加dev->parent的计数,同上
setup_parent(dev, parent); --设置dev->kobj.parent,对于这个device的parent还是比较麻烦的,其实device->kobj.parent不一定就是parent->kobj,其原因主要是
如果dev还属于某一个类,那他的parent应该是class的kobj,而不是parent,这时的parent其实升级为祖父了。不过在
一般情况下,dev->kobj.parent = &parent->kobj。具体是如何设置的,在下面仔细看。
error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev_name(dev)); 将设备的kobject加入dev->kobj.parent,至于如何加,下面看。
error = device_create_file(dev, &uevent_attr); --以后关于属性文件的创建,咱们就不管了,包括在sysfs下创建文件,因为首先需要了解的是设备在内核中
怎么去运转的。
error = bus_add_device(dev); --从函数名上理解是在总线下添加设备,其实仅仅是在/sys/bus下创建一些属性文件以及软连接。
device_pm_add(dev); --电源管理,不管
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_ADD_DEVICE, dev); --这个函数不太理解做什么用的。
kobject_uevent(&dev->kobj, KOBJ_ADD); --这个函数也是一个大的函数,在以后学习中来分析它,他的作用是调用kobject加入时所产生的事件。
bus_attach_device(dev); --这个就是用于设备探测驱动,实现设备与驱动的连接。怎么实现的下面看。
if (parent)
klist_add_tail(&dev->knode_parent, &parent->klist_children); --将节点连接到父设备的子链表上,这里的用词描述可能不准确。
if (dev->class) { --这个是针对设备有存在的类的时候使用。
mutex_lock(&dev->class->p->class_mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,&dev->class->p->class_devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf,
&dev->class->p->class_interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->class_mutex);
}
put_device(dev); --减少设备计数。
return error;
}
以上就是一个设备的添加,他将kobject以及kset的处理封装成一个函数来使用。这个就需要向下去一个一个函数去理解。
其实上面的函数的核心就是:
int device_add(struct device *dev)
{
setup_parent(dev, parent); --实现设置device的真正父设备
kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev_name(dev)); --将设备添加到父设备链表中。
bus_attach_device(dev); --设备匹配驱动,函数名会定义为bus_attach_device,其实针对不同的总线,其匹配的规则则不同。
}
下面针对上面的几个函数来看。
第一个是设置parent,也许会很奇怪,既然device->parnet存在的话,那dev->kobj.parent = &parent->kobj,那为什么还会有这个函数的存在。但是万一parent不存在的
话,即是这个设备就是老大的时候,也应该是可以的。
static void setup_parent(struct device *dev, struct device *parent)
{
struct kobject *kobj;
kobj = get_device_parent(dev, parent); --获取父kobject
if (kobj)
dev->kobj.parent = kobj;
}
在一般情况下,其parent一般为总线设备。而在总线设备加载的时候,则parent为NULL。这个还需要在理解class的作用后才可以彻底的理解。
static struct kobject *get_device_parent(struct device *dev,struct device *parent)
{
int retval;
list_for_each_entry(class_intf,
&dev->class->p->class_interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->class_mutex);
}
put_device(dev); --减少设备计数。
return error;
}
以上就是一个设备的添加,他将kobject以及kset的处理封装成一个函数来使用。这个就需要向下去一个一个函数去理解。
其实上面的函数的核心就是:
int device_add(struct device *dev)
{
setup_parent(dev, parent); --实现设置device的真正父设备
kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev_name(dev)); --将设备添加到父设备链表中。
bus_attach_device(dev); --设备匹配驱动,函数名会定义为bus_attach_device,其实针对不同的总线,其匹配的规则则不同。
}
下面针对上面的几个函数来看。
第一个是设置parent,也许会很奇怪,既然device->parnet存在的话,那dev->kobj.parent = &parent->kobj,那为什么还会有这个函数的存在。但是万一parent不存在的
话,即是这个设备就是老大的时候,也应该是可以的。
static void setup_parent(struct device *dev, struct device *parent)
{
struct kobject *kobj;
kobj = get_device_parent(dev, parent); --获取父kobject
if (kobj)
dev->kobj.parent = kobj;
}
在一般情况下,其parent一般为总线设备。而在总线设备加载的时候,则parent为NULL。这个还需要在理解class的作用后才可以彻底的理解。
static struct kobject *get_device_parent(struct device *dev,struct device *parent)
{
int retval;
if (dev->class) { --如果设备类存在时
struct kobject *parent_kobj;
struct kobject *k;
if (parent == NULL) --如果没有parent则设备诞生于虚无,则直接就挂在最古老的设备下。
parent_kobj = virtual_device_parent(dev);
else if (parent->class) --如果父的类也存在的话,则直接返回父的kobj
return &parent->kobj;
else --父设备存在,而父类不存在。
parent_kobj = &parent->kobj;
struct kobject *parent_kobj;
struct kobject *k;
if (parent == NULL) --如果没有parent则设备诞生于虚无,则直接就挂在最古老的设备下。
parent_kobj = virtual_device_parent(dev);
else if (parent->class) --如果父的类也存在的话,则直接返回父的kobj
return &parent->kobj;
else --父设备存在,而父类不存在。
parent_kobj = &parent->kobj;
spin_lock(&dev->class->p->class_dirs.list_lock);
list_for_each_entry(k, &dev->class->p->class_dirs.list, entry)
if (k->parent == parent_kobj) {
kobj = kobject_get(k);
break;
}
spin_unlock(&dev->class->p->class_dirs.list_lock);
if (kobj)
return kobj;
k = kobject_create();
if (!k)
return NULL;
k->kset = &dev->class->p->class_dirs;
retval = kobject_add(k, parent_kobj, "%s", dev->class->name);
if (retval < 0) {
kobject_put(k);
return NULL;
return k;
}
list_for_each_entry(k, &dev->class->p->class_dirs.list, entry)
if (k->parent == parent_kobj) {
kobj = kobject_get(k);
break;
}
spin_unlock(&dev->class->p->class_dirs.list_lock);
if (kobj)
return kobj;
k = kobject_create();
if (!k)
return NULL;
k->kset = &dev->class->p->class_dirs;
retval = kobject_add(k, parent_kobj, "%s", dev->class->name);
if (retval < 0) {
kobject_put(k);
return NULL;
return k;
}
if (parent)
return &parent->kobj;
return NULL;
}
return &parent->kobj;
return NULL;
}
在上面函数中查找到device的父设备后就是kobject_add.在把函数清理后,就会发现
kobject_add->kobject_add_varg
static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,const char *fmt, va_list vargs)
{
int retval;
kobject_add->kobject_add_varg
static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,const char *fmt, va_list vargs)
{
int retval;
retval = kobject_set_name_vargs(kobj, fmt, vargs); --重新设置kobject的name
kobj->parent = parent; --设置parent
return kobject_add_internal(kobj);
}
kobject_add_internal这个函数是kobject_add的真正的实现,其余都浮云,不过那些浮云还不能不要。就是不是浮云的浮云。
static int kobject_add_internal(struct kobject *kobj)
{
int error = 0;
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) { --如果kset被设置了
if (!parent) --parent为NULL,不过在device_add调用中这个parent一定不是NULL,否则就错了。
parent = kobject_get(&kobj->kset->kobj);
kobj_kset_join(kobj); --将kobject添加到kset的链表
kobj->parent = parent; --这里是个冗余
}
err=create_dir(kobj); --创建kobj的文件夹。
kobj->state_in_sysfs = 1; --对初始状态赋值。
kobj->parent = parent; --设置parent
return kobject_add_internal(kobj);
}
kobject_add_internal这个函数是kobject_add的真正的实现,其余都浮云,不过那些浮云还不能不要。就是不是浮云的浮云。
static int kobject_add_internal(struct kobject *kobj)
{
int error = 0;
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) { --如果kset被设置了
if (!parent) --parent为NULL,不过在device_add调用中这个parent一定不是NULL,否则就错了。
parent = kobject_get(&kobj->kset->kobj);
kobj_kset_join(kobj); --将kobject添加到kset的链表
kobj->parent = parent; --这里是个冗余
}
err=create_dir(kobj); --创建kobj的文件夹。
kobj->state_in_sysfs = 1; --对初始状态赋值。
return error;
}
到此一个kobject就ok了,其正确而无误的添加到parent下了。
对于父亲责任完成了,下面该实现如何匹配驱动了。
void bus_attach_device(struct device *dev)
{
struct bus_type *bus = dev->bus; --下面是很理解的
int ret = 0;
if (bus) {
if (bus->p->drivers_autoprobe)
ret = device_attach(dev);
WARN_ON(ret < 0);
if (ret >= 0)
klist_add_tail(&dev->knode_bus, &bus->p->klist_devices);
}
}
}
到此一个kobject就ok了,其正确而无误的添加到parent下了。
对于父亲责任完成了,下面该实现如何匹配驱动了。
void bus_attach_device(struct device *dev)
{
struct bus_type *bus = dev->bus; --下面是很理解的
int ret = 0;
if (bus) {
if (bus->p->drivers_autoprobe)
ret = device_attach(dev);
WARN_ON(ret < 0);
if (ret >= 0)
klist_add_tail(&dev->knode_bus, &bus->p->klist_devices);
}
}
int device_attach(struct device *dev)
{
int ret = 0;
{
int ret = 0;
down(&dev->sem);
if (dev->driver) { --如果设备的driver存在则直接绑定。这个会根据不同的bus之不同match办法实现
ret = device_bind_driver(dev);
if (ret == 0)
ret = 1;
else {
dev->driver = NULL;
ret = 0;
}
} else { --如果不存在则,依次查询bus下的所有driver进行匹配。真正起作用的是__device_attach函数
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
}
up(&dev->sem);
return ret;
}
if (dev->driver) { --如果设备的driver存在则直接绑定。这个会根据不同的bus之不同match办法实现
ret = device_bind_driver(dev);
if (ret == 0)
ret = 1;
else {
dev->driver = NULL;
ret = 0;
}
} else { --如果不存在则,依次查询bus下的所有driver进行匹配。真正起作用的是__device_attach函数
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
}
up(&dev->sem);
return ret;
}
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
if (drv->bus->match && !drv->bus->match(dev, drv)) --这个match是每一个bus所特有的。实现设备与驱动匹配。
return 0;
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
down(&dev->sem);
if (!dev->driver) --如果dev->driver为NULL,则调用probe
driver_probe_device(drv, dev);
up(&dev->sem);
if (dev->parent)
up(&dev->parent->sem);
{
struct device_driver *drv = data;
if (drv->bus->match && !drv->bus->match(dev, drv)) --这个match是每一个bus所特有的。实现设备与驱动匹配。
return 0;
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
down(&dev->sem);
if (!dev->driver) --如果dev->driver为NULL,则调用probe
driver_probe_device(drv, dev);
up(&dev->sem);
if (dev->parent)
up(&dev->parent->sem);
return 0;
}
函数driver_probe_device ->really_probe
这个函数是真正的探测
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
atomic_inc(&probe_count);
dev->driver = drv;
if (driver_sysfs_add(dev)) {
return 0;
}
if (dev->bus->probe) {
ret = dev->bus->probe(dev); --这个是总线probe
if (ret)
goto probe_failed;
} else if (drv->probe) { --这个会调用真正的设备probe
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
driver_bound(dev); --驱动绑定。
ret = 1;
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
}
函数driver_probe_device ->really_probe
这个函数是真正的探测
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
atomic_inc(&probe_count);
dev->driver = drv;
if (driver_sysfs_add(dev)) {
return 0;
}
if (dev->bus->probe) {
ret = dev->bus->probe(dev); --这个是总线probe
if (ret)
goto probe_failed;
} else if (drv->probe) { --这个会调用真正的设备probe
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
driver_bound(dev); --驱动绑定。
ret = 1;
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
0
上一篇:linux class device
下一篇:学习git笔记,git,最强大的版本管理工具,没有之一。
相关热门文章
- 欢迎为下载附件而注册在ChinaU...
- 欢迎Lyric_vfast_linux在China...
- Linux内核【链表】整理笔记(1)...
- hub驱动注册流程
- Linux GCC常用命令
- linux 常见服务端口
- 【ROOTFS搭建】busybox的httpd...
- xmanager 2.0 for linux配置
- 什么是shell
- linux socket的bug??
- 现在的博客积分不会更新了吗?...
- shell怎么读取网页内容...
- ssh等待连接的超时问题...
- curl: (56) Recv failure: Con...
- CACTI 不能安装WINE,怎么办?...
给主人留下些什么吧!~~
评论热议
0 0
- linux device注册
- linux device注册
- linux device注册
- linux 内核驱动--Platform Device和Platform_driver注册过程
- linux 内核驱动--Platform Device和Platform_driver注册过程
- Linux Device和Driver注册过程,以及Probe的时机
- Linux Device和Driver注册过程,以及Probe的时机。
- Linux Device和Driver注册过程,以及Probe的时机
- Linux Device和Driver注册过程,以及Probe的时机
- Linux Device和Driver注册过程,以及Probe的时机
- Linux Device和Driver注册过程,以及Probe的时机
- Linux Platform Device and Driver的注册过程解析
- Linux Device和Driver注册过程,以及Probe的时机
- Linux Device和Driver注册过程中的Probe时机
- Linux Device和Driver注册过程,以及Probe的时机
- Linux Device和Driver注册过程,以及Probe的时机
- Linux Device和Driver注册过程,以及Probe的时机
- Linux Device和Driver注册过程,以及Probe的时机
- linux bus总线
- 关于cdev platform以及class的一点见解
- linux cdev详解
- linux bus总线
- linux class device
- linux device注册
- 学习git笔记,git,最强大的版本管理工具,没有之一。
- 模电的一些零碎问题,不定期更新
- 1.the linux device model--kobject kset
- 2.the linux device model--bus device driver
- LCS LIS LCIS 字符串编辑距离 专题
- python多线程编程(五)
- 最小区间
- 2014秋C++第10周项目2参考-M$pszi$y是嘛意思
原创粉丝点击
热门IT博客
热门问题
老师的惩罚
人脸识别
我在镇武司摸鱼那些年
重生之率土为王
我在大康的咸鱼生活
盘龙之生命进化
天生仙种
凡人之先天五行
春回大明朝
姑娘不必设防,我是瞎子
wow侏儒
侏儒街
干侏儒mp4
欧美侏儒
侏儒症图片
wow矮人侏儒
侏儒曰侏儒
机械侏儒
侏儒拼音
侏儒的拼音
侏儒工程学
侏儒一节
干老侏儒在线播放
干侏儒在线播放
地精工程学侏儒工程学
玩一个侏儒女
侏儒工程学和地精工程学区别
老婆与男侏儒在线播放
侏儒兔怎么养
侏儒症在婴幼儿期有何表现
怀旧服侏儒法师升级路线
侏儒症身高一般多少
侏儒死亡射线
侏儒兔能长多大
侏儒症的原因
侏儒症大概几岁能发现
为什么淘宝侏儒兔那么便宜
侏儒症遗传吗
侏儒症胎儿股骨状态
侏儒工程学在哪里学
wow矮人侏儒x0x0
女人如雾张妍侏儒
18videosgratis侏儒
朝阳水野与三个侏儒
怀旧服工程学地精还是侏儒
侑
三侑
柳侑绮
壹原侑子
侑怎么读
忍足侑士