当前位置: 首页 > wzjs >正文

网站建设培训哪家好有创意的网络营销案例

网站建设培训哪家好,有创意的网络营销案例,免费做苗木的网站,国际购物app系列文章目录 xHCI 简单分析 USB Root Hub 分析 USB Hub 检测设备 usb host 驱动之 urb xHCI那些事儿 PCIe MMIO、DMA、TLP PCIe配置空间与CPU访问机制 PCIe总线协议基础实战 文章目录 系列文章目录一、xHCI 初始化二、xHCI 驱动识别根集线器(RootHub)…

在这里插入图片描述

系列文章目录


xHCI 简单分析
USB Root Hub 分析
USB Hub 检测设备

usb host 驱动之 urb
xHCI那些事儿

PCIe MMIO、DMA、TLP
PCIe配置空间与CPU访问机制
PCIe总线协议基础实战


文章目录

  • 系列文章目录
  • 一、xHCI 初始化
  • 二、xHCI 驱动识别根集线器(RootHub)
  • 三、发送 USB URB 包
    • 1、设备描述符获取步骤
      • 步骤 1:建立事务(Setup Phase)
      • 步骤 2:数据事务(Data Phase)
      • 步骤 3:状态事务(Status Phase)
      • 步骤 4:二次获取完整描述符(可选)
      • 关键包类型总结
      • 补充说明
    • 2、主机发送 SETUP 令牌包
      • usb_get_device_descriptor
      • usb_submit_urb
      • rh_urb_enqueue
      • rh_call_control
      • xhci_urb_enqueue
    • 3、事件处理
      • (1)HCD 中断注册
      • (2)中断处理
      • (3)usb_hcd_giveback_urb
      • (4)软中断处理
      • (5)__usb_hcd_giveback_urb


一、xHCI 初始化

    在文件 drivers/usb/host/xhci-pci.c 中有 module_init(xhci_pci_init); 函数,其实现 xHCI 的初始化。

在这里插入图片描述

二、xHCI 驱动识别根集线器(RootHub)

    xHCI 驱动调用 xhci_pci_probe 函数识别并初始化根集线器。xhci_pci_probe 函数最终会调用 usb_add_hcd 函数来添加 HCD ,该函数中有如下代码来初始化工作队列。

// drivers/usb/core/hcd.c
int usb_add_hcd(struct usb_hcd *hcd,unsigned int irqnum, unsigned long irqflags)
{// ...init_giveback_urb_bh(&hcd->high_prio_bh);init_giveback_urb_bh(&hcd->low_prio_bh);// ...
}static void init_giveback_urb_bh(struct giveback_urb_bh *bh)
{spin_lock_init(&bh->lock);INIT_LIST_HEAD(&bh->head);INIT_WORK(&bh->bh, usb_giveback_urb_bh);
}

    其初始化两个有优先级的工作队列,工作队列执行函数为 usb_giveback_urb_bh ,该函数实现了成功发送完 URB 后的清理及通知工作。

三、发送 USB URB 包

    USB 可以进行批量传输、中断传输、实时传输、控制传输。我们选取控制传输 usb_get_device_descriptor 函数来说明传输响应过程。

1、设备描述符获取步骤

在 USB 设备枚举过程中,获取设备描述符 的传输包流程分为以下步骤(以标准控制传输为例):


步骤 1:建立事务(Setup Phase)

  1. 主机发送 SETUP 令牌包
    • 包类型SETUP(PID=0xD)
    • 数据方向:主机→设备
    • 内容:包含标准请求 GET_DESCRIPTOR 的请求头(8 字节),例如:
      bmRequestType 	= 0x80 		// (Host→Device, 标准请求, 接收者为设备)
      bRequest 		= 0x06 		// (GET_DESCRIPTOR)
      wValue 			= 0x0100 	// (描述符类型为设备描述符)
      wIndex 			= 0x0000 	// (语言 ID 为 0)
      wLength 		= 0x0012 	// (请求 18 字节,但首次可能仅返回前 8 字节)
      

步骤 2:数据事务(Data Phase)

  1. 设备返回数据包
    • 包类型DATA0(PID=0x3)
    • 数据方向:设备→主机
    • 内容:设备描述符的前 8 字节(包含 bLengthbDescriptorType 等关键字段)。
    • 握手包:设备发送 ACK(PID=0x2)确认传输完成。

步骤 3:状态事务(Status Phase)

  1. 主机发送 ACK 握手包
    • 包类型ACK(PID=0x2)
    • 数据方向:主机→设备
    • 作用:确认设备描述符数据接收正确。

