USB 数据接收机制

前言

USB(通用串行总线)作为现代计算机和嵌入式设备中广泛使用的数据传输协议,提供了高效、稳定的通信方式。本文将重点探讨 USB 数据接收的工作机制、关键流程及其在 Linux 驱动中的实现。

USB 数据传输 4 种模式

USB 采用主从架构(Host-Device),所有数据传输均由主机(Host) 发起,设备(Device) 被动响应。USB 数据传输分为四种模式:

  • 控制传输(Control Transfer):用于设备初始化控制命令的交换,如枚举(Enumeration)。
  • 中断传输(Interrupt Transfer):用于低延迟、小数据量的传输,如键盘、鼠标。
  • 批量传输(Bulk Transfer):用于大数据量的可靠传输,如 U 盘、网卡。
  • 同步传输(Isochronous Transfer):用于对时延敏感的数据流,如音视频传输。

无论哪种传输模式,USB 接收的核心逻辑都是相似的——主机向设备请求数据,设备返回数据,主机确认接收

USB 数据传输 3 种数据包

USB 传输的基本单元是数据包,分为三类:

  • 令牌包(Token Packet):指示数据传输的方向(IN 或 OUT)和目标地址。
  • 数据包(Data Packet):包含实际的数据内容。
  • 握手包(Handshake Packet):用于确认数据包的接收情况,如 ACK(确认)、NAK(未就绪)等。

USB 接收流程

USB 设备的数据接收通常是主机请求数据后,设备返回相应数据包的过程。完整的数据接收流程如下:

  1. 主机发送 IN 令牌:主机请求设备发送数据。
  2. 设备响应数据包:如果有数据可供传输,设备发送 DATA 包,否则返回 NAK。
  3. 主机确认:若数据正确接收,主机返回 ACK,否则可能请求重传。
  4. 循环提交新的接收请求:USB 设备通常需要不断提交新的接收请求以持续接收数据。

示例:USB 键盘的接收过程

  1. 主机周期性发送 IN 请求。
  2. 键盘设备响应 DATA 包(包含按键信息)。
  3. 主机解析数据包,将按键信息传递到操作系统。

USB 驱动中的接收处理

以 usbnet.c 为例,当 USB 设备接收到数据包后,内核会调用 rx_complete 回调函数。

static void usbnet_rx_complete(struct urb *urb) {
    struct sk_buff *skb = urb->context;
    if (urb->status == 0) {
        netif_rx(skb);  // 将数据包提交给网络协议栈
    }
    usbnet_rx_submit(dev);  // 重新提交接收请求,继续监听数据
}

netif_rx(skb):将接收到的数据包交给网络协议栈处理。
usbnet_rx_submit(dev):重新提交接收请求,确保持续接收数据。

循环调用(rx_submit <-> rx_complete)

  • rx_submit 提交一个新的接收请求。
  • 当数据到达时,rx_complete 处理数据,并再次调用 rx_submit 继续监听。
  • 这个过程形成了一个 数据接收的循环机制,保证数据流不中断。

所以,USB 的数据传输机制本质上是基于主机的轮询(Polling),而非设备主动触发的中断。

为何采用轮询?

  • 避免总线冲突:USB 支持多达 127 个设备,轮询机制防止多个设备同时发送数据
    • 联想到 TTCAN
    • 当 CAN 节点数目过多时,仲裁冲突显著增加,改用时间轮询的数据传输机制可以有效改善这个问题。
  • 电源管理:主机可以控制设备的唤醒和休眠状态,节省能耗。
  • 确定性延迟:轮询周期固定,适合实时性要求高的设备(如音频设备)。

对比传统中断机制

  • 硬件中断:设备通过中断线(IRQ)直接通知CPU,立即抢占处理(如键盘的PS/2接口)。
  • USB“中断”:实为高频轮询,延迟取决于主机轮询间隔(例如1ms),无法做到真正的即时响应。

USB 数据同步(DATA0 / DATA1)

USB 采用 DATA0 / DATA1 标志位进行数据同步:

  • 设备发送的数据包会交替使用 DATA0 和 DATA1。
  • 主机检查数据包的标志位,如果接收数据重复,会丢弃该数据包,避免数据重复。

主机接收设备发送的完整流程

  1. 主机发送 IN 令牌包(IN Token Packet)
    主机向设备请求数据,表示它准备接收数据。
  2. 设备发送数据包(DATA0 / DATA1)
    设备附带一个 DATA0 / DATA1 标志位,确保数据包的唯一性。
  3. 主机检查数据并发送响应(ACK / NAK / STALL)
    ACK(Acknowledgment):如果数据正确接收,主机返回 ACK,设备继续发送下一个数据包。
    NAK(Negative Acknowledgment):如果主机暂时无法处理数据,返回 NAK,设备稍后重试发送。
    STALL:如果设备遇到严重错误,主机可能需要重新初始化通信。

主机发送设备接收数据的完整流程

  1. 主机发送 OUT 令牌包(OUT Token Packet)
    告诉设备接下来要发送数据。
    此时,设备不会立即回应,主机直接进入下一步。设备仍然可以在数据包后进行确认。
  2. 主机发送数据包(DATA0 / DATA1)
    主机附带一个 DATA0 或 DATA1 标志位,防止数据重复。
  3. 设备确认(ACK / NAK / STALL)
    ACK(Acknowledgment):数据正确接收,设备通知主机可以发送下一个数据包。
    NAK(Negative Acknowledgment):设备暂时无法处理,主机稍后重试。
    STALL:设备遇到严重错误,主机可能需要重新初始化传输。

如果数据包丢失或损坏怎么办?

  1. 如果 OUT 令牌包丢失(未到达设备)
    设备不会收到数据,自然不会返回 ACK,主机会超时并重新发送。
  2. 如果数据包(DATA0 / DATA1)丢失或损坏(例如 CRC 校验失败)
    设备不会发送 ACK,主机检测到超时后会重发该数据包(相同的 DATA0 / DATA1 标志位)。(解决丢包问题)
    设备通过 检查 DATA0 / DATA1 标志位,避免重复处理数据。
  3. 如果设备的 ACK 丢失了(主机没收到 ACK)
    主机会误以为设备没有收到数据,因此会重发相同的 OUT 令牌和数据包。
    设备检查 DATA0 / DATA1,如果是重复数据,则丢弃,并重新发送 ACK(解决接收重复包问题)

USB 错误处理

USB 采用 CRC(循环冗余校验) 检测数据包是否损坏:

  • 如果主机检测到数据包错误,不发送 ACK,设备会自动重发。
  • 如果主机长时间未收到正确数据,可能会触发超时机制并中断传输。

USB 接收性能优化

在高吞吐量应用(如 USB 网卡、U 盘)中,提高 USB 接收性能很重要,下面是几种方法:

  1. 使用多 URB 进行并行接收
    通过预提交多个 URB,减少等待时间,提高数据吞吐量。
  2. 增加缓冲区(Buffer)
    适当增大接收缓冲区,减少 CPU 频繁处理小包的开销。
  3. 采用 DMA 传输
    许多高性能 USB 控制器支持 DMA,可减少 CPU 负担,提高传输效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Li-Yongjun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值