USB之基本协议和数据波形1
============= 本系列参考 =============
《圈圈教你玩USB》、《Linux那些事儿之我是USB》
协议文档:https://www.usb.org/document-library/usb-20-specification usb_20_20190524/usb_20.pdf
调试工具:Beagle USB 480 逻辑分析仪
====================================
前言:
我们先不一上来讲USB大而全的协议规范文档, 会让人退而却步, 只要有协议, 在数据传输上波形就有规律可循, 翻译成数据, 也先不管USB1.1/2.0等版本, 因为最终的传输单元是一样的
一. 最基本传输单位 --包(packet)
1.电气信号:
a. 采用D+/D-差分信号传输, LSB在前, NRZI编码也就是0反转, 1不反转, 遇到连续6个1强插一个0
b. 低速Lowspeed 1.5Mb/s, 全速Fullspeed 12Mb/s, 高速Highspeed 480Mb/s, USB1.1支持L/F USB2.0支持L/F/H USB3.0也支持L/F/H 同时支持OTG功能
c. OTG(on the go) 就是多了根ID线, 用于判断主控器作Host还是device
d. L/F S采用电压传输(3.3v), HS采用电流传输(等效电阻后示波器显示400mv)
e. 传输方向以Host为准, 即IN表示device数据到Host, OUT表示Host数据到device
f. 插入上电波形分析(下面单独抽出来分析)
2. packet格式
SYNC同步域 + PID域 + 数据域 + CRC + EOP
a. 同步域: L/FS 固定00000001, HS前面31个0后一个1
b. PID占一个字节,高4bit是低4bit的反码, 用于校验PID本身, 而PID[3:0] 表示该packet的类型(协议文档8.3.1):
打*表示USB1.1不支持的, 而USB2.0全支持, 这里要特别注意令牌包, 任何事务传输, 必须先发个令牌包说明意图, 至于后面是否需要数据包还是握手吧取决事务类型(下面会说)
c. 数据域是可选的, 取决PID是不是数据包(DATA0/1/2/M)
d. CRC也是可选的, PID自校验,所以只对数据域校验, 若数据域没有那CRC也没有, 令牌包的数据域采用CRC5校验, 数据包的数据域采用CRC16校验
e. EOP结束包, 对于L/FS是两个数据位宽的SE0信号(D+/D-都是0), 对于HS使用故意位填充表示(待具体解释)
f. 空闲状态, 在SYNC同步域前和EOP后 总线上处于空闲状态, L/FS是一根高电平一根低电平(也就是J或K状态, 后面会讲), HS是SE0表示空闲状态
g. SYNC同步域、EOP、CRC是硬件发射器自动添加和硬件接收器自动解析的, 软件看到的只有PID域和数据域
2. packet类型
根据PID[3:0]可以将包的类型分成4类
a. 令牌包: 一次USB传输必须首发令牌包, 告知意图, 同时后面数据表示跟哪个设备及端点通信, 这点很重要, 设想一下一个Host接了很多外设, Host发出的信号会到达所有hub和普通外设, 如何避免串扰呢?
那就是总线某一时刻只有一个外设与Host通信, 外设硬件接口只响应令牌包, 因为令牌包的数据域表示设备的地址和端点地址, 外设可以解析是否和自己匹配, 如果是则响应(使能硬件接收数据), 以及后续的数据包交互, 如果不是
就不响应, 当然后续的数据包也会被外设硬件屏蔽, 不理会总线信号, 除非一段时间后又检测到令牌包, 再次进行地址匹配, 符合才使能硬件接收总线上的信号
IN OUT SETUP 包的数据域包含7bit设备地址和4bit端点地址, 所以一个Host能够最多接127个设备(0是外设刚插入时的默认地址, 握手后必须赋值非0, 不然下一个设备也是0就冲突了), 一个设备端点最多只能16个(端点0是必须的, 所以其他最多15个)
SOF(帧起始包) 相当于心跳包, 让所有外设知道Host还在活动(哪怕Host不是跟该设备通信但起码知道跟其他设备通信), L/FS每隔1ms发一次, 每发一次11bit帧号加1, HS把1ms分割8份即每隔125us发一次, 但这8份里面的11bit帧号是相同的
这个心跳包主要用于休眠唤醒用的, 当Host没有发SOF超过3ms时(一般是Host自己进入休眠或者想外设休眠), 外设设置自己进入低功耗状态(如果支持), 然后进入监听模式如果检测到总线有信号变化(只要跟睡眠前不一样)立即唤醒,
可能是Host要召唤设备了, 当然设备也可以唤醒Host, Host进入休眠也会设置监听总线状态,外设被人为唤醒改变总线信号接着唤醒Host
b. 数据包: 这没啥好说的就是PID表明自己是数据包(DATA0还是DATA1主要用于 确保对方收到), 后面就是字节数据了, 这里需要注意就是没有告知这个数据包到底多少个数据, 所以我猜想外设接收PID域后, 每接收一个字节counter计数器加1
直到EOP, 然后减2 CRC16校验值就是数据量, 接着对FIFO数据CRC16和最后两个字节对比, 不一致就产生数据错误中断, 一致就产生数据成功中断并将数据量填充RX counter寄存器
c. 握手包: 告知对方状态, 比如Host发送IN令牌包, 接着设备发送数据包, 然后Host接收完发送ACK握手包告知设备成功接收
不用数据域!
d. 特殊包主要用于高速, 比如上面Host发完IN令牌包后, 设备应该要发数据包的, 但设备还没准备好数据, 导致Host等待超时, Host可以再次发IN包让设备进入发送数据, Host切换等待接收数据状态,
这里有两个小问题, 一是设备数据未准备好, 却没有有效方式告知Host, 只能啥都不做靠超时告知, 浪费Host时间, 二是IN包让设备进入发送数据模式, 设备有数据早发了还等你吹, 还让外设进入发送模式影响准备数据
而PING特殊包就是当第一次超时后, Host不发IN包改发PING包询问设备准备好没, 设备若准备好了回复ACK握手包, 接着Host再发IN包, 如果还没准备好就发NAK告知, Host就知道设备还没准备好而不用死等超时,
其他几个读者可自行查阅
总结: 总线是一个一个packet传输的, 且信号达到所有外设, 当发送SYNC域所有外设接收并调整时钟采样点做好同步, 接着解析PID域, 如果是非令牌包就不理会(只有已被选中的外设才理会), 如果是令牌包就解析后面地址是否和自己匹配,
不匹配继续不会理, 匹配的使能硬件接收数据功能, 并根据PID是IN OUT SETUP SOF再细分, 如果是OUT,产生OUT中断, 软件应该清空使能FIFO准备接收数据, 如果是IN, 产生IN中断, 软件要填充好即将发的数据然后使能端点发送,
如果是SETUP包(Host会接着发DATA0数据包数据域包含8个字节的标准请求), 设备要清空特殊FIFO并做好接受下一个数据, 接受完才产生SETUP中断, 软件就解析FIFO里的8byte标准请求, 然后准备数据, 比如是获取设备描述符请求
那软件得准备好设备描述符缓存并ACK(必须ACK不能NAK)回复, 然后Host会发IN包, 接着设备IN中断将刚才准备好的设备描述符缓存丢到端点0发出去!
如果是SOF包, 设备会重置时间计数器, 当3ms内没有新的SOF包, 就会产生中断, 设备知道总线现在是空闲状态, 可以自行决定是否休眠
二、 事务--四种传输类型
一个个packet只是一盘散沙, 通过组织起来作为一个有效传输我们称之为事务, 所以一个事务起码包含:
一个令牌包, 通过地址选中具体外设
可选的数据包, 如果是IN/OUT/SETUP包那后续有数据包, 如果是 SOF则数据包和握手吧都没有
可选的握手包, 像视频聊天这种实时传输不需要ACK应该, 丢了就丢了, 省下带宽不如用来发数据
因此, 根据具体的使用场景, 事务可以分成四种传输类型:
1. 批量传输(Bulk transfers )
一个批量事务包含三个阶段, 令牌包阶段 + 数据包阶段 + 握手包阶段, 其中数据包阶段可以发一个或多个数据包
以Beagle USB 480 逻辑分析仪抓U盘上电时序时为例, 期间Host(PC机)会读取U盘数据(bluk传输), 我们可以猜测应该发一个读取U盘根目录命令, 然后读取扇区信息, 如下:
一个读取扇区信息命令分别为 Command + Data + Status, Command是一个写操作, 往设备发送数据告知想干嘛, 然后就是读数据, 最后检查状态, 可以看到这些操作都由三个packet构成 IN/OUT令牌包 + 数据包 + 握手包
因为U盘每次操作只能512byte/block, 所以想读取多个扇区只能分多次IN操作(传输最大字节数端点描述符有说明)
2. 中断传输(Interrupt transfers )
一个中断事务跟批量事务类似, 不同在于传输量比较少, 且希望Host每隔一段时间来访问设备(不是靠硬件中断告知系统, 而是端点描述符有个时间间隔变量, 告知Host最好小于这个时间间隔来访问设备), 像鼠标键盘都是这类传输模式,
以Beagle USB 480 逻辑分析仪抓键盘为例:
这里可以看出三点, 一是Host每间隔x时间就发起一次读取键盘数据操作(还是老样子 IN包 + DATA0包 + ACK包); 二是如果我没敲键盘, 则设备NAK告知Host没有数据; 三是间隔时间约 72/10 344/44 = 8ms
查看键盘端点描述符bInterval=1, 根据datasheet代表1ms, 即键盘希望Host每隔1ms读取一次数据, 但采不采纳在于Host端
3. 等时传输(Isochronous transfers )
等时事务跟前两种也差不多, 不同在于对时间敏感, 对数据准确性不关心, 所以不需要握手包, 主要用于音频、视频类设备
4. 控制传输(Control transfers )
控制传输稍微复杂一点, 上面三个一个传输就是一个事务, 但控制传输有三个状态, 每个状态对应一个事务, 所以需要三次事务
三次过程分别为:
建立过程:SETUP令牌包 + DATA0数据包(标准请求就在这) + ACK握手包(设备必须返回ACK, 不能NAK 如果设备连这个都不能保证的话就别玩了)
数据过程: 可选, 如上面是获取设备描述符这里就是 IN令牌包 + DATA1数据包 + ACK握手包; 如果是设置地址请求, 地址在请求内部了, 不需要数据过程
状态过程: 上面的数据过程必须是同一个方向的, 如果方向改变, 则就是状态过程, 如果没有数据过程, 则这个数据包就是状态过程不管哪个方向
以Beagle USB 480 逻辑分析仪抓U盘为例:
从捕捉的数据可看到, 建立过程的数据包包含着标准请求 80 06 00 01 00 00 12 00 (小端排序) , 前面的C3是PID, 后面E0 F4 是CRC16, 可以通过http://www.ip33.com/crc.html 验证
struct usb_ctrlrequest {
__u8 bRequestType; //0x80
__u8 bRequest; //0x06
__le16 wValue; //0x100
__le16 wIndex; //
__le16 wLength; //0x12
} __attribute__ ((packed));
具体请参考协议文档9-4
上面log还有个有趣的现象: 状态过程发送1字节0x00数据包, U盘竟然返回NAK, 不知为何, 由于是高速模式下, 所以Host接下来会发PING包探测U盘是否ready, 直到U盘回复ACK才再次发送OUT包,如果是L/FS则继续发OUT包直到接收ACK
剩余数据的解析将在下一篇博文讲解!
USB之基本协议和数据波形1的更多相关文章
- USB mass storage协议
这一节主要把在实现“linux模拟U盘功能”过程中的一些调试过程记录下来,并加以解析. 一.背景知识 1.USB Mass Storage类规范概述 USB 组织在univers ...
- USB学习笔记-协议
一.USB设备枚举过程 1.复位从设备使其设备地址为02.先从设备发送读取设备描述符的命令(只读取一次,即使端点0的最大包长小于18字节)3.设备返回设备描述符4.主机返回0长度确认数据包给到设备5. ...
- USB概述及协议基础
USB概述及协议基础 USB的拓扑结构 USB是一种主从结构的系统.主机叫做Host,从机叫做Device(也叫做设备). 通常所说的主机具有一个或者多个USB主控制器(host controller ...
- Android(java)学习笔记80:UDP协议发送数据
UDP协议发送数据:我们总是先运行接收端,再运行发送端发送端: 1 package cn.itcast_02; import java.io.IOException; import java.net. ...
- Java基础知识强化之网络编程笔记06:TCP之TCP协议发送数据 和 接收数据
1. TCP协议发送数据 和 接收数据 TCP协议接收数据:• 创建接收端的Socket对象• 监听客户端连接.返回一个对应的Socket对象• 获取输入流,读取数据显示在控制台• 释放资源 TCP协 ...
- Java基础知识强化之网络编程笔记03:UDP之UDP协议发送数据 和 接收数据
1. UDP协议发送数据 和 接收数据 UDP协议发送数据: • 创建发送端的Socket对象 • 创建数据,并把数据打包 • 调用Socket对象的发送方法,发送数据包 • 释放资源 UDP协议接 ...
- C# 读写欧姆龙(Omron)PLC ,C#使用Fins-tcp协议读写数据
本文将使用一个gitHub开源的组件技术来读写西门子plc数据,使用的是基于以太网的TCP/IP实现,不需要额外的组件,读取操作只要放到后台线程就不会卡死线程,本组件支持超级方便的高性能读写操作 ...
- c# tcp协议发送数据
private void tcp_send(string data)//tcp协议转发数据 { TcpClient tcpClient = new TcpClient(); tcpClient.Con ...
- Android(java)学习笔记20:UDP协议发送数据
1. UDP协议发送数据:我们总是先运行接收端,再运行发送端发送端: package cn.itcast_02; import java.io.IOException; import java.net ...
随机推荐
- CefSharp 提示 flash player is out of date 运行此插件 等问题解决办法
CefSharp 提示 flash player is out of date 或者 需要手动右键点 运行此插件 脚本 等问题解决办法 因为中国版FlashPlayer变得Ad模式之后,只好用旧版本的 ...
- SDN实验---Ryu的应用开发(一)Hub实现
补充: (一)Ubuntu下安装Notepadqq 背景:为什么安装Notepadqq Notepad++ 不仅有语法高亮度显示,也有语法折叠功能,并且支持宏以及扩充基本功能的外挂模组.但是可惜的是N ...
- 【视频开发】【CUDA开发】ffmpeg Nvidia硬件加速总结
原文链接:https://developer.nvidia.com/ffmpeg GPU-accelerated video processing integrated into the most p ...
- 本地dev环境,运行时用node模块自动读取并整合文件
const http = require('http'); const fs = require('fs'); const path = require('path'); const glob = r ...
- Ansible-Hoc--样例
一.常用场景 1. 列出支持的模块及模块功能说明: 2. sudo用法: 3. 检查服务器存活,复制本地文件到远程: 4. 多线程判断服务器的存活: 5. 显示所有主机的hostname: 6. 列 ...
- kali 扫描之burp Suite学习笔记1
1 安装 2 burs功能图解 3 工具栏详解 4 实战 (1) 网络配置 一台kali 一台msf 网络采用nat nat网络设置方法: 查看路由 配置文件 (2) 代理设置 bur代理设置 浏览器 ...
- Saltstack限制某些shell命令执行
在cmdmod模块中cmd.run.cmd.run_all.cmd.run_stdout等都可以执行shell命令,要静止某些shell命令,可以修改_run()这个函数来彻底的静止调用这个命令. c ...
- 2.RabbitMQ 的可靠性消息的发送
本篇包含 1. RabbitMQ 的可靠性消息的发送 2. RabbitMQ 集群的原理与高可用架构的搭建 3. RabbitMQ 的实践经验 上篇包含 1.MQ 的本质,MQ 的作用 2.R ...
- day33——进程的创建方式、pid、空间隔离、join方法、其他属性、守护进程
day33 进程创建的两种方式 在windows环境下,开启进程必须在______name______ == "______main______"下面 p.start(): 只是向 ...
- 在内网中 vue项目添加ECharts图表插件
原文地址:https://www.cnblogs.com/aknife/p/11753854.html 最近项目中要使用到图表 但是项目在内网中无法直接使用命令安装 然后我在外网中弄个vue的项目(随 ...