USB

USB在Linux系统中的架构框图

images/architecture.pngarchitecture

images/mass_storage.pngmass_storage

Host设备在sysfs的信息

下面的是rk3399中的usb信息

ls /sys/bus/usb/devices/
1-0:1.0  1-1:1.0  3-0:1.0  5-0:1.0  usb1  usb3  usb5
1-1      2-0:1.0  4-0:1.0  6-0:1.0  usb2  usb4  usb6

usb1-usb6表示有6个usb控制器

usb设备命名格式如下

bus-port:config.interface

Host设备在debugfs的信息

debugfs中信息位置如下

cat /sys/kernel/debug/usb/devices

usbmon 架构

images/usbmon.pngusbmon

usbmon ASCII capture

编译内核支持usbmon为模块

mount -t debugfs none_debugfs /sys/kernel/debug
mdprobe usbmon
cat /sys/kernel/debug/usbmon/1u > usbmon.mon
./vusb-analyzer usbmon.mon

usbmon binary capture

编译内核支持usbmon为模块

modprobe usbmon

ls /dev/usbmon*
/dev/usbmon0  /dev/usbmon1  /dev/usbmon2

tcpdump -i usbmon1 -w usbmon.pcap &
./wireshark usbmon.pcap

Bulk-Only传输协议

设备插入到USB后,USB即对设备进行搜索,并要求设备提供相应的描述符.在USBHost得到上述描述符后,即完成了设备的配置,识别出为Bulk-Only的Mass Storage设备,然后即进入Bulk-Only传输方式 在此方式下,USB与设备间的所有数据均通过Bulk-In和Bulk-Out来进行传输,不再通过控制端点传输任何数据

在这种传输方式下,有三种类型的数据在USB和设备之间传送,CBW、CSW和普通数据.

CBW(Command Block Wrapper,即命令块包)是从USB Host发送到设备的命令, 命令格式遵从接口中的bInterfaceSubClass 所指定的命令块,可以是为SCSI或是自定义传输命令集

USB设备需要将SCSI(自定义的)命令从CBW中提取出来,执行相应的命令,完成以后,向Host发出反映当前命令执行状态的CSW(Command Status Wrapper),Host根据CSW来决定是否继续发送下一个CBW或是数据

Host要求USB设备执行的命令可能为发送数据,则此时需要将特定数据传送出去,完毕后发出CSW,以使Host进行下一步的操作

CBW数据结构如下(kernel/include/linux/usb/storage.h)

/* command block wrapper */
struct bulk_cb_wrap {
	__le32	Signature;		/* Contains 'USBC' */
	u32	Tag;			/* Unique per command id */
	__le32	DataTransferLength;	/* Size of the data */
	u8	Flags;			/* Direction in bit 7 */
	u8	Lun;			/* LUN (normally 0) */
	u8	Length;			/* Of the CDB, <= MAX_COMMAND_SIZE */
	u8	CDB[16];		/* Command Data Block */
};

CSW数据结构如下

/* command status wrapper */
struct bulk_cs_wrap {
	__le32	Signature;		/* Should = 'USBS' */
	u32	Tag;			/* Same as original command */
	__le32	Residue;		/* Amount not transferred */
	u8	Status;			/* See below */
};

各字段含义

Signature

固定值: 字符串'USBC'

Tag

主机发送的一个命令块标识,设备需要原样作为dCSWTag(CSW中的一部分)再发送给Host;主要用于关联CSW到对应的CBW

FLags

反映数据传输的方向,0表示来自Host, 1表示发至Host

Lun

 对于有多个LUN逻辑单元的设备,用来选择具体目标.如果没有多个LUN,则写0

Length

命令的长度,范围在0~16

CDB

传输的具体命令,符合bInterfaceSubClass中定义的命令规范

Residue

还需要传送的数据,此数据根据dCBWDataTransferLength本次已经传送的数据得到

Status

指示命令的执行状态.如果命令正确执行,bCSWStatus返回0 即可

应用实例1(USB应用编程)

基础概念(核心数据结构)

CBW(Command Block Wrapper)

CDB(Command Data Block)

CSW(Command Status Wrapper)

每一个CBW都对应有一个CSW,且它们的Tag是相同且唯一的

情景简介

本应用实例为一个使用usb来传输数据的烧写工具软件rkflashtool

Host端rkflashtool,使用libusb库进行usb通讯

其中CBW中的CDB自定义为相应的命令,详细见代码

Slave端(开发板),进入下载模式后枚举成MassStroage设备,循环等待接收命令,处理命令

数据通信流程简图

