xHCI 简单分析_xHci



文章目录

  • 一、Linux 内核启动简述
  • start_kernel
  • do_initcalls
  • 二、acpi 搜索 pci 设备
  • acpi_pci_root_init
  • acpi_device_probe
  • 三、xhci 识别 PCI 设备
  • xhci_pci_init
  • xhci_pci_hc_driver 初始化
  • xhci_pci_overrides
  • xhci_hc_driver
  • xhci_pci_probe
  • usb_bus_type 注册
  • 四、bus_register
  • Bus、driver 和 device 角色说明
  • Device 和 driver 两者关系
  • 五、USB 子系统相关的结构体定义及说明
  • struct bus_type
  • struct usb_bus
  • struct usb_hcd
  • struct xhci_hcd
  • struct hc_driver
  • struct usb_device
  • struct usb_driver
  • struct usb_device_id
  • struct usb_interface
  • struct usb_host_interface
  • struct urb
  • 六、PCIe
  • type0
  • type1
  • PCI Standard Capabilities
  • PCIe Extended Capabilities
  • xHCI 架构图



一、Linux 内核启动简述

xHCI 简单分析_#define_02

start_kernel

void start_kernel(void)
{
	// ...
	setup_arch(&command_line);
	// ...
}

do_initcalls

static void __init do_initcalls(void)
{
	initcall_t *call;

	for (call = __early_initcall_end; call < __initcall_end; call++)
		do_one_initcall(*call);
	// ...
}

int do_one_initcall(initcall_t fn)
{
	// ...
	ret.result = fn();
	// ...	
}

    do_initcalls 函数的主体是将 __early_initcall_end__initcall_end 指针之间的函数全部执行一遍, 这两个指针在 vmlinux. lds 文件中定义。 在生成操作系统内核时, 一些需要在 Linux 系统初始化时执行的函数指针被加入到 __early_initcall_end__initcall_end 参数之间, 之后由 do_initcalls 函数统一调用这些函数。 Linux 系统定义了一系列需要在系统初始化时执行的模块, 如下所示。

#define __define_initcall(level,fn,id) \
	static initcall_t __initcall_##fn##id __used \
	__attribute__((__section__(".initcall" level ".init"))) = fn

/*
 * Early initcalls run before initializing SMP.
 *
 * Only for built-in code, not modules.
 */
#define early_initcall(fn)		__define_initcall("early",fn,early)

/*
 * A "pure" initcall has no dependencies on anything else, and purely
 * initializes variables that couldn't be statically initialized.
 *
 * This only exists for built-in code, not for modules.
 */
#define pure_initcall(fn)		__define_initcall("0",fn,0)

#define core_initcall(fn)		__define_initcall("1",fn,1)
#define core_initcall_sync(fn)		__define_initcall("1s",fn,1s)
#define postcore_initcall(fn)		__define_initcall("2",fn,2)
#define postcore_initcall_sync(fn)	__define_initcall("2s",fn,2s)
#define arch_initcall(fn)		__define_initcall("3",fn,3)
#define arch_initcall_sync(fn)		__define_initcall("3s",fn,3s)
#define subsys_initcall(fn)		__define_initcall("4",fn,4)
#define subsys_initcall_sync(fn)	__define_initcall("4s",fn,4s)
#define fs_initcall(fn)			__define_initcall("5",fn,5)
#define fs_initcall_sync(fn)		__define_initcall("5s",fn,5s)
#define rootfs_initcall(fn)		__define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn)		__define_initcall("6",fn,6)
#define device_initcall_sync(fn)	__define_initcall("6s",fn,6s)
#define late_initcall(fn)		__define_initcall("7",fn,7)
#define late_initcall_sync(fn)		__define_initcall("7s",fn,7s)

#define __initcall(fn) device_initcall(fn)

#define __exitcall(fn) \
	static exitcall_t __exitcall_##fn __exit_call = fn

#define module_init(x) 		__initcall(x)

二、acpi 搜索 pci 设备

acpi_pci_root_init

xHCI 简单分析_PCIe_03

static int __init acpi_pci_root_init(void)
{
	if (acpi_pci_disabled)
		return 0;

	if (acpi_bus_register_driver(&acpi_pci_root_driver) < 0)
		return -ENODEV;

	return 0;
}

subsys_initcall(acpi_pci_root_init);

// drivers/acpi/scan.c
struct bus_type acpi_bus_type = {
	.name		= "acpi",
	.suspend	= acpi_device_suspend,
	.resume		= acpi_device_resume,
	.match		= acpi_bus_match,
	.probe		= acpi_device_probe,
	.remove		= acpi_device_remove,
	.uevent		= acpi_device_uevent,
};

    acpi_pci_root_init 函数最终会调用 acpi_device_probe

