总线系统(上)

简介: 总线系统

内核支持大量总线,可能涉及多种硬件平台,也有可能只涉及一种平台。在Linux支持的大多数体系结构上都使用了PCI总线。我还会讨论广泛使用、系统无关的USB总线,该总线用于外设。

通用驱动程序模型

现代总线系统在布局和结构的细节上可能有所不同,但也有许多共同之处,内核的数据结构即反映了这个事实。结构中的许多成员用于所有的总线(以及相关设备的数据结构中)。在内核版本2.6开发期间,一个通用驱动程序模型(设备模型,device model)并入内该,以防止不必要的复制。所有总线共有的屈性封装到特殊的、可以用通用方法处理的数据结构中,再关联到总线相关的成员。

设备的表示

驱动程序模型采用一种持殊数据结构来表示几乎所有总线类型通用的设备属性。该结构直接嵌入到特定于总线的数据结构中,而不是通过指针引用。

struct device

//设备驱动模型中的device结构体
struct device {
  struct device   *parent;//指向设备的“父”设备,它所连接的设备
  struct device_private *p;//用于保存设备驱动核心部分的私有数据
  struct kobject kobj;//嵌入的struct kobject对象实例
  const char    *init_name; //设备的初始化名称
  /* initial name of the device */
  const struct device_type *type;//设备类型,哦那个与标识设备类型并携带特定类型的信息
  struct mutex    mutex;  //用于同步的互斥锁
  /* mutex to synchronize calls to
           * its driver.
           */
  struct bus_type *bus; //所在总线设备的类型 
  /* type of bus device is on */
  struct device_driver *driver;//分配device实例的驱动程序  
  /* which driver has allocated this
             device */
  void    *platform_data; //特定于平台的数据,设备模型代码不会访问
  /* Platform specific data, device core doesn't touch it */
  void    *driver_data; //驱动程序的私有数据
  /* Driver data, set and get with dev_set/get_drvdata */
  struct dev_pm_info  power;
  struct dev_pm_domain  *pm_domain;
#ifdef CONFIG_PINCTRL
  struct dev_pin_info *pins;
#endif
#ifdef CONFIG_NUMA
  int   numa_node;  /* NUMA node this device is close to */
#endif
  u64   *dma_mask;  /* dma mask (if dma'able device) */
  u64   coherent_dma_mask;/* Like dma_mask, but for
               alloc_coherent mappings as
               not all hardware supports
               64 bit addresses for consistent
               allocations such descriptors. */
  unsigned long dma_pfn_offset;
  struct device_dma_parameters *dma_parms;
  struct list_head  dma_pools;  /* dma pools (if dma'ble) */
  struct dma_coherent_mem *dma_mem; /* internal for coherent mem
               override */
#ifdef CONFIG_DMA_CMA
  struct cma *cma_area;   /* contiguous memory area for dma
             allocations */
#endif
  /* arch specific additions */
  struct dev_archdata archdata;
  struct device_node  *of_node; //与设备相联系的结构体指针
  /* associated device tree node */
  struct fwnode_handle  *fwnode; /* firmware device node */
  dev_t     devt;//设备的设备号 
  /* dev_t, creates the sysfs "dev" */
  u32     id; /* device instance */
  spinlock_t    devres_lock;//保护资源的自旋锁
  struct list_head  devres_head;//设备资源的双向链表
  struct klist_node knode_class;//接入class链表时所需要的klist节点
  struct class    *class;//指向设备所属的class指针
  const struct attribute_group **groups;//设备属性集合  /* optional groups */
  void  (*release)(struct device *dev);//函数指针,当设备需要释放时调用此函数
  struct iommu_group  *iommu_group;
  bool      offline_disabled:1;
  bool      offline:1;
};

通用驱动程序模型也为设驱动程序单独设计一种数据结构源码如下:

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);//此函数,用于检测系统中是否存在能够用该设备驱动程序处理的设备
  int (*remove) (struct device *dev);
  //下面三个用于电源管理
  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;
};

总线的表示

通用驱动程序模型不仅表示了设备,还用另一个数据结构表示了总线,定义如下:

bus_type

