总线,设备,驱动与class(三)

简介: 总线,设备,驱动与class(三)

驱动

内核为驱动对象定义的数据结构是struct device_driver:

//通用驱动程序模型为设备驱动程序单独设计一种数据结构
struct device_driver {
  const char    *name;//用于唯一标识驱动程序
  struct bus_type   *bus;//指向一个标识总线的对象,并提供特定于总线的操作
  struct module   *owner;//驱动所在的内核模块。
  const char    *mod_name;  /* used for built-in modules */
  bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
  const struct of_device_id *of_match_table;
  const struct acpi_device_id *acpi_match_table;
  int (*probe) (struct device *dev);
  /*驱动程序定义探测函数。当在总线bus中将该驱动与对应的设备进行绑定时,
  内核会首先调用bus中的probe函数(如果该bus实现了probe函数),如果bus
  没有实现自己的probe函数,那么内核会调用驱动程序中实现的probe函数。*/
  int (*remove) (struct device *dev);
  /*驱动程序所定义的卸载函数。当调用driverunregister从系统中卸载一个驱
  动对象时,内核会首先调用bus中的remove函数(如果该bus实现了remove函数),
  如果bus没有实现自己的remove函数,那么内核会调用驱动程序中实现的remove
  函数。*/
  //下面三个用于电源管理
  void (*shutdown) (struct device *dev);
  int (*suspend) (struct device *dev, pm_message_t state);
  int (*resume) (struct device *dev);
  const struct attribute_group **groups;
  const struct dev_pm_ops *pm;
  struct driver_private *p;
};

驱动上的主要操作有:

  • driver_find
    在一个bus的drivers_kset集合中查找指定的驱动,函数原型为:
struct device_driver *driver_find(const char *name, struct bus_type *bus)
{
  struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
  struct driver_private *priv;
  if (k) {
    /* Drop reference added by kset_find_obj() */
    kobject_put(k);
    priv = to_driver(k);
    return priv->driver;
  }
  return NULL;
}

参数name是要查找的驱动的名称,参数bus指明在哪个总线上进行当前的查找。如果查找成功,将返回该驱动对象的指针,否则返回0。

  • driver_register
    该函数用来向系统注册一个驱动,其核心实现代码为:
int driver_register(struct device_driver *drv)
{
  int ret;
  struct device_driver *other;
  BUG_ON(!drv->bus->p);
  if ((drv->bus->probe && drv->probe) ||
      (drv->bus->remove && drv->remove) ||
      (drv->bus->shutdown && drv->shutdown))
    printk(KERN_WARNING "Driver '%s' needs updating - please use "
      "bus_type methods\n", drv->name);
  other = driver_find(drv->name, drv->bus);
  if (other) {
    printk(KERN_ERR "Error: Driver '%s' is already registered, "
      "aborting...\n", drv->name);
    return -EBUSY;
  }
  ret = bus_add_driver(drv);
  if (ret)
    return ret;
  ret = driver_add_groups(drv, drv->groups);
  if (ret) {
    bus_remove_driver(drv);
    return ret;
  }
  kobject_uevent(&drv->p->kobj, KOBJ_ADD);
  return ret;
}

函数首先调用driver_find在drv->bus上查找当前要注册的drv,这主要是防止向系统重复注册同一个驱动,如果当前要注册的驱动没有被注册过,那么将调用bus_add_driver(drv)进行实际的注册操作。