acpi_device_probe

xHCI 简单分析_ci_04

    acpi_pci_root_init 函数调用 acpi_pci_root_addacpi_pci_root_start 函数遍历处理器系统中的 PCI 总线树。

static int acpi_device_probe(struct device * dev)
{
	// ...
	ret = acpi_bus_driver_init(acpi_dev, acpi_drv);
	// ...
}

static struct acpi_driver acpi_pci_root_driver = {
	.name = "pci_root",
	.class = ACPI_PCI_ROOT_CLASS,
	.ids = root_device_ids,
	.ops = {
		.add = acpi_pci_root_add,
		.remove = acpi_pci_root_remove,
		.start = acpi_pci_root_start,
		},
};

    pci_scan_single_device 函数会把扫描出的 pci_dev 加入到 pci_bus_type 中。

三、xhci 识别 PCI 设备

xhci_pci_init

xHCI 简单分析_#define_05

static int __init xhci_pci_init(void)
{
	xhci_init_driver(&xhci_pci_hc_driver, &xhci_pci_overrides);
	xhci_pci_hc_driver.pci_suspend = pm_ptr(xhci_pci_suspend);
	xhci_pci_hc_driver.pci_resume = pm_ptr(xhci_pci_resume);
	xhci_pci_hc_driver.pci_poweroff_late = pm_ptr(xhci_pci_poweroff_late);
	xhci_pci_hc_driver.shutdown = pm_ptr(xhci_pci_shutdown);
	xhci_pci_hc_driver.stop = xhci_pci_stop;
	return pci_register_driver(&xhci_pci_driver);
}
module_init(xhci_pci_init);

static struct pci_driver xhci_pci_driver = {
	.name =		hcd_name,
	.id_table =	pci_ids,

	.probe =	xhci_pci_probe,
	.remove =	xhci_pci_remove,
	/* suspend and resume implemented later */

	.shutdown = 	usb_hcd_pci_shutdown,
	.driver = {
		.pm = pm_ptr(&usb_hcd_pci_pm_ops),
	},
};

    xhci_pci_init 注册 xhci_pci_hc_driver 驱动到 pci_bus_type 总线上,同时它会识别 acpi_device_probe 扫描到 pci 设备(即 usb_hcdxhci_hcd)。这是通过函数 xhci_pci_driver .xhci_pci_probe 实现的。

xhci_pci_hc_driver 初始化

void xhci_init_driver(struct hc_driver *drv,
		      const struct xhci_driver_overrides *over)
{
	/* Copy the generic table to drv then apply the overrides */
	*drv = xhci_hc_driver;

	if (over) {
		drv->hcd_priv_size += over->extra_priv_size;
		if (over->reset)
			drv->reset = over->reset;
		if (over->start)
			drv->start = over->start;
		if (over->add_endpoint)
			drv->add_endpoint = over->add_endpoint;
		if (over->drop_endpoint)
			drv->drop_endpoint = over->drop_endpoint;
		if (over->check_bandwidth)
			drv->check_bandwidth = over->check_bandwidth;
		if (over->reset_bandwidth)
			drv->reset_bandwidth = over->reset_bandwidth;
		if (over->update_hub_device)
			drv->update_hub_device = over->update_hub_device;
		if (over->hub_control)
			drv->hub_control = over->hub_control;
	}
}

    xhci_pci_hc_driver,由 xhci_hc_driver 赋予基本值,并由 xhci_pci_overrides 覆盖改写。

xhci_pci_overrides

// drivers/usb/host/xhci-pci.c
static const struct xhci_driver_overrides xhci_pci_overrides __initconst = {
	.reset = xhci_pci_setup,
	.start = xhci_pci_run,
	.update_hub_device = xhci_pci_update_hub_device,
};

xhci_hc_driver

