总线系统(下)

简介: 总线系统(下)

注册设备驱动程序

注册设备驱动程序:在进行一些检查和初始化工作之后,driver_register调用bus_add_driver将一个新驱动程序添加到一个总线来。驱动程序要有名字,然后注册到通用数据结构框架。

bus_add_driver

//注册设备驱动程序
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;
}

如果总线支持自动探测,调用driver_attach,该函数迭代总线上所有设备,使用驱动程序的match函数进行检测,确定是否有某些设备可使用该驱动程序管理。最后将该驱动程序添加到总线上注册的所有驱动程序的链表中(klist_add_tail)。

PCI总线

PCI是peripheral component interconnect的缩写,是英持尔公司开发的一种标准总线,它迅速在系统组件和体系结构厂商中间确立了自身的地位,成为一种非常流行的总线。其原因不在于市场策略方面的技巧,而是因为其技术水平。

PCl总线设计目标:

  • 支持高传输带宽,以适合具有大数据流的多媒体应用;
  • 简单且易于自动化配置外设;
  • 平台独立性,即不绑定到特定的处理器类型或系统平台。

PCI系统布局

  • 设备标识:系统某个PCI总线上的每个设备,都由一组3个编号标识;
  • 总线编号:设备所有总线的编号,编号从0开始,PCI规范准许每个系统最多255个总线;
  • 插槽编号:总线内核的一个唯一标识编号,一个总线最多能够附接32个设备
  • 功能编号:用于在一个扩展卡上,实现包括多个扩展设备的设备。

PCI地址空间

有3个地址空间支持与PCI设备的通信。

  • I/O空间通过32个比特描述。对用于与设备通信端口地址,提供了最大4GB空间。
  • 取决于处理器类型,数据空间由32或64个比特位描述。
  • 配置空间包含各个设备的类型和特征的详细信息。

内核提供几个数据结构类型来管理系统的PCI结构:

总线的表示

在内存中,每个PCI总线都通过pci_bus數据结构的一个实例表示,该结构定义如下:

pci_bus

struct pci_bus {
  struct list_head node;  //总线链表中的节点
  /* node in list of buses */
  struct pci_bus  *parent;//桥接器所在的父总线
  /* parent bus this bridge is on */
  struct list_head children;  //子总线链表
  /* list of child buses */
  struct list_head devices;//总线上设备的链表
  /* list of devices on this bus */
  struct pci_dev  *self;  //父总线所看到的桥接器设备
  /* bridge device as seen by parent */
  struct list_head slots;   /* list of slots on this bus */
  struct resource *resource[PCI_BRIDGE_RESOURCE_NUM];//导向到该总线的地址空间
  struct list_head resources; /* address space routed to this bus */
  struct resource busn_res; /* bus numbers routed to this bus */
  struct pci_ops  *ops; //访问配置信息的各函数  
  /* configuration access functions */
  struct msi_controller *msi; /* MSI controller */
  void    *sysdata; //挂钩,用于特定硬件的拓展
  /* hook for sys-specific extension */
  struct proc_dir_entry *procdir; /* directory entry in /proc/bus/pci */
  unsigned char number; //总线号 
  /* bus number */
  unsigned char primary;//主桥接器编号
  /* number of primary bridge */
  unsigned char max_bus_speed;  //次桥接器编号
  /* enum pci_bus_speed */
  unsigned char cur_bus_speed;  /* enum pci_bus_speed */
#ifdef CONFIG_PCI_DOMAINS_GENERIC
  int   domain_nr;
#endif
  char    name[48];
  unsigned short  bridge_ctl; /* manage NO_ISA/FBB/et al behaviors */
  pci_bus_flags_t bus_flags;  /* inherited by child buses */
  struct device   *bridge;
  struct device   dev;
  struct bin_attribute  *legacy_io; /* legacy I/O for this bus */
  struct bin_attribute  *legacy_mem; /* legacy mem */
  unsigned int    is_added:1;
};

设备管理

struct pci_dev是一个关键的数据结构,用于表示系统中的各个PCI设备。

