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 device注册相关推荐

  1. linux怎么注册信息,linux device注册

    device注册 谨以此文纪念过往的岁月. device和driver是设备管理系统的核心,作为驱动工程师有必要去一探究竟. 1.device 对于上层而言device的注册很简单只要简单的去devi ...

  2. Linux device tree 简要笔记

    第一.DTS简介      在嵌入式设备上,可能有不同的主板---它们之间差异表现在主板资源不尽相同,比如I2C.SPI.GPIO等接口定义有差别,或者是Timer不同,等等.于是这就产生了BSP的一 ...

  3. linux device结构体,struct device结构体

    一.定义: linux/include/linux/device.h struct device { struct klist     klist_children; struct klist_nod ...

  4. Linux Device和Driver注册过程,以及Probe的时机

    Linux 2.6的设备驱动模型中,所有的device都是通过Bus相连.device_register() / driver_register()执行时通过枚举BUS上的Driver/Device来 ...

  5. 1.the linux device model--kobject kset学习笔记

    http://blog.chinaunix.net/uid-22547469-id-4590385.html?utm_source=jiancool Linux设备模型就是一栋规模宏大的建筑,为了构建 ...

  6. Android 驱动开发(14)---深入学习Linux Device Tree

    深入学习Linux Device Tree 这个世界需要的是全力以赴,战胜他人先战胜子自己!! Linux Device Tree可描述的信息包括: cpu的数量和类型 内存基地址和大小 总线 外设 ...

  7. Linux中SDIO命令,linux device driver之sdio驱动编程分享

    linux device driver之sdio驱动编程分享 闯客网 • 2018-12-19 • 技术交流 [p=26, null, left]先谈谈如何写linux驱动:[/p]- 在驱动模块初始 ...

  8. The Linux device model

    /sys和 /dev的疑问 1./dev 下放的是设备文件,是由应用层mknod创建的文件.假设底层驱动对mknod的设备号有相应的驱动,如open等函数.那么应用层open "/dev/* ...

  9. Linux rm -rf 之rm: cannot remove `linux': Device or resource busy

    2017年1月19日,清理linux服务器上一目录时出现灵异事件,居然有rm -rf不能删除的东西,排除用户进程占用,但是最后我还是把它给删掉了.     处理过程如下: [oracle@se31 ~ ...

最新文章

  1. iOS 关于pods-frameworks.sh:permission denied报错的解决
  2. 关于二叉树的链表表示的一个问题
  3. 路由及路由器工作原理深入解析3:路由与port
  4. python中randn函数_numpy常用函数之randn
  5. 【PAT乙级】1049 数列的片段和 (20 分)
  6. 查md5或者sha1值
  7. 2020年第十八届西电程序设计竞赛网络预选赛之Problem D 由比滨结衣的饼干(二分+前缀后缀)
  8. 使用Guava的AbstractInvocationHandler正确完成代理
  9. SpringBoot 整合 RabbitMQ 实践
  10. nfa确定化 dfa最小化_深度学习中的不确定性
  11. 手动卸载office 2010 亲测有效
  12. 两个栈实现一个队列以及两个队列实现一个栈(Java)
  13. 交返对于高频交易者尤为重要
  14. Docker(感谢狂神)
  15. ecshop ecmall shopex
  16. 如何完成一次 git pr
  17. python如何定义函数_python如何定义函数
  18. 刘德华五条抖音粉丝五千万,流量平台用什么“留量”?
  19. Elasticsearch-2.4.3的单节点安装(多种方式图文详解)
  20. Opencat-B——手机APP蓝牙遥控

热门文章

  1. 轻量级实用PDF转换工具
  2. Java 必会的 9 大技能,我请部门大神给你讲讲
  3. 也许,这样理解 HTTPS 更容易!
  4. 耐高温防腐计算机电缆,防腐耐高温计算机屏蔽控制电缆
  5. java poi excel读写_JAVA-POI操作Excel读写
  6. 清华大学 唐杰 计算机学院 怎么样,我国首位原创虚拟学生,后期希望“她”能够像人一样进行创新...
  7. resnet50 自定义
  8. c++ 调用python2类获取返回值
  9. you're probably running inside a thread without first calling pythoncom.CoInitialize
  10. PyQt5之QColor学习