// drivers/usb/host/xhci.c
static const struct hc_driver xhci_hc_driver = {
	.description =		"xhci-hcd",
	.product_desc =		"xHCI Host Controller",
	.hcd_priv_size =	sizeof(struct xhci_hcd),

	/*
	 * generic hardware linkage
	 */
	.irq =			xhci_irq,
	.flags =		HCD_MEMORY | HCD_DMA | HCD_USB3 | HCD_SHARED |
				HCD_BH,

	/*
	 * basic lifecycle operations
	 */
	.reset =		NULL, /* set in xhci_init_driver() */
	.start =		xhci_run,
	.stop =			xhci_stop,
	.shutdown =		xhci_shutdown,

	/*
	 * managing i/o requests and associated device resources
	 */
	.map_urb_for_dma =      xhci_map_urb_for_dma,
	.unmap_urb_for_dma =    xhci_unmap_urb_for_dma,
	.urb_enqueue =		xhci_urb_enqueue,
	.urb_dequeue =		xhci_urb_dequeue,
	.alloc_dev =		xhci_alloc_dev,
	.free_dev =		xhci_free_dev,
	.alloc_streams =	xhci_alloc_streams,
	.free_streams =		xhci_free_streams,
	.add_endpoint =		xhci_add_endpoint,
	.drop_endpoint =	xhci_drop_endpoint,
	.endpoint_disable =	xhci_endpoint_disable,
	.endpoint_reset =	xhci_endpoint_reset,
	.check_bandwidth =	xhci_check_bandwidth,
	.reset_bandwidth =	xhci_reset_bandwidth,
	.fixup_endpoint =	xhci_fixup_endpoint,
	.address_device =	xhci_address_device,
	.enable_device =	xhci_enable_device,
	.update_hub_device =	xhci_update_hub_device,
	.reset_device =		xhci_discover_or_reset_device,

	/*
	 * scheduling support
	 */
	.get_frame_number =	xhci_get_frame,

	/*
	 * root hub support
	 */
	.hub_control =		xhci_hub_control,
	.hub_status_data =	xhci_hub_status_data,
	.bus_suspend =		xhci_bus_suspend,
	.bus_resume =		xhci_bus_resume,
	.get_resuming_ports =	xhci_get_resuming_ports,

	/*
	 * call back when device connected and addressed
	 */
	.update_device =        xhci_update_device,
	.set_usb2_hw_lpm =	xhci_set_usb2_hardware_lpm,
	.enable_usb3_lpm_timeout =	xhci_enable_usb3_lpm_timeout,
	.disable_usb3_lpm_timeout =	xhci_disable_usb3_lpm_timeout,
	.find_raw_port_number =	xhci_find_raw_port_number,
	.clear_tt_buffer_complete = xhci_clear_tt_buffer_complete,
};

xhci_pci_probe

xHCI 简单分析_ci_06

static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
	// ...
	retval = usb_hcd_pci_probe(dev, &xhci_pci_hc_driver);
	// ...
	xhci->shared_hcd = usb_create_shared_hcd(&xhci_pci_hc_driver, &dev->dev,
						 pci_name(dev), hcd);
	retval = xhci_ext_cap_init(xhci);
	// ...
	retval = usb_add_hcd(xhci->shared_hcd, dev->irq, IRQF_SHARED);					 
	// ...
}

    xhci_pci_probe 函数创建了 usb_hcdRoot HubRoot Hub 挂在 usb_bus_type 总线上。

usb_bus_type 注册

const struct bus_type usb_bus_type = {
	.name =		"usb",
	.match =	usb_device_match,
	.uevent =	usb_uevent,
	.need_parent_lock =	true,
};

static int __init usb_init(void)
{
	// ...
	usb_acpi_register();
	retval = bus_register(&usb_bus_type);
	retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
	retval = usb_major_init();
	retval = class_register(&usbmisc_class);
	retval = usb_register(&usbfs_driver);
	retval = usb_devio_init();
	retval = usb_hub_init();
	retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
	// ...
}	
subsys_initcall(usb_init);

四、bus_register

Linux系统USB子系统2—数据结构篇XHCI主控在BIOS哪里修改DWC3和XHCI的区别

int bus_register(struct bus_type *bus)
{
    // ...
    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);
	// ...
}

bus_register 下面注册了 devices 和 drivers

Bus、driver 和 device 角色说明

   Linux Driver Model 里面有抽象出 Bus,device 和 driver 。Device 和 Driver 都要归属于某一个 bus,bus 会根据 device 或 driver 的注册来遍历匹配的 driver 或 device 。

  1. Bus:
       总线是处理器和设备之间的通道。总线有多种类型,每种总线可以挂载多个设备。如:usb、pci、i2c 和 platform(虚拟)等;
  • 总线上有两个重要的链表:1)设备(device)链表;2)驱动(driver)链表;
  • 每种总线下面可以挂载许多设备;
  • 每种总线下可以用很多驱动;
  1. driver
       驱动程序是在 CPU 运行时,提供操作的软件接口。所有的设备必须有与之配套驱动程序才能正常工作。一个驱动程序可以驱动多个类似或者完全不同的设备。
  2. device
       设备就是连接在总线上的物理实体。设备是有功能之分的。具有相同功能的设备被归到一个类,如输入设备(鼠标,键盘,游戏杆等)、输出设备等。