int bus_add_driver(struct device_driver *drv)
{
  struct bus_type *bus;
  struct driver_private *priv;
  int error = 0;
  bus = bus_get(drv->bus);
  if (!bus)
    return -EINVAL;
  pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
  priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  if (!priv) {
    error = -ENOMEM;
    goto out_put_bus;
  }
  klist_init(&priv->klist_devices, NULL, NULL);
  priv->driver = drv;
  drv->p = priv;
  priv->kobj.kset = bus->p->drivers_kset;
  error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
             "%s", drv->name);
  if (error)
    goto out_unregister;
  klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
  if (drv->bus->p->drivers_autoprobe) {
    error = driver_attach(drv);
    if (error)
      goto out_unregister;
  }
  module_add_driver(drv->owner, drv);
  error = driver_create_file(drv, &driver_attr_uevent);
  if (error) {
    printk(KERN_ERR "%s: uevent attr (%s) failed\n",
      __func__, drv->name);
  }
  error = driver_add_groups(drv, bus->drv_groups);
  if (error) {
    /* How the hell do we get out of this pickle? Give up */
    printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
      __func__, drv->name);
  }
  if (!drv->suppress_bind_attrs) {
    error = add_bind_files(drv);
    if (error) {
      /* Ditto */
      printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
        __func__, drv->name);
    }
  }
  return 0;
out_unregister:
  kobject_put(&priv->kobj);
  kfree(drv->p);
  drv->p = NULL;
out_put_bus:
  bus_put(bus);
  return error;
}

函数首先为drv分配了一块类型为struct driver_private的空间对象p前,然后将其与drv对象建立了关联,同时调用kobject_init_and_add把drv所对应的内核对象加入到sysfs文件树中,如此将在/sys/bus/drivers目录下新建一目录,其名称为drv->name。

如果drv所在的bus对应的dnvers_autoprobe属性值为1,将调用dnver_attach将当前注册的drv与该bus上所属的设备进行绑定。绑定的过程将遍历bus上的所有设备,对于其中的每个设备dev,将调用really_probe(dev,rv)进行实际的绑定操作。如果此前bus上定义了match方法,则它将被首先调用以确定drv与dev是否match,如果不match,那么将继续遍历下一个设备,否则调用really_probe进行实际的绑定操作。really_probe(dev,drv)函数如能将drv与dev成功绑定,则将在sysfs文件树中通过链接文件为dev和drv所对应的内核对象建立拓扑关系。同时,如果所在bus上定义有probe函数,将调用之,否则如果当前要注册的drv定义有probe函数,那么将调用之。

在bus_add-driver函数中,也会通过调用driver_create_file函数在新建的drv目录中生成属性文件,比如driver_create_file(drv,&driver_attru_event)等。驱动的属性由宏DRIVER_ATTR来定义:

#define DRIVER_ATTR(_name, _mode, _show, _store) \
  struct driver_attribute driver_attr_##_name = __ATTR(_name, _mode, _show, _store)
  • driver_unregister
    该函数用来将某一指定的驱动从系统中注销掉。函数原型为:
void driver_unregister(struct device_driver *drv)
{
  if (!drv || !drv->p) {
    WARN(1, "Unexpected driver unregister!\n");
    return;
  }
  driver_remove_groups(drv, drv->groups);
  bus_remove_driver(drv);
}

参数drv用于指定要注销的某一驱动对象。函数基本上是做的反向工作,其主要的工作是在bus_remove_driver(drv)函数中完成的。需要注意的是,在注销一个驱动对象的过程中,如果其所在的总线定义了remove方法,那么内核会调用它,否则要看驱动所在的驱动程序中有没有实现该方法,如果实现了的话内核会调用该函数。

class

Linux设备驱动模型中的另一个比较重要的概念是类class,相对于设备device,class是一种更高层次的抽象,用于对设备进行功能上的划分,有时候也被称为设备类。Linux的设备模型引入类,是将其用来作为具有同类型功能设备的一个容器。

struct class {
  const char    *name;//类的名称。
  struct module   *owner;//拥有该类的模块的指针。
  struct class_attribute    *class_attrs;//类的属性。
  const struct attribute_group  **dev_groups;//设备的属性。
  struct kobject      *dev_kobj;//代表当前类中设备的内核对象。
  int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
  char *(*devnode)(struct device *dev, umode_t *mode);
  void (*class_release)(struct class *class);
  void (*dev_release)(struct device *dev);
  int (*suspend)(struct device *dev, pm_message_t state);
  int (*resume)(struct device *dev);
  const struct kobj_ns_type_operations *ns_type;
  const void *(*namespace)(struct device *dev);
  const struct dev_pm_ops *pm;
  struct subsys_private *p;//类的私有数据区,用于处理类的子系统及其所包含的设备链表。
};

