文章目录
- 一、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 内核启动简述
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
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
acpi_pci_root_init
函数调用 acpi_pci_root_add
和 acpi_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
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_hcd
,xhci_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
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_hcd
和 Root Hub
。Root 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 。
- Bus:
总线是处理器和设备之间的通道。总线有多种类型,每种总线可以挂载多个设备。如:usb、pci、i2c 和 platform(虚拟)等;
- 总线上有两个重要的链表:1)设备(
device
)链表;2)驱动(driver
)链表; - 每种总线下面可以挂载许多设备;
- 每种总线下可以用很多驱动;
- driver
驱动程序是在 CPU 运行时,提供操作的软件接口。所有的设备必须有与之配套驱动程序才能正常工作。一个驱动程序可以驱动多个类似或者完全不同的设备。 - device
设备就是连接在总线上的物理实体。设备是有功能之分的。具有相同功能的设备被归到一个类,如输入设备(鼠标,键盘,游戏杆等)、输出设备等。
Device 和 driver 两者关系
两者有如下关系:
- 每次出现一个设备就要向总线汇报,每次出现一个驱动,也要向总线注册
- 系统初始化的时候,会扫描连接了哪些设备,为每一个设备建立起一个struct device的变量,并加入设备链表;
- 每次注册一个驱动,就要准备一个
struct device_driver
结构的变量,并加入驱动链表; - 这样所有的设备都挂载到总线上,当加载驱动时,驱动就去总线上找到自己对应的设备,或者先把驱动注册上,来了一个设备就去总线找驱动。
- 如果只有设备却没有对应的驱动,那么设备无法工作;如果只有驱动却没有设备,驱动也起不了任何作用。
五、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;
/* ... ... */
};
作用:
- 该结构体的使用通常涉及到USB设备的枚举、配置、数据传输等操作。
- 在USB设备连接或断开时,内核会更新这个结构体的信息,以反映当前USB总线的状态。
struct usb_hcd
- 说明:
在Linux内核中,struct usb_hcd(USB Host Controller Driver)是用于表示USB主控制器驱动的核心数据结构。它提供了USB主控制器的相关信息,并为USB设备的管理和交互提供了接口。 - 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; // 标志位
// 其他可能的字段...
};
- 作用:
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
- 说明:
struct hc_driver 结构体定义了USB主机控制器驱动程序与USB核心进行交互的行为。通过实现这些函数指针,驱动程序能够处理设备的连接、断开、挂起和恢复等事件。USB核心使用这些函数来管理设备生命周期,并确保设备能够与系统中的其他组件正确地通信。 - 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);
// 其他可能的函数指针...
};
- 主要作用
- 定义接口: 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
- 说明:
struct usb_device_id 结构体是USB驱动程序中用于设备匹配的重要数据结构。通过定义设备ID表,驱动程序可以告诉内核它支持哪些USB设备,从而在设备连接时能够正确地进行绑定和处理。 - 数据结构定义如下:
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; // 驱动程序私有信息
// 其他字段...
};
- 使用示例:
在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
- 说明:
Linux内核中,struct usb_interface 是用于表示USB接口的结构体。每个USB设备可以有一个或多个接口,每个接口可以支持不同的功能或协议。struct usb_interface 提供了描述和管理这些接口的必要信息。struct usb_interface 是USB接口管理的重要数据结构。它帮助驱动程序描述和管理USB设备的接口,通过与USB核心的交互,驱动程序可以有效地控制和操作USB设备的各个接口。 - 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; // 链表,用于连接多个接口
// 其他可能的字段...
};
- 功能和用途
struct usb_interface 结构体用于管理和描述USB设备的接口。以下是其主要功能和用途:
- 描述接口:提供有关接口的详细信息,如接口的类别、子类别、协议等。
- 管理备用设置:允许驱动程序在不同的备用设置之间切换,从而支持不同的功能或数据流。
- 驱动程序绑定:允许驱动程序与特定的接口进行绑定,以便正确处理该接口的请求和数据传输。
- 与USB设备交互:提供与设备的交互接口,使驱动程序能够发送控制请求、读取数据等。
struct usb_host_interface
- 说明:
usb_host_interface结构体主要用于描述USB接口的详细信息,包括接口的描述符、类驱动、USB驱动以及该接口下的所有端点信息。通过这个结构体,可以方便地管理和操作USB接口,包括设置、配置和传输数据等操作。
usb_host_interface和usb_interface都是USB接口类型,但usb_host_interface比较特殊,用于支持 USB OTG 协议;而 usb_interface 是通用的 USB 接口类型,用于连接 USB 设备到主机控制器。 - 结构体定义
struct usb_host_interface {
struct usb_interface_descriptor desc; // 接口描述符
struct usb_host_endpoint *endpoint; // 指向端点数组的指针
struct usb_host_interface *altsetting; // 可选的替代设置
int num_altsetting; // 替代设置的数量
};
- 用途
- USB 设备驱动: usb_host_interface 结构体在 USB 设备驱动中被广泛使用,驱动程序可以通过这个结构体访问和操作 USB 设备的接口和端点。
- 设备描述: 它帮助驱动程序获取和解析 USB 设备的接口描述信息,以便于正确地与设备进行交互。
- 多接口支持: 当一个 USB 设备有多个接口时,这个结构体可以帮助管理这些接口及其替代设置。
struct urb
- 作用:
struct urb 是 Linux 内核中用于表示 USB 请求块(USB Request Block)的数据结构。它是 USB 驱动程序与 USB 子系统之间进行通信的核心结构。urb 用于描述从主机到设备或从设备到主机的数据传输请求。
类似比喻如下,usb 总线就像一条高速公路,货物、人物之类的可以看成是系统与设备交互的具体数据,而 urb 就可以看成是汽车。USB 的 endpoint 有 4 种不同类型,也就是说能在这条高速公路上流动的数据就有四种。但是汽车(urb)可以运载这四种数据,不过需要先告诉司机运送的是什么,目的地是哪里。 - 数据结构(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;
/* ... 其他字段 ... */
};
- 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 设备驱动
- 示例
一个简单的示例可能如下所示:
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
type1
PCI Standard Capabilities
PCIe Extended Capabilities
xHCI 架构图
☆