Device 和 driver 两者关系

两者有如下关系:

  1. 每次出现一个设备就要向总线汇报,每次出现一个驱动,也要向总线注册
  2. 系统初始化的时候,会扫描连接了哪些设备,为每一个设备建立起一个struct device的变量,并加入设备链表;
  3. 每次注册一个驱动,就要准备一个 struct device_driver 结构的变量,并加入驱动链表;
  4. 这样所有的设备都挂载到总线上,当加载驱动时,驱动就去总线上找到自己对应的设备,或者先把驱动注册上,来了一个设备就去总线找驱动。
  5. 如果只有设备却没有对应的驱动,那么设备无法工作;如果只有驱动却没有设备,驱动也起不了任何作用。

五、USB 子系统相关的结构体定义及说明

struct bus_type

  struct bus_type 是 Linux 内核中用于表示一种特定类型总线的数据结构。在内核中,每种总线类型(如 USB、PCI、I2C 等)都有自己的 bus_type 实例,用于定义和管理该总线上设备的注册、枚举和驱动程序绑定等操作。

struct bus_type {
    const char *name;        // 总线名称,如:usb, pci等字符串;
    struct bus_attribute *attributes; // 总线属性列表
    struct device_driver *drivers;   // 驱动程序列表
    struct device *devices;      	 // 设备列表
    // ... 其他字段 ...
};

作用:

  • 抽象层: struct bus_type 为设备驱动程序提供了一个抽象层,使得它们可以与不同类型的总线交互,而不需要关心底层的硬件细节。
  • 设备管理: 通过 bus_type 结构,内核可以管理设备的生命周期,包括设备的添加、删除和驱动程序的绑定。
  • 驱动程序匹配: 总线类型是驱动程序和设备匹配过程中的关键部分,它帮助内核确定哪个驱动程序可以管理特定的设备。
  • sysfs 接口: 总线的属性可以通过 sysfs 接口暴露给用户空间,允许用户和应用程序获取总线和设备的信息。

struct usb_bus

  usb_bus 保存一个USB总线系统的信息,包括总线上设备地址信息,根集线器,带宽使用情况等。以下是 struct usb_bus 的定义及其各个字段的详细说明:

struct usb_bus {
    struct device *controller; /* 控制器的设备结构体 */
    struct device *self;  /* USB 总线的设备结构体 */
    int busnum;  /* USB 总线号 */
    char *bus_name;  /* USB 总线名称 */
    unsigned int is_b_host:1; /* 是否为 B 机主控制器 */
    struct usb_hcd *hcd;  /* 对应的 HCD 结构体 */
    struct list_head bus_list; /* 连接到总线上的所有 USB 总线的列表 */
    struct list_head devices;  /* 连接到这条总线的所有 USB 设备 */
    struct usb_device *root_hub; /* 根集线器的设备结构体 */
    int route_string_count;
    char *route_string;  /* 总线路由的字符串表示 */
    unsigned long route_can_sleep;
 
    /* ... ... */
};

作用:

  1. 该结构体的使用通常涉及到USB设备的枚举、配置、数据传输等操作。
  2. 在USB设备连接或断开时,内核会更新这个结构体的信息,以反映当前USB总线的状态。

struct usb_hcd

  1. 说明:
      
      在Linux内核中,struct usb_hcd(USB Host Controller Driver)是用于表示USB主控制器驱动的核心数据结构。它提供了USB主控制器的相关信息,并为USB设备的管理和交互提供了接口。
  2. struct usb_hcd 结构体定义
struct usb_hcd {
    struct usb_bus bus;                     // USB总线结构体
    struct device *self;                    // 指向主控制器设备的指针
    struct usb_host_config *host_config;    // 指向主机配置的指针
    struct list_head root_hub;              // 根集线器的设备列表
    struct usb_hcd *next;                   // 指向下一个HCD的指针
    spinlock_t lock;                         // 自旋锁,用于保护HCD的状态
    unsigned int flags;                      // 标志位
    // 其他可能的字段...
};
  1. 作用:

    struct usb_hcd 提供了USB主控制器驱动所需的基本信息和管理功能。通过这个结构体,usb core 能够与 USB主控制器进行交互,包括:
  • 管理连接和断开USB设备。
  • 处理USB设备的枚举和配置。
  • 处理USB数据传输。
  • 维护USB设备的状态信息。

struct xhci_hcd

struct xhci_hcd 挂接在 struct usb_hcd 结构的最后面,其提供了 USB 主控制器驱动所需的基本信息和管理功能。