内核针对类对象定义的主要操作有:

  • classes_init
    系统中类的起源函数,在系统初始化期间调用,主要作用是产生类对象的顶层kset—-—class_kset:
int __init classes_init(void)
{
  class_kset = kset_create_and_add("class", NULL, NULL);
  if (!class_kset)
    return -ENOMEM;
  return 0;
}

函数中对kset_create_and_add(“class”,NULL,NULL)的调用将导致在s目录下新生成一个"class”目录(/sy/class),在以后的class相关的操作中,class_kset将作为系统中所有class内核对象的顶层kset

  • __class_create
    其用途主要是将同类型的设备添加其中。
struct class *__class_create(struct module *owner, const char *name,
           struct lock_class_key *key)
{
  struct class *cls;
  int retval;
  cls = kzalloc(sizeof(*cls), GFP_KERNEL);
  if (!cls) {
    retval = -ENOMEM;
    goto error;
  }
  cls->name = name;
  cls->owner = owner;
  cls->class_release = class_create_release;
  retval = __class_register(cls, key);
  if (retval)
    goto error;
  return cls;
error:
  kfree(cls);
  return ERR_PTR(retval);
}

函数会动态生成一个class对象,经过一些初步的初始化之后,调用class_register向系统注册该新生成的类对象。限于篇幅,此处将不再列出class_regster函数的源代码,而是直接介绍其主要功能。class_register会首先为class_create函数中生成的新的类对象分配私有数据空间(structcs*cp=kzalloc(sizeof(*cp),GFPKERNEL)),代表class内核对象的kobject内嵌在struct class_private数据结构的class_subsys成员中(class_subsys.kobJ)。

函数将类对象的name成员赋值给代表类的kobJect对象名称(kobject_set_name(&cp->class_subsys.kobJ,“%s”,cls->name)),同时为类的kobJ指定kset和ktype:

int __class_register(struct class *cls, struct lock_class_key *key)
{
  struct subsys_private *cp;
  int error;
  pr_debug("device class '%s': registering\n", cls->name);
  cp = kzalloc(sizeof(*cp), GFP_KERNEL);
  if (!cp)
    return -ENOMEM;
  klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
  INIT_LIST_HEAD(&cp->interfaces);
  kset_init(&cp->glue_dirs);
  __mutex_init(&cp->mutex, "subsys mutex", key);
  error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);
  if (error) {
    kfree(cp);
    return error;
  }
  /* set the default /sys/dev directory for devices of this class */
  if (!cls->dev_kobj)
    cls->dev_kobj = sysfs_dev_char_kobj;
#if defined(CONFIG_BLOCK)
  /* let the block class directory show up in the root of sysfs */
  if (!sysfs_deprecated || cls != &block_class)
    cp->subsys.kobj.kset = class_kset;
#else
  cp->subsys.kobj.kset = class_kset;
#endif
  cp->subsys.kobj.ktype = &class_ktype;
  cp->class = cls;
  cls->p = cp;
  error = kset_register(&cp->subsys);
  if (error) {
    kfree(cp);
    return error;
  }
  error = add_class_attrs(class_get(cls));
  class_put(cls);
  return error;
}

之前讨论过class——kset为系统中所有class对象的顶层kset,此处将当前class对象的kobj.kset指向class_kset,意味着通过class_create生成的class,在sysfs文件系统中的入口点(目录)将在/sys/class目录下产生。

函数接下来调用kset_register将之前产生的class加入到系统中:

kset_regtster(&cp->classsubsys);