//总线数据结构类型的表示,成员:
struct bus_type {
  const char    *name;//bus名称
  const char    *dev_name;
  struct device   *dev_root;
  struct device_attribute *dev_attrs; /* use dev_groups instead */
  const struct attribute_group **bus_groups;
  const struct attribute_group **dev_groups;
  const struct attribute_group **drv_groups;
  int (*match)(struct device *dev, struct device_driver *drv);
  //匹配总线中dev和driver,返回1 匹配成功,否则匹配失败
  int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
  //用于总线对uevent的环境变量增加,但在总线下设备的dev_uevent处理函数也有对他的调用
  int (*probe)(struct device *dev);//匹配成功时调用此函数
  int (*remove)(struct device *dev);//总线上设备或者驱动要删除的时候调用
  void (*shutdown)(struct device *dev);//所有设备都关闭时调用
  int (*online)(struct device *dev);
  int (*offline)(struct device *dev);
  int (*suspend)(struct device *dev, pm_message_t state);//在总线上设备的休眠时调用
  int (*resume)(struct device *dev);//在总线上设备恢复时调用
  const struct dev_pm_ops *pm;
  const struct iommu_ops *iommu_ops;
  struct subsys_private *p;
  struct lock_class_key lock_key;
};

注册过程

为说明表示总线、设备和设备驱动程序的各个數据结构之间彼此的关联,了解各种类型数据结构的注册过程是很有用处的。

注册总线---->注册设备---->注册设备驱动程序

注册总线

注册总线:初始化设备的数据结构,将它加入到数据结构的网络当中。

bus_register

//注册总线
 /*在可以注册设备及驱动程序之前,需要有总线,提供此函数注册总线*/
int bus_register(struct bus_type *bus)
{
  int retval;
  struct subsys_private *priv;
  struct lock_class_key *key = &bus->lock_key;
  priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
  if (!priv)
    return -ENOMEM;
  priv->bus = bus;
  bus->p = priv;
  BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
  retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
  if (retval)
    goto out;
  priv->subsys.kobj.kset = bus_kset;
  priv->subsys.kobj.ktype = &bus_ktype;
  priv->drivers_autoprobe = 1;
  retval = kset_register(&priv->subsys);
  if (retval)
    goto out;
  retval = bus_create_file(bus, &bus_attr_uevent);
  if (retval)
    goto bus_uevent_fail;
  priv->devices_kset = kset_create_and_add("devices", NULL,
             &priv->subsys.kobj);
  if (!priv->devices_kset) {
    retval = -ENOMEM;
    goto bus_devices_fail;
  }
  priv->drivers_kset = kset_create_and_add("drivers", NULL,
             &priv->subsys.kobj);
  if (!priv->drivers_kset) {
    retval = -ENOMEM;
    goto bus_drivers_fail;
  }
  INIT_LIST_HEAD(&priv->interfaces);
  __mutex_init(&priv->mutex, "subsys mutex", key);
  klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
  klist_init(&priv->klist_drivers, NULL, NULL);
  retval = add_probe_files(bus);
  if (retval)
    goto bus_probe_files_fail;
  retval = bus_add_groups(bus, bus->bus_groups);
  if (retval)
    goto bus_groups_fail;
  pr_debug("bus: '%s': registered\n", bus->name);
  return 0;
bus_groups_fail:
  remove_probe_files(bus);
bus_probe_files_fail:
  kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
  kset_unregister(bus->p->devices_kset);
bus_devices_fail:
  bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
  kset_unregister(&bus->p->subsys);
out:
  kfree(bus->p);
  bus->p = NULL;
  return retval;
}

注册设备

注册设备:初始化设备的数据结构,并将它加入到数据结构的网络当中。

device_add