struct pci_dev {
  struct list_head bus_list;
  /* node in per-bus list */
  struct pci_bus  *bus; 
  /* bus this device is on */
  struct pci_bus  *subordinate;
  /* bus this device bridges to */
  void    *sysdata; /* hook for sys-specific extension */
  struct proc_dir_entry *procent; /* device entry in /proc/bus/pci */
  struct pci_slot *slot;    /* Physical slot this device is in */
  unsigned int  devfn;    /* encoded device & function index */
  unsigned short  vendor;
  unsigned short  device;
  unsigned short  subsystem_vendor;
  unsigned short  subsystem_device;
  unsigned int  class;    /* 3 bytes: (base,sub,prog-if) */
  u8    revision; /* PCI revision, low byte of class word */
  u8    hdr_type; /* PCI header type (`multi' flag masked out) */
  u8    pcie_cap; /* PCIe capability offset */
  u8    msi_cap;  /* MSI capability offset */
  u8    msix_cap; /* MSI-X capability offset */
  u8    pcie_mpss:3;  /* PCIe Max Payload Size Supported */
  u8    rom_base_reg; /* which config register controls the ROM */
  u8    pin;    /* which interrupt pin this device uses */
  u16   pcie_flags_reg; /* cached PCIe Capabilities Register */
  u8    dma_alias_devfn;/* devfn of DMA alias, if any */
  struct pci_driver *driver;  /* which driver has allocated this device */
  u64   dma_mask; /* Mask of the bits of bus address this
             device implements.  Normally this is
             0xffffffff.  You only need to change
             this if your device has broken DMA
             or supports 64-bit transfers.  */
  struct device_dma_parameters dma_parms;
  pci_power_t     current_state;  /* Current operating state. In ACPI-speak,
             this is D0-D3, D0 being fully functional,
             and D3 being off. */
  u8    pm_cap;   /* PM capability offset */
  unsigned int  pme_support:5;  /* Bitmask of states from which PME#
             can be generated */
  unsigned int  pme_interrupt:1;
  unsigned int  pme_poll:1; /* Poll device's PME status bit */
  unsigned int  d1_support:1; /* Low power state D1 is supported */
  unsigned int  d2_support:1; /* Low power state D2 is supported */
  unsigned int  no_d1d2:1;  /* D1 and D2 are forbidden */
  unsigned int  no_d3cold:1;  /* D3cold is forbidden */
  unsigned int  d3cold_allowed:1; /* D3cold is allowed by user */
  unsigned int  mmio_always_on:1; /* disallow turning off io/mem
               decoding during bar sizing */
  unsigned int  wakeup_prepared:1;
  unsigned int  runtime_d3cold:1; /* whether go through runtime
               D3cold, not set for devices
               powered on/off by the
               corresponding bridge */
  unsigned int  ignore_hotplug:1; /* Ignore hotplug events */
  unsigned int  d3_delay; /* D3->D0 transition time in ms */
  unsigned int  d3cold_delay; /* D3cold->D0 transition time in ms */
#ifdef CONFIG_PCIEASPM
  struct pcie_link_state  *link_state;  /* ASPM link state */
#endif
  pci_channel_state_t error_state;  /* current connectivity state */
  struct  device  dev;    /* Generic device interface */
  int   cfg_size; /* Size of configuration space */
  /*
   * Instead of touching interrupt line and base address registers
   * directly, use the values stored here. They might be different!
   */
  unsigned int  irq;
  struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */
  bool match_driver;    /* Skip attaching driver */
  /* These fields are used by common fixups */
  unsigned int  transparent:1;  /* Subtractive decode PCI bridge */
  unsigned int  multifunction:1;/* Part of multi-function device */
  /* keep track of device state */
  unsigned int  is_added:1;
  unsigned int  is_busmaster:1; /* device is busmaster */
  unsigned int  no_msi:1; /* device may not use msi */
  unsigned int  no_64bit_msi:1; /* device may only use 32-bit MSIs */
  unsigned int  block_cfg_access:1; /* config space access is blocked */
  unsigned int  broken_parity_status:1; /* Device generates false positive parity */
  unsigned int  irq_reroute_variant:2;  /* device needs IRQ rerouting variant */
  unsigned int  msi_enabled:1;
  unsigned int  msix_enabled:1;
  unsigned int  ari_enabled:1;  /* ARI forwarding */
  unsigned int  is_managed:1;
  unsigned int    needs_freset:1; /* Dev requires fundamental reset */
  unsigned int  state_saved:1;
  unsigned int  is_physfn:1;
  unsigned int  is_virtfn:1;
  unsigned int  reset_fn:1;
  unsigned int    is_hotplug_bridge:1;
  unsigned int    __aer_firmware_first_valid:1;
  unsigned int  __aer_firmware_first:1;
  unsigned int  broken_intx_masking:1;
  unsigned int  io_window_1k:1; /* Intel P2P bridge 1K I/O windows */
  unsigned int  irq_managed:1;
  pci_dev_flags_t dev_flags;
  atomic_t  enable_cnt; /* pci_enable_device has been called */
  u32   saved_config_space[16]; /* config space saved at suspend time */
  struct hlist_head saved_cap_space;
  struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
  int rom_attr_enabled;   /* has display of the rom attribute been enabled? */
  struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */
  struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */
#ifdef CONFIG_PCI_MSI
  struct list_head msi_list;
  const struct attribute_group **msi_irq_groups;
#endif
  struct pci_vpd *vpd;
#ifdef CONFIG_PCI_ATS
  union {
    struct pci_sriov *sriov;  /* SR-IOV capability related */
    struct pci_dev *physfn; /* the PF this VF is associated with */
  };
  struct pci_ats  *ats; /* Address Translation Service */
#endif
  phys_addr_t rom; /* Physical address of ROM if it's not from the BAR */
  size_t romlen; /* Length of ROM if it's not from the BAR */
  char *driver_override; /* Driver name to force a match */
};