// drivers/usb/host/xhci.h
struct xhci_hcd {
	struct usb_hcd *main_hcd;
	struct usb_hcd *shared_hcd;
	/* glue to PCI and HCD framework */
	struct xhci_cap_regs __iomem *cap_regs;
	struct xhci_op_regs __iomem *op_regs;
	struct xhci_run_regs __iomem *run_regs;
	struct xhci_doorbell_array __iomem *dba;

	/* Cached register copies of read-only HC data */
	__u32		hcs_params1;
	__u32		hcs_params2;
	__u32		hcs_params3;
	__u32		hcc_params;
	__u32		hcc_params2;

    // ...
}

struct hc_driver

  1. 说明:
      
      struct hc_driver 结构体定义了USB主机控制器驱动程序与USB核心进行交互的行为。通过实现这些函数指针,驱动程序能够处理设备的连接、断开、挂起和恢复等事件。USB核心使用这些函数来管理设备生命周期,并确保设备能够与系统中的其他组件正确地通信。
  2. struct hc_driver 结构体定义
struct hc_driver {
    const char *description; // 驱动程序的描述
    int (*probe) (struct usb_hcd *hcd); // 探测函数
    void (*remove) (struct usb_hcd *hcd); // 移除函数
    int (*start) (struct usb_hcd *hcd); // 开始函数
    void (*stop) (struct usb_hcd *hcd); // 停止函数
    int (*reset) (struct usb_hcd *hcd); // 重置函数
    int (*start_port_reset) (struct usb_hcd *hcd, unsigned int portnum); // 开始端口重置函数
    int (*start_root_hub) (struct usb_hcd *hcd); // 开始根集线器函数
    int (*stop_root_hub) (struct usb_hcd *hcd); // 停止根集线器函数
    int (*port_handed_over) (struct usb_hcd *hcd, int portnum); // 端口交出函数
    int (*port_reset) (struct usb_hcd *hcd, unsigned int portnum); // 端口重置函数
    int (*port_suspend) (struct usb_hcd *hcd, unsigned int portnum, bool suspend); // 端口挂起函数
    int (*port_resume) (struct usb_hcd *hcd, unsigned int portnum); // 端口恢复函数
    int (*get_root_hub_descriptor) (struct usb_hcd *hcd, struct usb_hub_descriptor *desc); // 获取根集线器描述符函数
    int (*hub_status_data) (struct usb_hcd *hcd, char *buf); // 获取集线器状态数据函数
    int (*port_status) (struct usb_hcd *hcd, char *buf, unsigned int portnum); // 获取端口状态函数
    int (*submit_urb) (struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); // 提交URB函数
    int (*address_device) (struct usb_hcd *hcd, unsigned int portnum, u8 address); // 地址设备函数
    int (*allocate_bandwidth) (struct usb_hcd *hcd, struct usb_host_endpoint *ep, int bandwidth); // 分配带宽函数
    int (*deallocate_bandwidth) (struct usb_hcd *hcd, struct usb_host_endpoint *ep); // 释放带宽函数
    int (*get_frame_number) (struct usb_hcd *hcd); // 获取帧编号函数
    /* USB 传输请求1: 将 URB(USB Request Block)添加到队列中,等待传输 */
    int (*urb_enqueue)(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags);
    /* USB 传输请求2:从队列中取出一个 URB 并开始传输 */
    int (*urb_dequeue)(struct usb_hcd *hcd,  struct urb *urb, int status);   
    // 其他可能的函数指针...
};
  1. 主要作用
  • 定义接口: struct hc_driver 提供了一组函数指针,这些指针指向与 USB 主机控制器相关的操作,如初始化、提交 URB(USB Request Block)、中断处理等。这使得不同的主机控制器可以实现相同的接口,从而统一管理。
  • 设备管理: 通过 hc_driver,内核可以管理 USB 设备的连接、断开和枚举等操作。它负责识别和处理连接到主机控制器的 USB 设备。
  • 控制传输: hc_driver 定义了用于处理控制传输的函数,这些函数负责处理 USB 设备的请求和响应。
  • 数据传输: 提供提交和处理 URB 的方法,以支持数据的发送和接收。这包括批量、等时和中断数据传输。
  • 中断处理:定义中断服务例程,用于处理来自 USB 设备的中断请求。
  • 资源管理:管理与主机控制器相关的资源,如内存分配、端点配置等。

struct usb_device

  struct usb_device:保存一个 USB 设备的信息,包括设备地址,设备描述符,配置描述符等。一个 USB 总线系统至少有一个主机控制器和一个根集线器,Linux 系统支持多 USB 总线系统。