步骤 4:二次获取完整描述符(可选)

若首次仅获取前 8 字节(因端点 0 最大包长限制),主机需重新发送请求:

  1. 主机发送 SETUP 令牌包

    • 包类型SETUP(PID=0xD)
    • 内容wLength=0x0012(请求完整 18 字节)。
  2. 设备返回数据包

    • 包类型DATA1(PID=0xB,交替使用 DATA0/DATA1)
    • 内容:完整设备描述符(18 字节)。
  3. 主机发送 ACK 握手包

    • 包类型ACK(PID=0x2)
    • 作用:确认完整描述符接收完成。

关键包类型总结

步骤包类型方向作用
建立事务SETUPHost→Dev发送 GET_DESCRIPTOR 请求
数据事务DATA0/DATA1Dev→Host返回描述符数据
状态事务ACKHost→Dev确认数据接收完成

补充说明

  • 首次获取限制:设备端点 0 的最大包长可能仅为 8 字节,因此首次传输可能仅返回前 8 字节。
  • 地址分配:获取设备描述符后,主机分配新地址(通过 SET_ADDRESS 请求),后续通信使用新地址。
  • 错误处理:若设备未响应,主机会重试或标记设备为不可用。

在这里插入图片描述

【USB笔记】 设备描述符Device Descriptor
USB主机是如何获取设备描述符

2、主机发送 SETUP 令牌包

    主机发送 SETUP 令牌包在系统中调用大致如下:

usb_get_device_descriptor

// drivers/usb/core/message.c
usb_get_device_descriptor();usb_get_descriptor();usb_control_msg();usb_internal_control_msg();usb_alloc_urb();			// 分配 URB 空间usb_fill_control_urb();		// 填充 URBusb_start_wait_urb();		usb_submit_urb();								// 发送 URB 包wait_for_completion_timeout(&ctx.done, expire);	// 等待设备返回设备描述符

usb_submit_urb

    usb_submit_urb() 是 ​USB 主机控制器驱动(HCD)与核心层交互的核心函数,负责将 URB(USB 请求块)提交给具体的主机控制器(如 EHCI、xHCI)进行实际传输。

usb_submit_urb();				// 发送 URB 包usb_hcd_submit_urb();rh_urb_enqueue(hcd, urb);	// 发送给 RootHub,直接获取rh_call_control();			//control urbrh_queue_status(hcd, urb);		//int urb 循环检测 hub 的状态hcd->driver->urb_enqueue(hcd, urb, mem_flags); // 即调用 xhci_urb_enqueue,从设备中获取

    usb_hcd_submit_urb() 代码如下:

int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) {// ...if (is_root_hub(urb->dev)) {	// 发送给 RootHub,调用 rh_urb_enqueue 获取status = rh_urb_enqueue(hcd, urb);} else {status = map_urb_for_dma(hcd, urb, mem_flags);	// 分配 DMA if (likely(status == 0)) {// 即调用 xhci_urb_enqueue,从设备中获取status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);if (unlikely(status))unmap_urb_for_dma(hcd, urb);}}// ...
}

rh_urb_enqueue

    rh_urb_enqueue 是 ​根集线器(Root Hub)的 URB 处理函数,专门用于处理与根集线器相关的 URB 请求。其核心功能是 ​将 URB 提交给根集线器对应的硬件控制器,并触发控制传输或中断传输的流程。

  1. ​核心功能
  • URB 提交:将 URB 传递给根集线器的主机控制器驱动(HCD)。
  • ​传输类型分发:根据 URB 的端点类型(控制传输或中断传输)选择不同的处理路径。
  • ​硬件状态同步:管理根集线器的电源状态和远程唤醒功能。
// drivers/usb/core/hcd.c
static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
{if (usb_endpoint_xfer_int(&urb->ep->desc))return rh_queue_status (hcd, urb);if (usb_endpoint_xfer_control(&urb->ep->desc))return rh_call_control (hcd, urb);return -EINVAL;
}

    其中:

  • 中断传输:调用 rh_queue_status,通过定时器轮询根集线器状态。
  • 控制传输:调用 rh_call_control,构造控制请求并触发硬件操作。

rh_call_control

