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中的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);

/* 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;

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;

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;

}

if (parent)

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;

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;     --对初始状态赋值。

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);

}

}

int device_attach(struct device *dev)

{

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;

}

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);

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;

}

linux怎么注册信息,linux device注册相关推荐

  1. linux查看显卡核心数,linux查看硬件信息,linux查看硬盘信息,linux查看CPU信息,linux查看显卡,硬件型号信息 | 帮助信息-动天数据...

    linux查看硬件信息,linux查看硬盘信息,linux查看CPU信息,linux查看显卡,硬件型号信息 作者:dthost | 时间:2015-09-30 | 8,325 次阅读 linux服务器 ...

  2. linux查看显示器名称命令,linux 查看显示器信息Linux下查看硬件信息命令大全

    /proc 虚拟的目录,是系统内存的映射.可直接访问这个目录来获取系统信息.其中也包含下面的信息: 主机CPU信息:cpuinfo 主机DMA通道信息:dma 文件系统信息:filesystems 主 ...

  3. 隐藏linux操作系统版本信息,linux centos 如何查看操作系统版本信息?

    本文介绍常用的四种查看linux下查看系统版本信息的方法: 一.uname -a [app@VM_11_211_centos ~]$ uname -a Linux VM_11_211_centos 2 ...

  4. java 注册成功跳转,写了个注册页面填了注册信息后点注册按钮居然不跳转,为什么?...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 JSP部分代码(包括判断注册信息是否填写完整的JS代码,在JSP代码的最后面) 会员登录 LOGIN 填写帐户信息 1 注册完成 2 *用户名: *设置密 ...

  5. 获取linux命令硬盘信息,Linux下如何获取磁盘信息

    HUX布斯 1,Linux下可以在/proc/cpuinfo中看到每个cpu的详细信息.但是对于双核的cpu,在cpuinfo中会看到两个cpu.常常会让人误以为是两个单核的cpu.其实应该通过Phy ...

  6. linux删除配置信息,linux 配置信息

    # uname -a # 查看内核/操作系统/CPU信息 # head -n 1 /etc/issue # 查看操作系统版本 # cat /proc/cpuinfo # 查看CPU信息 # hostn ...

  7. linux安装版本信息,Linux下查看Nginx安装目录、版本号信息?

    Linux环境下,怎么确定Nginx是以那个config文件启动的? 输入命令行: ps  -ef | grep nginx 摁回车,将出现如下图片: master process 后面的就是 ngi ...

  8. linux显示今日信息,linux lsmod命令详解

    lsmod  (list modules) 语 法:lsmod 功          能: lsmod 命令:是一个小程序,用来显示文件.proc/modules的信息,也就是显示当前内核模块装载的模 ...

  9. arm linux 时钟源 信息,Linux时间子系统之一:clock source(时钟源)

    clock source用于为linux内核提供一个时间基线,如果你用linux的date命令获取当前时间,内核会读取当前的clock source,转换并返回合适的时间单位给用户空间.在硬件层,它通 ...

最新文章

  1. 浅析Hyperledger Fabric共识算法
  2. 华为用MySQL还是oracle_25.Oracle和Mysql的区别
  3. ifstream java_C ifstream将读取一些值然后停止
  4. 程序员面试宝典(Java)Beta6.0免费下载
  5. hiho一下 第六周 Hihocoder #1038 : 01背包
  6. redhat搭建php环境,rhel5搭建PHP5.2.6+apache2.2.9开发环境(模块全)
  7. MySQL的四种不同查询的分析
  8. boss直聘用什么语言开发_我不在乎开发人员使用什么工具。 我根据基本原则聘用。...
  9. oracle rowid说明
  10. anaconda+python3.7安装keras_win10 python3.7 Anaconda3 安装tensorflow+Keras
  11. qmc0文件怎么转换mp3_音频转换器哪个好 怎么剪切MP3音频制作手机铃声
  12. 坦克世界无法连接服务器未响应,打开坦克世界提示网络异常或者连接不上
  13. HashMap源码剖析(代码基于JDK11)
  14. android imageview 图片模糊,android – Imageview变得模糊
  15. 数字电视 星座图 matlab,数字电视 MER 及星座图剖析
  16. 微信支付账号服务商快速进件H5源码
  17. 郭长波连任OpenStack基金会独立董事 继续推动中国力量话语权
  18. 大S首度曝光女儿正面照:小小幸福分享给大家
  19. Excel散点图 如何用平滑线 连接 不连续的点
  20. vue3 el-table结合seamless-scroll实现表格数据滚动

热门文章

  1. Keras Lambda层
  2. ATS 6.2.1中缓存文件过期并不回源校验的“坑”
  3. 利用python开源库制作并验证torrent种子文件
  4. Python for虚幻引擎编辑器工具脚本学习教程
  5. Blender创作你自己的动画短片学习教程
  6. byte endian(biglittle endian)
  7. leetcode-142 环形链表II
  8. 近20个绚丽实用的jQuery/CSS3侧边栏菜单(转载)
  9. POJ 1414 Life Line(搜索)
  10. SQL执行过程中的性能负载点