这样将会在/sys目录下生成一个新的目录。

  • device_create
    本来这个函数应该是属于设备相关的操作范畴,但是因为class的引入,使得设备的创建与class产生了相关性,所以我们把这个创建设备的函数放到class一节中讲解。
int device_create(struct class *class,  struct device *parent, dev_t devt, const char *fmt, ...)
{
   va_list vargs;
   struct class_device *class_dev;
   int err;
   va_start(vargs, fmt);
   vsnprintf(devfs_name, sizeof(devfs_name), fmt, vargs);
   va_end(vargs);
   class_dev = class_device_create(class, devt, parent, "%s", devfs_name);
   if (IS_ERR(class_dev)) {
      err = PTR_ERR(class_dev);
      goto out;
   }
   err = devfs_mk_cdev(devt, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP, devfs_name);
   if (err) {
      class_device_destroy(class, devt);
      goto out;
   }
   return 0;
out:
   return err;
}

函数的核心在device_create_vargs调用中,其功能基本和device_regster相同,但是

device_create_vargs会为设备指定class:

dev->class=class

之前在讨论device_register函数时曾讨论过“在sysfs文件系统中建立系统硬件拓扑关系结构图”的四种情况,如果用devicec爬№向系统中增加设备,显然属于这四种情况中的后两种。

  • device_destroy
    函数原型为:
void device_destroy(struct class *class, dev_t devt)
{
   class_device_destroy(class, devt);
   devfs_remove(devfs_name);
}

该函数用于从系统中移除通过device_create增加的设备device。

总结

这三章第二十五章 linux-总线,设备,驱动与class

第二十四章 linux-kset与热插拔中的uevent和call_usermodehelper

第二十三章 linux-sysfs文件系统与kobject讨论了Linux的设备驱动模型,该模型是个非常复杂的系统,从一个比较高的层次来看,主要由总线、设备和驱动构成。内核为了实现这些组件间的相关关系,定义了kobject和kset这样的基础底层数据结构,然后通过sysfs文件系统向用户空间展示发生在内核空间

中的各组件间的互联层次关系,并以文件系统接口的方式为用户空间程序提供了访问内核对象属性信息的简易方法。Linux设备模型通过总线将系统中的设备和驱动关联起来,由于设备和驱动的分离,增加了系统设计的灵活性,伴随而来的代价就是增加了复杂度。

目录
相关文章
|
10月前
|
Linux 程序员 容器
总线,设备,驱动与class(二)
总线,设备,驱动与class
62 1
|
10月前
|
Linux 程序员 Shell
总线,设备,驱动与class(一)
总线,设备,驱动与class
72 0
|
10月前
|
Linux
总线驱动--SPI驱动(下)
总线驱动--SPI驱动
149 0
|
10月前
|
Linux API SoC
总线驱动--SPI驱动(上)
总线驱动--SPI驱动
203 0
|
10月前
|
传感器 Linux
总线驱动---IIC驱动(下)
总线驱动---IIC驱动
61 0
|
10月前
|
传感器 算法 Linux
总线驱动---IIC驱动(上)
总线驱动---IIC驱动
81 0
|
10月前
|
Ubuntu 调度
usb摄像头驱动-core层usb设备的注册
usb摄像头驱动-core层usb设备的注册
59 0
|
12月前
|
Web App开发 芯片
USB2S可编程USB转串口适配器的开发原理
USB2S可编程USB转串口适配器的开发原理主要涉及USB接口协议、USB控制器芯片以及串口通信协议等方面。
USB2S可编程USB转串口适配器的开发原理
|
12月前
LED模板驱动程序的改造:总线设备驱动模型
LED模板驱动程序的改造:总线设备驱动模型
83 0
RK3399平台开发系列讲解(高速设备驱动篇)6.51、PCI总线信号定义
RK3399平台开发系列讲解(高速设备驱动篇)6.51、PCI总线信号定义
107 0
RK3399平台开发系列讲解(高速设备驱动篇)6.51、PCI总线信号定义