rh_call_control();			//control urbusb_hcd_link_urb_to_ep(hcd, urb);list_add_tail(&urb->urb_list, &urb->ep->urb_list);	// 添加到端点的 urb_list 链表中urb->hcpriv = hcd;hcd->driver->hub_control();		// 即调用 xhci_hub_control 函数usb_hcd_unlink_urb_from_ep(hcd, urb);list_del_init(&urb->urb_list);usb_hcd_giveback_urb(hcd, urb, status);list_add_tail(&urb->urb_list, &bh->head);	// 添加到工作队列中的链表里queue_work(system_bh_highpri_wq, &bh->bh);	// 触发执行 usb_giveback_urb_bh 函数// 或调用 queue_work(system_bh_wq, &bh->bh);

    rh_call_control 函数用来处理送给根集线器的主机控制器驱动(HCD)的控制传输。其通过 MMIO 直接获取相关的信息,如此处的设备描述符。同时启动工作队列,触发 usb_giveback_urb_bh 函数来对 URB 清理,同时通知 wait_for_completion_timeout 函数已完成了数据获取。

xhci_urb_enqueue

    对于外接的 USB 设备(如 U 盘)的设备描述符获取,则走 hcd->driver->urb_enqueue(hcd, urb, mem_flags)xhci_urb_enqueue 这条链路。

xhci_urb_enqueue();xhci_queue_ctrl_tx();	// 控制传输处理struct xhci_ring *ep_ring = xhci_urb_to_transfer_ring(xhci, urb);	// 即:xhci->devs[slot_id]->eps[ep_index]queue_trb();			// URB 转 TRB,并入传输环giveback_first_trb();	xhci_ring_ep_doorbell();	// 按响门铃寄存器,此时 xHCI 固件会处理此命令__le32 __iomem *db_addr = &xhci->dba->doorbell[slot_id];writel(DB_VALUE(ep_index, stream_id), db_addr);xhci_queue_bulk_tx();	xhci_queue_intr_tx();xhci_queue_isoc_tx_prepare();

    xhci_ring_ep_doorbell() 会按响门铃寄存器,此时 xHCI 固件会处理此命令,处理好后其会生成传输事件 TRB(Transfer Event TRB) 并插入事件环,同时给主机产生中断。主机会在中断处理函数 xhci_irq() 中处理此传输事件。

3、事件处理

(1)HCD 中断注册

    xHCI 驱动识别 HCD 时,其会调用 usb_add_hcd 函数初始化并添加主机控制器,在此函数中会获取中断号,并进行注册。

xhci_pci_probe();xhci_pci_common_probe(dev, id);usb_hcd_pci_probe(dev, &xhci_pci_hc_driver);usb_add_hcd(hcd, hcd_irq, IRQF_SHARED);usb_hcd_request_irqs(hcd, irqnum, irqflags);hcd->driver->start(hcd); 		// => xhci_pci_run
  1. usb_hcd_request_irqs
// drivers/usb/core/hcd.c
static int usb_hcd_request_irqs(struct usb_hcd *hcd,unsigned int irqnum, unsigned long irqflags) {if (hcd->driver->irq) {retval = request_irq(irqnum, &usb_hcd_irq, irqflags,hcd->irq_descr, hcd);hcd->irq = irqnum;}
}
  1. xhci_pci_run
// drivers/usb/host/xhci-pci.c
static int xhci_pci_run(struct usb_hcd *hcd) {int ret;if (usb_hcd_is_primary_hcd(hcd)) {ret = xhci_try_enable_msi(hcd);if (ret)return ret;}return xhci_run(hcd);
}static int xhci_try_enable_msi(struct usb_hcd *hcd) {// 注册中断ret = request_irq(pci_irq_vector(pdev, 0), xhci_msi_irq, 0, "xhci_hcd",xhci_to_hcd(xhci));
}// drivers/usb/host/xhci-ring.c
irqreturn_t xhci_msi_irq(int irq, void *hcd)
{return xhci_irq(hcd);
}	  

(2)中断处理

xhci_irq(hcd);xhci_handle_events(xhci, xhci->interrupters[0]);xhci_handle_event_trb(xhci, ir, ir->event_ring->dequeue);// 处理命令完成事件 TRB(Command Completion Event TRB)handle_cmd_completion(xhci, &event->event_cmd);	// 处理 Port Status Change Event TRB ?handle_port_status(xhci, event);// 处理 传输事件 TRB(Transfer Event TRB)handle_tx_event(xhci, ir, &event->trans_event);// 处理 Device Notification Event TRB ?handle_device_notification(xhci, event);handle_vendor_event(xhci, event, trb_type);

    handle_tx_event 函数正是处理获取从设备返回的设备描述符的地方。