//注册设备
int device_add(struct device *dev)
{
  struct device *parent = NULL;
  struct kobject *kobj;
  struct class_interface *class_intf;
  int error = -EINVAL;
  dev = get_device(dev);
  if (!dev)
    goto done;
  if (!dev->p) {
    error = device_private_init(dev);
    if (error)
      goto done;
  }
  /*
   * for statically allocated devices, which should all be converted
   * some day, we need to initialize the name. We prevent reading back
   * the name, and force the use of dev_name()
   */
  if (dev->init_name) {
    dev_set_name(dev, "%s", dev->init_name);
    dev->init_name = NULL;
  }
  /* subsystems can specify simple device enumeration */
  if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
    dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
  if (!dev_name(dev)) {
    error = -EINVAL;
    goto name_error;
  }
  pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
  parent = get_device(dev->parent);
  kobj = get_device_parent(dev, parent);
  if (kobj)
    dev->kobj.parent = kobj;
  /* use parent numa_node */
  if (parent)
    set_dev_node(dev, dev_to_node(parent));
  /* first, register with generic layer. */
  /* we require the name to be set before, and pass NULL */
  error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
  if (error)
    goto Error;
  /* notify platform of device entry */
  if (platform_notify)
    platform_notify(dev);
  error = device_create_file(dev, &dev_attr_uevent);
  if (error)
    goto attrError;
  error = device_add_class_symlinks(dev);
  if (error)
    goto SymlinkError;
  error = device_add_attrs(dev);
  if (error)
    goto AttrsError;
  error = bus_add_device(dev);
  if (error)
    goto BusError;
  error = dpm_sysfs_add(dev);
  if (error)
    goto DPMError;
  device_pm_add(dev);
  if (MAJOR(dev->devt)) {
    error = device_create_file(dev, &dev_attr_dev);
    if (error)
      goto DevAttrError;
    error = device_create_sys_dev_entry(dev);
    if (error)
      goto SysEntryError;
    devtmpfs_create_node(dev);
  }
  /* Notify clients of device addition.  This call must come
   * after dpm_sysfs_add() and before kobject_uevent().
   */
  if (dev->bus)
    blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
               BUS_NOTIFY_ADD_DEVICE, dev);
  kobject_uevent(&dev->kobj, KOBJ_ADD);
  bus_probe_device(dev);
  if (parent)
    klist_add_tail(&dev->p->knode_parent,
             &parent->p->klist_children);
  if (dev->class) {
    mutex_lock(&dev->class->p->mutex);
    /* tie the class to the device */
    klist_add_tail(&dev->knode_class,
             &dev->class->p->klist_devices);
    /* notify any interfaces that the device is here */
    list_for_each_entry(class_intf,
            &dev->class->p->interfaces, node)
      if (class_intf->add_dev)
        class_intf->add_dev(dev, class_intf);
    mutex_unlock(&dev->class->p->mutex);
  }
done:
  put_device(dev);
  return error;
 SysEntryError:
  if (MAJOR(dev->devt))
    device_remove_file(dev, &dev_attr_dev);
 DevAttrError:
  device_pm_remove(dev);
  dpm_sysfs_remove(dev);
 DPMError:
  bus_remove_device(dev);
 BusError:
  device_remove_attrs(dev);
 AttrsError:
  device_remove_class_symlinks(dev);
 SymlinkError:
  device_remove_file(dev, &dev_attr_uevent);
 attrError:
  kobject_uevent(&dev->kobj, KOBJ_REMOVE);
  kobject_del(&dev->kobj);
 Error:
  cleanup_device_parent(dev);
  put_device(parent);
name_error:
  kfree(dev->p);
  dev->p = NULL;
  goto done;
}


目录
相关文章
总线系统(下)
总线系统(下)
83 0
|
芯片
深入理解AMBA总线(一)APB总线入门(上)
深入理解AMBA总线(一)APB总线入门
1085 0
|
存储 芯片 异构计算
LocalBus总线介绍及FPGA总线编程
LocalBus总线介绍及FPGA总线编程
1132 0
LocalBus总线介绍及FPGA总线编程
|
缓存 SoC
深入理解AMBA总线(八)AHB2APB同步桥设计
深入理解AMBA总线(八)AHB2APB同步桥设计
750 0
|
算法 网络架构 iOS开发
详解CAN总线:什么是CAN总线?
CAN总线协议(Controller Area Network),控制器局域网总线,是德国BOSCH(博世)公司研发的一种串行通讯协议总线,它可以使用双绞线来传输信号,是世界上应用最广泛的现场总线之一。
详解CAN总线:高速CAN总线和低速CAN总线的特性
在ISO 11898-2和ISO 11898-3中分别规定了两种CAN总线结构(在BOSCH CAN2.0规范中,并没有关于总线拓扑结构的说明):高速CAN总线和低速CAN总线,本篇博文将详细介绍两者的特性和区别。
详解CAN总线:CAN总线通信优先级机制
在详解CAN总线:CAN总线报文格式—数据帧文章中,讲解到仲裁段。仲裁段用于写明需要发送到目的CAN节点的地址、确定发送的帧类型(当前发送的是数据帧还是遥控帧),并确定发送的帧格式是标准帧还是扩展帧。 本篇文章将讲解仲裁段的另一个重要功能:通信优先级。
RK3399平台开发系列讲解(高速设备驱动篇)6.51、PCI总线信号定义
RK3399平台开发系列讲解(高速设备驱动篇)6.51、PCI总线信号定义
142 0
RK3399平台开发系列讲解(高速设备驱动篇)6.51、PCI总线信号定义
|
存储 算法
二、总线控制
二、总线控制
208 0
二、总线控制