所有关于usb 的代码都位于: edk2\MdeModulePkg\Bus\Usb
基本概念
端点 (Endpoint) : 每一个USB 设备在主控制器看来就一个端点的集合, 主控制器只能通过端点与设备
进行通讯,以使用设备的功能。每个端点实际上就一个一定大小的数据缓冲区,这些端点在设备出厂时就已经
定义好。在USB系统中每一个端点都有一个唯一的地址。这是由设备地址和端点号给出的,每个端点都
有一定的特性。其中包括传输方式,总线访问频率,带宽,端点号,数据包的最大容量等等。端点必须
在设备配置后才能生效(端点0除外)。 端点0通常为控制端点,用于设备初始化参数等,端点1, 2 等
一般用作数据端点,存放主机与设备间往来的数据。
管道 (PiPe) : 一个USB 管道是主机端驱动程序的一个数据缓冲区与一个外设端点的连接,它代表了一种
在两者之间移动数据的能力。一旦设备被配置,管道就存在了,管道有两种类型,数据流管道 与消息管道。
管道只是一个逻辑上的概念。
四种传输方式。
Control Transfer
Isochronous Transfer
Interrupt Transfer
Bulk Transfer
控制传输的特点是:
1 通常用于配置/命令/状态等情形
2 其中的设置操作(setup) 和状态操作(status ) 的数据包具有USB 定义的结构因此控制传输只能通过
消息管道进行。
3. 支持双向传输
4, 对于高速设备允许数据包最大容量为8, 16, 32 或64 字节对于低速设备只有8字节一种选择。
5 端点不能指定总线访问的频率和占用总线的时间, USB 系统软件会做出限制。
6 具有数据传输保证在必要时可以重试。
中断传输的特点是:
1 用于非周期的自然发生的数据量很小的信息的传输如键盘鼠标等。
2. 数据没有USB 定义的结构数据流管道
3. 只有输入这一种传输方式,即外设到主机
4 对于高速设备允许数据包最大容量为小于或等于64 字节对于低速设备只能小于或等于8字节。
5 具有最大服务周期保证 即在规定时间内保证有一次数据传输
6 与同步方式一起占用总线时间不得超过一帧的90%
7 具有数据传输保证 在必要时可以重试
EDKII 中的USB 协议栈由三部分驱动程序组成: USB 主控制器驱动,USB 总线驱动 和
USB 设备驱动, 其中, USB 主控制器驱动源代码位于MdeModulePkg\Bus\Pci 目录下,
USB 总线驱动和USB 设备驱动源代码位于MdeModulePkg\Bus\Usb 目录下。
USB 主控制器驱动用于管理PCI 总线上不同USB 主控制器, 并向USB 总线驱动提供
USB 2 host controller protocol, USB 总线驱动程序用于管理所有连接在USB 拓扑结构中的
设备,并向USB 设备驱动程序提供IO Protocol; USB 设备驱动程序分别包括存储设备,
键盘,和鼠标,
一言以蔽之, 主控制器就是实例化USB2_HC_DEV;
//// Init EFI_USB2_HC_PROTOCOL interface and private data structure//Ehc->Signature = USB2_HC_DEV_SIGNATURE;Ehc->Usb2Hc.GetCapability = EhcGetCapability;Ehc->Usb2Hc.Reset = EhcReset;Ehc->Usb2Hc.GetState = EhcGetState;Ehc->Usb2Hc.SetState = EhcSetState;Ehc->Usb2Hc.ControlTransfer = EhcControlTransfer;Ehc->Usb2Hc.BulkTransfer = EhcBulkTransfer;Ehc->Usb2Hc.AsyncInterruptTransfer = EhcAsyncInterruptTransfer;Ehc->Usb2Hc.SyncInterruptTransfer = EhcSyncInterruptTransfer;Ehc->Usb2Hc.IsochronousTransfer = EhcIsochronousTransfer;Ehc->Usb2Hc.AsyncIsochronousTransfer = EhcAsyncIsochronousTransfer;Ehc->Usb2Hc.GetRootHubPortStatus = EhcGetRootHubPortStatus;Ehc->Usb2Hc.SetRootHubPortFeature = EhcSetRootHubPortFeature;Ehc->Usb2Hc.ClearRootHubPortFeature = EhcClearRootHubPortFeature;Ehc->Usb2Hc.MajorRevision = 0x2;Ehc->Usb2Hc.MinorRevision = 0x0;
接下来,来到协议栈的第二层, BUS
UsbBus 驱动程序用于管理USB 总线以及总线上所有USB 设备,同时向上层USB 设备
驱动程序提供统一的操作接口 usb io protocol, 以抽象底层不同的主控制器硬件。
关键数据结构与函数解析:
//
// Stands for the current USB Bus
//
struct _USB_BUS {
UINTN Signature;
EFI_USB_BUS_PROTOCOL BusId;
//
// Managed USB host controller
//
EFI_HANDLE HostHandle;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_USB2_HC_PROTOCOL *Usb2Hc;
EFI_USB_HC_PROTOCOL *UsbHc;
//
// Recorded the max supported usb devices.
// XHCI can support up to 255 devices.
// EHCI/UHCI/OHCI supports up to 127 devices.
//
UINT32 MaxDevices;
//
// An array of device that is on the bus. Devices[0] is
// for root hub. Device with address i is at Devices[i].
//
USB_DEVICE *Devices[256];
//
// USB Bus driver need to control the recursive connect policy of the bus, only those wanted
// usb child device will be recursively connected.
//
// WantedUsbIoDPList tracks the Usb child devices which user want to recursivly fully connecte,
// every wanted child device is stored in a item of the WantedUsbIoDPList, whose structrure is
// DEVICE_PATH_LIST_ITEM
//
LIST_ENTRY WantedUsbIoDPList;
};
USB_HC_DEV 是EHCI 驱动程序使用的核心数据结构,其中, Devices 指针数组域用
于管理所有USB 设备, 数组下标即设备地址,Devices[0] 缺省为Root Hub 设备;
//
// Stands for the real USB device. Each device may
// has several seperately working interfaces.
//
struct _USB_DEVICE {
USB_BUS *Bus;
//
// Configuration information
//
UINT8 Speed;
UINT8 Address;
UINT32 MaxPacket0;
//
// The device's descriptors and its configuration
//
USB_DEVICE_DESC *DevDesc;
USB_CONFIG_DESC *ActiveConfig;
UINT16 LangId [USB_MAX_LANG_ID];
UINT16 TotalLangId;
UINT8 NumOfInterface;
USB_INTERFACE *Interfaces [USB_MAX_INTERFACE];
//
// Parent child relationship
//
EFI_USB2_HC_TRANSACTION_TRANSLATOR Translator;
UINT8 ParentAddr;
USB_INTERFACE *ParentIf;
UINT8 ParentPort; // Start at 0
UINT8 Tier;
BOOLEAN DisconnectFail;
};
USB_DEVICE 是用于描述USB 设备的数据结构,其中, MaxPacket0 存放该设备端点
0的最大报文长度; DevDesc 域指向设备描述符结构;
//
// Stands for different functions of USB device
//
struct _USB_INTERFACE {
UINTN Signature;
USB_DEVICE *Device;
USB_INTERFACE_DESC *IfDesc;
USB_INTERFACE_SETTING *IfSetting;
//
// Handles and protocols
//
EFI_HANDLE Handle;
EFI_USB_IO_PROTOCOL UsbIo;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
BOOLEAN IsManaged;
//
// Hub device special data
//
BOOLEAN IsHub;
USB_HUB_API *HubApi;
UINT8 NumOfPort;
EFI_EVENT HubNotify;
//
// Data used only by normal hub devices
//
USB_ENDPOINT_DESC *HubEp;
UINT8 *ChangeMap;
//
// Data used only by root hub to hand over device to
// companion UHCI driver if low/full speed devices are
// connected to EHCI.
//
UINT8 MaxSpeed;
};
//
// Stands for the current USB Bus
USB_INTERFACE 是用于描述USB 设备功能接口的数据结构,通常一个USB
设备对应一个或多个功能接口,其中, IfDesc 域指向功能接口描述符的结构;
IfSetting 域指向功能接口设定结构
UsbBusControllerDriverStart() 函数
UsbBusControllerDriverStart () 是UsbBus 驱动程序的初始化函数。
{
//
// If first start, build the bus execute environment and install bus protocol
//
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_IO_BUS_USB | EFI_P_PC_ENABLE));
Status = UsbBusBuildProtocol (This, Controller, RemainingDevicePath);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Try get the Usb Bus protocol interface again
//
Status = gBS->OpenProtocol (
Controller,
&gEfiCallerIdGuid,
(VOID **) &UsbBusId,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
ASSERT (!EFI_ERROR (Status));
}
尝试打开USB BUS Protocol 判断UsbBus 驱动程序是否已经在当前主控制器句柄上启动。
如果第一次启动,则需要调用UsbBusBuildProtocol()进行初始化。
UsbBusBuildProtocol() 函数
UsbBusBuildProtocol() 是用于初始化USB_BUS 核心数据结构以及Root hub 设备,
并在当前主控制器句柄上安装USB Protocol.
UsbHcReset (UsbBus, EFI_USB_HC_RESET_GLOBAL);
UsbHcSetState (UsbBus, EfiUsbHcStateOperational);
//
// Install an EFI_USB_BUS_PROTOCOL to host controller to identify it.
//
Status = gBS->InstallProtocolInterface (
&Controller,
&gEfiCallerIdGuid,
EFI_NATIVE_INTERFACE,
&UsbBus->BusId
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "UsbBusStart: Failed to install bus protocol %r\n", Status));
goto CLOSE_HC;
}
重启主控制器硬件,并置其为工作状态。 在该主控制器句柄上安装USB BUS Protocol.
所有关于RootHubApi
都在下面这个数组里面
USB_HUB_API mUsbRootHubApi = {
UsbRootHubInit,
UsbRootHubGetPortStatus,
UsbRootHubClearPortChange,
UsbRootHubSetPortFeature,
UsbRootHubClearPortFeature,
UsbRootHubResetPort,
UsbRootHubRelease
};
UsbRootHubInit 是全局变量mUsbRootHubApi 句柄中初始化功能的实现函数
//
// Create a timer to poll root hub ports periodically
//
Status = gBS->CreateEvent (
EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
UsbRootHubEnumeration,
HubIf,
&HubIf->HubNotify
);
if (EFI_ERROR (Status)) {
return Status;
}
创建一个定时事件,它将定期触发, 由回调函数 UsbRootHubEnumeration()
轮询root hub 所有端口并枚举其上的热插拔USB 设备。 注意,该事件同时被定义成
通知事件,并随即被触发,以保证及时的发现USB 设备。
USB 总线枚举是指对USB 总线上接入的USB 设备进行识别和寻址操作。由于USB 支持热插拔
和即插即用,所以当一个USB 设备接入USB 或从USB 上拆除时,主机必须使用总线枚举的过程来
识别和管理必要的设备状态变化。并动态地对它进行配置。
当一个USB 设备接入后,下列事件将会发生:
- USB 设备所接入的集线器通过一个其状态变化管道上的响应向主机(主控制器) 报告该事件。这时
USB 设备处于连接状态,而连接它的端口则被禁用。
- 主机(主控制器) 通过询问集线器来确定变化的真实性质。
- 主机现在已经知道了新的设备所接入的端口。于是向该端口发送一个端口激活和复位信号。
- 集骊器把发往该端口的复位信号保持10ms. 当复位信号释放后,被激活的端口和集线器将向USB
设备提供100mA电流。现在USB 设备就处于加电状态。它的所有寄存器和状态都被重新设置,而且
它可以对缺省地址做出响应。
在为该USB 设备分配地址之前,利用缺省地址仍然可以访问其缺省管道。主控制器通过读取该设备的
描述符, 来确定这一USB 设备的缺省管理实际可以使用的最大数据负载尺寸。
- 主控制器为USB 设备分配一个唯一的USB 地址,然后用这个地址和端点0 来建立该USB设备的控制管道。
- 主控制器读取设备的每一项配置信息,这个过程可能需要传输若干个帧的数据。
- 根据配置信息,主控制器就知道如何来使用这一USB 设备,于是主机向设备分配一个配置值,这时设备
就处于配置完成状态,并且在这一配置中的所的端点都具有其描述的特征。现在USB 设备可以获得其
配置描述符中所描述的额定电流量。从设备的观点来看,它已经为使用做好了准备。
UsbRootHubEnumeration() 函数
UsbRootHubEnumeration () 函数是root hub 的设备枚举函数
/**
Enumerate all the changed hub ports.
@param Event The event that is triggered.
@param Context The context to the event.
**/
VOID
EFIAPI
UsbRootHubEnumeration (
IN EFI_EVENT Event,
IN VOID *Context
)
{
USB_INTERFACE *RootHub;
UINT8 Index;
USB_DEVICE *Child;
RootHub = (USB_INTERFACE *) Context;
for (Index = 0; Index < RootHub->NumOfPort; Index++) {
Child = UsbFindChild (RootHub, Index);
if ((Child != NULL) && (Child->DisconnectFail == TRUE)) {
DEBUG (( EFI_D_INFO, "UsbEnumeratePort: The device disconnect fails at port %d from root hub %p, try again\n", Index, RootHub));
UsbRemoveDevice (Child);
}
UsbEnumeratePort (RootHub, Index);
}
}
if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_CONNECTION)) {
//
// Case4:
// Device connected or disconnected normally.
//
DEBUG ((EFI_D_INFO, "UsbEnumeratePort: Device Connect/Disconnect Normally\n", Port));
}
端口的连接状态改变位 置1, 意味着有USB 设备连接上或者断开, 即状态变了
//
// Following as the above cases, it's safety to remove and create again.
//
Child = UsbFindChild (HubIf, Port);
if (Child != NULL) {
DEBUG (( EFI_D_INFO, "UsbEnumeratePort: device at port %d removed from root hub %p\n", Port, HubIf));
UsbRemoveDevice (Child);
}
在现有的设备树中查找连接在该端口的USB 设备,如果找到则移除它。
if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) {
//
// Now, new device connected, enumerate and configure the device
//
DEBUG (( EFI_D_INFO, "UsbEnumeratePort: new device connected at port %d\n", Port));
Status = UsbEnumerateNewDev (HubIf, Port);
端口的连接状态位 置1 , 意味着新USB 设备连接上该端口,则需要开始枚举该USB
设备,否则视为原有设备断开。
UsbMassStorageDxe 驱动程序
UsbMassStorage 驱动程序用于管理USB 存储类设备,它基于USB UsbMassStorage
Specification for Bootability 命令集, 并支持CBI (Control bulk interrupt ) 和BOT
Bulk only) 两种传输类型,该驱动程序向上层文件系统提供抽象的块设备操作接口
BLOCK IO protocol.
struct _USB_MASS_DEVICE {UINT32 Signature;EFI_HANDLE Controller;EFI_USB_IO_PROTOCOL *UsbIo;EFI_DEVICE_PATH_PROTOCOL *DevicePath;EFI_BLOCK_IO_PROTOCOL BlockIo;EFI_BLOCK_IO_MEDIA BlockIoMedia;BOOLEAN OpticalStorage;UINT8 Lun; ///< Logical Unit NumberUINT8 Pdt; ///< Peripheral Device TypeUSB_MASS_TRANSPORT *Transport; ///< USB mass storage transport protocolVOID *Context;EFI_DISK_INFO_PROTOCOL DiskInfo;USB_BOOT_INQUIRY_DATA InquiryData;BOOLEAN Cdb16Byte;
};
usb_mass_device 是UsbMassStorage 驱动程序使用的核心数据结构。其中,blockiomedia
域包含所有与媒质操作有关的信息,Transport域指向不同的传输类型句柄。
USBMassDriverBindingStart() 函数
USBMassDriverBindingStart() 是UsbMassStorage 驱动程序的初始化函数。
Status = UsbMassInitTransport (This, Controller, &Transport, &Context, &MaxLun);
UsbMassInitTransport 函数解析该USB 存储设备的传输类型以及最大逻辑单元。
//
// Traverse the USB_MASS_TRANSPORT arrary and try to find the
// matching transport protocol.
// If not found, return EFI_UNSUPPORTED.
// If found, execute USB_MASS_TRANSPORT.Init() to initialize the transport context.
//
for (Index = 0; Index < USB_MASS_TRANSPORT_COUNT; Index++) {
*Transport = mUsbMassTransport[Index];
if (Interface.InterfaceProtocol == (*Transport)->Protocol) {
Status = (*Transport)->Init (UsbIo, Context);
break;
}
}
transport 类型一共有下面几种:
USB_MASS_TRANSPORT *mUsbMassTransport[USB_MASS_TRANSPORT_COUNT] = {
&mUsbCbi0Transport,
&mUsbCbi1Transport,
&mUsbBotTransport,
};
上述函数主要功能是读取该USB 设备功能接口结构中的传输类型,如果是该驱动
程序支持的3种传输类型(BOT, CBI0, CBI1) 中的一种,则调用对应类型的初始化函数。
否则返回错误。
UsbMassWriteBlocks() 函数
UsbMassWriteBlocks() 是BLOCK IO Protocol 中Writeblcoks 方法的实现函数。
//
// If it is a removable media, such as CD-Rom or Usb-Floppy,
// need to detect the media before each read/write. Some of
// USB Flash is marked as removable media.
//
if (Media->RemovableMedia) {
Status = UsbBootDetectMedia (UsbMass);
if (EFI_ERROR (Status)) {
goto ON_EXIT;
}
}
在执行写操作之前,对于媒质可移除的USB 存储设备,需要探测原有媒质是否被
移除或都发生改变。