设备驱动程序

struct module;
struct pci_driver {
  struct list_head node;
  const char *name;
  const struct pci_device_id *id_table; /* must be non-NULL for probe to be called */
  int  (*probe)  (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
  void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
  int  (*suspend) (struct pci_dev *dev, pm_message_t state);  /* Device suspended */
  int  (*suspend_late) (struct pci_dev *dev, pm_message_t state);
  int  (*resume_early) (struct pci_dev *dev);
  int  (*resume) (struct pci_dev *dev);                 /* Device woken up */
  void (*shutdown) (struct pci_dev *dev);
  int (*sriov_configure) (struct pci_dev *dev, int num_vfs); /* PF pdev */
  const struct pci_error_handlers *err_handler;
  struct device_driver  driver;
  struct pci_dynids dynids;
};

PCI驱动程序通过pci_register_driver注册:

#define pci_register_driver(driver)   \
  __pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
int __pci_register_driver(struct pci_driver *drv, struct module *owner,
        const char *mod_name)
{
  /* initialize common driver fields */
  drv->driver.name = drv->name;
  drv->driver.bus = &pci_bus_type;
  drv->driver.owner = owner;
  drv->driver.mod_name = mod_name;
  spin_lock_init(&drv->dynids.lock);
  INIT_LIST_HEAD(&drv->dynids.list);
  /* register with core */
  return driver_register(&drv->driver);
}

USB

USB(Universal Serial Bus,通用串行总线)开发于上世纪90年代末。它是一种外部总线,用于满足不断发展的PC的需求,并用于建立针对新类型计机的解决方案,如手持设备、PDA等。作为一种通用的外部总线,在用于连接中低数据传输速率的设备时(如鼠标、网络摄像头、键盘),USB很有优势。但带宽要求更高的设备如外部硬盘、光驱、CD刻录机也可以通过USB总线运行。USB1.1的最大传输速率限于12兆比特/秒,该标准的2.0版本最高速率提升到480兆比特/杪。

所有USB设备都划分到不同类型当中,在内核源代码中,我们可以看到这样划分,各个驱动程序源代码按照所属类型归纳到不同的目录。

USB标准定义4种不同传输模式:控制传输、块传输、中断传输及同步传输。

USB子系统有4种主要任务:

