当前位置: 代码迷 >> 综合 >> edkii usb 驱动过程
  详细解决方案

edkii usb 驱动过程

热度:87   发布时间:2023-12-14 22:24:43.0

所有关于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 存储设备,需要探测原有媒质是否被

移除或都发生改变。