// drivers/usb/host/xhci-ring.c
static int handle_tx_event(struct xhci_hcd *xhci,struct xhci_interrupter *ir,struct xhci_transfer_event *event) {// ...struct xhci_td *td = list_first_entry(&ep_ring->td_list, struct xhci_td,  td_list);if (usb_endpoint_xfer_control(&td->urb->ep->desc))		// controlprocess_ctrl_td(xhci, ep, ep_ring, td, ep_trb, event);else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))	// isocprocess_isoc_td(xhci, ep, ep_ring, td, ep_trb, event);elseprocess_bulk_intr_td(xhci, ep, ep_ring, td, ep_trb, event);	// 中断传输
}	

    usb_get_device_descriptor 属于控制传输,所以这里走的是 process_ctrl_td 这条路径。

process_ctrl_td();td->urb->actual_length = requested - remaining;		// 实际接收到的数据长度finish_td(xhci, ep, ep_ring, td, trb_comp_code);	xhci_td_cleanup(xhci, td, ep_ring, td->status);xhci_unmap_td_bounce_buffer(xhci, ep_ring, td);	// 清理反弹缓冲区(Bounce Buffer)xhci_giveback_urb_in_irq(xhci, td, status);

    xhci_giveback_urb_in_irq 执行一些必要的清理工作,包括把 URB 从端点链表中脱离,同时调用 usb_hcd_giveback_urb 函数执行软中断进行清理。

// drivers/usb/host/xhci-ring.c
static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,struct xhci_td *cur_td, int status) {struct urb	*urb		= cur_td->urb;struct urb_priv	*urb_priv	= urb->hcpriv;struct usb_hcd	*hcd		= bus_to_hcd(urb->dev->bus);xhci_urb_free_priv(urb_priv);usb_hcd_unlink_urb_from_ep(hcd, urb);	// 从端点链表中脱离trace_xhci_urb_giveback(urb);usb_hcd_giveback_urb(hcd, urb, status);	// 启动工作队列,执行软中断,做必要的清理工作
}

    回顾上文讲述 rh_call_control 时,当 URB 是发送给根集线器的主机控制器驱动(HCD)时,其最后同样会调用 usb_hcd_giveback_urb 函数进行清理工作。

(3)usb_hcd_giveback_urb

// drivers/usb/core/hcd.c
void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
{// 没在 bh 中并且不是 HCD,则开始进行清理工作if (!hcd_giveback_urb_in_bh(hcd) && !is_root_hub(urb->dev)) {__usb_hcd_giveback_urb(urb);	return;}// 同步端点和中断端点走高优先级中断if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe))bh = &hcd->high_prio_bh;elsebh = &hcd->low_prio_bh;spin_lock(&bh->lock);list_add_tail(&urb->urb_list, &bh->head);	// 加入 bh 链表中running = bh->running;spin_unlock(&bh->lock);if (running)	// 运行中则等待;else if (bh->high_prio)	// 启动工作队列queue_work(system_bh_highpri_wq, &bh->bh);elsequeue_work(system_bh_wq, &bh->bh);
}

(4)软中断处理

    软中断触发流程大致如下,其最后调用处理函数 usb_giveback_urb_bh

__do_softirq();handle_softirqs(false);h->action(); => tasklet_action()workqueue_softirq_action(false);bh_workerprocess_scheduled_works(worker);process_one_work(worker, work);worker->current_func(work);// INIT_WORK(&bh->bh, usb_giveback_urb_bh);usb_giveback_urb_bh();

    usb_giveback_urb_bh 是 ​USB 子系统的底半部(Bottom Half)处理函数,负责将完成传输的 URB(USB Request Block)返回给设备驱动。其核心作用是将 URB 的状态同步到用户空间或驱动层,并触发驱动注册的回调函数。

  1. 核心功能如下:
  • URB 完成通知:将 URB 的传输结果(如成功、错误、取消)传递给设备驱动。
  • 资源回收:释放 URB 占用的 DMA 缓冲区、减少引用计数等。
  • 中断上下文切换:在硬件中断上下文中触发异步回调,避免阻塞中断处理。
  1. 底半部处理逻辑
