请保留-> 【原文: https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
3 EFI_USB_IO_PROTOCOL
EFI_USB_IO_PROTOCOL由USB总线驱动产生,可由UEFI应用和驱动使用,用来访问各种USB设备,比如USB键盘、鼠标和大容量存储设备等。EFI_USB_IO_PROTOCOL所提供的接口,可提供四种类型的传输方式与USB设备通信,即之前介绍过的控制传输、中断传输、批量传输和实时传输。
EFI_USB_IO_PROTOCOL的函数接口如下所示:
typedef struct _EFI_USB_IO_PROTOCOL {
EFI_USB_IO_CONTROL_TRANSFER UsbControlTransfer; //控制传输
EFI_USB_IO_BULK_TRANSFER UsbBulkTransfer; //批量传输
EFI_USB_IO_ASYNC_INTERRUPT_TRANSFER \
UsbAsyncInterruptTransfer; //异步中断传输
EFI_USB_IO_SYNC_INTERRPUT_TRANSFER UsbSyncInterruptTransfer //同步中断传输
EFI_USB_IO_ISOCHRONOUS_TRANSFER UsbIsochronousTransfer; //实时传输
EFI_USB_IO_ASYNC_ISOCHRONOUS_TRANSFER \
UsbAsyncIsochronousTransfer; //异步实时传输
EFI_USB_IO_GET_DEVICE_DESCRIPTOR UsbGetDeviceDescriptor; //获取设备描述符
EFI_USB_IO_GET_CONFIG_DESCRIPTOR UsbGetConfigDescriptor; //获取配置描述符
EFI_USB_IO_GET_INTERFACE_DESCRIPTOR \
UsbGetInterfaceDescriptor; //获取接口描述符
EFI_USB_IO_GET_ENDPOINT_DESCRIPTOR UsbGetEndpointDescriptor;
//获取端点描述符
EFI_USB_IO_GET_STRING_DESCRIPTOR UsbGetStringDescriptor;//获取字符串描述符
EFI_USB_IO_GET_SUPPORTED_LANGUAGES UsbGetSupportedLanguages;//获取支持语言
EFI_USB_IO_PORT_RESET UsbPortReset; //重启USB控制器
} EFI_USB_IO_PROTOCOL;
对照之前介绍过的USB协议的知识,很容易理解EFI_USB_IO_PROTOCOL所提供的各类接口函数。下面主要介绍下常用的两个接口函数,UsbGetDeviceDescriptor()和UsbControlTransfer(),其他接口函数的说明,请查询UEFI Spec。
接口函数UsbGetDeviceDescriptor()用来获取USB设备的设备描述符,其原型如下所示。
typedef EFI_STATUS (EFIAPI *EFI_USB_IO_GET_DEVICE_DESCRIPTOR) (
IN EFI_USB_IO_PROTOCOL *This, //Protocol实例
OUT EFI_USB_DEVICE_DESCRIPTOR *DeviceDescriptor //设备描述符指针变量
);
typedef struct {
UINT8 Length; //描述符长度(0x12)
UINT8 DescriptorType; //描述符类型(0x01)
UINT16 BcdUSB; //USB设备支持的协议版本号
UINT8 DeviceClass; //类代码
UINT8 DeviceSubClass; //子类代码
UINT8 DeviceProtocol; //协议码
UINT8 MaxPacketSize0; //端点0最大包长度
UINT16 IdVendor; //厂商ID
UINT16 IdProduct; //产品ID
UINT16 BcdDevice; //设备发行号
UINT8 StrManufacturer; //厂商信息
UINT8 StrProduct; //产品信息
UINT8 StrSerialNumber; //设备序列号
UINT8 NumConfigurations;//配置描述符数目
} EFI_USB_DEVICE_DESCRIPTOR;
UsbGetDeviceDescriptor()所获取的设备描述符,各成员变量与第82篇中介绍的描述符是完全一致的,对照着看,很容易理解。
而接口函数UsbControlTransfer()用来进行控制传输,包括USB标准命令、类命令,都是通过控制传输来进行的。其函数接口原型为:
typedef EFI_STATUS (EFIAPI *EFI_USB_IO_CONTROL_TRANSFER) (
IN EFI_USB_IO_PROTOCOL *This, //Protocol实例
IN EFI_USB_DEVICE_REQUEST *Request, //USB命令(标准命令、类命令、厂商命令)
IN EFI_USB_DATA_DIRECTION Direction,//方向
IN UINT32 Timeout, //超时时间,单位为毫秒
IN OUT VOID *Data OPTIONAL, //发往或接收自USB设备的数据缓冲区
IN UINTN DataLength OPTIONAL, //数据缓冲区的长度
OUT UINT32 *Status //USB传输的结果
);
typedef enum {
EfiUsbDataIn, //接收自USB设备,即从USB设备往USB主机发送
EfiUsbDataOut, //发往USB设备,即从USB主机发往USB设备
EfiUsbNoData
} EFI_USB_DATA_DIRECTION;
typedef struct {
UINT8 RequestType; //命令类型
UINT8 Request; //命令序号
UINT16 Value; //不同的命令,含义不同
UINT16 Index; //不同的命令,含义不同
UINT16 Length; //如果有数据阶段,此字段为数据的字节数
} EFI_USB_DEVICE_REQUEST; //USB命令结构体
UsbControlTransfer()搭建了USB设备驱动与USB设备间的传输通道。从函数原型可以看出,它主要的功能就是用来执行各类USB命令,包括USB标准命令、类命令和厂商命令。其参数Request的数据结构,与UEFI探索系列博客的第84篇给出的USB命令的结构是完全一样的,含义也完全相同。
参数Status给出的是USB传输的结果,与函数执行返回值是不一样的。函数返回值用来反映函数执行情况,比如参数是否错误、执行是否超时等。而参数Status给出的传输中错误的类型,比如CRC校验错误等,Status可能的值如下所示:
#define EFI_USB_NOERROR 0x0000
#define EFI_USB_ERR_NOTEXECUTE 0x0001
#define EFI_USB_ERR_STALL 0x0002
#define EFI_USB_ERR_BUFFER 0x0004
#define EFI_USB_ERR_BABBLE 0x0008
#define EFI_USB_ERR_NAK 0x0010
#define EFI_USB_ERR_CRC 0x0020
#define EFI_USB_ERR_TIMEOUT 0x0040
#define EFI_USB_ERR_BITSTUFF 0x0080
#define EFI_USB_ERR_SYSTEM 0x0100
4 使用USB Protocol
EFI_USB2_HC_PROTOCOL和EFI_USB_IO_PROTOCOL的用法,实际上与之前博客介绍的PCI Protocol、SMBUS Protocol等差不多。基本的使用过程包括:
- 添加支持USB访问的头文件,包括UsbHostController.h和UsbIo.h;
- 在INF文件的[Protocols] Section部分添加支持USB访问Protocol的GUID,包括 gEfiUsbIoProtocolGuid和gEfiUsb2HcProtocolGuid;
- 编写获取Protocol实例的函数。
下面给出了一个例子,数组gUsb2HC[]和gUsbIO[]中包括了EFI_USB2_HC_PROTOCOL和EFI_USB_IO_PROTOCOL的实例,可以直接使用它们来获取USB主控制器信息和USB设备信息。
示例1 获取USB主控制器信息
VOID lsUsb2HC(void)
{
EFI_STATUS Status;
UINTN i;
UINT8 maxSpeed,portNumber,is64BC;
EFI_USB_HC_STATE state;
CHAR16 *speed[]={L"FULL ",L"LOW ",L"HIGH ",L"SUPER"};
CHAR16 *hcState[]={L"Halt",L"Operational",L"Suspend"};
if(gUsb2HCCount == 0) //没有Protocol实例,直接退出
return;
Print(L"Usb HC: %d \n",gUsb2HCCount);
Print(L"No. MaxSpeed PortNumber Is64BitCapable State\n");
for(i=0;i<gUsb2HCCount;i++)
{
Print(L"%03d ",i);
Status = gUsb2HC[i]->GetCapability(gUsb2HC[i],&maxSpeed, \
&portNumber,&is64BC); //获取属性
if(EFI_ERROR(Status))
Print(L"???");
else
Print(L"%*S %*d %*d ",8,speed[maxSpeed],10,portNumber,14,is64BC);
Status = gUsb2HC[i]->GetState(gUsb2HC[i],&state); //获取状态
if(EFI_ERROR(Status))
Print(L" ???\n");
else
Print(L"%S \n",hcState[state]);
}
}
函数的逻辑比较简单,主要是调用EFI_USB2_HC_PROTOCOL的接口函数GetCapability()和GetState(),得到USB主控制器的属性和状态。
获取USB设备信息的方式类似,使用EFI_USB_IO_PROTOCOL的接口函数UsbGetDeviceDescriptor(),得到USB设备的设备描述符。将设备描述符中的类、子类、厂商ID和产品ID打印出来接口。其实现代码如示例2所示。
示例2 获取USB设备信息
VOID lsUsbIO(void)
{
EFI_STATUS Status;
UINTN i;
EFI_USB_DEVICE_DESCRIPTOR UsbDevDesc;
if(gUsbIOCount == 0) //没有Protocol实例,直接退出
return;
Print(L"Usb device: %d \n",gUsbIOCount);
Print(L"No. DevClass SubClass IdVendor IdProduct \n");
for(i=0;i<gUsbIOCount;i++)
{
Print(L"%03d ",i);
Status = gUsbIO[i]->UsbGetDeviceDescriptor(gUsbIO[i], &UsbDevDesc);
if(EFI_ERROR(Status))
Print(L"Get Device Descriptor Error!\n");
else
{
Print(L" %03d %03d ", \
UsbDevDesc.DeviceClass,UsbDevDesc.DeviceSubClass);
Print(L" 0x%04x 0x%04x\n", \
UsbDevDesc.IdVendor,UsbDevDesc.IdProduct);
}
}
}
至此,关于UEFI系统中对USB的支持,就介绍完成了。
下一篇,将使用这些知识,构建访问USB HID设备的例程。
991 total views, 4 views today