读流程(usb_read)

  • Host发送cbw,其中cdb里包含读命令,Slave解析后返回相应的数据,Slave返回csw

写流程(usb_write)

  • Host发送cbw,其中cdb里包含写命令,并发送要写的数据,Slave返回csw

下图中的所有术语是按主机的角度说明的

images/sequenceDiagram.pngwork flow

Libusb编程核心步骤

初始化,设置调试级别,使用vid,pid打开设备,连接设备,获取设备通讯接口,获取描述符

libusb_init
libusb_set_debug
libusb_open_device_with_vid_pid
libusb_kernel_driver_active
libusb_claim_interface
libusb_get_device_descriptor

控制传输接口

libusb_control_transfer

bulk传输接口

libusb_bulk_transfer

应用实例2(USB设备驱动编程)

需要去掉下面两个配置才能做下面的实验

CONFIG_USB_HID
CONFIG_HID_GENERIC

详细流程见代码注释

驱动大致说明如下

使用usb鼠标来模拟一个键盘设备,将鼠标左键定义为按键”L”, 鼠标右键定义为按键”S”,滚轮按键定义为”Enter”

测试方法1

hexdump /dev/input/event3 //这里的event3对应我的鼠标设备

测试方法2

cat /dev/tty1    //按下鼠标左右,和滚轮查看是否有相应字符输出到串口

Firefly_RK3399 USB

RK3399支持两个Type-C USB3, DP,两个USB2.0 HOST

Type-C0 USB3支持OTG(USB Peripheral和USB HOST)

Type-C1 USB3仅支持USB3 HOST

在Firefly_RK3399上,两路TypeC物理形态如下

Type-C0 USB3设计为Type-C USB3

Type-C1 USB3设计为Type-A USB3 HOST

RK3399DTS的默认配置支持Type-C0 USB3 OTG,Type-C1 USB3 HOST

Type-C0 USB3 OTG(dr_mode = otg)

usbdrd3_0: usb@fe800000 {
	compatible = "rockchip,rk3399-dwc3";
	clocks = <&cru SCLK_USB3OTG0_REF>, <&cru SCLK_USB3OTG0_SUSPEND>,
		 <&cru ACLK_USB3OTG0>, <&cru ACLK_USB3_GRF>;
	clock-names = "ref_clk", "suspend_clk",
		      "bus_clk", "grf_clk";
	power-domains = <&power RK3399_PD_USB3>;
	resets = <&cru SRST_A_USB3_OTG0>;
	reset-names = "usb3-otg";
	#address-cells = <2>;
	#size-cells = <2>;
	ranges;
	status = "disabled";
	usbdrd_dwc3_0: dwc3@fe800000 {
		compatible = "snps,dwc3";
		reg = <0x0 0xfe800000 0x0 0x100000>;
		interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH 0>;
		dr_mode = "otg";
		phys = <&u2phy0_otg>, <&tcphy0 1>;
		phy-names = "usb2-phy", "usb3-phy";
		phy_type = "utmi_wide";
		snps,dis_enblslpm_quirk;
		snps,dis-u2-freeclk-exists-quirk;
		snps,dis-del-phy-power-chg-quirk;
		snps,xhci-slow-suspend-quirk;
		status = "disabled";
	};
};

Type-C1 USB3 HOST(dr_mode = host)

usbdrd3_1: usb@fe900000 {
	compatible = "rockchip,rk3399-dwc3";
	clocks = <&cru SCLK_USB3OTG1_REF>, <&cru SCLK_USB3OTG1_SUSPEND>,
		 <&cru ACLK_USB3OTG1>, <&cru ACLK_USB3_GRF>;
	clock-names = "ref_clk", "suspend_clk",
		      "bus_clk", "grf_clk";
	power-domains = <&power RK3399_PD_USB3>;
	resets = <&cru SRST_A_USB3_OTG1>;
	reset-names = "usb3-otg";
	#address-cells = <2>;
	#size-cells = <2>;
	ranges;
	status = "disabled";
	usbdrd_dwc3_1: dwc3@fe900000 {
		compatible = "snps,dwc3";
		reg = <0x0 0xfe900000 0x0 0x100000>;
		interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH 0>;
		dr_mode = "host";
		phys = <&u2phy1_otg>, <&tcphy1 1>;
		phy-names = "usb2-phy", "usb3-phy";
		phy_type = "utmi_wide";
		snps,dis_enblslpm_quirk;
		snps,dis-u2-freeclk-exists-quirk;
		snps,dis-del-phy-power-chg-quirk;
		snps,xhci-slow-suspend-quirk;
		status = "disabled";
	};
};