// drivers/usb/core/hcd.c
static void usb_giveback_urb_bh(struct work_struct *work)
{struct giveback_urb_bh *bh =container_of(work, struct giveback_urb_bh, bh);struct list_head local_list;spin_lock_irq(&bh->lock);bh->running = true;list_replace_init(&bh->head, &local_list);	// 原子替换链表spin_unlock_irq(&bh->lock);// 循环处理已发送好的 URBwhile (!list_empty(&local_list)) {struct urb *urb;urb = list_entry(local_list.next, struct urb, urb_list);list_del_init(&urb->urb_list);bh->completing_ep = urb->ep;__usb_hcd_giveback_urb(urb);	// 真正的清理与通知处理的地方bh->completing_ep = NULL;}/** giveback new URBs next time to prevent this function* from not exiting for a long time.*/spin_lock_irq(&bh->lock);// 如果还有需要处理的 URB 则开启下一轮处理if (!list_empty(&bh->head)) {if (bh->high_prio)queue_work(system_bh_highpri_wq, &bh->bh);elsequeue_work(system_bh_wq, &bh->bh);}bh->running = false;spin_unlock_irq(&bh->lock);
}

    注意: usb_hcd_giveback_urb 中也调用 __usb_hcd_giveback_urb(urb) 函数进行清理与通知。

(5)__usb_hcd_giveback_urb

__usb_hcd_giveback_urb(urb);unmap_urb_for_dma(hcd, urb);usb_unanchor_urb(urb);urb->complete(urb); 	// => usb_api_blocking_completion

    urb->complete(urb)complete 函数会对等待进行通知,其赋值一般是在初始化 URB 时进行的,如 usb_fill_control_urb

// drivers/usb/core/message.c
usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data,len, usb_api_blocking_completion, NULL);usb_fill_int_urb(urb, usb_dev, pipe, data, len,usb_api_blocking_completion, NULL,ep->desc.bInterval);usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,usb_api_blocking_completion, NULL);

usb_api_blocking_completion 实现如下,其会对如 usb_start_wait_urb 函数进行通知,传输已完成。

// drivers/usb/core/message.c
static void usb_api_blocking_completion(struct urb *urb) {struct api_context *ctx = urb->context;ctx->status = urb->status;complete(&ctx->done);	// 发送
}static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length) {struct api_context ctx;unsigned long expire;int retval;init_completion(&ctx.done);urb->context = &ctx;urb->actual_length = 0;retval = usb_submit_urb(urb, GFP_NOIO);expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;if (!wait_for_completion_timeout(&ctx.done, expire)) {	// 等待usb_kill_urb(urb);retval = (ctx.status == -ENOENT ? -ETIMEDOUT : ctx.status);} elseretval = ctx.status;
out:if (actual_length)*actual_length = urb->actual_length;usb_free_urb(urb);return retval;
}

    至此,整个发送与接收流程大致已分析完成。关于 DMA 分配与释放请参考此链接。


   
 

http://www.dtcms.com/wzjs/405090.html

相关文章:

  • lamp wordpress主题太原百度快速优化排名
  • 品牌推广部河南网站seo
  • 哈尔滨大型网站制作开发站长素材网站
  • ppt网站超链接怎么做企业seo顾问服务阿亮
  • 论坛门户网站开发企业自助建站
  • 如何承接设计网站建设英语seo什么意思
  • 数字广东网络建设有限公司电话杭州网络排名优化
  • 沈阳网站建设多少钱邵阳网站seo
  • 推荐几个没封的正能量网站产品市场营销策划方案
  • ubuntu中wordpressseo推广系统
  • 容桂营销网站建设建站公司最新报价
  • 网站邮箱接口怎么设置搜索推广公司
  • 网站的横幅怎么做吸引人的软文
  • wordpress虚拟3d网站sem代运营
  • 邢台移动网站建设报价比较正规的代运营
  • 常德做网站2022适合小学生的简短新闻
  • 网站建设中常用的技术有哪些seo超级外链发布
  • 网站建设课程设计论文百度网站推广申请
  • 网站建设与维护教学课件acca少女网课视频
  • 软件下载网站整站源码成年s8视频加密线路
  • 能用VUE做网站自己怎么做网站网页
  • 做设计最好的参考网站百度云搜索引擎入口盘多多
  • 深圳做棋牌网站建设哪家好trinseo公司
  • 济南城乡建设官方网站线下推广活动策划方案
  • 中国怎么样做跨境网站北京外包seo公司
  • 如何做赚钱的网站域名备案查询
  • 温州网站设计制作课程关键词一般是指什么
  • 旅游网站html5代码可口可乐软文范例
  • 长安网站建设网络推广优秀的营销策划案例
  • 如何做网站栏目规划英文站友情链接去哪里查