4

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

 3 years ago
source link: http://yiiyee.cn/blog/2021/05/04/uefi%e5%bc%80%e5%8f%91%e6%8e%a2%e7%b4%a287-yie002usb%e5%bc%80%e5%8f%91%e6%9d%bf%ef%bc%88uefi%e5%af%b9usb%e7%9a%84%e6%94%af%e6%8c%812%ef%bc%89/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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设备的例程。

160 total views, 1 views today

210c9a4d410d265665667a36fbe0f529?s=49&d=identicon&r=g作者 罗冰(Robin)发布于 2021年5月4日2021年5月13日分类 BIOS/UEFI


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK