=============  本系列参考  =============

《圈圈教你玩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的更多相关文章

  1. USB mass storage协议

    这一节主要把在实现“linux模拟U盘功能”过程中的一些调试过程记录下来,并加以解析. 一.背景知识     1.USB Mass Storage类规范概述        USB 组织在univers ...

  2. USB学习笔记-协议

    一.USB设备枚举过程 1.复位从设备使其设备地址为02.先从设备发送读取设备描述符的命令(只读取一次,即使端点0的最大包长小于18字节)3.设备返回设备描述符4.主机返回0长度确认数据包给到设备5. ...

  3. USB概述及协议基础

    USB概述及协议基础 USB的拓扑结构 USB是一种主从结构的系统.主机叫做Host,从机叫做Device(也叫做设备). 通常所说的主机具有一个或者多个USB主控制器(host controller ...

  4. Android(java)学习笔记80:UDP协议发送数据

    UDP协议发送数据:我们总是先运行接收端,再运行发送端发送端: 1 package cn.itcast_02; import java.io.IOException; import java.net. ...

  5. Java基础知识强化之网络编程笔记06:TCP之TCP协议发送数据 和 接收数据

    1. TCP协议发送数据 和 接收数据 TCP协议接收数据:• 创建接收端的Socket对象• 监听客户端连接.返回一个对应的Socket对象• 获取输入流,读取数据显示在控制台• 释放资源 TCP协 ...

  6. Java基础知识强化之网络编程笔记03:UDP之UDP协议发送数据 和 接收数据

    1. UDP协议发送数据 和 接收数据 UDP协议发送数据: • 创建发送端的Socket对象 • 创建数据,并把数据打包 • 调用Socket对象的发送方法,发送数据包 • 释放资源  UDP协议接 ...

  7. C# 读写欧姆龙(Omron)PLC ,C#使用Fins-tcp协议读写数据

      本文将使用一个gitHub开源的组件技术来读写西门子plc数据,使用的是基于以太网的TCP/IP实现,不需要额外的组件,读取操作只要放到后台线程就不会卡死线程,本组件支持超级方便的高性能读写操作 ...

  8. c# tcp协议发送数据

    private void tcp_send(string data)//tcp协议转发数据 { TcpClient tcpClient = new TcpClient(); tcpClient.Con ...

  9. Android(java)学习笔记20:UDP协议发送数据

    1. UDP协议发送数据:我们总是先运行接收端,再运行发送端发送端: package cn.itcast_02; import java.io.IOException; import java.net ...

随机推荐

  1. CefSharp 提示 flash player is out of date 运行此插件 等问题解决办法

    CefSharp 提示 flash player is out of date 或者 需要手动右键点 运行此插件 脚本 等问题解决办法 因为中国版FlashPlayer变得Ad模式之后,只好用旧版本的 ...

  2. SDN实验---Ryu的应用开发(一)Hub实现

    补充: (一)Ubuntu下安装Notepadqq 背景:为什么安装Notepadqq Notepad++ 不仅有语法高亮度显示,也有语法折叠功能,并且支持宏以及扩充基本功能的外挂模组.但是可惜的是N ...

  3. 【视频开发】【CUDA开发】ffmpeg Nvidia硬件加速总结

    原文链接:https://developer.nvidia.com/ffmpeg GPU-accelerated video processing integrated into the most p ...

  4. 本地dev环境,运行时用node模块自动读取并整合文件

    const http = require('http'); const fs = require('fs'); const path = require('path'); const glob = r ...

  5. Ansible-Hoc--样例

    一.常用场景 1. 列出支持的模块及模块功能说明: 2. sudo用法: 3. 检查服务器存活,复制本地文件到远程: 4. 多线程判断服务器的存活: 5.  显示所有主机的hostname: 6. 列 ...

  6. kali 扫描之burp Suite学习笔记1

    1 安装 2 burs功能图解 3 工具栏详解 4 实战 (1) 网络配置 一台kali 一台msf 网络采用nat nat网络设置方法: 查看路由 配置文件 (2) 代理设置 bur代理设置 浏览器 ...

  7. Saltstack限制某些shell命令执行

    在cmdmod模块中cmd.run.cmd.run_all.cmd.run_stdout等都可以执行shell命令,要静止某些shell命令,可以修改_run()这个函数来彻底的静止调用这个命令. c ...

  8. 2.RabbitMQ 的可靠性消息的发送

      本篇包含 1. RabbitMQ 的可靠性消息的发送 2. RabbitMQ 集群的原理与高可用架构的搭建 3. RabbitMQ 的实践经验   上篇包含 1.MQ 的本质,MQ 的作用 2.R ...

  9. day33——进程的创建方式、pid、空间隔离、join方法、其他属性、守护进程

    day33 进程创建的两种方式 在windows环境下,开启进程必须在______name______ == "______main______"下面 p.start(): 只是向 ...

  10. 在内网中 vue项目添加ECharts图表插件

    原文地址:https://www.cnblogs.com/aknife/p/11753854.html 最近项目中要使用到图表 但是项目在内网中无法直接使用命令安装 然后我在外网中弄个vue的项目(随 ...