struct usb_device {
    struct usb_bus *bus;                // 指向所属USB总线的指针
    struct usb_device *parent;          // 指向父设备的指针,对于根设备为NULL
    struct usb_interface *interface;    // 指向连接到该设备的接口的指针
    struct usb_device_descriptor desc;   // USB设备描述符
    struct usb_configuration *config;   // 指向当前配置的指针
    struct usb_host_config *host_config; // 指向USB主机配置的指针
    struct usb_driver *driver;          // 指向设备驱动的指针
    struct usb_endpoint *ep0;           // 指向端点0的指针
    unsigned int devnum;                // 设备在总线上的编号
    unsigned char address;              // 设备的地址
    unsigned char dev_class;            // 设备类别
    unsigned char dev_subclass;         // 设备子类别
    unsigned char dev_protocol;         // 设备协议
    unsigned char speed;                // 设备速度
    unsigned char Tusbspeed;            // USB版本号
    unsigned int portsc;                // 端口状态寄存器
    unsigned char bConfigurationValue;  // 当前配置的值
    unsigned char bNumConfigurations;   // 配置数量
    unsigned char bNumInterfaces;       // 接口数量
    unsigned char bInterfaceNumber;     // 当前接口的编号
    unsigned char bAlternateSetting;    // 当前接口的备选设置
    unsigned char bNumEndpoints;        // 端点数量
    unsigned char bInterfaceClass;      // 接口类别
    unsigned char bInterfaceSubClass;   // 接口子类别
    unsigned char bInterfaceProtocol;   // 接口协议
    unsigned char bInterfaceAssociation; // 接口关联
    struct usb_ctrlrequest req;         // 控制请求缓冲区
    struct list_head config_list;        // 配置链表
    struct list_head interface_list;     // 接口链表
    struct list_head endpoint_list;      // 端点链表
    spinlock_t lock;                    // 自旋锁,用于保护设备结构体
    unsigned long flags;                // 标志位
    // 其他可能的字段...
};

其他说明

  • struct usb_device 结构体包含了USB设备的所有相关信息,包括设备描述符、配置、接口、端点等。
  • 该结构体是USB设备驱动模型的基础,所有与USB设备交互的代码都需要使用这个结构体。
  • 在内核中,struct usb_device 结构体通常与USB设备驱动程序相关联,用于管理设备的生命周期和功能。

struct usb_driver

  struct usb_driver 是用于表示USB设备驱动程序的数据结构。它定义了驱动程序如何与USB核心通信,以及如何响应和处理USB设备的事件。以下是 struct usb_driver 结构体的详细说明:

struct usb_driver {
	/* 驱动程序的名称, 用于标识驱动程序*/
	const char *name;

	/*用于识别设备的ID表, 包含了驱动程序支持的所有设备的供应商ID、产品ID、设备类等信息*/
	struct usb_device_id id_table;

	/*探测函数, 当USB核心发现一个新的USB设备时,它会调用这个函数来尝试绑定一个驱动程序到该设备。如果探测成功,函数返回0*/
	int (*probe)(struct usb_interface *, const struct usb_device_id *);

	/*断开连接函数, 当USB设备被断开或被卸载时,USB核心会调用这个函数*/
	void (*disconnect)(struct usb_interface *);

	/* 挂起函数, 当USB设备进入挂起状态时,USB核心会调用这个函数*/
	int (*suspend)(struct usb_interface *, int);

	/*恢复函数, 当USB设备从挂起状态恢复时,USB核心会调用这个函数*/
	int (*resume)(struct usb_interface *, int);

	// 其他的函数指针...
};

其他函数指针

  除了上述的函数指针,struct usb_driver 可能还包含以下函数指针:

  • int (*pre_reset)(struct usb_interface *):在设备重置之前调用的函数指针。
  • int (*post_reset)(struct usb_interface *):在设备重置之后调用的函数指针。
  • int (*set_config)(struct usb_interface *, int):当设备的配置更改时调用的函数指针.
  • int (*get_altsetting)(struct usb_interface *, int):当需要获取接口的替代设置时调用的函数指针

struct usb_device_id

  1. 说明:
      
      struct usb_device_id 结构体是USB驱动程序中用于设备匹配的重要数据结构。通过定义设备ID表,驱动程序可以告诉内核它支持哪些USB设备,从而在设备连接时能够正确地进行绑定和处理。
  2. 数据结构定义如下:
struct usb_device_id {
    __u16 idVendor;         // 设备的供应商ID
    __u16 idProduct;        // 设备的产品ID
    __u16 bcdDevice_lo;     // 设备版本号(低字节)
    __u16 bcdDevice_hi;     // 设备版本号(高字节)
    __u8 bDeviceClass;      // 设备类别
    __u8 bDeviceSubClass;   // 设备子类别
    __u8 bDeviceProtocol;   // 设备协议
    __u8 bInterfaceClass;   // 接口类别
    __u8 bInterfaceSubClass; // 接口子类别
    __u8 bInterfaceProtocol; // 接口协议
    __u32 driver_info;      // 驱动程序私有信息
    // 其他字段...
};
  1. 使用示例:
      
    在USB驱动程序中,通常会定义一个设备ID表,包含多个 struct usb_device_id 结构体的实例,用于匹配支持的设备。例如:
      
static struct usb_device_id my_usb_table[] = {
	{ USB_DEVICE(0x1234,
		     0x5678) }, // 供应商ID为0x1234,产品ID为0x5678的设备
	{ USB_DEVICE_INFO(0x1234, 0x5678, 0x0100, 0x0200,
			  0x0300) }, // 设备版本等信息
	{}
};
MODULE_DEVICE_TABLE(usb, my_usb_table);

struct usb_interface

  1. 说明:
      
    Linux内核中,struct usb_interface 是用于表示USB接口的结构体。每个USB设备可以有一个或多个接口,每个接口可以支持不同的功能或协议。struct usb_interface 提供了描述和管理这些接口的必要信息。struct usb_interface 是USB接口管理的重要数据结构。它帮助驱动程序描述和管理USB设备的接口,通过与USB核心的交互,驱动程序可以有效地控制和操作USB设备的各个接口。
  2. struct usb_interface 结构体定义
      
struct usb_interface {
    struct usb_device *dev;                // 指向所属USB设备的指针
    struct usb_host_interface *altsetting;  // 指向当前选择的接口设置
    unsigned int num_altsetting;            // 备用设置的数量
    struct usb_host_interface *cur_altsetting; // 当前选择的备用设置
    struct usb_driver *driver;              // 指向接口驱动的指针
    void *dev_id;                           // 与设备相关的私有数据
    struct list_head list;                  // 链表,用于连接多个接口
    // 其他可能的字段...
};
  1. 功能和用途
    struct usb_interface 结构体用于管理和描述USB设备的接口。以下是其主要功能和用途:
  • 描述接口:提供有关接口的详细信息,如接口的类别、子类别、协议等。
  • 管理备用设置:允许驱动程序在不同的备用设置之间切换,从而支持不同的功能或数据流。
  • 驱动程序绑定:允许驱动程序与特定的接口进行绑定,以便正确处理该接口的请求和数据传输。
  • 与USB设备交互:提供与设备的交互接口,使驱动程序能够发送控制请求、读取数据等。

struct usb_host_interface

  1. 说明:
      
      usb_host_interface结构体主要用于描述USB接口的详细信息,包括接口的描述符、类驱动、USB驱动以及该接口下的所有端点信息。通过这个结构体,可以方便地管理和操作USB接口,包括设置、配置和传输数据等操作。
      
      usb_host_interface和usb_interface都是USB接口类型,但usb_host_interface比较特殊,用于支持 USB OTG 协议;而 usb_interface 是通用的 USB 接口类型,用于连接 USB 设备到主机控制器。
  2. 结构体定义
struct usb_host_interface {
    struct usb_interface_descriptor desc;  // 接口描述符
    struct usb_host_endpoint *endpoint;     // 指向端点数组的指针
    struct usb_host_interface *altsetting;  // 可选的替代设置
    int num_altsetting;                     // 替代设置的数量
};
  1. 用途
  • USB 设备驱动: usb_host_interface 结构体在 USB 设备驱动中被广泛使用,驱动程序可以通过这个结构体访问和操作 USB 设备的接口和端点。
  • 设备描述: 它帮助驱动程序获取和解析 USB 设备的接口描述信息,以便于正确地与设备进行交互。
  • 多接口支持: 当一个 USB 设备有多个接口时,这个结构体可以帮助管理这些接口及其替代设置。

struct urb

  1. 作用:
      struct urb 是 Linux 内核中用于表示 USB 请求块(USB Request Block)的数据结构。它是 USB 驱动程序与 USB 子系统之间进行通信的核心结构。urb 用于描述从主机到设备或从设备到主机的数据传输请求。
      
      类似比喻如下,usb 总线就像一条高速公路,货物、人物之类的可以看成是系统与设备交互的具体数据,而 urb 就可以看成是汽车。USB 的 endpoint 有 4 种不同类型,也就是说能在这条高速公路上流动的数据就有四种。但是汽车(urb)可以运载这四种数据,不过需要先告诉司机运送的是什么,目的地是哪里。
  2. 数据结构(include/linux/usb.h):