  • 注册和管理现存的设备驱动程序;
  • 为USB设备查找适当的驱动程序,以及初始化和配置;
  • 在内核内存中表示设备树;
  • 与设备通信(交换数据)

USB设备

struct usb_device {
  int   devnum;
  char    devpath[16];
  u32   route;
  enum usb_device_state state;
  enum usb_device_speed speed;
  struct usb_tt *tt;
  int   ttport;
  unsigned int toggle[2];
  struct usb_device *parent;
  struct usb_bus *bus;
  struct usb_host_endpoint ep0;
  struct device dev;
  struct usb_device_descriptor descriptor;
  struct usb_host_bos *bos;
  struct usb_host_config *config;
  struct usb_host_config *actconfig;
  struct usb_host_endpoint *ep_in[16];
  struct usb_host_endpoint *ep_out[16];
  char **rawdescriptors;
  unsigned short bus_mA;
  u8 portnum;
  u8 level;
  unsigned can_submit:1;
  unsigned persist_enabled:1;
  unsigned have_langid:1;
  unsigned authorized:1;
  unsigned authenticated:1;
  unsigned wusb:1;
  unsigned lpm_capable:1;
  unsigned usb2_hw_lpm_capable:1;
  unsigned usb2_hw_lpm_besl_capable:1;
  unsigned usb2_hw_lpm_enabled:1;
  unsigned usb2_hw_lpm_allowed:1;
  unsigned usb3_lpm_enabled:1;
  int string_langid;
  /* static strings from the device */
  char *product;
  char *manufacturer;
  char *serial;
  struct list_head filelist;
  int maxchild;
  u32 quirks;
  atomic_t urbnum;
  unsigned long active_duration;
#ifdef CONFIG_PM
  unsigned long connect_time;
  unsigned do_remote_wakeup:1;
  unsigned reset_resume:1;
  unsigned port_is_suspended:1;
#endif
  struct wusb_dev *wusb_dev;
  int slot_id;
  enum usb_device_removable removable;
  struct usb2_lpm_parameters l1_params;
  struct usb3_lpm_parameters u1_params;
  struct usb3_lpm_parameters u2_params;
  unsigned lpm_disable_count;
};

USB驱动

struct usb_driver {
  const char *name;
  int (*probe) (struct usb_interface *intf,
          const struct usb_device_id *id);
  void (*disconnect) (struct usb_interface *intf);
  int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,
      void *buf);
  int (*suspend) (struct usb_interface *intf, pm_message_t message);
  int (*resume) (struct usb_interface *intf);
  int (*reset_resume)(struct usb_interface *intf);
  int (*pre_reset)(struct usb_interface *intf);
  int (*post_reset)(struct usb_interface *intf);
  const struct usb_device_id *id_table;
  struct usb_dynids dynids;
  struct usbdrv_wrap drvwrap;
  unsigned int no_dynamic_id:1;
  unsigned int supports_autosuspend:1;
  unsigned int disable_hub_initiated_lpm:1;
  unsigned int soft_unbind:1;
};

USB设备驱动

struct usb_device_driver {
  const char *name;
  int (*probe) (struct usb_device *udev);
  void (*disconnect) (struct usb_device *udev);
  int (*suspend) (struct usb_device *udev, pm_message_t message);
  int (*resume) (struct usb_device *udev, pm_message_t message);
  struct usbdrv_wrap drvwrap;
  unsigned int supports_autosuspend:1;
};
目录
相关文章
|
芯片
深入理解AMBA总线(一)APB总线入门(上)
深入理解AMBA总线(一)APB总线入门
1085 0
|
存储 芯片 异构计算
LocalBus总线介绍及FPGA总线编程
LocalBus总线介绍及FPGA总线编程
1132 0
LocalBus总线介绍及FPGA总线编程
|
缓存 SoC
深入理解AMBA总线(八)AHB2APB同步桥设计
深入理解AMBA总线(八)AHB2APB同步桥设计
750 0
|
Linux
总线系统(上)
总线系统
89 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
二、总线控制