UEFI开发探索87- YIE002USB开发板(10 UEFI对USB的支持2)

请保留-> 【原文:  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等差不多。基本的使用过程包括:

  1. 添加支持USB访问的头文件,包括UsbHostController.h和UsbIo.h;
  2. 在INF文件的[Protocols] Section部分添加支持USB访问Protocol的GUID,包括 gEfiUsbIoProtocolGuid和gEfiUsb2HcProtocolGuid;
  3. 编写获取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

发表评论

电子邮件地址不会被公开。