struct urb {
    /* 链接到其他 URB 的链表, 用于管理 URB 的列表 */
    struct list_head        urb_list; 
    /* 指向相关 USB 设备的指针, 指示该 URB 所属的 USB 设备 */
    struct usb_device       *dev;
    /* 用户定义的上下文指针, 可以在 URB 完成时用于传递特定的信息 */
    void                    *context; 
    /* 传输状态, 表示 URB 的当前状态。成功时为 0,失败时通常为负值 */
    int                     status; 
    /* 数据传输缓冲区的指针,存储要发送或接收的数据 */
    unsigned char           *transfer_buffer; 
    /* 传输缓冲区长度 */
    unsigned int            transfer_buffer_length;
    /* 实际传输的字节数,表示在完成传输后实际传输的数据量 */
    unsigned int            actual_length;
    /* 数据包数量, 适用于批量和等时传输 */
    unsigned int            number_of_packets;
    /* SG(Scatter-Gather)请求, 用于描述分散的缓冲区 */
    struct usb_sg_request   *sg; 
    /* 端点管道信息, 表示传输的方向和端点。 */
    unsigned int            pipe; 
    /* 超时时间, 指示传输请求的最大等待时间 */
    unsigned char           timeout;
    /* URB_SHORT_NOT_OK | ,这个变量可被设置为不同的位值,
     * 根据这个位,usb 驱动可以设置 urb 传输的状态 */
    unsigned int transfer_flags;

    /* 发送数据到设备或从设备接收数据的缓冲区,指向缓冲区的指针,
     * 这个指针可以是 OUT urb 或者是 In urb 。*/
    void *transfer_buffer;
    /* 用来以 DMA 方式向设备传输数据的缓冲区 */ 
    dma_addr_t transfer_dma; 
    /* transfer_buffer 或 transfer_dma 指向缓冲区的大小 =wMaxPacketSize,
     * USB2.0 全时速端点长度分别包括:8、16、32、64字节 */
    int transfer_buffer_length; 
    /* URB结束后,发送或接收数据的实际长度 */                 
    int actual_length;
    /* 指向控制 URB 的设置数据包的指针 */
    unsigned char *setup_packet; 
    /* 控制 URB 的设置数据包的 DMA 缓冲区 */
    dma_addr_t setup_dma; 		
    /* ... 其他字段 ... */
};
  1. URB 处理流程
  • 在 USB 设备驱动中创建 URB
struct urb *usb_alloc_urb(int iso_packets,int mem_flags);
  • 初始化传输端点类型
void usb_fill_control_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, unsigned char *setup_packet, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context)

void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context)

void usb_fill_int_urb(struct urb *urb,struct usb_device *dev, unsigned int pipe, void *transfer_buffer,intbuffer_length, usb_complete_t complete, void*context, int interval);
  • USB 设备驱动将 URB 提交给 USB 核心
int usb_submit_urb(struct urb *urb, intmem_flags);
  • GFP_ATOMIC:在中断处理函数、底半部、tasklet、定时器处理函数以及 urb 完成函数中,在调用者持有自旋锁或者读写锁时以及当驱动将 current->state 修改为非 TASK_RUNNING 时,应使用此标志。
  • GFP_NOIO:在存储设备的块 I/O 和错误处理路径中,应使用此标志;
  • GFP_KERNEL:如果没有任何理由使用 GFP_ATOMIC 和 GFP_NOIO ,就使用 GFP_ KERNEL 。
  • USB 核心将 URB 提交给 USB 主机控制器驱动
  • USB主机控制器处理,进行一次数据传送
  • 当一次 URB 传输完成后,USB 主机控制器驱动再原路返回通知 USB 设备驱动
  1. 示例
      一个简单的示例可能如下所示:
struct urb *my_urb;
my_urb = usb_alloc_urb(0, GFP_KERNEL); // 分配 URB
my_urb->dev = my_usb_device; // 设置设备
my_urb->transfer_buffer = my_buffer; // 设置传输缓冲区
my_urb->transfer_buffer_length = BUFFER_SIZE; // 设置缓冲区长度
my_urb->complete = my_urb_complete_callback; // 设置完成回调
usb_submit_urb(my_urb, GFP_KERNEL); // 提交 URB

// 在这个例子中,my_urb 被分配并配置,用于与 USB 设备进行数据传输。

六、PCIe

type0

xHCI 简单分析_#define_07

type1

xHCI 简单分析_PCIe_08

PCI Standard Capabilities

xHCI 简单分析_ci_09

PCIe Extended Capabilities

xHCI 简单分析_PCIe_10

xHCI 架构图

xHCI 简单分析_ci_11