diff options
author | vanjeff <vanjeff@6f19259b-4bc3-4df7-8a09-765794883524> | 2007-07-11 08:47:37 +0000 |
---|---|---|
committer | vanjeff <vanjeff@6f19259b-4bc3-4df7-8a09-765794883524> | 2007-07-11 08:47:37 +0000 |
commit | e237e7ae9fc23f32a25040d49cc9a16f2a7f3b4c (patch) | |
tree | f7c7ea55cac3b9816f5f6f2a39fbc09406e7a134 /MdeModulePkg | |
parent | f183b4f349e207768bfbf3287b9d37ac7b16d101 (diff) | |
download | edk2-e237e7ae9fc23f32a25040d49cc9a16f2a7f3b4c.zip edk2-e237e7ae9fc23f32a25040d49cc9a16f2a7f3b4c.tar.gz edk2-e237e7ae9fc23f32a25040d49cc9a16f2a7f3b4c.tar.bz2 |
Import Usb/UsbBusDxe and Usb/UsbMassStorageDxe into MdeModulePkg.
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3193 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'MdeModulePkg')
25 files changed, 10355 insertions, 0 deletions
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/ComponentName.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/ComponentName.c new file mode 100644 index 0000000..da97372 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/ComponentName.c @@ -0,0 +1,164 @@ +/** @file
+
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ ComponentName.c
+
+Abstract:
+
+
+**/
+
+//
+// The package level header files this module uses
+//
+#include <PiDxe.h>
+
+//
+// The Library classes this module consumes
+//
+#include <Library/UefiLib.h>
+
+
+//
+// EFI Component Name Functions
+//
+EFI_STATUS
+EFIAPI
+UsbBusComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+EFI_STATUS
+EFIAPI
+UsbBusComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle, OPTIONAL
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI Component Name Protocol
+//
+EFI_COMPONENT_NAME_PROTOCOL mUsbBusComponentName = {
+ UsbBusComponentNameGetDriverName,
+ UsbBusComponentNameGetControllerName,
+ "eng"
+};
+
+STATIC EFI_UNICODE_STRING_TABLE mUsbBusDriverNameTable[] = {
+ { "eng", L"Usb Bus Driver" },
+ { NULL , NULL }
+};
+
+EFI_STATUS
+EFIAPI
+UsbBusComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME2_PROTOCOL instance.
+ Language - A pointer to a three character ISO 639-2 language identifier.
+ This is the language of the driver name that that the caller
+ is requesting, and it must match one of the languages specified
+ in SupportedLanguages. The number of languages supported by a
+ driver is up to the driver writer.
+ DriverName - A pointer to the Unicode string to return. This Unicode string
+ is the name of the driver specified by This in the language
+ specified by Language.
+
+ Returns:
+ EFI_SUCCESS - The Unicode string for the Driver specified by This
+ and the language specified by Language was returned
+ in DriverName.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - DriverName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return LookupUnicodeString (
+ Language,
+ mUsbBusComponentName.SupportedLanguages,
+ mUsbBusDriverNameTable,
+ DriverName
+ );
+}
+
+EFI_STATUS
+EFIAPI
+UsbBusComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle, OPTIONAL
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME2_PROTOCOL instance.
+ ControllerHandle - The handle of a controller that the driver specified by
+ This is managing. This handle specifies the controller
+ whose name is to be returned.
+ ChildHandle - The handle of the child controller to retrieve the name
+ of. This is an optional parameter that may be NULL. It
+ will be NULL for device drivers. It will also be NULL
+ for a bus drivers that wish to retrieve the name of the
+ bus controller. It will not be NULL for a bus driver
+ that wishes to retrieve the name of a child controller.
+ Language - A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that that the caller is requesting, and it must match one
+ of the languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up to the
+ driver writer.
+ ControllerName - A pointer to the Unicode string to return. This Unicode
+ string is the name of the controller specified by
+ ControllerHandle and ChildHandle in the language specified
+ by Language from the point of view of the driver specified
+ by This.
+
+ Returns:
+ EFI_SUCCESS - The Unicode string for the user readable name in the
+ language specified by Language for the driver
+ specified by This was returned in DriverName.
+ EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - ControllerName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This is not currently managing
+ the controller specified by ControllerHandle and
+ ChildHandle.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf new file mode 100644 index 0000000..423d9fb --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf @@ -0,0 +1,99 @@ +#/** @file
+# Component name for module UsbBus
+#
+# Copyright (c) 2006, Intel Corporation. All right reserved.
+#
+# All rights reserved. This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+#**/
+
+################################################################################
+#
+# Defines Section - statements that will be processed to create a Makefile.
+#
+################################################################################
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = UsbBusDxe
+ FILE_GUID = 240612B7-A063-11d4-9A3A-0090273FC14D
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ EDK_RELEASE_VERSION = 0x00020000
+ EFI_SPECIFICATION_VERSION = 0x00020000
+
+ ENTRY_POINT = UsbBusDriverEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+################################################################################
+#
+# Sources Section - list of files that are required for the build to succeed.
+#
+################################################################################
+
+[Sources.common]
+ UsbDesc.c
+ UsbEnumer.c
+ UsbEnumer.h
+ usbbus.c
+ UsbHub.c
+ ComponentName.c
+ UsbUtility.h
+ UsbHub.h
+ UsbUtility.c
+ UsbDesc.h
+ usbbus.h
+
+################################################################################
+#
+# Package Dependency Section - list of Package files that are required for
+# this module.
+#
+################################################################################
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+################################################################################
+#
+# Library Class Section - list of Library Classes that are required for
+# this module.
+#
+################################################################################
+
+[LibraryClasses]
+ MemoryAllocationLib
+ DevicePathLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ BaseMemoryLib
+ DebugLib
+
+
+################################################################################
+#
+# Protocol C Name Section - list of Protocol and Protocol Notify C Names
+# that this module uses or produces.
+#
+################################################################################
+
+[Protocols]
+ gEfiUsbIoProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiDevicePathProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiUsb2HcProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiUsbHcProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.msa b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.msa new file mode 100644 index 0000000..deaecc6 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.msa @@ -0,0 +1,85 @@ +<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <MsaHeader>
+ <ModuleName>UsbBusDxe</ModuleName>
+ <ModuleType>DXE_DRIVER</ModuleType>
+ <GuidValue>240612B7-A063-11d4-9A3A-0090273FC14D</GuidValue>
+ <Version>1.0</Version>
+ <Abstract>Component name for module UsbBus</Abstract>
+ <Description>FIX ME!</Description>
+ <Copyright>Copyright (c) 2006, Intel Corporation. All right reserved.</Copyright>
+ <License>All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.</License>
+ <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052</Specification>
+ </MsaHeader>
+ <ModuleDefinitions>
+ <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>
+ <BinaryModule>false</BinaryModule>
+ <OutputFileBasename>UsbBusDxe</OutputFileBasename>
+ </ModuleDefinitions>
+ <LibraryClassDefinitions>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>DebugLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>BaseMemoryLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiDriverEntryPoint</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiBootServicesTableLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>DevicePathLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>MemoryAllocationLib</Keyword>
+ </LibraryClass>
+ </LibraryClassDefinitions>
+ <SourceFiles>
+ <Filename>usbbus.h</Filename>
+ <Filename>UsbDesc.h</Filename>
+ <Filename>UsbUtility.c</Filename>
+ <Filename>UsbHub.h</Filename>
+ <Filename>UsbUtility.h</Filename>
+ <Filename>ComponentName.c</Filename>
+ <Filename>UsbHub.c</Filename>
+ <Filename>usbbus.c</Filename>
+ <Filename>UsbEnumer.h</Filename>
+ <Filename>UsbEnumer.c</Filename>
+ <Filename>UsbDesc.c</Filename>
+ </SourceFiles>
+ <PackageDependencies>
+ <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>
+ <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>
+ </PackageDependencies>
+ <Protocols>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiUsbHcProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiUsb2HcProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiDevicePathProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiUsbIoProtocolGuid</ProtocolCName>
+ </Protocol>
+ </Protocols>
+ <Externs>
+ <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>
+ <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>
+ <Extern>
+ <ModuleEntryPoint>UsbBusDriverEntryPoint</ModuleEntryPoint>
+ </Extern>
+ </Externs>
+</ModuleSurfaceArea>
\ No newline at end of file diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c new file mode 100644 index 0000000..ffed149 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c @@ -0,0 +1,991 @@ +/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+ Module Name:
+
+ UsbDesc.c
+
+ Abstract:
+
+ Manage Usb Descriptor List
+
+ Revision History
+
+
+**/
+
+#include "UsbBus.h"
+
+
+/**
+ Free the interface setting descriptor
+
+ @param Setting The descriptor to free
+
+ @return None
+
+**/
+STATIC
+VOID
+UsbFreeInterfaceDesc (
+ IN USB_INTERFACE_SETTING *Setting
+ )
+{
+ USB_ENDPOINT_DESC *Ep;
+ UINTN Index;
+
+ if (Setting->Endpoints != NULL) {
+ //
+ // Each interface setting may have several endpoints, free them first.
+ //
+ for (Index = 0; Index < Setting->Desc.NumEndpoints; Index++) {
+ Ep = Setting->Endpoints[Index];
+
+ if (Ep != NULL) {
+ gBS->FreePool (Ep);
+ }
+ }
+
+ gBS->FreePool (Setting->Endpoints);
+ }
+
+ gBS->FreePool (Setting);
+}
+
+
+
+/**
+ Free a configuration descriptor with its interface
+ descriptors. It may be initialized partially
+
+ @param Config The configuration descriptor to free
+
+ @return None
+
+**/
+STATIC
+VOID
+UsbFreeConfigDesc (
+ IN USB_CONFIG_DESC *Config
+ )
+{
+ USB_INTERFACE_DESC *Interface;
+ UINTN Index;
+ UINTN SetIndex;
+
+ if (Config->Interfaces != NULL) {
+ //
+ // A configuration may have several interfaces, free the interface
+ //
+ for (Index = 0; Index < Config->Desc.NumInterfaces; Index++) {
+ Interface = Config->Interfaces[Index];
+
+ if (Interface == NULL) {
+ continue;
+ }
+
+ //
+ // Each interface may have several settings, free the settings
+ //
+ for (SetIndex = 0; SetIndex < Interface->NumOfSetting; SetIndex++) {
+ if (Interface->Settings[SetIndex] != NULL) {
+ UsbFreeInterfaceDesc (Interface->Settings[SetIndex]);
+ }
+ }
+
+ gBS->FreePool (Interface);
+ }
+
+ gBS->FreePool (Config->Interfaces);
+ }
+
+ gBS->FreePool (Config);
+
+}
+
+
+
+/**
+ Free a device descriptor with its configurations
+
+ @param DevDesc The device descriptor
+
+ @return None
+
+**/
+VOID
+UsbFreeDevDesc (
+ IN USB_DEVICE_DESC *DevDesc
+ )
+{
+ UINTN Index;
+
+ if (DevDesc->Configs != NULL) {
+ for (Index = 0; Index < DevDesc->Desc.NumConfigurations; Index++) {
+ if (DevDesc->Configs[Index] != NULL) {
+ UsbFreeConfigDesc (DevDesc->Configs[Index]);
+ }
+ }
+
+ gBS->FreePool (DevDesc->Configs);
+ }
+
+ gBS->FreePool (DevDesc);
+}
+
+
+/**
+ Create a descriptor
+
+ @param DescBuf The buffer of raw descriptor
+ @param Len The lenght of the raw descriptor buffer
+ @param Type The type of descriptor to create
+ @param Consumed Number of bytes consumed
+
+ @return Created descriptor or NULL
+
+**/
+STATIC
+VOID *
+UsbCreateDesc (
+ IN UINT8 *DescBuf,
+ IN INTN Len,
+ IN UINT8 Type,
+ OUT INTN *Consumed
+ )
+{
+ USB_DESC_HEAD *Head;
+ INTN DescLen;
+ INTN CtrlLen;
+ INTN Offset;
+ VOID *Desc;
+
+ DescLen = 0;
+ CtrlLen = 0;
+ *Consumed = 0;
+
+ switch (Type) {
+ case USB_DESC_TYPE_DEVICE:
+ DescLen = sizeof (EFI_USB_DEVICE_DESCRIPTOR);
+ CtrlLen = sizeof (USB_DEVICE_DESC);
+ break;
+
+ case USB_DESC_TYPE_CONFIG:
+ DescLen = sizeof (EFI_USB_CONFIG_DESCRIPTOR);
+ CtrlLen = sizeof (USB_CONFIG_DESC);
+ break;
+
+ case USB_DESC_TYPE_INTERFACE:
+ DescLen = sizeof (EFI_USB_INTERFACE_DESCRIPTOR);
+ CtrlLen = sizeof (USB_INTERFACE_SETTING);
+ break;
+
+ case USB_DESC_TYPE_ENDPOINT:
+ DescLen = sizeof (EFI_USB_ENDPOINT_DESCRIPTOR);
+ CtrlLen = sizeof (USB_ENDPOINT_DESC);
+ break;
+ }
+
+ //
+ // All the descriptor has a common LTV (Length, Type, Value)
+ // format. Skip the descriptor that isn't of this Type
+ //
+ Offset = 0;
+ Head = (USB_DESC_HEAD*)DescBuf;
+
+ while ((Offset < Len) && (Head->Type != Type)) {
+ Offset += Head->Len;
+ Head = (USB_DESC_HEAD*)(DescBuf + Offset);
+ }
+
+ if ((Len <= Offset) || (Len < Offset + DescLen) ||
+ (Head->Type != Type) || (Head->Len != DescLen)) {
+ USB_ERROR (("UsbCreateDesc: met mal-format descriptor\n"));
+ return NULL;
+ }
+
+ Desc = AllocateZeroPool (CtrlLen);
+
+ if (Desc == NULL) {
+ return NULL;
+ }
+
+ CopyMem (Desc, Head, DescLen);
+ *Consumed = Offset + Head->Len;
+
+ return Desc;
+}
+
+
+/**
+ Parse an interface desciptor and its endpoints
+
+ @param DescBuf The buffer of raw descriptor
+ @param Len The lenght of the raw descriptor buffer
+ @param Consumed The number of raw descriptor consumed
+
+ @return The create interface setting or NULL if failed
+
+**/
+STATIC
+USB_INTERFACE_SETTING *
+UsbParseInterfaceDesc (
+ IN UINT8 *DescBuf,
+ IN INTN Len,
+ OUT INTN *Consumed
+ )
+{
+ USB_INTERFACE_SETTING *Setting;
+ USB_ENDPOINT_DESC *Ep;
+ UINTN Index;
+ UINTN NumEp;
+ INTN Used;
+ INTN Offset;
+
+ *Consumed = 0;
+ Setting = UsbCreateDesc (DescBuf, Len, USB_DESC_TYPE_INTERFACE, &Used);
+
+ if (Setting == NULL) {
+ USB_ERROR (("UsbParseInterfaceDesc: failed to create interface descriptor\n"));
+ return NULL;
+ }
+
+ Offset = Used;
+
+ //
+ // Create an arry to hold the interface's endpoints
+ //
+ NumEp = Setting->Desc.NumEndpoints;
+
+ USB_DEBUG (("UsbParseInterfaceDesc: interface %d(setting %d) has %d endpoints\n",
+ Setting->Desc.InterfaceNumber, Setting->Desc.AlternateSetting, NumEp));
+
+ if (NumEp == 0) {
+ goto ON_EXIT;
+ }
+
+ Setting->Endpoints = AllocateZeroPool (sizeof (USB_ENDPOINT_DESC *) * NumEp);
+
+ if (Setting->Endpoints == NULL) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Create the endpoints for this interface
+ //
+ for (Index = 0; Index < NumEp; Index++) {
+ Ep = UsbCreateDesc (DescBuf + Offset, Len - Offset, USB_DESC_TYPE_ENDPOINT, &Used);
+
+ if (Ep == NULL) {
+ USB_ERROR (("UsbParseInterfaceDesc: failed to create endpoint(index %d)\n", Index));
+ goto ON_ERROR;
+ }
+
+ Setting->Endpoints[Index] = Ep;
+ Offset += Used;
+ }
+
+
+ON_EXIT:
+ *Consumed = Offset;
+ return Setting;
+
+ON_ERROR:
+ UsbFreeInterfaceDesc (Setting);
+ return NULL;
+}
+
+
+/**
+ Parse the configuration descriptor and its interfaces.
+
+ @param DescBuf The buffer of raw descriptor
+ @param Len The lenght of the raw descriptor buffer
+
+ @return The created configuration descriptor
+
+**/
+STATIC
+USB_CONFIG_DESC *
+UsbParseConfigDesc (
+ IN UINT8 *DescBuf,
+ IN INTN Len
+ )
+{
+ USB_CONFIG_DESC *Config;
+ USB_INTERFACE_SETTING *Setting;
+ USB_INTERFACE_DESC *Interface;
+ UINTN Index;
+ UINTN NumIf;
+ INTN Consumed;
+
+ ASSERT (DescBuf != NULL);
+
+ Config = UsbCreateDesc (DescBuf, Len, USB_DESC_TYPE_CONFIG, &Consumed);
+
+ if (Config == NULL) {
+ return NULL;
+ }
+
+ //
+ // Initialize an array of setting for the configuration's interfaces.
+ //
+ NumIf = Config->Desc.NumInterfaces;
+ Config->Interfaces = AllocateZeroPool (sizeof (USB_INTERFACE_DESC *) * NumIf);
+
+ if (Config->Interfaces == NULL) {
+ goto ON_ERROR;
+ }
+
+ USB_DEBUG (("UsbParseConfigDesc: config %d has %d interfaces\n",
+ Config->Desc.ConfigurationValue, NumIf));
+
+ for (Index = 0; Index < NumIf; Index++) {
+ Interface = AllocateZeroPool (sizeof (USB_INTERFACE_DESC));
+
+ if (Interface == NULL) {
+ goto ON_ERROR;
+ }
+
+ Config->Interfaces[Index] = Interface;
+ }
+
+ //
+ // If a configuration has several interfaces, these interfaces are
+ // numbered from zero to n. If a interface has several settings,
+ // these settings are also number from zero to m. The interface
+ // setting must be organized as |interface 0, setting 0|interface 0
+ // setting 1|interface 1, setting 0|interface 2, setting 0|. Check
+ // USB2.0 spec, page 267.
+ //
+ DescBuf += Consumed;
+ Len -= Consumed;
+
+ while (Len > 0) {
+ Setting = UsbParseInterfaceDesc (DescBuf, Len, &Consumed);
+
+ if ((Setting == NULL)) {
+ USB_ERROR (("UsbParseConfigDesc: failed to parse interface setting\n"));
+ goto ON_ERROR;
+
+ } else if (Setting->Desc.InterfaceNumber >= NumIf) {
+ USB_ERROR (("UsbParseConfigDesc: mal-formated interface descriptor\n"));
+
+ UsbFreeInterfaceDesc (Setting);
+ goto ON_ERROR;
+ }
+
+ //
+ // Insert the descriptor to the corresponding set.
+ //
+ Interface = Config->Interfaces[Setting->Desc.InterfaceNumber];
+
+ if (Interface->NumOfSetting >= USB_MAX_INTERFACE_SETTING) {
+ goto ON_ERROR;
+ }
+
+ Interface->Settings[Interface->NumOfSetting] = Setting;
+ Interface->NumOfSetting++;
+
+ DescBuf += Consumed;
+ Len -= Consumed;
+ }
+
+ return Config;
+
+ON_ERROR:
+ UsbFreeConfigDesc (Config);
+ return NULL;
+}
+
+
+
+/**
+ USB standard control transfer support routine. This
+ function is used by USB device. It is possible that
+ the device's interfaces are still waiting to be
+ enumerated.
+
+ @param UsbDev The usb device
+ @param Direction The direction of data transfer
+ @param Type Standard / class specific / vendor specific
+ @param Target The receiving target
+ @param Request Which request
+ @param Value The wValue parameter of the request
+ @param Index The wIndex parameter of the request
+ @param Buf The buffer to receive data into / transmit from
+ @param Length The length of the buffer
+
+ @retval EFI_SUCCESS The control request is executed
+ @retval EFI_DEVICE_ERROR Failed to execute the control transfer
+
+**/
+EFI_STATUS
+UsbCtrlRequest (
+ IN USB_DEVICE *UsbDev,
+ IN EFI_USB_DATA_DIRECTION Direction,
+ IN UINTN Type,
+ IN UINTN Target,
+ IN UINTN Request,
+ IN UINT16 Value,
+ IN UINT16 Index,
+ IN OUT VOID *Buf,
+ IN UINTN Length
+ )
+{
+ EFI_USB_DEVICE_REQUEST DevReq;
+ EFI_STATUS Status;
+ UINT32 Result;
+ UINTN Len;
+
+ ASSERT ((UsbDev != NULL) && (UsbDev->Bus != NULL));
+
+ DevReq.RequestType = USB_REQUEST_TYPE (Direction, Type, Target);
+ DevReq.Request = (UINT8) Request;
+ DevReq.Value = Value;
+ DevReq.Index = Index;
+ DevReq.Length = (UINT16) Length;
+
+ Len = Length;
+ Status = UsbHcControlTransfer (
+ UsbDev->Bus,
+ UsbDev->Address,
+ UsbDev->Speed,
+ UsbDev->MaxPacket0,
+ &DevReq,
+ Direction,
+ Buf,
+ &Len,
+ 50 * USB_STALL_1_MS,
+ &UsbDev->Translator,
+ &Result
+ );
+
+ return Status;
+}
+
+
+
+/**
+ Get the standard descriptors.
+
+ @param UsbDev The USB device to read descriptor from
+ @param DescType The type of descriptor to read
+ @param DescIndex The index of descriptor to read
+ @param LangId Language ID, only used to get string, otherwise set
+ it to 0
+ @param Buf The buffer to hold the descriptor read
+ @param Length The length of the buffer
+
+ @retval EFI_SUCCESS The descriptor is read OK
+ @retval Others Failed to retrieve the descriptor
+
+**/
+STATIC
+EFI_STATUS
+UsbCtrlGetDesc (
+ IN USB_DEVICE *UsbDev,
+ IN UINTN DescType,
+ IN UINTN DescIndex,
+ IN UINT16 LangId,
+ OUT VOID *Buf,
+ IN UINTN Length
+ )
+{
+ EFI_STATUS Status;
+
+ Status = UsbCtrlRequest (
+ UsbDev,
+ EfiUsbDataIn,
+ USB_REQ_TYPE_STANDARD,
+ USB_TARGET_DEVICE,
+ USB_REQ_GET_DESCRIPTOR,
+ (UINT16) ((DescType << 8) | DescIndex),
+ LangId,
+ Buf,
+ Length
+ );
+
+ return Status;
+}
+
+
+
+/**
+ Return the max packet size for endpoint zero. This function
+ is the first function called to get descriptors during bus
+ enumeration.
+
+ @param UsbDev The usb device
+
+ @retval EFI_SUCCESS The max packet size of endpoint zero is retrieved
+ @retval EFI_DEVICE_ERROR Failed to retrieve it
+
+**/
+EFI_STATUS
+UsbGetMaxPacketSize0 (
+ IN USB_DEVICE *UsbDev
+ )
+{
+ EFI_USB_DEVICE_DESCRIPTOR DevDesc;
+ EFI_STATUS Status;
+ UINTN Index;
+
+
+ //
+ // Get the first 8 bytes of the device descriptor which contains
+ // max packet size for endpoint 0, which is at least 8.
+ //
+ UsbDev->MaxPacket0 = 8;
+
+ for (Index = 0; Index < 3; Index++) {
+ Status = UsbCtrlGetDesc (UsbDev, USB_DESC_TYPE_DEVICE, 0, 0, &DevDesc, 8);
+
+ if (!EFI_ERROR (Status)) {
+ UsbDev->MaxPacket0 = DevDesc.MaxPacketSize0;
+ return EFI_SUCCESS;
+ }
+
+ gBS->Stall (100 * USB_STALL_1_MS);
+ }
+
+ return EFI_DEVICE_ERROR;
+}
+
+
+
+/**
+ Get the device descriptor for the device.
+
+ @param UsbDev The Usb device to retrieve descriptor from
+
+ @retval EFI_SUCCESS The device descriptor is returned
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory
+
+**/
+STATIC
+EFI_STATUS
+UsbGetDevDesc (
+ IN USB_DEVICE *UsbDev
+ )
+{
+ USB_DEVICE_DESC *DevDesc;
+ EFI_STATUS Status;
+
+ DevDesc = AllocateZeroPool (sizeof (USB_DEVICE_DESC));
+
+ if (DevDesc == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = UsbCtrlGetDesc (
+ UsbDev,
+ USB_DESC_TYPE_DEVICE,
+ 0,
+ 0,
+ DevDesc,
+ sizeof (EFI_USB_DEVICE_DESCRIPTOR)
+ );
+
+ if (EFI_ERROR (Status)) {
+ gBS->FreePool (DevDesc);
+ } else {
+ UsbDev->DevDesc = DevDesc;
+ }
+
+ return Status;
+}
+
+
+
+/**
+ Retrieve the indexed string for the language. It requires two
+ steps to get a string, first to get the string's length. Then
+ the string itself.
+
+ @param UsbDev The usb device
+ @param Index The index the string to retrieve
+ @param LangId Language ID
+
+ @return The created string descriptor or NULL
+
+**/
+EFI_USB_STRING_DESCRIPTOR *
+UsbGetOneString (
+ IN USB_DEVICE *UsbDev,
+ IN UINT8 Index,
+ IN UINT16 LangId
+ )
+{
+ EFI_USB_STRING_DESCRIPTOR Desc;
+ EFI_STATUS Status;
+ UINT8 *Buf;
+
+ //
+ // First get two bytes which contains the string length.
+ //
+ Status = UsbCtrlGetDesc (UsbDev, USB_DESC_TYPE_STRING, Index, LangId, &Desc, 2);
+
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ Buf = AllocateZeroPool (Desc.Length);
+
+ if (Buf == NULL) {
+ return NULL;
+ }
+
+ Status = UsbCtrlGetDesc (
+ UsbDev,
+ USB_DESC_TYPE_STRING,
+ Index,
+ LangId,
+ Buf,
+ Desc.Length
+ );
+
+ if (EFI_ERROR (Status)) {
+ gBS->FreePool (Buf);
+ return NULL;
+ }
+
+ return (EFI_USB_STRING_DESCRIPTOR *) Buf;
+}
+
+
+
+/**
+ Build the language ID table for string descriptors
+
+ @param UsbDev The Usb device
+
+ @retval EFI_UNSUPPORTED This device doesn't support string table
+
+**/
+STATIC
+EFI_STATUS
+UsbBuildLangTable (
+ IN USB_DEVICE *UsbDev
+ )
+{
+ EFI_USB_STRING_DESCRIPTOR *Desc;
+ EFI_STATUS Status;
+ UINTN Index;
+ UINTN Max;
+ UINT16 *Point;
+
+ //
+ // The string of language ID zero returns the supported languages
+ //
+ Desc = UsbGetOneString (UsbDev, 0, 0);
+
+ if (Desc == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (Desc->Length < 4) {
+ Status = EFI_UNSUPPORTED;
+ goto ON_EXIT;
+ }
+
+ Status = EFI_SUCCESS;
+
+ Max = (Desc->Length - 2) / 2;
+ Max = (Max < USB_MAX_LANG_ID ? Max : USB_MAX_LANG_ID);
+
+ Point = Desc->String;
+ for (Index = 0; Index < Max; Index++) {
+ UsbDev->LangId[Index] = *Point;
+ Point++;
+ }
+
+ UsbDev->TotalLangId = (UINT16)Max;
+
+ON_EXIT:
+ gBS->FreePool (Desc);
+ return Status;
+}
+
+
+
+/**
+ Retrieve the indexed configure for the device. USB device
+ returns the configuration togetther with the interfaces for
+ this configuration. Configuration descriptor is also of
+ variable length
+
+ @param UsbDev The Usb interface
+ @param Index The index of the configuration
+
+ @return The created configuration descriptor
+
+**/
+STATIC
+EFI_USB_CONFIG_DESCRIPTOR *
+UsbGetOneConfig (
+ IN USB_DEVICE *UsbDev,
+ IN UINT8 Index
+ )
+{
+ EFI_USB_CONFIG_DESCRIPTOR Desc;
+ EFI_STATUS Status;
+ VOID *Buf;
+
+ //
+ // First get four bytes which contains the total length
+ // for this configuration.
+ //
+ Status = UsbCtrlGetDesc (UsbDev, USB_DESC_TYPE_CONFIG, Index, 0, &Desc, 8);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbGetOneConfig: failed to get descript length(%d) %r\n",
+ Status, Desc.TotalLength));
+
+ return NULL;
+ }
+
+ USB_DEBUG (("UsbGetOneConfig: total length is %d\n", Desc.TotalLength));
+
+ Buf = AllocateZeroPool (Desc.TotalLength);
+
+ if (Buf == NULL) {
+ return NULL;
+ }
+
+ Status = UsbCtrlGetDesc (UsbDev, USB_DESC_TYPE_CONFIG, Index, 0, Buf, Desc.TotalLength);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbGetOneConfig: failed to get full descript %r\n", Status));
+
+ gBS->FreePool (Buf);
+ return NULL;
+ }
+
+ return Buf;
+}
+
+
+
+/**
+ Build the whole array of descriptors. This function must
+ be called after UsbGetMaxPacketSize0 returns the max packet
+ size correctly for endpoint 0.
+
+ @param UsbDev The Usb device
+
+ @retval EFI_SUCCESS The descriptor table is build
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the descriptor
+
+**/
+EFI_STATUS
+UsbBuildDescTable (
+ IN USB_DEVICE *UsbDev
+ )
+{
+ EFI_USB_CONFIG_DESCRIPTOR *Config;
+ USB_DEVICE_DESC *DevDesc;
+ USB_CONFIG_DESC *ConfigDesc;
+ UINT8 NumConfig;
+ EFI_STATUS Status;
+ UINT8 Index;
+
+ //
+ // Get the device descriptor, then allocate the configure
+ // descriptor pointer array to hold configurations.
+ //
+ Status = UsbGetDevDesc (UsbDev);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbBuildDescTable: failed to get device descriptor - %r\n", Status));
+ return Status;
+ }
+
+ DevDesc = UsbDev->DevDesc;
+ NumConfig = DevDesc->Desc.NumConfigurations;
+ DevDesc->Configs = AllocateZeroPool (NumConfig * sizeof (USB_CONFIG_DESC *));
+
+ if (DevDesc->Configs == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ USB_DEBUG (("UsbBuildDescTable: device has %d configures\n", NumConfig));
+
+ //
+ // Read each configurations, then parse them
+ //
+ for (Index = 0; Index < NumConfig; Index++) {
+ Config = UsbGetOneConfig (UsbDev, Index);
+
+ if (Config == NULL) {
+ USB_ERROR (("UsbBuildDescTable: failed to get configure (index %d)\n", Index));
+
+ //
+ // If we can get the default descriptor, it is likely that the
+ // device is still operational.
+ //
+ if (Index == 0) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ break;
+ }
+
+ ConfigDesc = UsbParseConfigDesc ((UINT8 *) Config, Config->TotalLength);
+
+ gBS->FreePool (Config);
+
+ if (ConfigDesc == NULL) {
+ USB_ERROR (("UsbBuildDescTable: failed to parse configure (index %d)\n", Index));
+
+ //
+ // If we can get the default descriptor, it is likely that the
+ // device is still operational.
+ //
+ if (Index == 0) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ break;
+ }
+
+ DevDesc->Configs[Index] = ConfigDesc;
+ }
+
+ //
+ // Don't return error even this function failed because
+ // it is possible for the device to not support strings.
+ //
+ Status = UsbBuildLangTable (UsbDev);
+
+ if (EFI_ERROR (Status)) {
+ USB_DEBUG (("UsbBuildDescTable: get language ID table %r\n", Status));
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Set the device's address.
+
+ @param UsbDev The device to set address to
+ @param Address The address to set
+
+ @retval EFI_SUCCESS The device is set to the address
+ @retval Others Failed to set the device address
+
+**/
+EFI_STATUS
+UsbSetAddress (
+ IN USB_DEVICE *UsbDev,
+ IN UINT8 Address
+ )
+{
+ EFI_STATUS Status;
+
+ Status = UsbCtrlRequest (
+ UsbDev,
+ EfiUsbNoData,
+ USB_REQ_TYPE_STANDARD,
+ USB_TARGET_DEVICE,
+ USB_REQ_SET_ADDRESS,
+ Address,
+ 0,
+ NULL,
+ 0
+ );
+
+ return Status;
+}
+
+
+/**
+ Set the device's configuration. This function changes
+ the device's internal state. UsbSelectConfig changes
+ the Usb bus's internal state.
+
+ @param UsbDev The USB device to set configure to
+ @param ConfigIndex The configure index to set
+
+ @retval EFI_SUCCESS The device is configured now
+ @retval Others Failed to set the device configure
+
+**/
+EFI_STATUS
+UsbSetConfig (
+ IN USB_DEVICE *UsbDev,
+ IN UINT8 ConfigIndex
+ )
+{
+ EFI_STATUS Status;
+
+ Status = UsbCtrlRequest (
+ UsbDev,
+ EfiUsbNoData,
+ USB_REQ_TYPE_STANDARD,
+ USB_TARGET_DEVICE,
+ USB_REQ_SET_CONFIG,
+ ConfigIndex,
+ 0,
+ NULL,
+ 0
+ );
+
+ return Status;
+}
+
+
+
+/**
+ Usb UsbIo interface to clear the feature. This is should
+ only be used by HUB which is considered a device driver
+ on top of the UsbIo interface.
+
+ @param UsbIo The UsbIo interface
+ @param Target The target of the transfer: endpoint/device
+ @param Feature The feature to clear
+ @param Index The wIndex parameter
+
+ @retval EFI_SUCCESS The device feature is cleared
+ @retval Others Failed to clear the feature
+
+**/
+EFI_STATUS
+UsbIoClearFeature (
+ IN EFI_USB_IO_PROTOCOL *UsbIo,
+ IN UINTN Target,
+ IN UINT16 Feature,
+ IN UINT16 Index
+ )
+{
+ EFI_USB_DEVICE_REQUEST DevReq;
+ UINT32 UsbResult;
+ EFI_STATUS Status;
+
+ DevReq.RequestType = USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_STANDARD, Target);
+ DevReq.Request = USB_REQ_CLEAR_FEATURE;
+ DevReq.Value = Feature;
+ DevReq.Index = Index;
+ DevReq.Length = 0;
+
+ Status = UsbIo->UsbControlTransfer (
+ UsbIo,
+ &DevReq,
+ EfiUsbNoData,
+ 10 * USB_STALL_1_MS,
+ NULL,
+ 0,
+ &UsbResult
+ );
+
+ return Status;
+}
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h new file mode 100644 index 0000000..bdecfcf --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h @@ -0,0 +1,146 @@ +/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+ Module Name:
+
+ UsbDesc.h
+
+ Abstract:
+
+ Manage Usb Descriptor List
+
+ Revision History
+
+
+**/
+
+#ifndef _USB_DESCRIPTOR_H_
+#define _USB_DESCRIPTOR_H_
+
+enum {
+ USB_MAX_INTERFACE_SETTING = 8,
+};
+
+//
+// The RequestType in EFI_USB_DEVICE_REQUEST is composed of
+// three fields: One bit direction, 2 bit type, and 5 bit
+// target.
+//
+#define USB_REQUEST_TYPE(Dir, Type, Target) \
+ ((UINT8)((((Dir) == EfiUsbDataIn ? 0x01 : 0) << 7) | (Type) | (Target)))
+
+//
+// A common header for usb standard descriptor.
+// Each stand descriptor has a length and type.
+//
+#pragma pack(1)
+typedef struct {
+ UINT8 Len;
+ UINT8 Type;
+} USB_DESC_HEAD;
+#pragma pack()
+
+
+//
+// Each USB device has a device descriptor. Each device may
+// have several configures. Each configure contains several
+// interfaces. Each interface may have several settings. Each
+// setting has several endpoints.
+//
+// EFI_USB_..._DESCRIPTOR must be the first member of the
+// structure.
+//
+typedef struct {
+ EFI_USB_ENDPOINT_DESCRIPTOR Desc;
+ UINT8 Toggle;
+} USB_ENDPOINT_DESC;
+
+typedef struct {
+ EFI_USB_INTERFACE_DESCRIPTOR Desc;
+ USB_ENDPOINT_DESC **Endpoints;
+} USB_INTERFACE_SETTING;
+
+//
+// An interface may have several settings. Use a
+// fixed max number of settings to simplify code.
+// It should sufice in most environments.
+//
+typedef struct {
+ USB_INTERFACE_SETTING* Settings[USB_MAX_INTERFACE_SETTING];
+ UINTN NumOfSetting;
+ UINT8 ActiveIndex; // Index of active setting
+} USB_INTERFACE_DESC;
+
+typedef struct {
+ EFI_USB_CONFIG_DESCRIPTOR Desc;
+ USB_INTERFACE_DESC **Interfaces;
+} USB_CONFIG_DESC;
+
+typedef struct {
+ EFI_USB_DEVICE_DESCRIPTOR Desc;
+ USB_CONFIG_DESC **Configs;
+} USB_DEVICE_DESC;
+
+EFI_STATUS
+UsbCtrlRequest (
+ IN USB_DEVICE *UsbDev,
+ IN EFI_USB_DATA_DIRECTION Direction,
+ IN UINTN Type,
+ IN UINTN Target,
+ IN UINTN Request,
+ IN UINT16 Value,
+ IN UINT16 Index,
+ IN OUT VOID *Buf,
+ IN UINTN Length
+ );
+
+EFI_STATUS
+UsbGetMaxPacketSize0 (
+ IN USB_DEVICE *UsbDev
+ );
+
+VOID
+UsbFreeDevDesc (
+ IN USB_DEVICE_DESC *DevDesc
+ );
+
+EFI_USB_STRING_DESCRIPTOR*
+UsbGetOneString (
+ IN USB_DEVICE *UsbDev,
+ IN UINT8 StringIndex,
+ IN UINT16 LangId
+ );
+
+EFI_STATUS
+UsbBuildDescTable (
+ IN USB_DEVICE *UsbDev
+ );
+
+EFI_STATUS
+UsbSetAddress (
+ IN USB_DEVICE *UsbDev,
+ IN UINT8 Address
+ );
+
+EFI_STATUS
+UsbSetConfig (
+ IN USB_DEVICE *UsbDev,
+ IN UINT8 ConfigIndex
+ );
+
+EFI_STATUS
+UsbIoClearFeature (
+ IN EFI_USB_IO_PROTOCOL *UsbIo,
+ IN UINTN Target,
+ IN UINT16 Feature,
+ IN UINT16 Index
+ );
+#endif
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c new file mode 100644 index 0000000..fc0007d --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c @@ -0,0 +1,991 @@ +/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+ Module Name:
+
+ UsbEnumer.c
+
+ Abstract:
+
+ Usb bus enumeration support
+
+ Revision History
+
+
+**/
+
+#include "UsbBus.h"
+
+
+/**
+ Return the endpoint descriptor in this interface
+
+ @param UsbIf The interface to search in
+ @param EpAddr The address of the endpoint to return
+
+ @return The endpoint descriptor or NULL
+
+**/
+USB_ENDPOINT_DESC *
+UsbGetEndpointDesc (
+ IN USB_INTERFACE *UsbIf,
+ IN UINT8 EpAddr
+ )
+{
+ USB_ENDPOINT_DESC *EpDesc;
+ UINTN Index;
+
+ for (Index = 0; Index < UsbIf->IfSetting->Desc.NumEndpoints; Index++) {
+ EpDesc = UsbIf->IfSetting->Endpoints[Index];
+
+ if (EpDesc->Desc.EndpointAddress == EpAddr) {
+ return EpDesc;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Free the resource used by USB interface
+
+ @param UsbIf The USB interface to free
+
+ @return None
+
+**/
+STATIC
+VOID
+UsbFreeInterface (
+ IN USB_INTERFACE *UsbIf
+ )
+{
+ UsbCloseHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle);
+
+ gBS->UninstallMultipleProtocolInterfaces (
+ UsbIf->Handle,
+ &gEfiDevicePathProtocolGuid,
+ UsbIf->DevicePath,
+ &gEfiUsbIoProtocolGuid,
+ &UsbIf->UsbIo,
+ NULL
+ );
+
+ if (UsbIf->DevicePath != NULL) {
+ gBS->FreePool (UsbIf->DevicePath);
+ }
+
+ gBS->FreePool (UsbIf);
+}
+
+
+/**
+ Create an interface for the descriptor IfDesc. Each
+ device's configuration can have several interfaces.
+
+ @param Device The device has the interface descriptor
+ @param IfDesc The interface descriptor
+
+ @return The created USB interface for the descriptor, or NULL.
+
+**/
+STATIC
+USB_INTERFACE *
+UsbCreateInterface (
+ IN USB_DEVICE *Device,
+ IN USB_INTERFACE_DESC *IfDesc
+ )
+{
+ USB_DEVICE_PATH UsbNode;
+ USB_INTERFACE *UsbIf;
+ USB_INTERFACE *HubIf;
+ EFI_STATUS Status;
+
+ UsbIf = AllocateZeroPool (sizeof (USB_INTERFACE));
+
+ if (UsbIf == NULL) {
+ return NULL;
+ }
+
+ UsbIf->Signature = USB_INTERFACE_SIGNATURE;
+ UsbIf->Device = Device;
+ UsbIf->IfDesc = IfDesc;
+ UsbIf->IfSetting = IfDesc->Settings[IfDesc->ActiveIndex];
+ UsbIf->UsbIo = mUsbIoProtocol;
+
+ //
+ // Install protocols for USBIO and device path
+ //
+ UsbNode.Header.Type = MESSAGING_DEVICE_PATH;
+ UsbNode.Header.SubType = MSG_USB_DP;
+ UsbNode.ParentPortNumber = Device->ParentPort;
+ UsbNode.InterfaceNumber = UsbIf->IfSetting->Desc.InterfaceNumber;
+
+ SetDevicePathNodeLength (&UsbNode.Header, sizeof (UsbNode));
+
+ HubIf = Device->ParentIf;
+ ASSERT (HubIf != NULL);
+
+ UsbIf->DevicePath = AppendDevicePathNode (HubIf->DevicePath, &UsbNode.Header);
+
+ if (UsbIf->DevicePath == NULL) {
+ USB_ERROR (("UsbCreateInterface: failed to create device path\n"));
+
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &UsbIf->Handle,
+ &gEfiDevicePathProtocolGuid,
+ UsbIf->DevicePath,
+ &gEfiUsbIoProtocolGuid,
+ &UsbIf->UsbIo,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbCreateInterface: failed to install UsbIo - %r\n", Status));
+ goto ON_ERROR;
+ }
+
+ //
+ // Open USB Host Controller Protocol by Child
+ //
+ Status = UsbOpenHostProtoByChild (Device->Bus, UsbIf->Handle);
+
+ if (EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ &UsbIf->Handle,
+ &gEfiDevicePathProtocolGuid,
+ UsbIf->DevicePath,
+ &gEfiUsbIoProtocolGuid,
+ &UsbIf->UsbIo,
+ NULL
+ );
+
+ USB_ERROR (("UsbCreateInterface: failed to open host for child - %r\n", Status));
+ goto ON_ERROR;
+ }
+
+ return UsbIf;
+
+ON_ERROR:
+ if (UsbIf->DevicePath) {
+ gBS->FreePool (UsbIf->DevicePath);
+ }
+
+ gBS->FreePool (UsbIf);
+ return NULL;
+}
+
+
+/**
+ Free the resource used by this USB device
+
+ @param Device The USB device to free
+
+ @return None
+
+**/
+STATIC
+VOID
+UsbFreeDevice (
+ IN USB_DEVICE *Device
+ )
+{
+ if (Device->DevDesc != NULL) {
+ UsbFreeDevDesc (Device->DevDesc);
+ }
+
+ gBS->FreePool (Device);
+}
+
+
+/**
+ Create a device which is on the parent's ParentPort port.
+
+ @param ParentIf The parent HUB interface
+ @param ParentPort The port on the HUB this device is connected to
+
+ @return Created USB device
+
+**/
+STATIC
+USB_DEVICE *
+UsbCreateDevice (
+ IN USB_INTERFACE *ParentIf,
+ IN UINT8 ParentPort
+ )
+{
+ USB_DEVICE *Device;
+
+ ASSERT (ParentIf != NULL);
+
+ Device = AllocateZeroPool (sizeof (USB_DEVICE));
+
+ if (Device == NULL) {
+ return NULL;
+ }
+
+ Device->Bus = ParentIf->Device->Bus;
+ Device->MaxPacket0 = 8;
+ Device->ParentAddr = ParentIf->Device->Address;
+ Device->ParentIf = ParentIf;
+ Device->ParentPort = ParentPort;
+ return Device;
+}
+
+
+/**
+ Connect the USB interface with its driver. EFI USB bus will
+ create a USB interface for each seperate interface descriptor.
+
+ @param UsbIf The interface to connect driver to
+
+ @return EFI_SUCCESS : Interface is managed by some driver
+ @return Others : Failed to locate a driver for this interface
+
+**/
+STATIC
+EFI_STATUS
+UsbConnectDriver (
+ IN USB_INTERFACE *UsbIf
+ )
+{
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ Status = EFI_SUCCESS;
+
+ //
+ // Hub is maintained by the USB bus driver. Otherwise try to
+ // connect drivers with this interface
+ //
+ if (UsbIsHubInterface (UsbIf)) {
+ USB_DEBUG (("UsbConnectDriver: found a hub device\n"));
+ Status = mUsbHubApi.Init (UsbIf);
+
+ } else {
+ //
+ // This function is called in both UsbIoControlTransfer and
+ // the timer callback in hub enumeration. So, at least it is
+ // called at TPL_CALLBACK. Some driver sitting on USB has
+ // twisted TPL used. It should be no problem for us to connect
+ // or disconnect at CALLBACK.
+ //
+ OldTpl = UsbGetCurrentTpl ();
+ USB_DEBUG (("UsbConnectDriver: TPL before connect is %d\n", OldTpl));
+
+ gBS->RestoreTPL (TPL_CALLBACK);
+
+ Status = gBS->ConnectController (UsbIf->Handle, NULL, NULL, TRUE);
+ UsbIf->IsManaged = (BOOLEAN)!EFI_ERROR (Status);
+
+ USB_DEBUG (("UsbConnectDriver: TPL after connect is %d\n", UsbGetCurrentTpl()));
+ ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);
+
+ gBS->RaiseTPL (OldTpl);
+ }
+
+ return Status;
+}
+
+
+/**
+ Select an alternate setting for the interface.
+ Each interface can have several mutually exclusive
+ settings. Only one setting is active. It will
+ also reset its endpoints' toggle to zero.
+
+ @param IfDesc The interface descriptor to set
+ @param Alternate The alternate setting number to locate
+
+ @retval EFI_NOT_FOUND There is no setting with this alternate index
+ @retval EFI_SUCCESS The interface is set to Alternate setting.
+
+**/
+EFI_STATUS
+UsbSelectSetting (
+ IN USB_INTERFACE_DESC *IfDesc,
+ IN UINT8 Alternate
+ )
+{
+ USB_INTERFACE_SETTING *Setting;
+ UINT8 Index;
+
+ //
+ // Locate the active alternate setting
+ //
+ Setting = NULL;
+
+ for (Index = 0; Index < IfDesc->NumOfSetting; Index++) {
+ Setting = IfDesc->Settings[Index];
+
+ if (Setting->Desc.AlternateSetting == Alternate) {
+ break;
+ }
+ }
+
+ if (Index == IfDesc->NumOfSetting) {
+ return EFI_NOT_FOUND;
+ }
+
+ IfDesc->ActiveIndex = Index;
+
+ USB_DEBUG (("UsbSelectSetting: setting %d selected for interface %d\n",
+ Alternate, Setting->Desc.InterfaceNumber));
+
+ //
+ // Reset the endpoint toggle to zero
+ //
+ for (Index = 0; Index < Setting->Desc.NumEndpoints; Index++) {
+ Setting->Endpoints[Index]->Toggle = 0;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Select a new configuration for the device. Each
+ device may support several configurations.
+
+ @param Device The device to select configuration
+ @param ConfigValue The index of the configuration ( != 0)
+
+ @retval EFI_NOT_FOUND There is no configuration with the index
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource
+ @retval EFI_SUCCESS The configuration is selected.
+
+**/
+EFI_STATUS
+UsbSelectConfig (
+ IN USB_DEVICE *Device,
+ IN UINT8 ConfigValue
+ )
+{
+ USB_DEVICE_DESC *DevDesc;
+ USB_CONFIG_DESC *ConfigDesc;
+ USB_INTERFACE_DESC *IfDesc;
+ USB_INTERFACE *UsbIf;
+ EFI_STATUS Status;
+ UINT8 Index;
+
+ //
+ // Locate the active config, then set the device's pointer
+ //
+ DevDesc = Device->DevDesc;
+ ConfigDesc = NULL;
+
+ for (Index = 0; Index < DevDesc->Desc.NumConfigurations; Index++) {
+ ConfigDesc = DevDesc->Configs[Index];
+
+ if (ConfigDesc->Desc.ConfigurationValue == ConfigValue) {
+ break;
+ }
+ }
+
+ if (Index == DevDesc->Desc.NumConfigurations) {
+ return EFI_NOT_FOUND;
+ }
+
+ Device->ActiveConfig = ConfigDesc;
+
+ USB_DEBUG (("UsbSelectConfig: config %d selected for device %d\n",
+ ConfigValue, Device->Address));
+
+ //
+ // Create interfaces for each USB interface descriptor.
+ //
+ for (Index = 0; Index < ConfigDesc->Desc.NumInterfaces; Index++) {
+ //
+ // First select the default interface setting, and reset
+ // the endpoint toggles to zero for its endpoints.
+ //
+ IfDesc = ConfigDesc->Interfaces[Index];
+ UsbSelectSetting (IfDesc, IfDesc->Settings[0]->Desc.AlternateSetting);
+
+ //
+ // Create a USB_INTERFACE and install USB_IO and other protocols
+ //
+ UsbIf = UsbCreateInterface (Device, ConfigDesc->Interfaces[Index]);
+
+ if (UsbIf == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Device->Interfaces[Index] = UsbIf;
+
+ //
+ // Connect the device to drivers, if it failed, ignore
+ // the error. Don't let the unsupported interfaces to block
+ // the supported interfaces.
+ //
+ Status = UsbConnectDriver (UsbIf);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbSelectConfig: failed to connect driver %r, ignored\n", Status));
+ }
+ }
+
+ Device->NumOfInterface = Index;
+
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Disconnect the USB interface with its driver.
+
+ @param UsbIf The interface to disconnect driver from
+
+ @return None
+
+**/
+STATIC
+VOID
+UsbDisconnectDriver (
+ IN USB_INTERFACE *UsbIf
+ )
+{
+ EFI_TPL OldTpl;
+
+ //
+ // Release the hub if it's a hub controller, otherwise
+ // disconnect the driver if it is managed by other drivers.
+ //
+ if (UsbIf->IsHub) {
+ UsbIf->HubApi->Release (UsbIf);
+
+ } else if (UsbIf->IsManaged) {
+ //
+ // This function is called in both UsbIoControlTransfer and
+ // the timer callback in hub enumeration. So, at least it is
+ // called at TPL_CALLBACK. Some driver sitting on USB has
+ // twisted TPL used. It should be no problem for us to connect
+ // or disconnect at CALLBACK.
+ //
+ OldTpl = UsbGetCurrentTpl ();
+ USB_DEBUG (("UsbDisconnectDriver: old TPL is %d\n", OldTpl));
+
+ gBS->RestoreTPL (TPL_CALLBACK);
+
+ gBS->DisconnectController (UsbIf->Handle, NULL, NULL);
+ UsbIf->IsManaged = FALSE;
+
+ USB_DEBUG (("UsbDisconnectDriver: TPL after disconnect is %d\n", UsbGetCurrentTpl()));
+ ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);
+
+ gBS->RaiseTPL (OldTpl);
+ }
+}
+
+
+
+/**
+ Remove the current device configuration
+
+ @param Device The USB device to remove configuration from
+
+ @return None
+
+**/
+VOID
+UsbRemoveConfig (
+ IN USB_DEVICE *Device
+ )
+{
+ USB_INTERFACE *UsbIf;
+ UINTN Index;
+
+ //
+ // Remove each interface of the device
+ //
+ for (Index = 0; Index < Device->NumOfInterface; Index++) {
+ UsbIf = Device->Interfaces[Index];
+
+ if (UsbIf == NULL) {
+ continue;
+ }
+
+ UsbDisconnectDriver (UsbIf);
+ UsbFreeInterface (UsbIf);
+ Device->Interfaces[Index] = NULL;
+ }
+
+ Device->ActiveConfig = NULL;
+ Device->NumOfInterface = 0;
+}
+
+
+
+/**
+ Remove the device and all its children from the bus.
+
+ @param Device The device to remove
+
+ @retval EFI_SUCCESS The device is removed
+
+**/
+EFI_STATUS
+UsbRemoveDevice (
+ IN USB_DEVICE *Device
+ )
+{
+ USB_BUS *Bus;
+ USB_DEVICE *Child;
+ EFI_STATUS Status;
+ UINT8 Index;
+
+ Bus = Device->Bus;
+
+ //
+ // Remove all the devices on its downstream ports. Search from devices[1].
+ // Devices[0] is the root hub.
+ //
+ for (Index = 1; Index < USB_MAX_DEVICES; Index++) {
+ Child = Bus->Devices[Index];
+
+ if ((Child == NULL) || (Child->ParentAddr != Device->Address)) {
+ continue;
+ }
+
+ Status = UsbRemoveDevice (Child);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbRemoveDevice: failed to remove child, ignore error\n"));
+ Bus->Devices[Index] = NULL;
+ }
+ }
+
+ UsbRemoveConfig (Device);
+
+ USB_DEBUG (("UsbRemoveDevice: device %d removed\n", Device->Address));
+
+ Bus->Devices[Device->Address] = NULL;
+ UsbFreeDevice (Device);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Find the child device on the hub's port
+
+ @param HubIf The hub interface
+ @param Port The port of the hub this child is connected to
+
+ @return The device on the hub's port, or NULL if there is none
+
+**/
+STATIC
+USB_DEVICE *
+UsbFindChild (
+ IN USB_INTERFACE *HubIf,
+ IN UINT8 Port
+ )
+{
+ USB_DEVICE *Device;
+ USB_BUS *Bus;
+ UINTN Index;
+
+ Bus = HubIf->Device->Bus;
+
+ //
+ // Start checking from device 1, device 0 is the root hub
+ //
+ for (Index = 1; Index < USB_MAX_DEVICES; Index++) {
+ Device = Bus->Devices[Index];
+
+ if ((Device != NULL) && (Device->ParentAddr == HubIf->Device->Address) &&
+ (Device->ParentPort == Port)) {
+
+ return Device;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+/**
+ Enumerate and configure the new device on the port of this HUB interface.
+
+ @param HubIf The HUB that has the device connected
+ @param Port The port index of the hub (started with zero)
+
+ @retval EFI_SUCCESS The device is enumerated (added or removed)
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the device
+ @retval Others Failed to enumerate the device
+
+**/
+STATIC
+EFI_STATUS
+UsbEnumerateNewDev (
+ IN USB_INTERFACE *HubIf,
+ IN UINT8 Port
+ )
+{
+ USB_BUS *Bus;
+ USB_HUB_API *HubApi;
+ USB_DEVICE *Child;
+ USB_DEVICE *Parent;
+ EFI_USB_PORT_STATUS PortState;
+ UINT8 Address;
+ UINT8 Config;
+ EFI_STATUS Status;
+
+ Address = USB_MAX_DEVICES;
+ Parent = HubIf->Device;
+ Bus = Parent->Bus;
+ HubApi = HubIf->HubApi;
+
+
+ //
+ // Wait at least 100 ms for the power on port to stable
+ //
+ gBS->Stall (100 * USB_STALL_1_MS);
+
+ //
+ // Hub resets the device for at least 10 milliseconds.
+ // Host learns device speed. If device is of low/full speed
+ // and the hub is a EHCI root hub, ResetPort will release
+ // the device to its companion UHCI and return an error.
+ //
+ Status = HubApi->ResetPort (HubIf, Port);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbEnumerateNewDev: failed to reset port %d - %r\n", Port, Status));
+
+ return Status;
+ }
+
+ USB_DEBUG (("UsbEnumerateNewDev: hub port %d is reset\n", Port));
+
+ Child = UsbCreateDevice (HubIf, Port);
+
+ if (Child == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // OK, now identify the device speed. After reset, hub
+ // fully knows the actual device speed.
+ //
+ Status = HubApi->GetPortStatus (HubIf, Port, &PortState);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbEnumerateNewDev: failed to get speed of port %d\n", Port));
+ goto ON_ERROR;
+ }
+
+ if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_LOW_SPEED)) {
+ Child->Speed = EFI_USB_SPEED_LOW;
+
+ } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_HIGH_SPEED)) {
+ Child->Speed = EFI_USB_SPEED_HIGH;
+
+ } else {
+ Child->Speed = EFI_USB_SPEED_FULL;
+ }
+
+ USB_DEBUG (("UsbEnumerateNewDev: device is of %d speed\n", Child->Speed));
+
+ if (Child->Speed != EFI_USB_SPEED_HIGH) {
+ //
+ // If the child isn't a high speed device, it is necessary to
+ // set the transaction translator. This is quite simple:
+ // 1. if parent is of high speed, then parent is our translator
+ // 2. otherwise use parent's translator.
+ //
+ if (Parent->Speed == EFI_USB_SPEED_HIGH) {
+ Child->Translator.TranslatorHubAddress = Parent->Address;
+ Child->Translator.TranslatorPortNumber = Port;
+
+ } else {
+ Child->Translator = Parent->Translator;
+ }
+
+ USB_DEBUG (("UsbEnumerateNewDev: device uses translator (%d, %d)\n",
+ Child->Translator.TranslatorHubAddress,
+ Child->Translator.TranslatorPortNumber));
+ }
+
+ //
+ // After port is reset, hub establishes a signal path between
+ // the device and host (DEFALUT state). Device¡¯s registers are
+ // reset, use default address 0 (host enumerates one device at
+ // a time) , and ready to respond to control transfer at EP 0.
+ //
+
+ //
+ // Host sends a Get_Descriptor request to learn the max packet
+ // size of default pipe (only part of the device¡¯s descriptor).
+ //
+ Status = UsbGetMaxPacketSize0 (Child);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbEnumerateNewDev: failed to get max packet for EP 0 - %r\n", Status));
+ goto ON_ERROR;
+ }
+
+ USB_DEBUG (("UsbEnumerateNewDev: max packet size for EP 0 is %d\n", Child->MaxPacket0));
+
+ //
+ // Host assigns an address to the device. Device completes the
+ // status stage with default address, then switches to new address.
+ // ADDRESS state. Address zero is reserved for root hub.
+ //
+ for (Address = 1; Address < USB_MAX_DEVICES; Address++) {
+ if (Bus->Devices[Address] == NULL) {
+ break;
+ }
+ }
+
+ if (Address == USB_MAX_DEVICES) {
+ USB_ERROR (("UsbEnumerateNewDev: address pool is full for port %d\n", Port));
+
+ Status = EFI_ACCESS_DENIED;
+ goto ON_ERROR;
+ }
+
+ Bus->Devices[Address] = Child;
+ Status = UsbSetAddress (Child, Address);
+ Child->Address = Address;
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbEnumerateNewDev: failed to set device address - %r\n", Status));
+ goto ON_ERROR;
+ }
+
+ //
+ // Wait 20ms for set address to complete
+ //
+ gBS->Stall (20 * USB_STALL_1_MS);
+
+ USB_DEBUG (("UsbEnumerateNewDev: device is now ADDRESSED at %d\n", Address));
+
+ //
+ // Host learns about the device¡¯s abilities by requesting device's
+ // entire descriptions.
+ //
+ Status = UsbBuildDescTable (Child);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbEnumerateNewDev: failed to build descriptor table - %r\n", Status));
+ goto ON_ERROR;
+ }
+
+ //
+ // Select a default configuration: UEFI must set the configuration
+ // before the driver can connect to the device.
+ //
+ Config = Child->DevDesc->Configs[0]->Desc.ConfigurationValue;
+ Status = UsbSetConfig (Child, Config);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbEnumerateNewDev: failed to set configure %d - %r\n", Config, Status));
+ goto ON_ERROR;
+ }
+
+ USB_DEBUG (("UsbEnumerateNewDev: device %d is now in CONFIGED state\n", Address));
+
+ //
+ // Host assigns and loads a device driver.
+ //
+ Status = UsbSelectConfig (Child, Config);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbEnumerateNewDev: failed to create interfaces - %r\n", Status));
+ goto ON_ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ if (Address != USB_MAX_DEVICES) {
+ Bus->Devices[Address] = NULL;
+ }
+
+ if (Child != NULL) {
+ UsbFreeDevice (Child);
+ }
+
+ return Status;
+}
+
+
+
+/**
+ Process the events on the port.
+
+ @param HubIf The HUB that has the device connected
+ @param Port The port index of the hub (started with zero)
+
+ @retval EFI_SUCCESS The device is enumerated (added or removed)
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the device
+ @retval Others Failed to enumerate the device
+
+**/
+STATIC
+EFI_STATUS
+UsbEnumeratePort (
+ IN USB_INTERFACE *HubIf,
+ IN UINT8 Port
+ )
+{
+ USB_HUB_API *HubApi;
+ USB_DEVICE *Child;
+ EFI_USB_PORT_STATUS PortState;
+ EFI_STATUS Status;
+
+ Child = NULL;
+ HubApi = HubIf->HubApi;
+
+ //
+ // Host learns of the new device by polling the hub for port changes.
+ //
+ Status = HubApi->GetPortStatus (HubIf, Port, &PortState);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbEnumeratePort: failed to get state of port %d\n", Port));
+ return Status;
+ }
+
+ if (PortState.PortChangeStatus == 0) {
+ return EFI_SUCCESS;
+ }
+
+ USB_DEBUG (("UsbEnumeratePort: port %d state - %x, change - %x\n",
+ Port, PortState.PortStatus, PortState.PortChangeStatus));
+
+ //
+ // This driver only process two kinds of events now: over current and
+ // connect/disconnect. Other three events are: ENABLE, SUSPEND, RESET.
+ // ENABLE/RESET is used to reset port. SUSPEND isn't supported.
+ //
+ Status = EFI_SUCCESS;
+
+ if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_OVERCURRENT)) {
+ //
+ // If overcurrent condition is cleared, enable the port again
+ //
+ if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_OVERCURRENT)) {
+ HubApi->SetPortFeature (HubIf, Port, USB_HUB_PORT_POWER);
+ }
+
+ } else if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_CONNECTION)) {
+ //
+ // Device connected or disconnected. Either way, if there is
+ // already a device present in the bus, need to remove it.
+ //
+ Child = UsbFindChild (HubIf, Port);
+
+ if (Child != NULL) {
+ USB_DEBUG (("UsbEnumeratePort: device at port %d removed from system\n", Port));
+ UsbRemoveDevice (Child);
+ }
+
+ if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) {
+ //
+ // Now, new device connected, enumerate and configure the device
+ //
+ USB_DEBUG (("UsbEnumeratePort: new device connected at port %d\n", Port));
+ Status = UsbEnumerateNewDev (HubIf, Port);
+
+ } else {
+ USB_DEBUG (("UsbEnumeratePort: device disconnected event on port %d\n", Port));
+ }
+ }
+
+ HubApi->ClearPortChange (HubIf, Port);
+ return Status;
+}
+
+
+/**
+ Enumerate all the changed hub ports
+
+ @param Event The event that is triggered
+ @param Context The context to the event
+
+ @return None
+
+**/
+VOID
+UsbHubEnumeration (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ USB_INTERFACE *HubIf;
+ UINT8 Byte;
+ UINT8 Bit;
+ UINT8 Index;
+
+ ASSERT (Context);
+
+ HubIf = (USB_INTERFACE *) Context;
+
+ if (HubIf->ChangeMap == NULL) {
+ return ;
+ }
+
+ //
+ // HUB starts its port index with 1.
+ //
+ Byte = 0;
+ Bit = 1;
+
+ for (Index = 0; Index < HubIf->NumOfPort; Index++) {
+ if (USB_BIT_IS_SET (HubIf->ChangeMap[Byte], USB_BIT (Bit))) {
+ UsbEnumeratePort (HubIf, Index);
+ }
+
+ USB_NEXT_BIT (Byte, Bit);
+ }
+
+ UsbHubAckHubStatus (HubIf->Device);
+
+ gBS->FreePool (HubIf->ChangeMap);
+ HubIf->ChangeMap = NULL;
+ return ;
+}
+
+
+/**
+ Enumerate all the changed hub ports
+
+ @param Event The event that is triggered
+ @param Context The context to the event
+
+ @return None
+
+**/
+VOID
+UsbRootHubEnumeration (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ USB_INTERFACE *RootHub;
+ UINT8 Index;
+
+ RootHub = (USB_INTERFACE *) Context;
+
+ for (Index = 0; Index < RootHub->NumOfPort; Index++) {
+ UsbEnumeratePort (RootHub, Index);
+ }
+}
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.h b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.h new file mode 100644 index 0000000..e14b2d7 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.h @@ -0,0 +1,153 @@ +/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+ Module Name:
+
+ UsbEnumer.h
+
+ Abstract:
+
+ USB bus enumeration interface
+
+ Revision History
+
+
+**/
+
+#ifndef _USB_ENUMERATION_H_
+#define _USB_ENUMERATION_H_
+
+//
+// Advance the byte and bit to the next bit, adjust byte accordingly.
+//
+#define USB_NEXT_BIT(Byte, Bit) \
+ do { \
+ (Bit)++; \
+ if ((Bit) > 7) { \
+ (Byte)++; \
+ (Bit) = 0; \
+ } \
+ } while (0)
+
+
+//
+// Common interface used by usb bus enumeration process.
+// This interface is defined to mask the difference between
+// the root hub and normal hub. So, bus enumeration code
+// can be shared by both root hub and normal hub
+//
+typedef
+EFI_STATUS
+(*USB_HUB_INIT) (
+ IN USB_INTERFACE *UsbIf
+ );
+
+//
+// Get the port status. This function is required to
+// ACK the port change bits although it will return
+// the port changes in PortState. Bus enumeration code
+// doesn't need to ACK the port change bits.
+//
+typedef
+EFI_STATUS
+(*USB_HUB_GET_PORT_STATUS) (
+ IN USB_INTERFACE *UsbIf,
+ IN UINT8 Port,
+ OUT EFI_USB_PORT_STATUS *PortState
+ );
+
+typedef
+VOID
+(*USB_HUB_CLEAR_PORT_CHANGE) (
+ IN USB_INTERFACE *HubIf,
+ IN UINT8 Port
+ );
+
+typedef
+EFI_STATUS
+(*USB_HUB_SET_PORT_FEATURE) (
+ IN USB_INTERFACE *UsbIf,
+ IN UINT8 Port,
+ IN EFI_USB_PORT_FEATURE Feature
+ );
+
+typedef
+EFI_STATUS
+(*USB_HUB_CLEAR_PORT_FEATURE) (
+ IN USB_INTERFACE *UsbIf,
+ IN UINT8 Port,
+ IN EFI_USB_PORT_FEATURE Feature
+ );
+
+typedef
+EFI_STATUS
+(*USB_HUB_RESET_PORT) (
+ IN USB_INTERFACE *UsbIf,
+ IN UINT8 Port
+ );
+
+typedef
+EFI_STATUS
+(*USB_HUB_RELEASE) (
+ IN USB_INTERFACE *UsbIf
+ );
+
+typedef struct _USB_HUB_API{
+ USB_HUB_INIT Init;
+ USB_HUB_GET_PORT_STATUS GetPortStatus;
+ USB_HUB_CLEAR_PORT_CHANGE ClearPortChange;
+ USB_HUB_SET_PORT_FEATURE SetPortFeature;
+ USB_HUB_CLEAR_PORT_FEATURE ClearPortFeature;
+ USB_HUB_RESET_PORT ResetPort;
+ USB_HUB_RELEASE Release;
+} USB_HUB_API;
+
+USB_ENDPOINT_DESC*
+UsbGetEndpointDesc (
+ IN USB_INTERFACE *UsbIf,
+ IN UINT8 EpAddr
+ );
+
+
+EFI_STATUS
+UsbSelectSetting (
+ IN USB_INTERFACE_DESC *IfDesc,
+ IN UINT8 Alternate
+ );
+
+EFI_STATUS
+UsbSelectConfig (
+ IN USB_DEVICE *Device,
+ IN UINT8 ConfigIndex
+ );
+
+VOID
+UsbRemoveConfig (
+ IN USB_DEVICE *Device
+ );
+
+EFI_STATUS
+UsbRemoveDevice (
+ IN USB_DEVICE *Device
+ );
+
+VOID
+UsbHubEnumeration (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+VOID
+UsbRootHubEnumeration (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+#endif
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c new file mode 100644 index 0000000..25332f4 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.c @@ -0,0 +1,1334 @@ +/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+ Module Name:
+
+ UsbHub.c
+
+ Abstract:
+
+ Unified interface for RootHub and Hub
+
+ Revision History
+
+
+**/
+
+#include "UsbBus.h"
+
+//
+// USB hub class specific requests. Although USB hub
+// is related to an interface, these requests are sent
+// to the control endpoint of the device.
+//
+
+
+/**
+ USB hub control transfer to clear the hub feature
+
+ @param HubDev The device of the hub
+ @param Feature The feature to clear
+
+ @retval EFI_SUCCESS Feature of the hub is cleared
+ @retval Others Failed to clear the feature
+
+**/
+STATIC
+EFI_STATUS
+UsbHubCtrlClearHubFeature (
+ IN USB_DEVICE *HubDev,
+ IN UINT16 Feature
+ )
+{
+ EFI_STATUS Status;
+
+ Status = UsbCtrlRequest (
+ HubDev,
+ EfiUsbNoData,
+ USB_REQ_TYPE_CLASS,
+ USB_HUB_TARGET_HUB,
+ USB_HUB_REQ_CLEAR_FEATURE,
+ Feature,
+ 0,
+ NULL,
+ 0
+ );
+
+ return Status;
+}
+
+
+/**
+ Clear the feature of the device's port
+
+ @param HubDev The hub device
+ @param Port The port to clear feature
+ @param Feature The feature to clear
+
+ @retval EFI_SUCCESS The feature of the port is cleared.
+ @retval Others Failed to clear the feature.
+
+**/
+STATIC
+EFI_STATUS
+UsbHubCtrlClearPortFeature (
+ IN USB_DEVICE *HubDev,
+ IN UINT8 Port,
+ IN UINT16 Feature
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // In USB bus, all the port index starts from 0. But HUB
+ // indexes its port from 1. So, port number is added one.
+ //
+ Status = UsbCtrlRequest (
+ HubDev,
+ EfiUsbNoData,
+ USB_REQ_TYPE_CLASS,
+ USB_HUB_TARGET_PORT,
+ USB_HUB_REQ_CLEAR_FEATURE,
+ Feature,
+ Port + 1,
+ NULL,
+ 0
+ );
+
+ return Status;
+}
+
+
+
+/**
+ Clear the transaction translate buffer if full/low
+ speed control/bulk transfer failed and the transfer
+ uses this hub as translator.Remember to clear the TT
+ buffer of transaction translator, not that of the
+ parent.
+
+ @param HubDev The hub device
+ @param Port The port of the hub
+ @param DevAddr Address of the failed transaction
+ @param EpNum The endpoint number of the failed transaction
+ @param EpType The type of failed transaction
+
+ @retval EFI_SUCCESS The TT buffer is cleared
+ @retval Others Failed to clear the TT buffer
+
+**/
+EFI_STATUS
+UsbHubCtrlClearTTBuffer (
+ IN USB_DEVICE *HubDev,
+ IN UINT8 Port,
+ IN UINT16 DevAddr,
+ IN UINT16 EpNum,
+ IN UINT16 EpType
+ )
+{
+ EFI_STATUS Status;
+ UINT16 Value;
+
+ //
+ // Check USB2.0 spec page 424 for wValue's encoding
+ //
+ Value = (EpNum & 0x0F) | (DevAddr << 4) |
+ ((EpType & 0x03) << 11) | ((EpNum & 0x80) << 15);
+
+ Status = UsbCtrlRequest (
+ HubDev,
+ EfiUsbNoData,
+ USB_REQ_TYPE_CLASS,
+ USB_HUB_TARGET_PORT,
+ USB_HUB_REQ_CLEAR_TT,
+ Value,
+ Port + 1,
+ NULL,
+ 0
+ );
+
+ return Status;
+}
+
+
+/**
+ Usb hub control transfer to get the hub descriptor
+
+ @param HubDev The hub device
+ @param Buf The buffer to hold the descriptor
+ @param Len The length to retrieve
+
+ @retval EFI_SUCCESS The hub descriptor is retrieved
+ @retval Others Failed to retrieve the hub descriptor
+
+**/
+STATIC
+EFI_STATUS
+UsbHubCtrlGetHubDesc (
+ IN USB_DEVICE *HubDev,
+ OUT VOID *Buf,
+ IN UINTN Len
+ )
+{
+ EFI_STATUS Status;
+
+ Status = UsbCtrlRequest (
+ HubDev,
+ EfiUsbDataIn,
+ USB_REQ_TYPE_CLASS,
+ USB_HUB_TARGET_HUB,
+ USB_HUB_REQ_GET_DESC,
+ (UINT16) (USB_DESC_TYPE_HUB << 8),
+ 0,
+ Buf,
+ Len
+ );
+
+ return Status;
+}
+
+
+/**
+ Usb hub control transfer to get the hub status
+
+ @param HubDev The hub device
+ @param State The variable to return the status
+
+ @retval EFI_SUCCESS The hub status is returned in State
+ @retval Others Failed to get the hub status
+
+**/
+STATIC
+EFI_STATUS
+UsbHubCtrlGetHubStatus (
+ IN USB_DEVICE *HubDev,
+ OUT UINT32 *State
+ )
+{
+ EFI_STATUS Status;
+
+ Status = UsbCtrlRequest (
+ HubDev,
+ EfiUsbDataIn,
+ USB_REQ_TYPE_CLASS,
+ USB_HUB_TARGET_HUB,
+ USB_HUB_REQ_GET_STATUS,
+ 0,
+ 0,
+ State,
+ 4
+ );
+
+ return Status;
+}
+
+
+/**
+ Usb hub control transfer to get the port status
+
+ @param HubDev The hub device
+ @param Port The port of the hub
+ @param State Variable to return the hub port state
+
+ @retval EFI_SUCCESS The port state is returned in State
+ @retval Others Failed to retrive the port state
+
+**/
+STATIC
+EFI_STATUS
+UsbHubCtrlGetPortStatus (
+ IN USB_DEVICE *HubDev,
+ IN UINT8 Port,
+ OUT VOID *State
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // In USB bus, all the port index starts from 0. But HUB
+ // indexes its port from 1. So, port number is added one.
+ // No need to convert the hub bit to UEFI definition, they
+ // are the same
+ //
+ Status = UsbCtrlRequest (
+ HubDev,
+ EfiUsbDataIn,
+ USB_REQ_TYPE_CLASS,
+ USB_HUB_TARGET_PORT,
+ USB_HUB_REQ_GET_STATUS,
+ 0,
+ Port + 1,
+ State,
+ 4
+ );
+
+ return Status;
+}
+
+
+/**
+ Usb hub control transfer to reset the TT (Transaction Transaltor)
+
+ @param HubDev The hub device
+ @param Port The port of the hub
+
+ @retval EFI_SUCCESS The TT of the hub is reset
+ @retval Others Failed to reset the port
+
+**/
+STATIC
+EFI_STATUS
+UsbHubCtrlResetTT (
+ IN USB_DEVICE *HubDev,
+ IN UINT8 Port
+ )
+{
+ EFI_STATUS Status;
+
+ Status = UsbCtrlRequest (
+ HubDev,
+ EfiUsbNoData,
+ USB_REQ_TYPE_CLASS,
+ USB_HUB_TARGET_HUB,
+ USB_HUB_REQ_RESET_TT,
+ 0,
+ Port + 1,
+ NULL,
+ 0
+ );
+
+ return Status;
+}
+
+
+/**
+ Usb hub control transfer to set the hub feature
+
+ @param HubDev The hub device
+ @param Feature The feature to set
+
+ @retval EFI_SUCESS The feature is set for the hub
+ @retval Others Failed to set the feature
+
+**/
+STATIC
+EFI_STATUS
+UsbHubCtrlSetHubFeature (
+ IN USB_DEVICE *HubDev,
+ IN UINT8 Feature
+ )
+{
+ EFI_STATUS Status;
+
+ Status = UsbCtrlRequest (
+ HubDev,
+ EfiUsbNoData,
+ USB_REQ_TYPE_CLASS,
+ USB_HUB_TARGET_HUB,
+ USB_HUB_REQ_SET_FEATURE,
+ Feature,
+ 0,
+ NULL,
+ 0
+ );
+
+ return Status;
+}
+
+
+/**
+ Usb hub control transfer to set the port feature
+
+ @param HubDev The Usb hub device
+ @param Port The Usb port to set feature for
+ @param Feature The feature to set
+
+ @retval EFI_SUCCESS The feature is set for the port
+ @retval Others Failed to set the feature
+
+**/
+STATIC
+EFI_STATUS
+UsbHubCtrlSetPortFeature (
+ IN USB_DEVICE *HubDev,
+ IN UINT8 Port,
+ IN UINT8 Feature
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // In USB bus, all the port index starts from 0. But HUB
+ // indexes its port from 1. So, port number is added one.
+ //
+ Status = UsbCtrlRequest (
+ HubDev,
+ EfiUsbNoData,
+ USB_REQ_TYPE_CLASS,
+ USB_HUB_TARGET_PORT,
+ USB_HUB_REQ_SET_FEATURE,
+ Feature,
+ Port + 1,
+ NULL,
+ 0
+ );
+
+ return Status;
+}
+
+
+/**
+ Read the whole usb hub descriptor. It is necessary
+ to do it in two steps because hub descriptor is of
+ variable length
+
+ @param HubDev The hub device
+ @param HubDesc The variable to return the descriptor
+
+ @retval EFI_SUCCESS The hub descriptor is read
+ @retval Others Failed to read the hub descriptor
+
+**/
+STATIC
+EFI_STATUS
+UsbHubReadDesc (
+ IN USB_DEVICE *HubDev,
+ OUT EFI_USB_HUB_DESCRIPTOR *HubDesc
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // First get the hub descriptor length
+ //
+ Status = UsbHubCtrlGetHubDesc (HubDev, HubDesc, 2);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Get the whole hub descriptor
+ //
+ Status = UsbHubCtrlGetHubDesc (HubDev, HubDesc, HubDesc->Length);
+
+ return Status;
+}
+
+
+
+/**
+ Ack the hub change bits. If these bits are not ACKed, Hub will
+ always return changed bit map from its interrupt endpoint.
+
+ @param HubDev The hub device
+
+ @retval EFI_SUCCESS The hub change status is ACKed
+ @retval Others Failed to ACK the hub status
+
+**/
+EFI_STATUS
+UsbHubAckHubStatus (
+ IN USB_DEVICE *HubDev
+ )
+{
+ EFI_USB_PORT_STATUS HubState;
+ EFI_STATUS Status;
+
+ Status = UsbHubCtrlGetHubStatus (HubDev, (UINT32 *) &HubState);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (USB_BIT_IS_SET (HubState.PortChangeStatus, USB_HUB_STAT_C_LOCAL_POWER)) {
+ UsbHubCtrlClearHubFeature (HubDev, USB_HUB_C_HUB_LOCAL_POWER);
+ }
+
+ if (USB_BIT_IS_SET (HubState.PortChangeStatus, USB_HUB_STAT_C_OVER_CURRENT)) {
+ UsbHubCtrlClearHubFeature (HubDev, USB_HUB_C_HUB_OVER_CURRENT);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Test whether the interface is a hub interface.
+
+ @param UsbIf The interface to test
+
+ @retval TRUE The interface is a hub interface
+ @retval FALSE The interface isn't a hub interface
+
+**/
+BOOLEAN
+UsbIsHubInterface (
+ IN USB_INTERFACE *UsbIf
+ )
+{
+ EFI_USB_INTERFACE_DESCRIPTOR *Setting;
+
+ //
+ // If the hub is a high-speed hub with multiple TT,
+ // the hub will has a default setting of single TT.
+ //
+ Setting = &UsbIf->IfSetting->Desc;
+
+ if ((Setting->InterfaceClass == USB_HUB_CLASS_CODE) &&
+ (Setting->InterfaceSubClass == USB_HUB_SUBCLASS_CODE)) {
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ The callback function to the USB hub status change
+ interrupt endpoint. It is called periodically by
+ the underlying host controller.
+
+ @param Data The data read
+ @param DataLength The length of the data read
+ @param Context The context
+ @param Result The result of the last interrupt transfer
+
+ @retval EFI_SUCCESS The process is OK
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource
+
+**/
+STATIC
+EFI_STATUS
+UsbOnHubInterrupt (
+ IN VOID *Data,
+ IN UINTN DataLength,
+ IN VOID *Context,
+ IN UINT32 Result
+ )
+{
+ USB_INTERFACE *HubIf;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ EFI_USB_ENDPOINT_DESCRIPTOR *EpDesc;
+ EFI_STATUS Status;
+
+ HubIf = (USB_INTERFACE *) Context;
+ UsbIo = &(HubIf->UsbIo);
+ EpDesc = &(HubIf->HubEp->Desc);
+
+ if (Result != EFI_USB_NOERROR) {
+ //
+ // If endpoint is stalled, clear the stall. Use UsbIo to access
+ // the control transfer so internal status are maintained.
+ //
+ if (USB_BIT_IS_SET (Result, EFI_USB_ERR_STALL)) {
+ UsbIoClearFeature (
+ UsbIo,
+ USB_TARGET_ENDPOINT,
+ USB_FEATURE_ENDPOINT_HALT,
+ EpDesc->EndpointAddress
+ );
+ }
+
+ //
+ // Delete and submit a new async interrupt
+ //
+ Status = UsbIo->UsbAsyncInterruptTransfer (
+ UsbIo,
+ EpDesc->EndpointAddress,
+ FALSE,
+ 0,
+ 0,
+ NULL,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbOnHubInterrupt: failed to remove async transfer - %r\n", Status));
+ return Status;
+ }
+
+ Status = UsbIo->UsbAsyncInterruptTransfer (
+ UsbIo,
+ EpDesc->EndpointAddress,
+ TRUE,
+ USB_HUB_POLL_INTERVAL,
+ HubIf->NumOfPort / 8 + 1,
+ UsbOnHubInterrupt,
+ HubIf
+ );
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbOnHubInterrupt: failed to submit new async transfer - %r\n", Status));
+ }
+
+ return Status;
+ }
+
+ if ((DataLength == 0) || (Data == NULL)) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // OK, actually something is changed, save the change map
+ // then signal the HUB to do enumeration. This is a good
+ // practise since UsbOnHubInterrupt is called in the context
+ // of host contrller's AsyncInterrupt monitor.
+ //
+ HubIf->ChangeMap = AllocateZeroPool (DataLength);
+
+ if (HubIf->ChangeMap == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (HubIf->ChangeMap, Data, DataLength);
+ gBS->SignalEvent (HubIf->HubNotify);
+
+ return EFI_SUCCESS;
+}
+
+//
+// Array that maps the change bit to feature value which is
+// used to clear these change bit. USB HUB API will clear
+// these change bit automatically. For non-root hub, these
+// bits determine whether hub will report the port in changed
+// bit maps.
+//
+#define USB_HUB_MAP_SIZE 5
+
+USB_CHANGE_FEATURE_MAP mHubFeatureMap[USB_HUB_MAP_SIZE] = {
+ {USB_PORT_STAT_C_CONNECTION, USB_HUB_C_PORT_CONNECT},
+ {USB_PORT_STAT_C_ENABLE, USB_HUB_C_PORT_ENABLE},
+ {USB_PORT_STAT_C_SUSPEND, USB_HUB_C_PORT_SUSPEND},
+ {USB_PORT_STAT_C_OVERCURRENT, USB_HUB_C_PORT_OVER_CURRENT},
+ {USB_PORT_STAT_C_RESET, USB_HUB_C_PORT_RESET},
+};
+
+#define USB_ROOT_HUB_MAP_SIZE 5
+
+USB_CHANGE_FEATURE_MAP mRootHubFeatureMap[USB_ROOT_HUB_MAP_SIZE] = {
+ {USB_PORT_STAT_C_CONNECTION, EfiUsbPortConnectChange},
+ {USB_PORT_STAT_C_ENABLE, EfiUsbPortEnableChange},
+ {USB_PORT_STAT_C_SUSPEND, EfiUsbPortSuspendChange},
+ {USB_PORT_STAT_C_OVERCURRENT, EfiUsbPortOverCurrentChange},
+ {USB_PORT_STAT_C_RESET, EfiUsbPortResetChange},
+};
+
+
+
+/**
+ Initialize the device for a non-root hub
+
+ @param HubIf The USB hub interface
+
+ @retval EFI_SUCCESS The hub is initialized
+ @retval EFI_DEVICE_ERROR Failed to initialize the hub
+
+**/
+STATIC
+EFI_STATUS
+UsbHubInit (
+ IN USB_INTERFACE *HubIf
+ )
+{
+ EFI_USB_HUB_DESCRIPTOR HubDesc;
+ USB_ENDPOINT_DESC *EpDesc;
+ USB_INTERFACE_SETTING *Setting;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ USB_DEVICE *HubDev;
+ EFI_STATUS Status;
+ UINT8 Index;
+
+ //
+ // Locate the interrupt endpoint for port change map
+ //
+ HubIf->IsHub = FALSE;
+ Setting = HubIf->IfSetting;
+ HubDev = HubIf->Device;
+ EpDesc = NULL;
+
+ for (Index = 0; Index < Setting->Desc.NumEndpoints; Index++) {
+ ASSERT ((Setting->Endpoints != NULL) && (Setting->Endpoints[Index] != NULL));
+
+ EpDesc = Setting->Endpoints[Index];
+
+ if (USB_BIT_IS_SET (EpDesc->Desc.EndpointAddress, USB_ENDPOINT_DIR_IN) &&
+ (USB_ENDPOINT_TYPE (&EpDesc->Desc) == USB_ENDPOINT_INTERRUPT)) {
+ break;
+ }
+ }
+
+ if (Index == Setting->Desc.NumEndpoints) {
+ USB_ERROR (("UsbHubInit: no interrupt endpoint found for hub %d\n", HubDev->Address));
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = UsbHubReadDesc (HubDev, &HubDesc);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbHubInit: failed to read HUB descriptor %r\n", Status));
+ return Status;
+ }
+
+ HubIf->NumOfPort = HubDesc.NumPorts;
+
+ USB_DEBUG (("UsbHubInit: hub %d has %d ports\n", HubDev->Address,HubIf->NumOfPort));
+
+ //
+ // Create an event to enumerate the hub's port. On
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ UsbHubEnumeration,
+ HubIf,
+ &HubIf->HubNotify
+ );
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbHubInit: failed to create signal for hub %d - %r\n",
+ HubDev->Address, Status));
+
+ return Status;
+ }
+
+ //
+ // Create AsyncInterrupt to query hub port change endpoint
+ // periodically. If the hub ports are changed, hub will return
+ // changed port map from the interrupt endpoint. The port map
+ // must be able to hold (HubIf->NumOfPort + 1) bits (one bit for
+ // host change status).
+ //
+ UsbIo = &HubIf->UsbIo;
+ Status = UsbIo->UsbAsyncInterruptTransfer (
+ UsbIo,
+ EpDesc->Desc.EndpointAddress,
+ TRUE,
+ USB_HUB_POLL_INTERVAL,
+ HubIf->NumOfPort / 8 + 1,
+ UsbOnHubInterrupt,
+ HubIf
+ );
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbHubInit: failed to queue interrupt transfer for hub %d - %r\n",
+ HubDev->Address, Status));
+
+ gBS->CloseEvent (HubIf->HubNotify);
+ HubIf->HubNotify = NULL;
+
+ return Status;
+ }
+
+ //
+ // OK, set IsHub to TRUE. Now usb bus can handle this device
+ // as a working HUB. If failed eariler, bus driver will not
+ // recognize it as a hub. Other parts of the bus should be able
+ // to work.
+ //
+ HubIf->IsHub = TRUE;
+ HubIf->HubApi = &mUsbHubApi;
+ HubIf->HubEp = EpDesc;
+
+ //
+ // Feed power to all the hub ports. It should be ok
+ // for both gang/individual powered hubs.
+ //
+ for (Index = 0; Index < HubDesc.NumPorts; Index++) {
+ UsbHubCtrlSetPortFeature (HubIf->Device, Index, USB_HUB_PORT_POWER);
+ }
+
+ gBS->Stall (HubDesc.PwrOn2PwrGood * 2 * USB_STALL_1_MS);
+ UsbHubAckHubStatus (HubIf->Device);
+
+ USB_DEBUG (("UsbHubInit: hub %d initialized\n", HubDev->Address));
+ return Status;
+}
+
+
+
+/**
+ Get the port status. This function is required to
+ ACK the port change bits although it will return
+ the port changes in PortState. Bus enumeration code
+ doesn't need to ACK the port change bits.
+
+ @param HubIf The hub interface
+ @param Port The port of the hub to get state
+ @param PortState Variable to return the port state
+
+ @retval EFI_SUCCESS The port status is successfully returned
+ @retval Others Failed to return the status
+
+**/
+STATIC
+EFI_STATUS
+UsbHubGetPortStatus (
+ IN USB_INTERFACE *HubIf,
+ IN UINT8 Port,
+ OUT EFI_USB_PORT_STATUS *PortState
+ )
+{
+ EFI_STATUS Status;
+
+ Status = UsbHubCtrlGetPortStatus (HubIf->Device, Port, PortState);
+
+ return Status;
+}
+
+
+
+/**
+ Clear the port change status.
+
+ @param HubIf The hub interface
+ @param Port The hub port
+
+ @return None
+
+**/
+STATIC
+VOID
+UsbHubClearPortChange (
+ IN USB_INTERFACE *HubIf,
+ IN UINT8 Port
+ )
+{
+ EFI_USB_PORT_STATUS PortState;
+ USB_CHANGE_FEATURE_MAP *Map;
+ UINTN Index;
+ EFI_STATUS Status;
+
+ Status = UsbHubGetPortStatus (HubIf, Port, &PortState);
+
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+
+ //
+ // OK, get the usb port status, now ACK the change bits.
+ // Don't return error when failed to clear the change bits.
+ // It may lead to extra port state report. USB bus should
+ // be able to handle this.
+ //
+ for (Index = 0; Index < USB_HUB_MAP_SIZE; Index++) {
+ Map = &mHubFeatureMap[Index];
+
+ if (USB_BIT_IS_SET (PortState.PortChangeStatus, Map->ChangedBit)) {
+ UsbHubCtrlClearPortFeature (HubIf->Device, Port, Map->Feature);
+ }
+ }
+}
+
+
+
+/**
+ Function to set the port feature for non-root hub
+
+ @param HubIf The hub interface
+ @param Port The port of the hub
+ @param Feature The feature of the port to set
+
+ @retval EFI_SUCCESS The hub port feature is set
+ @retval Others Failed to set the port feature
+
+**/
+STATIC
+EFI_STATUS
+UsbHubSetPortFeature (
+ IN USB_INTERFACE *HubIf,
+ IN UINT8 Port,
+ IN EFI_USB_PORT_FEATURE Feature
+ )
+{
+ EFI_STATUS Status;
+
+ Status = UsbHubCtrlSetPortFeature (HubIf->Device, Port, Feature);
+
+ return Status;
+}
+
+
+/**
+ Interface function to clear the port feature for non-root hub
+
+ @param HubIf The hub interface
+ @param Port The port of the hub to clear feature for
+ @param Feature The feature to clear
+
+ @retval EFI_SUCCESS The port feature is cleared
+ @retval Others Failed to clear the port feature
+
+**/
+STATIC
+EFI_STATUS
+UsbHubClearPortFeature (
+ IN USB_INTERFACE *HubIf,
+ IN UINT8 Port,
+ IN EFI_USB_PORT_FEATURE Feature
+ )
+{
+ EFI_STATUS Status;
+
+ Status = UsbHubCtrlClearPortFeature (HubIf->Device, Port, Feature);
+
+ return Status;
+}
+
+
+/**
+ Interface funtion to reset the port
+
+ @param HubIf The hub interface
+ @param Port The port to reset
+
+ @retval EFI_SUCCESS The hub port is reset
+ @retval EFI_TIMEOUT Failed to reset the port in time
+ @retval Others Failed to reset the port
+
+**/
+STATIC
+EFI_STATUS
+UsbHubResetPort (
+ IN USB_INTERFACE *HubIf,
+ IN UINT8 Port
+ )
+{
+ EFI_USB_PORT_STATUS PortState;
+ UINTN Index;
+ EFI_STATUS Status;
+
+ Status = UsbHubSetPortFeature (HubIf, Port, USB_HUB_PORT_RESET);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Drive the reset signal for at least 10ms. Check USB 2.0 Spec
+ // section 7.1.7.5 for timing requirements.
+ //
+ gBS->Stall (20 * USB_STALL_1_MS);
+
+ //
+ // USB hub will clear RESET bit if reset is actually finished.
+ //
+ ZeroMem (&PortState, sizeof (EFI_USB_PORT_STATUS));
+
+ for (Index = 0; Index < 20; Index++) {
+ Status = UsbHubGetPortStatus (HubIf, Port, &PortState);
+
+ if (!EFI_ERROR (Status) &&
+ !USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_RESET)) {
+
+ return EFI_SUCCESS;
+ }
+
+ gBS->Stall (5 * USB_STALL_1_MS);
+ }
+
+ return EFI_TIMEOUT;
+}
+
+
+/**
+ Release the hub's control of the interface
+
+ @param HubIf The hub interface
+
+ @retval EFI_SUCCESS The interface is release of hub control
+
+**/
+STATIC
+EFI_STATUS
+UsbHubRelease (
+ IN USB_INTERFACE *HubIf
+ )
+{
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ EFI_STATUS Status;
+
+ UsbIo = &HubIf->UsbIo;
+ Status = UsbIo->UsbAsyncInterruptTransfer (
+ UsbIo,
+ HubIf->HubEp->Desc.EndpointAddress,
+ FALSE,
+ USB_HUB_POLL_INTERVAL,
+ 0,
+ NULL,
+ 0
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ gBS->CloseEvent (HubIf->HubNotify);
+
+ HubIf->IsHub = FALSE;
+ HubIf->HubApi = NULL;
+ HubIf->HubEp = NULL;
+ HubIf->HubNotify = NULL;
+
+ USB_DEBUG (("UsbHubRelease: hub device %d released\n", HubIf->Device->Address));
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Initialize the interface for root hub
+
+ @param HubIf The root hub interface
+
+ @retval EFI_SUCCESS The interface is initialied for root hub
+ @retval Others Failed to initialize the hub
+
+**/
+STATIC
+EFI_STATUS
+UsbRootHubInit (
+ IN USB_INTERFACE *HubIf
+ )
+{
+ EFI_STATUS Status;
+ UINT8 MaxSpeed;
+ UINT8 NumOfPort;
+ UINT8 Support64;
+
+ Status = UsbHcGetCapability (HubIf->Device->Bus, &MaxSpeed, &NumOfPort, &Support64);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ USB_DEBUG (("UsbRootHubInit: root hub %x - max speed %d, %d ports\n",
+ HubIf, MaxSpeed, NumOfPort));
+
+ HubIf->IsHub = TRUE;
+ HubIf->HubApi = &mUsbRootHubApi;
+ HubIf->HubEp = NULL;
+ HubIf->MaxSpeed = MaxSpeed;
+ HubIf->NumOfPort = NumOfPort;
+ HubIf->HubNotify = NULL;
+
+ //
+ // 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;
+ }
+
+ Status = gBS->SetTimer (
+ HubIf->HubNotify,
+ TimerPeriodic,
+ USB_ROOTHUB_POLL_INTERVAL
+ );
+
+ if (EFI_ERROR (Status)) {
+ gBS->CloseEvent (HubIf->HubNotify);
+ }
+
+ return Status;
+}
+
+
+
+/**
+ Get the port status. This function is required to
+ ACK the port change bits although it will return
+ the port changes in PortState. Bus enumeration code
+ doesn't need to ACK the port change bits.
+
+ @param HubIf The root hub interface
+ @param Port The root hub port to get the state
+ @param PortState Variable to return the port state
+
+ @retval EFI_SUCCESS The port state is returned
+ @retval Others Failed to retrieve the port state
+
+**/
+STATIC
+EFI_STATUS
+UsbRootHubGetPortStatus (
+ IN USB_INTERFACE *HubIf,
+ IN UINT8 Port,
+ OUT EFI_USB_PORT_STATUS *PortState
+ )
+{
+ USB_BUS *Bus;
+ EFI_STATUS Status;
+
+ Bus = HubIf->Device->Bus;
+ Status = UsbHcGetRootHubPortStatus (Bus, Port, PortState);
+
+ return Status;
+}
+
+
+/**
+ Clear the port change status.
+
+ @param HubIf The root hub interface
+ @param Port The root hub port
+
+ @retval EFI_SUCCESS The port state is returned
+ @retval Others Failed to retrieve the port state
+
+**/
+STATIC
+VOID
+UsbRootHubClearPortChange (
+ IN USB_INTERFACE *HubIf,
+ IN UINT8 Port
+ )
+{
+ EFI_USB_PORT_STATUS PortState;
+ USB_CHANGE_FEATURE_MAP *Map;
+ UINTN Index;
+ EFI_STATUS Status;
+
+ Status = UsbRootHubGetPortStatus (HubIf, Port, &PortState);
+
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+
+ //
+ // OK, get the usb port status, now ACK the change bits.
+ // Don't return error when failed to clear the change bits.
+ // It may lead to extra port state report. USB bus should
+ // be able to handle this.
+ //
+ for (Index = 0; Index < USB_ROOT_HUB_MAP_SIZE; Index++) {
+ Map = &mRootHubFeatureMap[Index];
+
+ if (USB_BIT_IS_SET (PortState.PortChangeStatus, Map->ChangedBit)) {
+ UsbHcClearRootHubPortFeature (HubIf->Device->Bus, Port, Map->Feature);
+ }
+ }
+}
+
+
+
+/**
+ Set the root hub port feature
+
+ @param HubIf The Usb hub interface
+ @param Port The hub port
+ @param Feature The feature to set
+
+ @retval EFI_SUCCESS The root hub port is set with the feature
+ @retval Others Failed to set the feature
+
+**/
+STATIC
+EFI_STATUS
+UsbRootHubSetPortFeature (
+ IN USB_INTERFACE *HubIf,
+ IN UINT8 Port,
+ IN EFI_USB_PORT_FEATURE Feature
+ )
+{
+ EFI_STATUS Status;
+
+ Status = UsbHcSetRootHubPortFeature (HubIf->Device->Bus, Port, Feature);
+
+ return Status;
+}
+
+
+/**
+ Clear the root hub port feature
+
+ @param HubIf The root hub interface
+ @param Port The root hub port
+ @param Feature The feature to clear
+
+ @retval EFI_SUCCESS The root hub port is cleared of the feature
+ @retval Others Failed to clear the feature
+
+**/
+STATIC
+EFI_STATUS
+UsbRootHubClearPortFeature (
+ IN USB_INTERFACE *HubIf,
+ IN UINT8 Port,
+ IN EFI_USB_PORT_FEATURE Feature
+ )
+{
+ EFI_STATUS Status;
+
+ Status = UsbHcClearRootHubPortFeature (HubIf->Device->Bus, Port, Feature);
+
+ return Status;
+}
+
+
+/**
+ Interface funtion to reset the root hub port
+
+ @param RootIf The root hub interface
+ @param Port The port to reset
+
+ @retval EFI_SUCCESS The hub port is reset
+ @retval EFI_TIMEOUT Failed to reset the port in time
+ @retval EFI_NOT_FOUND The low/full speed device connected to high speed
+ root hub is released to the companion UHCI
+ @retval Others Failed to reset the port
+
+**/
+STATIC
+EFI_STATUS
+UsbRootHubResetPort (
+ IN USB_INTERFACE *RootIf,
+ IN UINT8 Port
+ )
+{
+ USB_BUS *Bus;
+ EFI_STATUS Status;
+ EFI_USB_PORT_STATUS PortState;
+ UINTN Index;
+
+ //
+ // Notice: although EHCI requires that ENABLED bit be cleared
+ // when reset the port, we don't need to care that here. It
+ // should be handled in the EHCI driver.
+ //
+ Bus = RootIf->Device->Bus;
+ Status = UsbHcSetRootHubPortFeature (Bus, Port, EfiUsbPortReset);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbRootHubResetPort: failed to start reset on port %d\n", Port));
+ return Status;
+ }
+
+ //
+ // Drive the reset signal for at least 50ms. Check USB 2.0 Spec
+ // section 7.1.7.5 for timing requirements.
+ //
+ gBS->Stall (50 * USB_STALL_1_MS);
+
+ Status = UsbHcClearRootHubPortFeature (Bus, Port, EfiUsbPortReset);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbRootHubResetPort: failed to clear reset on port %d\n", Port));
+ return Status;
+ }
+
+ gBS->Stall (USB_STALL_1_MS);
+
+ //
+ // USB host controller won't clear the RESET bit until
+ // reset is actually finished.
+ //
+ ZeroMem (&PortState, sizeof (EFI_USB_PORT_STATUS));
+
+ for (Index = 0; Index < USB_HUB_LOOP; Index++) {
+ Status = UsbHcGetRootHubPortStatus (Bus, Port, &PortState);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_RESET)) {
+ break;
+ }
+
+ gBS->Stall (10 * USB_STALL_1_MS);
+ }
+
+ if (Index == USB_HUB_LOOP) {
+ USB_ERROR (("UsbRootHubResetPort: reset not finished in time on port %d\n", Port));
+ return EFI_TIMEOUT;
+ }
+
+ if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_ENABLE)) {
+ //
+ // OK, the port is reset. If root hub is of high speed and
+ // the device is of low/full speed, release the ownership to
+ // companion UHCI. If root hub is of full speed, it won't
+ // automatically enable the port, we need to enable it manually.
+ //
+ if (RootIf->MaxSpeed == EFI_USB_SPEED_HIGH) {
+ USB_ERROR (("UsbRootHubResetPort: release low/full speed device (%d) to UHCI\n", Port));
+
+ UsbRootHubSetPortFeature (RootIf, Port, EfiUsbPortOwner);
+ return EFI_NOT_FOUND;
+
+ } else {
+
+ Status = UsbRootHubSetPortFeature (RootIf, Port, EfiUsbPortEnable);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbRootHubResetPort: failed to enable port %d for UHCI\n", Port));
+ return Status;
+ }
+
+ gBS->Stall (20 * USB_STALL_1_MS);
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Release the root hub's control of the interface
+
+ @param HubIf The root hub interface
+
+ @retval EFI_SUCCESS The root hub's control of the interface is
+ released.
+
+**/
+STATIC
+EFI_STATUS
+UsbRootHubRelease (
+ IN USB_INTERFACE *HubIf
+ )
+{
+ USB_DEBUG (("UsbRootHubRelease: root hub released for hub %x\n", HubIf));
+
+ gBS->SetTimer (HubIf->HubNotify, TimerCancel, USB_ROOTHUB_POLL_INTERVAL);
+ gBS->CloseEvent (HubIf->HubNotify);
+
+ return EFI_SUCCESS;
+}
+
+USB_HUB_API mUsbHubApi = {
+ UsbHubInit,
+ UsbHubGetPortStatus,
+ UsbHubClearPortChange,
+ UsbHubSetPortFeature,
+ UsbHubClearPortFeature,
+ UsbHubResetPort,
+ UsbHubRelease
+};
+
+USB_HUB_API mUsbRootHubApi = {
+ UsbRootHubInit,
+ UsbRootHubGetPortStatus,
+ UsbRootHubClearPortChange,
+ UsbRootHubSetPortFeature,
+ UsbRootHubClearPortFeature,
+ UsbRootHubResetPort,
+ UsbRootHubRelease
+};
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.h b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.h new file mode 100644 index 0000000..0238ea0 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbHub.h @@ -0,0 +1,140 @@ +/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+ Module Name:
+
+ UsbHub.h
+
+ Abstract:
+
+ The definition for USB hub
+
+ Revision History
+
+
+**/
+
+#ifndef _USB_HUB_H_
+#define _USB_HUB_H_
+
+#define USB_ENDPOINT_ADDR(EpAddr) ((EpAddr) & 0x7F)
+#define USB_ENDPOINT_TYPE(Desc) ((Desc)->Attributes & USB_ENDPOINT_TYPE_MASK)
+
+enum {
+ USB_DESC_TYPE_HUB = 0x29,
+
+ //
+ // Hub class control transfer target
+ //
+ USB_HUB_TARGET_HUB = 0,
+ USB_HUB_TARGET_PORT = 3,
+
+ //
+ // HUB class specific contrl transfer request type
+ //
+ USB_HUB_REQ_GET_STATUS = 0,
+ USB_HUB_REQ_CLEAR_FEATURE = 1,
+ USB_HUB_REQ_SET_FEATURE = 3,
+ USB_HUB_REQ_GET_DESC = 6,
+ USB_HUB_REQ_SET_DESC = 7,
+ USB_HUB_REQ_CLEAR_TT = 8,
+ USB_HUB_REQ_RESET_TT = 9,
+ USB_HUB_REQ_GET_TT_STATE = 10,
+ USB_HUB_REQ_STOP_TT = 11,
+
+
+ //
+ // USB hub class feature selector
+ //
+ USB_HUB_C_HUB_LOCAL_POWER = 0,
+ USB_HUB_C_HUB_OVER_CURRENT = 1,
+ USB_HUB_PORT_CONNECTION = 0,
+ USB_HUB_PORT_ENABLE = 1,
+ USB_HUB_PORT_SUSPEND = 2,
+ USB_HUB_PORT_OVER_CURRENT = 3,
+ USB_HUB_PORT_RESET = 4,
+ USB_HUB_PORT_POWER = 8,
+ USB_HUB_PORT_LOW_SPEED = 9,
+ USB_HUB_C_PORT_CONNECT = 16,
+ USB_HUB_C_PORT_ENABLE = 17,
+ USB_HUB_C_PORT_SUSPEND = 18,
+ USB_HUB_C_PORT_OVER_CURRENT = 19,
+ USB_HUB_C_PORT_RESET = 20,
+ USB_HUB_PORT_TEST = 21,
+ USB_HUB_PORT_INDICATOR = 22,
+
+ //
+ // USB hub power control method. In gang power control
+ //
+ USB_HUB_GANG_POWER_CTRL = 0,
+ USB_HUB_PORT_POWER_CTRL = 0x01,
+
+ //
+ // USB hub status bits
+ //
+ USB_HUB_STAT_LOCAL_POWER = 0x01,
+ USB_HUB_STAT_OVER_CURRENT = 0x02,
+ USB_HUB_STAT_C_LOCAL_POWER = 0x01,
+ USB_HUB_STAT_C_OVER_CURRENT = 0x02,
+
+ USB_HUB_CLASS_CODE = 0x09,
+ USB_HUB_SUBCLASS_CODE = 0x00,
+
+
+ USB_HUB_LOOP = 50,
+};
+
+#pragma pack(1)
+//
+// Hub descriptor, the last two fields are of variable lenght.
+//
+typedef struct {
+ UINT8 Length;
+ UINT8 DescType;
+ UINT8 NumPorts;
+ UINT16 HubCharacter;
+ UINT8 PwrOn2PwrGood;
+ UINT8 HubContrCurrent;
+ UINT8 Filler[16];
+} EFI_USB_HUB_DESCRIPTOR;
+#pragma pack()
+
+
+typedef struct {
+ UINT16 ChangedBit;
+ EFI_USB_PORT_FEATURE Feature;
+} USB_CHANGE_FEATURE_MAP;
+
+
+EFI_STATUS
+UsbHubCtrlClearTTBuffer (
+ IN USB_DEVICE *UsbDev,
+ IN UINT8 Port,
+ IN UINT16 DevAddr,
+ IN UINT16 EpNum,
+ IN UINT16 EpType
+ );
+
+
+BOOLEAN
+UsbIsHubInterface (
+ IN USB_INTERFACE *UsbIf
+ );
+
+EFI_STATUS
+UsbHubAckHubStatus (
+ IN USB_DEVICE *UsbDev
+ );
+
+extern USB_HUB_API mUsbHubApi;
+extern USB_HUB_API mUsbRootHubApi;
+#endif
+
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.c new file mode 100644 index 0000000..216f948 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.c @@ -0,0 +1,769 @@ +/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+ Module Name:
+
+ UsbUtility.c
+
+ Abstract:
+
+ Wrapper function for usb host controller interface
+
+ Revision History
+
+
+**/
+
+
+#include "UsbBus.h"
+
+
+/**
+ Get the capability of the host controller
+
+ @param UsbBus The usb driver
+ @param MaxSpeed The maximum speed this host controller supports
+ @param NumOfPort The number of the root hub port
+ @param Is64BitCapable Whether this controller support 64 bit addressing
+
+ @retval EFI_SUCCESS The host controller capability is returned
+ @retval Others Failed to retrieve the host controller capability.
+
+**/
+EFI_STATUS
+UsbHcGetCapability (
+ IN USB_BUS *UsbBus,
+ OUT UINT8 *MaxSpeed,
+ OUT UINT8 *NumOfPort,
+ OUT UINT8 *Is64BitCapable
+ )
+{
+ EFI_STATUS Status;
+
+ if (UsbBus->Usb2Hc != NULL) {
+ Status = UsbBus->Usb2Hc->GetCapability (
+ UsbBus->Usb2Hc,
+ MaxSpeed,
+ NumOfPort,
+ Is64BitCapable
+ );
+
+ } else {
+ Status = UsbBus->UsbHc->GetRootHubPortNumber (UsbBus->UsbHc, NumOfPort);
+
+ *MaxSpeed = EFI_USB_SPEED_FULL;
+ *Is64BitCapable = (UINT8) FALSE;
+ }
+
+ return Status;
+}
+
+
+/**
+ Reset the host controller
+
+ @param UsbBus The usb bus driver
+ @param Attributes The reset type, only global reset is used by this driver
+
+ @return GC_TODO: add return values
+
+**/
+EFI_STATUS
+UsbHcReset (
+ IN USB_BUS *UsbBus,
+ IN UINT16 Attributes
+ )
+{
+ EFI_STATUS Status;
+
+ if (UsbBus->Usb2Hc != NULL) {
+ Status = UsbBus->Usb2Hc->Reset (UsbBus->Usb2Hc, Attributes);
+ } else {
+ Status = UsbBus->UsbHc->Reset (UsbBus->UsbHc, Attributes);
+ }
+
+ return Status;
+}
+
+
+/**
+ Get the current operation state of the host controller
+
+ @param UsbBus The USB bus driver
+ @param State The host controller operation state
+
+ @retval EFI_SUCCESS The operation state is returned in State
+ @retval Others Failed to get the host controller state
+
+**/
+EFI_STATUS
+UsbHcGetState (
+ IN USB_BUS *UsbBus,
+ OUT EFI_USB_HC_STATE *State
+ )
+{
+ EFI_STATUS Status;
+
+ if (UsbBus->Usb2Hc != NULL) {
+ Status = UsbBus->Usb2Hc->GetState (UsbBus->Usb2Hc, State);
+ } else {
+ Status = UsbBus->UsbHc->GetState (UsbBus->UsbHc, State);
+ }
+
+ return Status;
+}
+
+
+/**
+ Set the host controller operation state
+
+ @param UsbBus The USB bus driver
+ @param State The state to set
+
+ @retval EFI_SUCCESS The host controller is now working at State
+ @retval Others Failed to set operation state
+
+**/
+EFI_STATUS
+UsbHcSetState (
+ IN USB_BUS *UsbBus,
+ IN EFI_USB_HC_STATE State
+ )
+{
+ EFI_STATUS Status;
+
+ if (UsbBus->Usb2Hc != NULL) {
+ Status = UsbBus->Usb2Hc->SetState (UsbBus->Usb2Hc, State);
+ } else {
+ Status = UsbBus->UsbHc->SetState (UsbBus->UsbHc, State);
+ }
+
+ return Status;
+}
+
+
+/**
+ Get the root hub port state
+
+ @param UsbBus The USB bus driver
+ @param PortIndex The index of port
+ @param PortStatus The variable to save port state
+
+ @retval EFI_SUCCESS The root port state is returned in
+ @retval Others Failed to get the root hub port state
+
+**/
+EFI_STATUS
+UsbHcGetRootHubPortStatus (
+ IN USB_BUS *UsbBus,
+ IN UINT8 PortIndex,
+ OUT EFI_USB_PORT_STATUS *PortStatus
+ )
+{
+ EFI_STATUS Status;
+
+ if (UsbBus->Usb2Hc != NULL) {
+ Status = UsbBus->Usb2Hc->GetRootHubPortStatus (UsbBus->Usb2Hc, PortIndex, PortStatus);
+ } else {
+ Status = UsbBus->UsbHc->GetRootHubPortStatus (UsbBus->UsbHc, PortIndex, PortStatus);
+ }
+
+ return Status;
+}
+
+
+/**
+ Set the root hub port feature
+
+ @param UsbBus The USB bus driver
+ @param PortIndex The port index
+ @param Feature The port feature to set
+
+ @retval EFI_SUCCESS The port feature is set
+ @retval Others Failed to set port feature
+
+**/
+EFI_STATUS
+UsbHcSetRootHubPortFeature (
+ IN USB_BUS *UsbBus,
+ IN UINT8 PortIndex,
+ IN EFI_USB_PORT_FEATURE Feature
+ )
+{
+ EFI_STATUS Status;
+
+
+ if (UsbBus->Usb2Hc != NULL) {
+ Status = UsbBus->Usb2Hc->SetRootHubPortFeature (UsbBus->Usb2Hc, PortIndex, Feature);
+ } else {
+ Status = UsbBus->UsbHc->SetRootHubPortFeature (UsbBus->UsbHc, PortIndex, Feature);
+ }
+
+ return Status;
+}
+
+
+/**
+ Clear the root hub port feature
+
+ @param UsbBus The USB bus driver
+ @param PortIndex The port index
+ @param Feature The port feature to clear
+
+ @retval EFI_SUCCESS The port feature is clear
+ @retval Others Failed to clear port feature
+
+**/
+EFI_STATUS
+UsbHcClearRootHubPortFeature (
+ IN USB_BUS *UsbBus,
+ IN UINT8 PortIndex,
+ IN EFI_USB_PORT_FEATURE Feature
+ )
+{
+ EFI_STATUS Status;
+
+ if (UsbBus->Usb2Hc != NULL) {
+ Status = UsbBus->Usb2Hc->ClearRootHubPortFeature (UsbBus->Usb2Hc, PortIndex, Feature);
+ } else {
+ Status = UsbBus->UsbHc->ClearRootHubPortFeature (UsbBus->UsbHc, PortIndex, Feature);
+ }
+
+ return Status;
+}
+
+
+/**
+ Execute a control transfer to the device
+
+ @param UsbBus The USB bus driver
+ @param DevAddr The device address
+ @param DevSpeed The device speed
+ @param MaxPacket Maximum packet size of endpoint 0
+ @param Request The control transfer request
+ @param Direction The direction of data stage
+ @param Data The buffer holding data
+ @param DataLength The length of the data
+ @param TimeOut Timeout (in ms) to wait until timeout
+ @param Translator The transaction translator for low/full speed device
+ @param UsbResult The result of transfer
+
+ @retval EFI_SUCCESS The control transfer finished without error
+ @retval Others The control transfer failed, reason returned in UsbReslt
+
+**/
+EFI_STATUS
+UsbHcControlTransfer (
+ IN USB_BUS *UsbBus,
+ IN UINT8 DevAddr,
+ IN UINT8 DevSpeed,
+ IN UINTN MaxPacket,
+ IN EFI_USB_DEVICE_REQUEST *Request,
+ IN EFI_USB_DATA_DIRECTION Direction,
+ IN OUT VOID *Data,
+ IN OUT UINTN *DataLength,
+ IN UINTN TimeOut,
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
+ OUT UINT32 *UsbResult
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN IsSlowDevice;
+
+ if (UsbBus->Usb2Hc != NULL) {
+ Status = UsbBus->Usb2Hc->ControlTransfer (
+ UsbBus->Usb2Hc,
+ DevAddr,
+ DevSpeed,
+ MaxPacket,
+ Request,
+ Direction,
+ Data,
+ DataLength,
+ TimeOut,
+ Translator,
+ UsbResult
+ );
+
+ } else {
+ IsSlowDevice = (BOOLEAN)(EFI_USB_SPEED_LOW == DevSpeed);
+ Status = UsbBus->UsbHc->ControlTransfer (
+ UsbBus->UsbHc,
+ DevAddr,
+ IsSlowDevice,
+ (UINT8) MaxPacket,
+ Request,
+ Direction,
+ Data,
+ DataLength,
+ TimeOut,
+ UsbResult
+ );
+ }
+
+ return Status;
+}
+
+
+/**
+ Execute a bulk transfer to the device's endpoint
+
+ @param UsbBus The USB bus driver
+ @param DevAddr The target device address
+ @param EpAddr The target endpoint address, with direction encoded in
+ bit 7
+ @param DevSpeed The device's speed
+ @param MaxPacket The endpoint's max packet size
+ @param BufferNum The number of data buffer
+ @param Data Array of pointers to data buffer
+ @param DataLength The length of data buffer
+ @param DataToggle On input, the initial data toggle to use, also return
+ the next toggle on output.
+ @param TimeOut The time to wait until timeout
+ @param Translator The transaction translator for low/full speed device
+ @param UsbResult The result of USB execution
+
+ @retval EFI_SUCCESS The bulk transfer is finished without error
+ @retval Others Failed to execute bulk transfer, result in UsbResult
+
+**/
+EFI_STATUS
+UsbHcBulkTransfer (
+ IN USB_BUS *UsbBus,
+ IN UINT8 DevAddr,
+ IN UINT8 EpAddr,
+ IN UINT8 DevSpeed,
+ IN UINTN MaxPacket,
+ IN UINT8 BufferNum,
+ IN OUT VOID *Data[EFI_USB_MAX_BULK_BUFFER_NUM],
+ IN OUT UINTN *DataLength,
+ IN OUT UINT8 *DataToggle,
+ IN UINTN TimeOut,
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
+ OUT UINT32 *UsbResult
+ )
+{
+ EFI_STATUS Status;
+
+ if (UsbBus->Usb2Hc != NULL) {
+ Status = UsbBus->Usb2Hc->BulkTransfer (
+ UsbBus->Usb2Hc,
+ DevAddr,
+ EpAddr,
+ DevSpeed,
+ MaxPacket,
+ BufferNum,
+ Data,
+ DataLength,
+ DataToggle,
+ TimeOut,
+ Translator,
+ UsbResult
+ );
+ } else {
+ Status = UsbBus->UsbHc->BulkTransfer (
+ UsbBus->UsbHc,
+ DevAddr,
+ EpAddr,
+ (UINT8) MaxPacket,
+ *Data,
+ DataLength,
+ DataToggle,
+ TimeOut,
+ UsbResult
+ );
+ }
+
+ return Status;
+}
+
+
+/**
+ Queue or cancel an asynchronous interrupt transfer
+
+ @param UsbBus The USB bus driver
+ @param DevAddr The target device address
+ @param EpAddr The target endpoint address, with direction encoded in
+ bit 7
+ @param DevSpeed The device's speed
+ @param MaxPacket The endpoint's max packet size
+ @param IsNewTransfer Whether this is a new request. If not, cancel the old
+ request
+ @param DataToggle Data toggle to use on input, next toggle on output
+ @param PollingInterval The interval to poll the interrupt transfer (in ms)
+ @param DataLength The length of periodical data receive
+ @param Translator The transaction translator for low/full speed device
+ @param Callback Function to call when data is received
+ @param Context The context to the callback
+
+ @retval EFI_SUCCESS The asynchronous transfer is queued
+ @retval Others Failed to queue the transfer
+
+**/
+EFI_STATUS
+UsbHcAsyncInterruptTransfer (
+ IN USB_BUS *UsbBus,
+ IN UINT8 DevAddr,
+ IN UINT8 EpAddr,
+ IN UINT8 DevSpeed,
+ IN UINTN MaxPacket,
+ IN BOOLEAN IsNewTransfer,
+ IN OUT UINT8 *DataToggle,
+ IN UINTN PollingInterval,
+ IN UINTN DataLength,
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
+ IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,
+ IN VOID *Context OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN IsSlowDevice;
+
+ if (UsbBus->Usb2Hc != NULL) {
+ Status = UsbBus->Usb2Hc->AsyncInterruptTransfer (
+ UsbBus->Usb2Hc,
+ DevAddr,
+ EpAddr,
+ DevSpeed,
+ MaxPacket,
+ IsNewTransfer,
+ DataToggle,
+ PollingInterval,
+ DataLength,
+ Translator,
+ Callback,
+ Context
+ );
+ } else {
+ IsSlowDevice = (BOOLEAN)(EFI_USB_SPEED_LOW == DevSpeed);
+
+ Status = UsbBus->UsbHc->AsyncInterruptTransfer (
+ UsbBus->UsbHc,
+ DevAddr,
+ EpAddr,
+ IsSlowDevice,
+ (UINT8) MaxPacket,
+ IsNewTransfer,
+ DataToggle,
+ PollingInterval,
+ DataLength,
+ Callback,
+ Context
+ );
+ }
+
+ return Status;
+}
+
+
+/**
+ Execute a synchronous interrupt transfer to the target endpoint
+
+ @param UsbBus The USB bus driver
+ @param DevAddr The target device address
+ @param EpAddr The target endpoint address, with direction encoded in
+ bit 7
+ @param DevSpeed The device's speed
+ @param MaxPacket The endpoint's max packet size
+ @param Data Pointer to data buffer
+ @param DataLength The length of data buffer
+ @param DataToggle On input, the initial data toggle to use, also return
+ the next toggle on output.
+ @param TimeOut The time to wait until timeout
+ @param Translator The transaction translator for low/full speed device
+ @param UsbResult The result of USB execution
+
+ @retval EFI_SUCCESS The synchronous interrupt transfer is OK
+ @retval Others Failed to execute the synchronous interrupt transfer
+
+**/
+EFI_STATUS
+UsbHcSyncInterruptTransfer (
+ IN USB_BUS *UsbBus,
+ IN UINT8 DevAddr,
+ IN UINT8 EpAddr,
+ IN UINT8 DevSpeed,
+ IN UINTN MaxPacket,
+ IN OUT VOID *Data,
+ IN OUT UINTN *DataLength,
+ IN OUT UINT8 *DataToggle,
+ IN UINTN TimeOut,
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
+ OUT UINT32 *UsbResult
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN IsSlowDevice;
+
+ if (UsbBus->Usb2Hc != NULL) {
+ Status = UsbBus->Usb2Hc->SyncInterruptTransfer (
+ UsbBus->Usb2Hc,
+ DevAddr,
+ EpAddr,
+ DevSpeed,
+ MaxPacket,
+ Data,
+ DataLength,
+ DataToggle,
+ TimeOut,
+ Translator,
+ UsbResult
+ );
+ } else {
+ IsSlowDevice = (EFI_USB_SPEED_LOW == DevSpeed) ? TRUE : FALSE;
+ Status = UsbBus->UsbHc->SyncInterruptTransfer (
+ UsbBus->UsbHc,
+ DevAddr,
+ EpAddr,
+ IsSlowDevice,
+ (UINT8) MaxPacket,
+ Data,
+ DataLength,
+ DataToggle,
+ TimeOut,
+ UsbResult
+ );
+ }
+
+ return Status;
+}
+
+
+/**
+ Execute a synchronous Isochronous USB transfer
+
+ @param UsbBus The USB bus driver
+ @param DevAddr The target device address
+ @param EpAddr The target endpoint address, with direction encoded in
+ bit 7
+ @param DevSpeed The device's speed
+ @param MaxPacket The endpoint's max packet size
+ @param BufferNum The number of data buffer
+ @param Data Array of pointers to data buffer
+ @param DataLength The length of data buffer
+ @param Translator The transaction translator for low/full speed device
+ @param UsbResult The result of USB execution
+
+ @retval EFI_UNSUPPORTED The isochronous transfer isn't supported now
+
+**/
+EFI_STATUS
+UsbHcIsochronousTransfer (
+ IN USB_BUS *UsbBus,
+ IN UINT8 DevAddr,
+ IN UINT8 EpAddr,
+ IN UINT8 DevSpeed,
+ IN UINTN MaxPacket,
+ IN UINT8 BufferNum,
+ IN OUT VOID *Data[EFI_USB_MAX_ISO_BUFFER_NUM],
+ IN UINTN DataLength,
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
+ OUT UINT32 *UsbResult
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ Queue an asynchronous isochronous transfer
+
+ @param UsbBus The USB bus driver
+ @param DevAddr The target device address
+ @param EpAddr The target endpoint address, with direction encoded in
+ bit 7
+ @param DevSpeed The device's speed
+ @param MaxPacket The endpoint's max packet size
+ @param BufferNum The number of data buffer
+ @param Data Array of pointers to data buffer
+ @param DataLength The length of data buffer
+ @param Translator The transaction translator for low/full speed device
+ @param Callback The function to call when data is transferred
+ @param Context The context to the callback function
+
+ @retval EFI_UNSUPPORTED The asynchronous isochronous transfer isn't supported
+
+**/
+EFI_STATUS
+UsbHcAsyncIsochronousTransfer (
+ IN USB_BUS *UsbBus,
+ IN UINT8 DevAddr,
+ IN UINT8 EpAddr,
+ IN UINT8 DevSpeed,
+ IN UINTN MaxPacket,
+ IN UINT8 BufferNum,
+ IN OUT VOID *Data[EFI_USB_MAX_ISO_BUFFER_NUM],
+ IN UINTN DataLength,
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
+ IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,
+ IN VOID *Context
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ Open the USB host controller protocol BY_CHILD
+
+ @param Bus The USB bus driver
+ @param Child The child handle
+
+ @return The open protocol return
+
+**/
+EFI_STATUS
+UsbOpenHostProtoByChild (
+ IN USB_BUS *Bus,
+ IN EFI_HANDLE Child
+ )
+{
+ EFI_USB_HC_PROTOCOL *UsbHc;
+ EFI_USB2_HC_PROTOCOL *Usb2Hc;
+ EFI_STATUS Status;
+
+ if (Bus->Usb2Hc != NULL) {
+ Status = gBS->OpenProtocol (
+ Bus->HostHandle,
+ &gEfiUsb2HcProtocolGuid,
+ &Usb2Hc,
+ mUsbBusDriverBinding.DriverBindingHandle,
+ Child,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+
+ } else {
+ Status = gBS->OpenProtocol (
+ Bus->HostHandle,
+ &gEfiUsbHcProtocolGuid,
+ &UsbHc,
+ mUsbBusDriverBinding.DriverBindingHandle,
+ Child,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ }
+
+ return Status;
+}
+
+
+/**
+ Close the USB host controller protocol BY_CHILD
+
+ @param Bus The USB bus driver
+ @param Child The child handle
+
+ @return None
+
+**/
+VOID
+UsbCloseHostProtoByChild (
+ IN USB_BUS *Bus,
+ IN EFI_HANDLE Child
+ )
+{
+ if (Bus->Usb2Hc != NULL) {
+ gBS->CloseProtocol (
+ Bus->HostHandle,
+ &gEfiUsb2HcProtocolGuid,
+ mUsbBusDriverBinding.DriverBindingHandle,
+ Child
+ );
+
+ } else {
+ gBS->CloseProtocol (
+ Bus->HostHandle,
+ &gEfiUsbHcProtocolGuid,
+ mUsbBusDriverBinding.DriverBindingHandle,
+ Child
+ );
+ }
+}
+
+
+
+/**
+ return the current TPL, copied from the EDKII glue lib.
+
+ VOID
+
+ @return Current TPL
+
+**/
+EFI_TPL
+UsbGetCurrentTpl (
+ VOID
+ )
+{
+ EFI_TPL Tpl;
+
+ Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
+ gBS->RestoreTPL (Tpl);
+
+ return Tpl;
+}
+
+
+#ifdef EFI_DEBUG
+VOID
+UsbDebug (
+ IN CHAR8 *Format,
+ ...
+ )
+/*++
+
+Routine Description:
+
+ USB's debug output function.
+
+Arguments:
+
+ Format - The format parameters to the print
+ ... - The variable length parameters after format
+
+Returns:
+
+ None
+
+--*/
+{
+ VA_LIST Marker;
+
+ VA_START (Marker, Format);
+ DebugVPrint (DEBUG_INFO, Format, Marker);
+ VA_END (Marker);
+}
+
+
+
+/**
+ USB's error output function.
+
+ @param Format The format parameters to the print
+ @param ... The variable length parameters after format
+
+ @return None
+
+**/
+VOID
+UsbError (
+ IN CHAR8 *Format,
+ ...
+ )
+{
+ VA_LIST Marker;
+
+ VA_START (Marker, Format);
+ DebugVPrint (DEBUG_ERROR, Format, Marker);
+ VA_END (Marker);
+}
+
+#endif
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.h b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.h new file mode 100644 index 0000000..2dafd1c --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbUtility.h @@ -0,0 +1,220 @@ +/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+ Module Name:
+
+ UsbUtility.h
+
+ Abstract:
+
+ Manage Usb Port/Hc/Etc
+
+ Revision History
+
+
+**/
+
+#ifndef _USB_UTILITY_H
+#define _USB_UTILITY_H
+
+EFI_STATUS
+UsbHcGetCapability (
+ IN USB_BUS *UsbBus,
+ OUT UINT8 *MaxSpeed,
+ OUT UINT8 *NumOfPort,
+ OUT UINT8 *Is64BitCapable
+ );
+
+EFI_STATUS
+UsbHcReset (
+ IN USB_BUS *UsbBus,
+ IN UINT16 Attributes
+ );
+
+
+EFI_STATUS
+UsbHcGetState (
+ IN USB_BUS *UsbBus,
+ OUT EFI_USB_HC_STATE *State
+ );
+
+
+EFI_STATUS
+UsbHcSetState (
+ IN USB_BUS *UsbBus,
+ IN EFI_USB_HC_STATE State
+ );
+
+
+EFI_STATUS
+UsbHcGetRootHubPortStatus (
+ IN USB_BUS *UsbBus,
+ IN UINT8 PortIndex,
+ OUT EFI_USB_PORT_STATUS *PortStatus
+ );
+
+
+EFI_STATUS
+UsbHcSetRootHubPortFeature (
+ IN USB_BUS *UsbBus,
+ IN UINT8 PortIndex,
+ IN EFI_USB_PORT_FEATURE Feature
+ );
+
+
+EFI_STATUS
+UsbHcClearRootHubPortFeature (
+ IN USB_BUS *UsbBus,
+ IN UINT8 PortIndex,
+ IN EFI_USB_PORT_FEATURE Feature
+ );
+
+
+EFI_STATUS
+UsbHcControlTransfer (
+ IN USB_BUS *UsbBus,
+ IN UINT8 DevAddr,
+ IN UINT8 DevSpeed,
+ IN UINTN MaxPacket,
+ IN EFI_USB_DEVICE_REQUEST *Request,
+ IN EFI_USB_DATA_DIRECTION Direction,
+ IN OUT VOID *Data,
+ IN OUT UINTN *DataLength,
+ IN UINTN TimeOut,
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
+ OUT UINT32 *UsbResult
+ );
+
+
+EFI_STATUS
+UsbHcBulkTransfer (
+ IN USB_BUS *UsbBus,
+ IN UINT8 DevAddr,
+ IN UINT8 EpAddr,
+ IN UINT8 DevSpeed,
+ IN UINTN MaxPacket,
+ IN UINT8 BufferNum,
+ IN OUT VOID *Data[EFI_USB_MAX_BULK_BUFFER_NUM],
+ IN OUT UINTN *DataLength,
+ IN OUT UINT8 *DataToggle,
+ IN UINTN TimeOut,
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
+ OUT UINT32 *UsbResult
+ );
+
+
+EFI_STATUS
+UsbHcAsyncInterruptTransfer (
+ IN USB_BUS *UsbBus,
+ IN UINT8 DevAddr,
+ IN UINT8 EpAddr,
+ IN UINT8 DevSpeed,
+ IN UINTN MaxPacket,
+ IN BOOLEAN IsNewTransfer,
+ IN OUT UINT8 *DataToggle,
+ IN UINTN PollingInterval,
+ IN UINTN DataLength,
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
+ IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,
+ IN VOID *Context OPTIONAL
+ );
+
+
+EFI_STATUS
+UsbHcSyncInterruptTransfer (
+ IN USB_BUS *UsbBus,
+ IN UINT8 DevAddr,
+ IN UINT8 EpAddr,
+ IN UINT8 DevSpeed,
+ IN UINTN MaxPacket,
+ IN OUT VOID *Data,
+ IN OUT UINTN *DataLength,
+ IN OUT UINT8 *DataToggle,
+ IN UINTN TimeOut,
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
+ OUT UINT32 *UsbResult
+ );
+
+
+EFI_STATUS
+UsbHcIsochronousTransfer (
+ IN USB_BUS *UsbBus,
+ IN UINT8 DevAddr,
+ IN UINT8 EpAddr,
+ IN UINT8 DevSpeed,
+ IN UINTN MaxPacket,
+ IN UINT8 BufferNum,
+ IN OUT VOID *Data[EFI_USB_MAX_ISO_BUFFER_NUM],
+ IN UINTN DataLength,
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
+ OUT UINT32 *UsbResult
+ );
+
+
+EFI_STATUS
+UsbHcAsyncIsochronousTransfer (
+ IN USB_BUS *UsbBus,
+ IN UINT8 DevAddr,
+ IN UINT8 EpAddr,
+ IN UINT8 DevSpeed,
+ IN UINTN MaxPacket,
+ IN UINT8 BufferNum,
+ IN OUT VOID *Data[EFI_USB_MAX_ISO_BUFFER_NUM],
+ IN UINTN DataLength,
+ IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator,
+ IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,
+ IN VOID *Context
+ );
+
+
+EFI_STATUS
+UsbOpenHostProtoByChild (
+ IN USB_BUS *Bus,
+ IN EFI_HANDLE Child
+ );
+
+
+VOID
+UsbCloseHostProtoByChild (
+ IN USB_BUS *Bus,
+ IN EFI_HANDLE Child
+ );
+
+
+EFI_TPL
+UsbGetCurrentTpl (
+ VOID
+ );
+
+//
+// USB debug support routines
+//
+#ifdef EFI_DEBUG
+ #define USB_DEBUG(arg) UsbDebug arg
+ #define USB_ERROR(arg) UsbError arg
+#else
+ #define USB_DEBUG(arg)
+ #define USB_ERROR(arg)
+#endif
+
+VOID
+UsbDebug (
+ IN CHAR8 *Format,
+ ...
+ );
+
+
+VOID
+UsbError (
+ IN CHAR8 *Format,
+ ...
+ );
+#endif
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/usbbus.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/usbbus.c new file mode 100644 index 0000000..c5a0604 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/usbbus.c @@ -0,0 +1,1384 @@ +/** @file
+
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+ Module Name:
+
+ UsbBus.c
+
+ Abstract:
+
+ Usb Bus Driver Binding and Bus IO Protocol
+
+ Revision History
+
+
+**/
+
+#include "UsbBus.h"
+
+//
+// USB_BUS_PROTOCOL is only used to locate USB_BUS
+//
+EFI_GUID mUsbBusProtocolGuid = EFI_USB_BUS_PROTOCOL_GUID;
+
+
+/**
+ USB_IO function to execute a control transfer. This
+ function will execute the USB transfer. If transfer
+ successes, it will sync the internal state of USB bus
+ with device state.
+
+ @param This The USB_IO instance
+ @param Request The control transfer request
+ @param Direction Direction for data stage
+ @param Timeout The time to wait before timeout
+ @param Data The buffer holding the data
+ @param DataLength Then length of the data
+ @param UsbStatus USB result
+
+ @retval EFI_INVALID_PARAMETER The parameters are invalid
+ @retval EFI_SUCCESS The control transfer succeded.
+ @retval Others Failed to execute the transfer
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UsbIoControlTransfer (
+ IN EFI_USB_IO_PROTOCOL *This,
+ IN EFI_USB_DEVICE_REQUEST *Request,
+ IN EFI_USB_DATA_DIRECTION Direction,
+ IN UINT32 Timeout,
+ IN OUT VOID *Data, OPTIONAL
+ IN UINTN DataLength, OPTIONAL
+ OUT UINT32 *UsbStatus
+ )
+{
+ USB_DEVICE *Dev;
+ USB_INTERFACE *UsbIf;
+ USB_ENDPOINT_DESC *EpDesc;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (UsbStatus == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
+
+ UsbIf = USB_INTERFACE_FROM_USBIO (This);
+ Dev = UsbIf->Device;
+
+ Status = UsbHcControlTransfer (
+ Dev->Bus,
+ Dev->Address,
+ Dev->Speed,
+ Dev->MaxPacket0,
+ Request,
+ Direction,
+ Data,
+ &DataLength,
+ (UINTN) Timeout,
+ &Dev->Translator,
+ UsbStatus
+ );
+
+ if (EFI_ERROR (Status) || (*UsbStatus != EFI_USB_NOERROR)) {
+ //
+ // Clear TT buffer when CTRL/BULK split transaction failes
+ // Clear the TRANSLATOR TT buffer, not parent's buffer
+ //
+ if (Dev->Translator.TranslatorHubAddress != 0) {
+ UsbHubCtrlClearTTBuffer (
+ Dev->Bus->Devices[Dev->Translator.TranslatorHubAddress],
+ Dev->Translator.TranslatorPortNumber,
+ Dev->Address,
+ 0,
+ USB_ENDPOINT_CONTROL
+ );
+ }
+
+ goto ON_EXIT;
+ }
+
+ //
+ // Some control transfer will change the device's internal
+ // status, such as Set_Configuration and Set_Interface.
+ // We must synchronize the bus driver's status with that in
+ // device. We ignore the Set_Descriptor request because it's
+ // hardly used by any device, especially in pre-boot environment
+ //
+
+ //
+ // Reset the endpoint toggle when endpoint stall is cleared
+ //
+ if ((Request->Request == USB_REQ_CLEAR_FEATURE) &&
+ (Request->RequestType == USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_STANDARD,
+ USB_TARGET_ENDPOINT)) &&
+ (Request->Value == USB_FEATURE_ENDPOINT_HALT)) {
+
+ EpDesc = UsbGetEndpointDesc (UsbIf, (UINT8) Request->Index);
+
+ if (EpDesc != NULL) {
+ EpDesc->Toggle = 0;
+ }
+ }
+
+ //
+ // Select a new configuration. This is a dangerous action. Upper driver
+ // should stop use its current UsbIo after calling this driver. The old
+ // UsbIo will be uninstalled and new UsbIo be installed. We can't use
+ // ReinstallProtocol since interfaces in different configuration may be
+ // completely irrellvant.
+ //
+ if ((Request->Request == USB_REQ_SET_CONFIG) &&
+ (Request->RequestType == USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_STANDARD,
+ USB_TARGET_DEVICE))) {
+ //
+ // Don't re-create the USB interfaces if configuration isn't changed.
+ //
+ if ((Dev->ActiveConfig != NULL) &&
+ (Request->Value == Dev->ActiveConfig->Desc.ConfigurationValue)) {
+
+ goto ON_EXIT;
+ }
+
+ USB_DEBUG (("UsbIoControlTransfer: configure changed!!! Do NOT use old UsbIo!!!\n"));
+
+ if (Dev->ActiveConfig != NULL) {
+ UsbRemoveConfig (Dev);
+ }
+
+ if (Request->Value != 0) {
+ Status = UsbSelectConfig (Dev, (UINT8) Request->Value);
+ }
+
+ //
+ // Exit now, Old USB_IO is invalid now
+ //
+ goto ON_EXIT;
+ }
+
+ //
+ // A new alternative setting is selected for the interface.
+ // No need to reinstall UsbIo in this case because only
+ // underlying communication endpoints are changed. Functionality
+ // should remains the same.
+ //
+ if ((Request->Request == USB_REQ_SET_INTERFACE) &&
+ (Request->RequestType == USB_REQUEST_TYPE (EfiUsbNoData, USB_REQ_TYPE_STANDARD,
+ USB_TARGET_INTERFACE)) &&
+ (Request->Index == UsbIf->IfSetting->Desc.InterfaceNumber)) {
+
+ Status = UsbSelectSetting (UsbIf->IfDesc, (UINT8) Request->Value);
+
+ if (!EFI_ERROR (Status)) {
+ UsbIf->IfSetting = UsbIf->IfDesc->Settings[UsbIf->IfDesc->ActiveIndex];
+ }
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Execute a bulk transfer to the device endpoint
+
+ @param This The USB IO instance
+ @param Endpoint The device endpoint
+ @param Data The data to transfer
+ @param DataLength The length of the data to transfer
+ @param Timeout Time to wait before timeout
+ @param UsbStatus The result of USB transfer
+
+ @retval EFI_SUCCESS The bulk transfer is OK
+ @retval EFI_INVALID_PARAMETER Some parameters are invalid
+ @retval Others Failed to execute transfer, reason returned in
+ UsbStatus
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UsbIoBulkTransfer (
+ IN EFI_USB_IO_PROTOCOL *This,
+ IN UINT8 Endpoint,
+ IN OUT VOID *Data,
+ IN OUT UINTN *DataLength,
+ IN UINTN Timeout,
+ OUT UINT32 *UsbStatus
+ )
+{
+ USB_DEVICE *Dev;
+ USB_INTERFACE *UsbIf;
+ USB_ENDPOINT_DESC *EpDesc;
+ UINT8 BufNum;
+ UINT8 Toggle;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if ((USB_ENDPOINT_ADDR (Endpoint) == 0) || (USB_ENDPOINT_ADDR(Endpoint) > 15) ||
+ (UsbStatus == NULL)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
+
+ UsbIf = USB_INTERFACE_FROM_USBIO (This);
+ Dev = UsbIf->Device;
+
+ EpDesc = UsbGetEndpointDesc (UsbIf, Endpoint);
+
+ if ((EpDesc == NULL) || (USB_ENDPOINT_TYPE (&EpDesc->Desc) != USB_ENDPOINT_BULK)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ BufNum = 1;
+ Toggle = EpDesc->Toggle;
+ Status = UsbHcBulkTransfer (
+ Dev->Bus,
+ Dev->Address,
+ Endpoint,
+ Dev->Speed,
+ EpDesc->Desc.MaxPacketSize,
+ BufNum,
+ &Data,
+ DataLength,
+ &Toggle,
+ Timeout,
+ &Dev->Translator,
+ UsbStatus
+ );
+
+ EpDesc->Toggle = Toggle;
+
+ if (EFI_ERROR (Status) || (*UsbStatus != EFI_USB_NOERROR)) {
+ //
+ // Clear TT buffer when CTRL/BULK split transaction failes.
+ // Clear the TRANSLATOR TT buffer, not parent's buffer
+ //
+ if (Dev->Translator.TranslatorHubAddress != 0) {
+ UsbHubCtrlClearTTBuffer (
+ Dev->Bus->Devices[Dev->Translator.TranslatorHubAddress],
+ Dev->Translator.TranslatorPortNumber,
+ Dev->Address,
+ 0,
+ USB_ENDPOINT_BULK
+ );
+ }
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Execute a synchronous interrupt transfer
+
+ @param This The USB IO instance
+ @param Endpoint The device endpoint
+ @param Data The data to transfer
+ @param DataLength The length of the data to transfer
+ @param Timeout Time to wait before timeout
+ @param UsbStatus The result of USB transfer
+
+ @retval EFI_SUCCESS The synchronous interrupt transfer is OK
+ @retval EFI_INVALID_PARAMETER Some parameters are invalid
+ @retval Others Failed to execute transfer, reason returned in
+ UsbStatus
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UsbIoSyncInterruptTransfer (
+ IN EFI_USB_IO_PROTOCOL *This,
+ IN UINT8 Endpoint,
+ IN OUT VOID *Data,
+ IN OUT UINTN *DataLength,
+ IN UINTN Timeout,
+ OUT UINT32 *UsbStatus
+ )
+{
+ USB_DEVICE *Dev;
+ USB_INTERFACE *UsbIf;
+ USB_ENDPOINT_DESC *EpDesc;
+ EFI_TPL OldTpl;
+ UINT8 Toggle;
+ EFI_STATUS Status;
+
+ if ((USB_ENDPOINT_ADDR (Endpoint) == 0) || (USB_ENDPOINT_ADDR(Endpoint) > 15) ||
+ (UsbStatus == NULL)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
+
+ UsbIf = USB_INTERFACE_FROM_USBIO (This);
+ Dev = UsbIf->Device;
+
+ EpDesc = UsbGetEndpointDesc (UsbIf, Endpoint);
+
+ if ((EpDesc == NULL) || (USB_ENDPOINT_TYPE (&EpDesc->Desc) != USB_ENDPOINT_INTERRUPT)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Toggle = EpDesc->Toggle;
+ Status = UsbHcSyncInterruptTransfer (
+ Dev->Bus,
+ Dev->Address,
+ Endpoint,
+ Dev->Speed,
+ EpDesc->Desc.MaxPacketSize,
+ Data,
+ DataLength,
+ &Toggle,
+ Timeout,
+ &Dev->Translator,
+ UsbStatus
+ );
+
+ EpDesc->Toggle = Toggle;
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Queue a new asynchronous interrupt transfer, or remove the old
+ request if (IsNewTransfer == FALSE)
+
+ @param This The USB_IO instance
+ @param Endpoint The device endpoint
+ @param IsNewTransfer Whether this is a new request, if it's old, remove
+ the request
+ @param PollInterval The interval to poll the transfer result, (in ms)
+ @param DataLength The length of perodic data transfer
+ @param Callback The function to call periodicaly when transfer is
+ ready
+ @param Context The context to the callback
+
+ @retval EFI_SUCCESS New transfer is queued or old request is removed
+ @retval EFI_INVALID_PARAMETER Some parameters are invalid
+ @retval Others Failed to queue the new request or remove the old
+ request
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UsbIoAsyncInterruptTransfer (
+ IN EFI_USB_IO_PROTOCOL *This,
+ IN UINT8 Endpoint,
+ IN BOOLEAN IsNewTransfer,
+ IN UINTN PollInterval, OPTIONAL
+ IN UINTN DataLength, OPTIONAL
+ IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback, OPTIONAL
+ IN VOID *Context OPTIONAL
+ )
+{
+ USB_DEVICE *Dev;
+ USB_INTERFACE *UsbIf;
+ USB_ENDPOINT_DESC *EpDesc;
+ EFI_TPL OldTpl;
+ UINT8 Toggle;
+ EFI_STATUS Status;
+
+ if ((USB_ENDPOINT_ADDR (Endpoint) == 0) || (USB_ENDPOINT_ADDR (Endpoint) > 15)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
+ UsbIf = USB_INTERFACE_FROM_USBIO (This);
+ Dev = UsbIf->Device;
+
+ EpDesc = UsbGetEndpointDesc (UsbIf, Endpoint);
+
+ if ((EpDesc == NULL) || (USB_ENDPOINT_TYPE (&EpDesc->Desc) != USB_ENDPOINT_INTERRUPT)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Toggle = EpDesc->Toggle;
+ Status = UsbHcAsyncInterruptTransfer (
+ Dev->Bus,
+ Dev->Address,
+ Endpoint,
+ Dev->Speed,
+ EpDesc->Desc.MaxPacketSize,
+ IsNewTransfer,
+ &Toggle,
+ PollInterval,
+ DataLength,
+ &Dev->Translator,
+ Callback,
+ Context
+ );
+
+ EpDesc->Toggle = Toggle;
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Execute a synchronous isochronous transfer
+
+ @param This The USB IO instance
+ @param DeviceEndpoint The device endpoint
+ @param Data The data to transfer
+ @param DataLength The length of the data to transfer
+ @param UsbStatus The result of USB transfer
+
+ @retval EFI_UNSUPPORTED Currently isochronous transfer isn't supported
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UsbIoIsochronousTransfer (
+ IN EFI_USB_IO_PROTOCOL *This,
+ IN UINT8 DeviceEndpoint,
+ IN OUT VOID *Data,
+ IN UINTN DataLength,
+ OUT UINT32 *Status
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ Queue an asynchronous isochronous transfer
+
+ @param This The USB_IO instance
+ @param DeviceEndpoint The device endpoint
+ @param DataLength The length of perodic data transfer
+ @param IsochronousCallBack The function to call periodicaly when transfer is
+ ready
+ @param Context The context to the callback
+
+ @retval EFI_UNSUPPORTED Currently isochronous transfer isn't supported
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UsbIoAsyncIsochronousTransfer (
+ IN EFI_USB_IO_PROTOCOL *This,
+ IN UINT8 DeviceEndpoint,
+ IN OUT VOID *Data,
+ IN UINTN DataLength,
+ IN EFI_ASYNC_USB_TRANSFER_CALLBACK IsochronousCallBack,
+ IN VOID *Context OPTIONAL
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ Retrieve the device descriptor of the device
+
+ @param This The USB IO instance
+ @param Descriptor The variable to receive the device descriptor
+
+ @retval EFI_SUCCESS The device descriptor is returned
+ @retval EFI_INVALID_PARAMETER The parameter is invalid
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UsbIoGetDeviceDescriptor (
+ IN EFI_USB_IO_PROTOCOL *This,
+ OUT EFI_USB_DEVICE_DESCRIPTOR *Descriptor
+ )
+{
+ USB_DEVICE *Dev;
+ USB_INTERFACE *UsbIf;
+ EFI_TPL OldTpl;
+
+ if (Descriptor == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
+
+ UsbIf = USB_INTERFACE_FROM_USBIO (This);
+ Dev = UsbIf->Device;
+
+ CopyMem (Descriptor, &Dev->DevDesc->Desc, sizeof (EFI_USB_DEVICE_DESCRIPTOR));
+
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Return the configuration descriptor of the current active configuration
+
+ @param This The USB IO instance
+ @param Descriptor The USB configuration descriptor
+
+ @retval EFI_SUCCESS The active configuration descriptor is returned
+ @retval EFI_INVALID_PARAMETER Some parameter is invalid
+ @retval EFI_NOT_FOUND Currently no active configuration is selected.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UsbIoGetActiveConfigDescriptor (
+ IN EFI_USB_IO_PROTOCOL *This,
+ OUT EFI_USB_CONFIG_DESCRIPTOR *Descriptor
+ )
+{
+ USB_DEVICE *Dev;
+ USB_INTERFACE *UsbIf;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if (Descriptor == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EFI_SUCCESS;
+ OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
+
+ UsbIf = USB_INTERFACE_FROM_USBIO (This);
+ Dev = UsbIf->Device;
+
+ if (Dev->ActiveConfig == NULL) {
+ Status = EFI_NOT_FOUND;
+ goto ON_EXIT;
+ }
+
+ CopyMem (Descriptor, &(Dev->ActiveConfig->Desc), sizeof (EFI_USB_CONFIG_DESCRIPTOR));
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Retrieve the active interface setting descriptor for this USB IO instance
+
+ @param This The USB IO instance
+ @param Descriptor The variable to receive active interface setting
+
+ @retval EFI_SUCCESS The active interface setting is returned
+ @retval EFI_INVALID_PARAMETER Some parameter is invalid
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UsbIoGetInterfaceDescriptor (
+ IN EFI_USB_IO_PROTOCOL *This,
+ OUT EFI_USB_INTERFACE_DESCRIPTOR *Descriptor
+ )
+{
+ USB_INTERFACE *UsbIf;
+ EFI_TPL OldTpl;
+
+ if (Descriptor == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
+
+ UsbIf = USB_INTERFACE_FROM_USBIO (This);
+ CopyMem (Descriptor, &(UsbIf->IfSetting->Desc), sizeof (EFI_USB_INTERFACE_DESCRIPTOR));
+
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Retrieve the endpoint descriptor from this interface setting
+
+ @param This The USB IO instance
+ @param Index The index (start from zero) of the endpoint to
+ retrieve
+ @param Descriptor The variable to receive the descriptor
+
+ @retval EFI_SUCCESS The endpoint descriptor is returned
+ @retval EFI_INVALID_PARAMETER Some parameter is invalid
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UsbIoGetEndpointDescriptor (
+ IN EFI_USB_IO_PROTOCOL *This,
+ IN UINT8 Index,
+ OUT EFI_USB_ENDPOINT_DESCRIPTOR *Descriptor
+ )
+{
+ USB_INTERFACE *UsbIf;
+ EFI_TPL OldTpl;
+
+ OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
+
+ UsbIf = USB_INTERFACE_FROM_USBIO (This);
+
+ if ((Descriptor == NULL) || (Index >= UsbIf->IfSetting->Desc.NumEndpoints)) {
+ gBS->RestoreTPL (OldTpl);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CopyMem (
+ Descriptor,
+ &(UsbIf->IfSetting->Endpoints[Index]->Desc),
+ sizeof (EFI_USB_ENDPOINT_DESCRIPTOR)
+ );
+
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Retrieve the supported language ID table from the device
+
+ @param This The USB IO instance
+ @param LangIDTable The table to return the language IDs
+ @param TableSize The number of supported languanges
+
+ @retval EFI_SUCCESS The language ID is return
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UsbIoGetSupportedLanguages (
+ IN EFI_USB_IO_PROTOCOL *This,
+ OUT UINT16 **LangIDTable,
+ OUT UINT16 *TableSize
+ )
+{
+ USB_DEVICE *Dev;
+ USB_INTERFACE *UsbIf;
+ EFI_TPL OldTpl;
+
+ OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
+
+ UsbIf = USB_INTERFACE_FROM_USBIO (This);
+ Dev = UsbIf->Device;
+
+ *LangIDTable = Dev->LangId;
+ *TableSize = Dev->TotalLangId;
+
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Retrieve an indexed string in the language of LangID
+
+ @param This The USB IO instance
+ @param LangID The language ID of the string to retrieve
+ @param StringIndex The index of the string
+ @param String The variable to receive the string
+
+ @retval EFI_SUCCESS The string is returned
+ @retval EFI_NOT_FOUND No such string existed
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UsbIoGetStringDescriptor (
+ IN EFI_USB_IO_PROTOCOL *This,
+ IN UINT16 LangID,
+ IN UINT8 StringIndex,
+ OUT CHAR16 **String
+ )
+{
+ USB_DEVICE *Dev;
+ USB_INTERFACE *UsbIf;
+ EFI_USB_STRING_DESCRIPTOR *StrDesc;
+ EFI_TPL OldTpl;
+ UINT8 *Buf;
+ UINT8 Index;
+ EFI_STATUS Status;
+
+ if ((StringIndex == 0) || (LangID == 0)) {
+ return EFI_NOT_FOUND;
+ }
+
+ OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
+
+ UsbIf = USB_INTERFACE_FROM_USBIO (This);
+ Dev = UsbIf->Device;
+
+ //
+ // Check whether language ID is supported
+ //
+ Status = EFI_NOT_FOUND;
+
+ for (Index = 0; Index < Dev->TotalLangId; Index++) {
+ if (Dev->LangId[Index] == LangID) {
+ break;
+ }
+ }
+
+ if (Index == Dev->TotalLangId) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Retrieve the string descriptor then allocate a buffer
+ // to hold the string itself.
+ //
+ StrDesc = UsbGetOneString (Dev, StringIndex, LangID);
+
+ if (StrDesc == NULL) {
+ goto ON_EXIT;
+ }
+
+ if (StrDesc->Length <= 2) {
+ goto FREE_STR;
+ }
+
+ Buf = AllocateZeroPool (StrDesc->Length);
+
+ if (Buf == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FREE_STR;
+ }
+
+ CopyMem (Buf, StrDesc->String, StrDesc->Length - 2);
+ *String = (CHAR16 *) Buf;
+ Status = EFI_SUCCESS;
+
+FREE_STR:
+ gBS->FreePool (StrDesc);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Reset the device, then if that succeeds, reconfigure the
+ device with its address and current active configuration.
+
+ @param This The USB IO instance
+
+ @retval EFI_SUCCESS The device is reset and configured
+ @retval Others Failed to reset the device
+
+**/
+EFI_STATUS
+EFIAPI
+UsbIoPortReset (
+ IN EFI_USB_IO_PROTOCOL *This
+ )
+{
+ USB_INTERFACE *UsbIf;
+ USB_INTERFACE *HubIf;
+ USB_DEVICE *Dev;
+ UINT8 Address;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
+
+ UsbIf = USB_INTERFACE_FROM_USBIO (This);
+ Dev = UsbIf->Device;
+
+ HubIf = Dev->ParentIf;
+ Status = HubIf->HubApi->ResetPort (HubIf, Dev->ParentPort);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbIoPortReset: failed to reset hub port %d@hub %d, %r \n",
+ Dev->ParentPort, Dev->ParentAddr, Status));
+
+ goto ON_EXIT;
+ }
+
+ //
+ // Reset the device to its current address. The device now has a
+ // address of ZERO, so need to set Dev->Address to zero first for
+ // host to communicate with the device
+ //
+ Address = Dev->Address;
+ Dev->Address = 0;
+ Status = UsbSetAddress (Dev, Address);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbIoPortReset: failed to set address for device %d - %r\n",
+ Address, Status));
+
+ goto ON_EXIT;
+ }
+
+ Dev->Address = Address;
+
+ //
+ // Reset the current active configure, after this device
+ // is in CONFIGURED state.
+ //
+ if (Dev->ActiveConfig != NULL) {
+ Status = UsbSetConfig (Dev, Dev->ActiveConfig->Desc.ConfigurationValue);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbIoPortReset: failed to set configure for device %d - %r\n",
+ Address, Status));
+ }
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+EFI_USB_IO_PROTOCOL mUsbIoProtocol = {
+ UsbIoControlTransfer,
+ UsbIoBulkTransfer,
+ UsbIoAsyncInterruptTransfer,
+ UsbIoSyncInterruptTransfer,
+ UsbIoIsochronousTransfer,
+ UsbIoAsyncIsochronousTransfer,
+ UsbIoGetDeviceDescriptor,
+ UsbIoGetActiveConfigDescriptor,
+ UsbIoGetInterfaceDescriptor,
+ UsbIoGetEndpointDescriptor,
+ UsbIoGetStringDescriptor,
+ UsbIoGetSupportedLanguages,
+ UsbIoPortReset
+};
+
+//@MT: EFI_DRIVER_ENTRY_POINT (UsbBusDriverEntryPoint)
+
+EFI_STATUS
+EFIAPI
+UsbBusDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+/*++
+
+Routine Description:
+
+ The USB bus driver entry pointer
+
+Arguments:
+
+ ImageHandle - The driver image handle
+ SystemTable - The system table
+
+Returns:
+
+ EFI_SUCCESS - The component name protocol is installed
+ Others - Failed to init the usb driver
+
+--*/
+{
+ return EfiLibInstallAllDriverProtocols (
+ ImageHandle,
+ SystemTable,
+ &mUsbBusDriverBinding,
+ ImageHandle,
+ &mUsbBusComponentName,
+ NULL,
+ NULL
+ );
+}
+
+
+/**
+ Check whether USB bus driver support this device
+
+ @param This The USB bus driver binding protocol
+ @param Controller The controller handle to test againist
+ @param RemainingDevicePath The remaining device path
+
+ @retval EFI_SUCCESS The bus supports this controller.
+ @retval EFI_UNSUPPORTED This device isn't supported
+
+**/
+EFI_STATUS
+EFIAPI
+UsbBusControllerDriverSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_DEV_PATH_PTR DevicePathNode;
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
+ EFI_USB2_HC_PROTOCOL *Usb2Hc;
+ EFI_USB_HC_PROTOCOL *UsbHc;
+ EFI_STATUS Status;
+
+ //
+ // Check whether device path is valid
+ //
+ if (RemainingDevicePath != NULL) {
+ DevicePathNode.DevPath = RemainingDevicePath;
+
+ if ((DevicePathNode.DevPath->Type != MESSAGING_DEVICE_PATH) ||
+ (DevicePathNode.DevPath->SubType != MSG_USB_DP) ||
+ (DevicePathNodeLength (DevicePathNode.DevPath) != sizeof (USB_DEVICE_PATH))) {
+
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &ParentDevicePath,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (Status == EFI_ALREADY_STARTED) {
+ return EFI_SUCCESS;
+ }
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ //
+ // Check whether USB_HC2 protocol is installed
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiUsb2HcProtocolGuid,
+ (VOID **) &Usb2Hc,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (Status == EFI_ALREADY_STARTED) {
+ return EFI_SUCCESS;
+ }
+
+ if (!EFI_ERROR (Status)) {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiUsb2HcProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ return EFI_SUCCESS;
+ }
+
+ //
+ // If failed to open USB_HC2, fall back to USB_HC
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiUsbHcProtocolGuid,
+ (VOID **) &UsbHc,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (Status == EFI_ALREADY_STARTED) {
+ return EFI_SUCCESS;
+ }
+
+ if (!EFI_ERROR (Status)) {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiUsbHcProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ }
+
+ return Status;
+}
+
+
+/**
+ Start to process the controller
+
+ @param This The USB bus driver binding instance
+ @param Controller The controller to check
+ @param RemainingDevicePath The remaining device patch
+
+ @retval EFI_SUCCESS The controller is controlled by the usb bus
+ @retval EFI_ALREADY_STARTED The controller is already controlled by the usb
+ bus
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources
+
+**/
+EFI_STATUS
+EFIAPI
+UsbBusControllerDriverStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ USB_BUS *UsbBus;
+ USB_DEVICE *RootHub;
+ USB_INTERFACE *RootIf;
+ EFI_USB_BUS_PROTOCOL *UsbBusId;
+ EFI_STATUS Status;
+ EFI_STATUS Status2;
+
+ //
+ // Locate the USB bus protocol, if it is found, USB bus
+ // is already started on this controller.
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &mUsbBusProtocolGuid,
+ (VOID **) &UsbBusId,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ UsbBus = AllocateZeroPool (sizeof (USB_BUS));
+
+ if (UsbBus == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ UsbBus->Signature = USB_BUS_SIGNATURE;
+ UsbBus->HostHandle = Controller;
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &UsbBus->DevicePath,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbBusStart: Failed to open device path %r\n", Status));
+
+ gBS->FreePool (UsbBus);
+ return Status;
+ }
+
+ //
+ // Get USB_HC2/USB_HC host controller protocol (EHCI/UHCI).
+ // This is for backward compatbility with EFI 1.x. In UEFI
+ // 2.x, USB_HC2 replaces USB_HC. We will open both USB_HC2
+ // and USB_HC because EHCI driver will install both protocols
+ // (for the same reason). If we don't consume both of them,
+ // the unconsumed one may be opened by others.
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiUsb2HcProtocolGuid,
+ (VOID **) &(UsbBus->Usb2Hc),
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ Status2 = gBS->OpenProtocol (
+ Controller,
+ &gEfiUsbHcProtocolGuid,
+ (VOID **) &(UsbBus->UsbHc),
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status) && EFI_ERROR (Status2)) {
+ USB_ERROR (("UsbBusStart: Failed to open USB_HC/USB2_HC %r\n", Status));
+
+ Status = EFI_DEVICE_ERROR;
+ goto CLOSE_HC;
+ }
+
+ //
+ // Create a fake usb device for root hub
+ //
+ RootHub = AllocateZeroPool (sizeof (USB_DEVICE));
+
+ if (RootHub == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto CLOSE_HC;
+ }
+
+ RootIf = AllocateZeroPool (sizeof (USB_INTERFACE));
+
+ if (RootIf == NULL) {
+ gBS->FreePool (RootHub);
+ Status = EFI_OUT_OF_RESOURCES;
+ goto CLOSE_HC;
+ }
+
+ RootHub->Bus = UsbBus;
+ RootHub->NumOfInterface = 1;
+ RootHub->Interfaces[0] = RootIf;
+ RootIf->Signature = USB_INTERFACE_SIGNATURE;
+ RootIf->Device = RootHub;
+ RootIf->DevicePath = UsbBus->DevicePath;
+
+ Status = mUsbRootHubApi.Init (RootIf);
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbBusStart: Failed to init root hub %r\n", Status));
+ goto FREE_ROOTHUB;
+ }
+
+ UsbBus->Devices[0] = RootHub;
+
+ //
+ // Install an EFI_USB_BUS_PROTOCOL to host controler to identify it.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &Controller,
+ &mUsbBusProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &UsbBus->BusId
+ );
+
+ if (EFI_ERROR (Status)) {
+ USB_ERROR (("UsbBusStart: Failed to install bus protocol %r\n", Status));
+
+ mUsbRootHubApi.Release (RootIf);
+ goto FREE_ROOTHUB;
+ }
+
+ UsbHcReset (UsbBus, EFI_USB_HC_RESET_GLOBAL);
+ UsbHcSetState (UsbBus, EfiUsbHcStateOperational);
+
+ USB_DEBUG (("UsbBusStart: usb bus started on %x, root hub %x\n", Controller, RootIf));
+ return EFI_SUCCESS;
+
+FREE_ROOTHUB:
+ gBS->FreePool (RootIf);
+ gBS->FreePool (RootHub);
+
+CLOSE_HC:
+ if (UsbBus->Usb2Hc != NULL) {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiUsb2HcProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ }
+
+ if (UsbBus->UsbHc != NULL) {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiUsbHcProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ }
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ gBS->FreePool (UsbBus);
+
+ USB_ERROR (("UsbBusStart: Failed to start bus driver %r\n", Status));
+ return Status;
+}
+
+
+/**
+ Stop handle the controller by this USB bus driver
+
+ @param This The USB bus driver binding protocol
+ @param Controller The controller to release
+ @param NumberOfChildren The child of USB bus that opened controller
+ BY_CHILD
+ @param ChildHandleBuffer The array of child handle
+
+ @retval EFI_SUCCESS The controller or children are stopped
+ @retval EFI_DEVICE_ERROR Failed to stop the driver
+
+**/
+EFI_STATUS
+EFIAPI
+UsbBusControllerDriverStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ USB_BUS *Bus;
+ USB_DEVICE *RootHub;
+ USB_DEVICE *UsbDev;
+ USB_INTERFACE *RootIf;
+ USB_INTERFACE *UsbIf;
+ EFI_USB_BUS_PROTOCOL *BusId;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ EFI_TPL OldTpl;
+ UINTN Index;
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ if (NumberOfChildren > 0) {
+ OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
+
+ for (Index = 0; Index < NumberOfChildren; Index++) {
+ Status = gBS->OpenProtocol (
+ ChildHandleBuffer[Index],
+ &gEfiUsbIoProtocolGuid,
+ (VOID **) &UsbIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ //
+ // It is possible that the child has already been released:
+ // 1. For combo device, free one device will release others.
+ // 2. If a hub is released, all devices on its down facing
+ // ports are released also.
+ //
+ continue;
+ }
+
+ UsbIf = USB_INTERFACE_FROM_USBIO (UsbIo);
+ UsbDev = UsbIf->Device;
+
+ UsbRemoveDevice (UsbDev);
+ }
+
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+ }
+
+ USB_DEBUG (("UsbBusStop: usb bus stopped on %x\n", Controller));
+
+ //
+ // Locate USB_BUS for the current host controller
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &mUsbBusProtocolGuid,
+ (VOID **) &BusId,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Bus = USB_BUS_FROM_THIS (BusId);
+
+ //
+ // Stop the root hub, then free all the devices
+ //
+ OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
+ UsbHcSetState (Bus, EfiUsbHcStateHalt);
+
+ RootHub = Bus->Devices[0];
+ RootIf = RootHub->Interfaces[0];
+
+ mUsbRootHubApi.Release (RootIf);
+
+ for (Index = 1; Index < USB_MAX_DEVICES; Index++) {
+ if (Bus->Devices[Index] != NULL) {
+ UsbRemoveDevice (Bus->Devices[Index]);
+ }
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ gBS->FreePool (RootIf);
+ gBS->FreePool (RootHub);
+
+ //
+ // Uninstall the bus identifier and close USB_HC/USB2_HC protocols
+ //
+ gBS->UninstallProtocolInterface (Controller, &mUsbBusProtocolGuid, &Bus->BusId);
+
+ if (Bus->Usb2Hc != NULL) {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiUsb2HcProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ }
+
+ if (Bus->UsbHc != NULL) {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiUsbHcProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ }
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ gBS->FreePool (Bus);
+
+ return Status;
+}
+
+EFI_DRIVER_BINDING_PROTOCOL mUsbBusDriverBinding = {
+ UsbBusControllerDriverSupported,
+ UsbBusControllerDriverStart,
+ UsbBusControllerDriverStop,
+ 0xa,
+ NULL,
+ NULL
+};
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/usbbus.h b/MdeModulePkg/Bus/Usb/UsbBusDxe/usbbus.h new file mode 100644 index 0000000..2ecc2c8 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/usbbus.h @@ -0,0 +1,210 @@ +/** @file
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+ Module Name:
+
+ UsbBus.h
+
+ Abstract:
+
+ Usb Bus Driver Binding and Bus IO Protocol
+
+ Revision History
+
+
+**/
+
+#ifndef _EFI_USB_BUS_H_
+#define _EFI_USB_BUS_H_
+
+//
+// The package level header files this module uses
+//
+#include <PiDxe.h>
+//
+// The protocols, PPI and GUID defintions for this module
+//
+#include <Protocol/Usb2HostController.h>
+#include <Protocol/UsbHostController.h>
+#include <Protocol/UsbIo.h>
+#include <Protocol/DevicePath.h>
+//
+// The Library classes this module consumes
+//
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+
+#include <IndustryStandard/Usb.h>
+
+typedef struct _USB_DEVICE USB_DEVICE;
+typedef struct _USB_INTERFACE USB_INTERFACE;
+typedef struct _USB_BUS USB_BUS;
+typedef struct _USB_HUB_API USB_HUB_API;
+
+
+#include "UsbUtility.h"
+#include "UsbDesc.h"
+#include "UsbHub.h"
+#include "UsbEnumer.h"
+
+enum {
+ //
+ // Time definition
+ //
+ USB_STALL_1_MS = 1000,
+ TICKS_PER_MS = 10000U,
+ USB_ROOTHUB_POLL_INTERVAL = 1000 * TICKS_PER_MS,
+ USB_HUB_POLL_INTERVAL = 64,
+
+ //
+ // Maximum definition
+ //
+ USB_MAX_LANG_ID = 16,
+ USB_MAX_INTERFACE = 16,
+ USB_MAX_DEVICES = 128,
+
+ //
+ // Bus raises TPL to TPL_NOTIFY to serialize all its operations
+ // to protect shared data structures.
+ //
+ USB_BUS_TPL = TPL_NOTIFY,
+
+ USB_INTERFACE_SIGNATURE = EFI_SIGNATURE_32 ('U', 'S', 'B', 'I'),
+ USB_BUS_SIGNATURE = EFI_SIGNATURE_32 ('U', 'S', 'B', 'B'),
+};
+
+#define USB_BIT(a) ((UINTN)(1 << (a)))
+#define USB_BIT_IS_SET(Data, Bit) ((BOOLEAN)(((Data) & (Bit)) == (Bit)))
+
+#define EFI_USB_BUS_PROTOCOL_GUID \
+ {0x2B2F68CC, 0x0CD2, 0x44cf, 0x8E, 0x8B, 0xBB, 0xA2, 0x0B, 0x1B, 0x5B, 0x75}
+
+#define USB_INTERFACE_FROM_USBIO(a) \
+ CR(a, USB_INTERFACE, UsbIo, USB_INTERFACE_SIGNATURE)
+
+#define USB_BUS_FROM_THIS(a) \
+ CR(a, USB_BUS, BusId, USB_BUS_SIGNATURE)
+
+//
+// Used to locate USB_BUS
+//
+typedef struct _EFI_USB_BUS_PROTOCOL {
+ UINT64 Reserved;
+} EFI_USB_BUS_PROTOCOL;
+
+
+//
+// Stands for the real USB device. Each device may
+// has several seperately working interfaces.
+//
+typedef struct _USB_DEVICE {
+ USB_BUS *Bus;
+
+ //
+ // Configuration information
+ //
+ UINT8 Speed;
+ UINT8 Address;
+ UINT8 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
+} USB_DEVICE;
+
+//
+// Stands for different functions of USB device
+//
+typedef 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;
+} USB_INTERFACE;
+
+//
+// Stands for the current USB Bus
+//
+typedef 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;
+
+ //
+ // 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[USB_MAX_DEVICES];
+} USB_BUS;
+
+extern EFI_USB_IO_PROTOCOL mUsbIoProtocol;
+extern EFI_DRIVER_BINDING_PROTOCOL mUsbBusDriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL mUsbBusComponentName;
+
+#endif
diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/ComponentName.c b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/ComponentName.c new file mode 100644 index 0000000..bd0f939 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/ComponentName.c @@ -0,0 +1,153 @@ + /*++
+
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ ComponentName.c
+
+Abstract:
+
+--*/
+
+//
+// The package level header files this module uses
+//
+#include <PiDxe.h>
+
+//
+// The Library classes this module consumes
+//
+#include <Library/UefiLib.h>
+
+//
+// EFI Component Name Functions
+//
+EFI_STATUS
+EFIAPI
+UsbMassStorageGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+EFI_STATUS
+EFIAPI
+UsbMassStorageGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI Component Name Protocol
+//
+EFI_COMPONENT_NAME_PROTOCOL gUsbMassStorageComponentName = {
+ UsbMassStorageGetDriverName,
+ UsbMassStorageGetControllerName,
+ "eng"
+};
+
+STATIC EFI_UNICODE_STRING_TABLE
+mUsbMassStorageDriverNameTable[] = {
+ {"eng", L"Usb Mass Storage Driver"},
+ {NULL, NULL}
+};
+
+EFI_STATUS
+EFIAPI
+UsbMassStorageGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ Language - A pointer to a three character ISO 639-2 language identifier.
+ This is the language of the driver name that that the caller
+ is requesting, and it must match one of the languages specified
+ in SupportedLanguages. The number of languages supported by a
+ driver is up to the driver writer.
+ DriverName - A pointer to the Unicode string to return. This Unicode string
+ is the name of the driver specified by This in the language
+ specified by Language.
+
+ Returns:
+ EFI_SUCCESS - The Unicode string for the Driver specified by This
+ and the language specified by Language was returned
+ in DriverName.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - DriverName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return LookupUnicodeString (
+ Language,
+ gUsbMassStorageComponentName.SupportedLanguages,
+ mUsbMassStorageDriverNameTable,
+ DriverName
+ );
+}
+
+EFI_STATUS
+EFIAPI
+UsbMassStorageGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ ControllerHandle - The handle of a controller that the driver specified by
+ This is managing. This handle specifies the controller
+ whose name is to be returned.
+ ChildHandle - The handle of the child controller to retrieve the name
+ of. This is an optional parameter that may be NULL. It
+ will be NULL for device drivers. It will also be NULL
+ for a bus drivers that wish to retrieve the name of the
+ bus controller. It will not be NULL for a bus driver
+ that wishes to retrieve the name of a child controller.
+ Language - A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that that the caller is requesting, and it must match one
+ of the languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up to the
+ driver writer.
+ ControllerName - A pointer to the Unicode string to return. This Unicode
+ string is the name of the controller specified by
+ ControllerHandle and ChildHandle in the language specified
+ by Language from the point of view of the driver specified
+ by This.
+
+ Returns:
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMass.h b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMass.h new file mode 100644 index 0000000..c48dee5 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMass.h @@ -0,0 +1,149 @@ +/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ UsbMass.h
+
+Abstract:
+
+ Defination for the USB mass storage class driver. The USB mass storage
+ class is specified in two layers: the bottom layer is the transportation
+ protocol. The top layer is the command set. The transportation layer
+ provides the transportation of the command, data and result. The command
+ set defines what the command, data and result. The Bulk-Only-Transport and
+ Control/Bulk/Interrupt transport are two transportation protocol. USB mass
+ storage class adopts various industrial standard as its command set.
+
+Revision History
+
+
+**/
+
+#ifndef _EFI_USBMASS_H_
+#define _EFI_USBMASS_H_
+
+//
+// The package level header files this module uses
+//
+#include <PiDxe.h>
+//
+// The protocols, PPI and GUID defintions for this module
+//
+#include <Protocol/BlockIo.h>
+#include <Protocol/UsbIo.h>
+//
+// The Library classes this module consumes
+//
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#define USB_IS_IN_ENDPOINT(EndPointAddr) (((EndPointAddr) & 0x80) == 0x80)
+#define USB_IS_OUT_ENDPOINT(EndPointAddr) (((EndPointAddr) & 0x80) == 0)
+#define USB_IS_BULK_ENDPOINT(Attribute) (((Attribute) & 0x03) == 0x02)
+#define USB_IS_INTERRUPT_ENDPOINT(Attribute) (((Attribute) & 0x03) == 0x03)
+#define USB_IS_ERROR(Result, Error) (((Result) & (Error)) != 0)
+
+enum {
+ //
+ // Usb mass storage class code
+ //
+ USB_MASS_STORE_CLASS = 0x08,
+
+ //
+ // Usb mass storage subclass code, specify the command set used.
+ //
+ USB_MASS_STORE_RBC = 0x01, // Reduced Block Commands
+ USB_MASS_STORE_8020I = 0x02, // SFF-8020i, typically a CD/DVD device
+ USB_MASS_STORE_QIC = 0x03, // Typically a tape device
+ USB_MASS_STORE_UFI = 0x04, // Typically a floopy disk driver device
+ USB_MASS_STORE_8070I = 0x05, // SFF-8070i, typically a floppy disk driver device.
+ USB_MASS_STORE_SCSI = 0x06, // SCSI transparent command set
+
+ //
+ // Usb mass storage protocol code, specify the transport protocol
+ //
+ USB_MASS_STORE_CBI0 = 0x00, // CBI protocol with command completion interrupt
+ USB_MASS_STORE_CBI1 = 0x01, // CBI protocol without command completion interrupt
+ USB_MASS_STORE_BOT = 0x50, // Bulk-Only Transport
+
+ USB_MASS_STALL_1_MS = 1000,
+ USB_MASS_STALL_1_S = 1000 * USB_MASS_STALL_1_MS,
+
+ USB_MASS_CMD_SUCCESS = 0,
+ USB_MASS_CMD_FAIL,
+ USB_MASS_CMD_PERSISTENT,
+};
+
+typedef
+EFI_STATUS
+(*USB_MASS_INIT_TRANSPORT) (
+ IN EFI_USB_IO_PROTOCOL *Usb,
+ IN EFI_HANDLE Controller,
+ OUT VOID **Context OPTIONAL
+ );
+
+typedef
+EFI_STATUS
+(*USB_MASS_EXEC_COMMAND) (
+ IN VOID *Context,
+ IN VOID *Cmd,
+ IN UINT8 CmdLen,
+ IN EFI_USB_DATA_DIRECTION DataDir,
+ IN VOID *Data,
+ IN UINT32 DataLen,
+ IN UINT32 Timeout,
+ OUT UINT32 *CmdStatus
+ );
+
+typedef
+EFI_STATUS
+(*USB_MASS_RESET) (
+ IN VOID *Context,
+ IN BOOLEAN ExtendedVerification
+ );
+
+typedef
+EFI_STATUS
+(*USB_MASS_FINI) (
+ IN VOID *Context
+ );
+
+//
+// This structure contains information necessary to select the
+// proper transport protocol. The mass storage class defines
+// two transport protocols. One is the CBI, and the other is BOT.
+// CBI is being obseleted. The design is made modular by this
+// structure so that the CBI protocol can be easily removed when
+// it is no longer necessary.
+//
+typedef struct {
+ UINT8 Protocol;
+ USB_MASS_INIT_TRANSPORT Init; // Initialize the mass storage transport protocol
+ USB_MASS_EXEC_COMMAND ExecCommand; // Transport command to the device then get result
+ USB_MASS_RESET Reset; // Reset the device
+ USB_MASS_FINI Fini; // Clean up the resources.
+} USB_MASS_TRANSPORT;
+
+
+EFI_STATUS
+UsbClearEndpointStall (
+ IN EFI_USB_IO_PROTOCOL *UsbIo,
+ IN UINT8 EndpointAddress
+ );
+
+extern UINTN mUsbMscInfo;
+extern UINTN mUsbMscError;
+#endif
diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.c b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.c new file mode 100644 index 0000000..980f8b2 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.c @@ -0,0 +1,886 @@ +/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ UsbMassBoot.c
+
+Abstract:
+
+ This file implement the command set of "USB Mass Storage Specification
+ for Bootability".
+
+Revision History
+
+
+**/
+
+#include "UsbMassImpl.h"
+
+
+/**
+ Read an UINT32 from the buffer to avoid byte alignment problems, then
+ convert that to the little endia. The USB mass storage bootability spec
+ use big endia
+
+ @param Buf The buffer contains the first byte of the UINT32
+ in big endia.
+
+ @return The UINT32 value read from the buffer in little endia.
+
+**/
+STATIC
+UINT32
+UsbBootGetUint32 (
+ IN UINT8 *Buf
+ )
+{
+ UINT32 Value;
+
+ CopyMem (&Value, Buf, sizeof (UINT32));
+ return USB_BOOT_SWAP32 (Value);
+}
+
+
+/**
+ Put an UINT32 in little endia to the buffer. The data is converted to
+ big endia before writing.
+
+ @param Buf The buffer to write data to
+ @param Data32 The data to write.
+
+ @return None
+
+**/
+STATIC
+VOID
+UsbBootPutUint32 (
+ IN UINT8 *Buf,
+ IN UINT32 Data32
+ )
+{
+ Data32 = USB_BOOT_SWAP32 (Data32);
+ CopyMem (Buf, &Data32, sizeof (UINT32));
+}
+
+
+/**
+ Put an UINT16 in little endia to the buffer. The data is converted to
+ big endia before writing.
+
+ @param Buf The buffer to write data to
+ @param Data16 The data to write
+
+ @return None
+
+**/
+STATIC
+VOID
+UsbBootPutUint16 (
+ IN UINT8 *Buf,
+ IN UINT16 Data16
+ )
+{
+ Data16 = USB_BOOT_SWAP16 (Data16);
+ CopyMem (Buf, &Data16, sizeof (UINT16));
+}
+
+
+/**
+ Request sense information via sending Request Sense
+ Packet Command.
+
+ @param UsbMass The device to be requested sense data
+
+ @retval EFI_DEVICE_ERROR Hardware error
+ @retval EFI_SUCCESS Success
+
+**/
+EFI_STATUS
+UsbBootRequestSense (
+ IN USB_MASS_DEVICE *UsbMass
+ )
+{
+ USB_BOOT_REQUEST_SENSE_CMD SenseCmd;
+ USB_BOOT_REQUEST_SENSE_DATA SenseData;
+ EFI_BLOCK_IO_MEDIA *Media;
+ USB_MASS_TRANSPORT *Transport;
+ EFI_STATUS Status;
+ UINT32 CmdResult;
+
+ Transport = UsbMass->Transport;
+
+ //
+ // Request the sense data from the device if command failed
+ //
+ ZeroMem (&SenseCmd, sizeof (USB_BOOT_REQUEST_SENSE_CMD));
+ ZeroMem (&SenseData, sizeof (USB_BOOT_REQUEST_SENSE_DATA));
+
+ SenseCmd.OpCode = USB_BOOT_REQUEST_SENSE_OPCODE;
+ SenseCmd.Lun = USB_BOOT_LUN (UsbMass->Lun);
+ SenseCmd.AllocLen = sizeof (USB_BOOT_REQUEST_SENSE_DATA);
+
+ Status = Transport->ExecCommand (
+ UsbMass->Context,
+ &SenseCmd,
+ sizeof (USB_BOOT_REQUEST_SENSE_CMD),
+ EfiUsbDataIn,
+ &SenseData,
+ sizeof (USB_BOOT_REQUEST_SENSE_DATA),
+ USB_BOOT_GENERAL_CMD_TIMEOUT,
+ &CmdResult
+ );
+ if (EFI_ERROR (Status) || CmdResult != USB_MASS_CMD_SUCCESS) {
+ DEBUG ((mUsbMscError, "UsbBootRequestSense: (%r) CmdResult=0x%x\n", Status, CmdResult));
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Interpret the sense data and update the media status if necessary.
+ //
+ Media = &UsbMass->BlockIoMedia;
+
+ switch (USB_BOOT_SENSE_KEY (SenseData.SenseKey)) {
+
+ case USB_BOOT_SENSE_NO_SENSE:
+ case USB_BOOT_SENSE_RECOVERED:
+ //
+ // Suppose hardware can handle this case, and recover later by itself
+ //
+ Status = EFI_NOT_READY;
+ break;
+
+ case USB_BOOT_SENSE_NOT_READY:
+ switch (SenseData.ASC) {
+ case USB_BOOT_ASC_NO_MEDIA:
+ Status = EFI_NO_MEDIA;
+ Media->MediaPresent = FALSE;
+ break;
+
+ case USB_BOOT_ASC_MEDIA_UPSIDE_DOWN:
+ Status = EFI_DEVICE_ERROR;
+ Media->MediaPresent = FALSE;
+ break;
+
+ case USB_BOOT_ASC_NOT_READY:
+ if (SenseData.ASCQ == USB_BOOT_ASCQ_IN_PROGRESS ||
+ SenseData.ASCQ == USB_BOOT_ASCQ_DEVICE_BUSY) {
+ //
+ // Regular timeout, and need retry once more
+ //
+ DEBUG ((mUsbMscInfo, "UsbBootRequestSense: Not ready and need retry once more\n"));
+ Status = EFI_NOT_READY;
+ }
+ }
+ break;
+
+ case USB_BOOT_SENSE_ILLEGAL_REQUEST:
+ Status = EFI_INVALID_PARAMETER;
+ break;
+
+ case USB_BOOT_SENSE_UNIT_ATTENTION:
+ Status = EFI_DEVICE_ERROR;
+ if (SenseData.ASC == USB_BOOT_ASC_MEDIA_CHANGE) {
+ Status = EFI_MEDIA_CHANGED;
+ UsbMass->BlockIoMedia.MediaId++;
+ }
+ break;
+
+ case USB_BOOT_SNESE_DATA_PROTECT:
+ Status = EFI_WRITE_PROTECTED;
+ UsbMass->BlockIoMedia.ReadOnly = TRUE;
+ break;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ break;
+ }
+
+ DEBUG ((mUsbMscInfo, "UsbBootRequestSense: (%r) with sense key %x/%x/%x\n",
+ Status,
+ USB_BOOT_SENSE_KEY (SenseData.SenseKey),
+ SenseData.ASC,
+ SenseData.ASCQ
+ ));
+
+ return Status;
+}
+
+
+/**
+ Execute the USB mass storage bootability commands. If execution
+ failed, retrieve the error by REQUEST_SENSE then update the device's
+ status, such as ReadyOnly.
+
+ @param UsbMass The device to issue commands to
+ @param Cmd The command to execute
+ @param CmdLen The length of the command
+ @param DataDir The direction of data transfer
+ @param Data The buffer to hold the data
+ @param DataLen The length of expected data
+ @param Timeout The timeout used to transfer
+
+ @retval EFI_SUCCESS The command is excuted OK
+ @retval EFI_DEVICE_ERROR Failed to request sense
+ @retval EFI_INVALID_PARAMETER The command has some invalid parameters
+ @retval EFI_WRITE_PROTECTED The device is write protected
+ @retval EFI_MEDIA_CHANGED The device media has been changed
+
+**/
+STATIC
+EFI_STATUS
+UsbBootExecCmd (
+ IN USB_MASS_DEVICE *UsbMass,
+ IN VOID *Cmd,
+ IN UINT8 CmdLen,
+ IN EFI_USB_DATA_DIRECTION DataDir,
+ IN VOID *Data,
+ IN UINT32 DataLen,
+ IN UINT32 Timeout
+ )
+{
+ USB_MASS_TRANSPORT *Transport;
+ EFI_STATUS Status;
+ UINT32 CmdResult;
+
+ Transport = UsbMass->Transport;
+ Status = Transport->ExecCommand (
+ UsbMass->Context,
+ Cmd,
+ CmdLen,
+ DataDir,
+ Data,
+ DataLen,
+ Timeout,
+ &CmdResult
+ );
+ //
+ // ExecCommand return success and get the right CmdResult means
+ // the commnad transfer is OK.
+ //
+ if ((CmdResult == USB_MASS_CMD_SUCCESS) && !EFI_ERROR(Status)) {
+ return EFI_SUCCESS;
+ }
+
+ return UsbBootRequestSense (UsbMass);
+}
+
+
+/**
+ Execute the USB mass storage bootability commands. If execution
+ failed, retrieve the error by REQUEST_SENSE then update the device's
+ status, such as ReadyOnly.
+
+ @param UsbMass The device to issue commands to
+ @param Cmd The command to execute
+ @param CmdLen The length of the command
+ @param DataDir The direction of data transfer
+ @param Data The buffer to hold the data
+ @param DataLen The length of expected data
+
+ @retval EFI_SUCCESS The command is excuted OK
+ @retval EFI_DEVICE_ERROR Failed to request sense
+ @retval EFI_INVALID_PARAMETER The command has some invalid parameters
+ @retval EFI_WRITE_PROTECTED The device is write protected
+ @retval EFI_MEDIA_CHANGED The device media has been changed
+
+**/
+STATIC
+EFI_STATUS
+UsbBootExecCmdWithRetry (
+ IN USB_MASS_DEVICE *UsbMass,
+ IN VOID *Cmd,
+ IN UINT8 CmdLen,
+ IN EFI_USB_DATA_DIRECTION DataDir,
+ IN VOID *Data,
+ IN UINT32 DataLen,
+ IN UINT32 Timeout
+ )
+{
+ EFI_STATUS Status;
+ INT16 Index;
+
+ //
+ // If the device isn't ready, wait some time. If the device is ready,
+ // retry the command again.
+ //
+ Status = EFI_SUCCESS;
+
+ for (Index = 0; Index < USB_BOOT_COMMAND_RETRY; Index++) {
+ //
+ // Execute the command with an increasingly larger timeout value.
+ //
+ Status = UsbBootExecCmd (
+ UsbMass,
+ Cmd,
+ CmdLen,
+ DataDir,
+ Data,
+ DataLen,
+ Timeout * (Index + 1)
+ );
+ if (Status == EFI_SUCCESS ||
+ Status == EFI_MEDIA_CHANGED) {
+ break;
+ }
+ //
+ // Need retry once more, so reset index
+ //
+ if (Status == EFI_NOT_READY) {
+ Index = 0;
+ }
+ }
+
+ return Status;
+}
+
+
+
+/**
+ Use the TEST UNIT READY command to check whether it is ready.
+ If it is ready, update the parameters.
+
+ @param UsbMass The device to test
+
+ @retval EFI_SUCCESS The device is ready and parameters are updated.
+ @retval Others Device not ready.
+
+**/
+EFI_STATUS
+UsbBootIsUnitReady (
+ IN USB_MASS_DEVICE *UsbMass
+ )
+{
+ USB_BOOT_TEST_UNIT_READY_CMD TestCmd;
+
+ ZeroMem (&TestCmd, sizeof (USB_BOOT_TEST_UNIT_READY_CMD));
+
+ TestCmd.OpCode = USB_BOOT_TEST_UNIT_READY_OPCODE;
+ TestCmd.Lun = USB_BOOT_LUN (UsbMass->Lun);
+
+ return UsbBootExecCmdWithRetry (
+ UsbMass,
+ &TestCmd,
+ sizeof (USB_BOOT_TEST_UNIT_READY_CMD),
+ EfiUsbNoData,
+ NULL,
+ 0,
+ USB_BOOT_GENERAL_CMD_TIMEOUT
+ );
+}
+
+
+/**
+ Inquiry Command requests that information regrarding parameters of
+ the Device be sent to the Host.
+
+ @param UsbMass The device to inquiry.
+
+ @retval EFI_SUCCESS The device is ready and parameters are updated.
+ @retval Others Device not ready.
+
+**/
+EFI_STATUS
+UsbBootInquiry (
+ IN USB_MASS_DEVICE *UsbMass
+ )
+{
+ USB_BOOT_INQUIRY_CMD InquiryCmd;
+ USB_BOOT_INQUIRY_DATA InquiryData;
+ EFI_BLOCK_IO_MEDIA *Media;
+ EFI_STATUS Status;
+
+ Media = &(UsbMass->BlockIoMedia);
+
+ //
+ // Use the Inquiry command to get the RemovableMedia setting.
+ //
+ ZeroMem (&InquiryCmd, sizeof (USB_BOOT_INQUIRY_CMD));
+ ZeroMem (&InquiryData, sizeof (USB_BOOT_INQUIRY_DATA));
+
+ InquiryCmd.OpCode = USB_BOOT_INQUIRY_OPCODE;
+ InquiryCmd.Lun = USB_BOOT_LUN (UsbMass->Lun);
+ InquiryCmd.AllocLen = sizeof (InquiryData);
+
+ Status = UsbBootExecCmdWithRetry (
+ UsbMass,
+ &InquiryCmd,
+ sizeof (USB_BOOT_INQUIRY_CMD),
+ EfiUsbDataIn,
+ &InquiryData,
+ sizeof (USB_BOOT_INQUIRY_DATA),
+ USB_BOOT_INQUIRY_CMD_TIMEOUT
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ UsbMass->Pdt = USB_BOOT_PDT (InquiryData.Pdt);
+ Media->RemovableMedia = USB_BOOT_REMOVABLE (InquiryData.Removable);
+ //
+ // Default value 512 Bytes, in case no media present at first time
+ //
+ Media->BlockSize = 0x0200;
+
+ return Status;
+}
+
+
+/**
+ Get the capacity of the USB mass storage media, including
+ the presentation, block size, and last block number. This
+ function is used to get the disk parameters at the start if
+ it is a non-removable media or to detect the media if it is
+ removable.
+
+ @param UsbMass The device to retireve disk gemotric.
+
+ @retval EFI_SUCCESS The disk gemotric is successfully retrieved.
+ @retval EFI_DEVICE_ERROR Something is inconsistent with the disk gemotric.
+
+**/
+EFI_STATUS
+UsbBootReadCapacity (
+ IN USB_MASS_DEVICE *UsbMass
+ )
+{
+ USB_BOOT_READ_CAPACITY_CMD CapacityCmd;
+ USB_BOOT_READ_CAPACITY_DATA CapacityData;
+ EFI_BLOCK_IO_MEDIA *Media;
+ EFI_STATUS Status;
+
+ Media = &UsbMass->BlockIoMedia;
+
+ //
+ // Use the READ CAPACITY command to get the block length and last blockno
+ //
+ ZeroMem (&CapacityCmd, sizeof (USB_BOOT_READ_CAPACITY_CMD));
+ ZeroMem (&CapacityData, sizeof (USB_BOOT_READ_CAPACITY_DATA));
+
+ CapacityCmd.OpCode = USB_BOOT_READ_CAPACITY_OPCODE;
+ CapacityCmd.Lun = USB_BOOT_LUN (UsbMass->Lun);
+
+ Status = UsbBootExecCmdWithRetry (
+ UsbMass,
+ &CapacityCmd,
+ sizeof (USB_BOOT_READ_CAPACITY_CMD),
+ EfiUsbDataIn,
+ &CapacityData,
+ sizeof (USB_BOOT_READ_CAPACITY_DATA),
+ USB_BOOT_GENERAL_CMD_TIMEOUT
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Media->MediaPresent = TRUE;
+ Media->LastBlock = UsbBootGetUint32 (CapacityData.LastLba);
+ Media->BlockSize = UsbBootGetUint32 (CapacityData.BlockLen);
+
+ DEBUG ((mUsbMscInfo, "UsbBootReadCapacity Success LBA=%d BlockSize=%d\n",
+ Media->LastBlock, Media->BlockSize));
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Retrieves mode sense information via sending Mode Sense
+ Packet Command.
+
+ @param UsbMass The USB_FLOPPY_DEV instance.
+
+ @retval EFI_DEVICE_ERROR Hardware error
+ @retval EFI_SUCCESS Success
+
+**/
+EFI_STATUS
+UsbBootModeSense (
+ IN USB_MASS_DEVICE *UsbMass
+ )
+{
+ EFI_STATUS Status;
+ USB_BOOT_MODE_SENSE_CMD ModeSenseCmd;
+ USB_BOOT_MODE_PARA_HEADER ModeParaHeader;
+ UINT8 CommandSet;
+
+ ZeroMem (&ModeSenseCmd, sizeof (USB_BOOT_MODE_SENSE_CMD));
+ ZeroMem (&ModeParaHeader, sizeof (USB_BOOT_MODE_PARA_HEADER));
+
+ //
+ // overuse Context Pointer, the first field of Bot or Cbi is EFI_USB_INTERFACE_DESCRIPTOR
+ //
+ CommandSet = ((EFI_USB_INTERFACE_DESCRIPTOR *) (UsbMass->Context))->InterfaceSubClass;
+
+ if (CommandSet == USB_MASS_STORE_SCSI) {
+ //
+ // Not UFI Command Set, no ModeSense Command
+ //
+ return EFI_SUCCESS;
+ }
+
+ ModeSenseCmd.OpCode = USB_BOOT_MODE_SENSE10_OPCODE;
+ ModeSenseCmd.PageCode = 0x3f;
+ ModeSenseCmd.ParaListLenLsb = (UINT8) sizeof (USB_BOOT_MODE_PARA_HEADER);
+
+ Status = UsbBootExecCmdWithRetry (
+ UsbMass,
+ &ModeSenseCmd,
+ sizeof (USB_BOOT_MODE_SENSE_CMD),
+ EfiUsbDataIn,
+ &ModeParaHeader,
+ sizeof (USB_BOOT_MODE_PARA_HEADER),
+ USB_BOOT_GENERAL_CMD_TIMEOUT
+ );
+ //
+ // Did nothing with the Header here
+ // But probably should
+ //
+
+ return Status;
+}
+
+
+/**
+ Get the parameters for the USB mass storage media, including
+ the RemovableMedia, block size, and last block number. This
+ function is used both to initialize the media during the
+ DriverBindingStart and to re-initialize it when the media is
+ changed. Althought the RemoveableMedia is unlikely to change,
+ I include it here.
+
+ @param UsbMass The device to retireve disk gemotric.
+
+ @retval EFI_SUCCESS The disk gemotric is successfully retrieved.
+ @retval EFI_DEVICE_ERROR Something is inconsistent with the disk gemotric.
+
+**/
+EFI_STATUS
+UsbBootGetParams (
+ IN USB_MASS_DEVICE *UsbMass
+ )
+{
+ EFI_BLOCK_IO_MEDIA *Media;
+ EFI_STATUS Status;
+
+ Status = UsbBootInquiry (UsbMass);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((mUsbMscError, "UsbBootGetParams: UsbBootInquiry (%r)\n", Status));
+ return Status;
+ }
+
+ Media = &(UsbMass->BlockIoMedia);
+ //
+ // Don't use the Removable bit in inquirydata to test whether the media
+ // is removable because many flash disks wrongly set this bit.
+ //
+ if ((UsbMass->Pdt == USB_PDT_CDROM) || (UsbMass->Pdt == USB_PDT_OPTICAL)) {
+ //
+ // CD-Rom or Optical device
+ //
+ UsbMass->OpticalStorage = TRUE;
+ //
+ // Default value 2048 Bytes, in case no media present at first time
+ //
+ Media->BlockSize = 0x0800;
+ } else {
+ //
+ // Non CD-Rom device need ModeSenseCmd between InquiryCmd and ReadCapacityCmd
+ //
+ Status = UsbBootModeSense (UsbMass);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((mUsbMscError, "UsbBootGetParams: UsbBootModeSense (%r)\n", Status));
+ return Status;
+ }
+ }
+
+ return UsbBootReadCapacity (UsbMass);
+}
+
+
+/**
+ Detect whether the removable media is present and whether it has changed.
+ The Non-removable media doesn't need it.
+
+ @param UsbMass The device to retireve disk gemotric.
+
+ @retval EFI_SUCCESS The disk gemotric is successfully retrieved.
+ @retval EFI_DEVICE_ERROR Something is inconsistent with the disk gemotric.
+
+**/
+EFI_STATUS
+UsbBootDetectMedia (
+ IN USB_MASS_DEVICE *UsbMass
+ )
+{
+ EFI_BLOCK_IO_MEDIA OldMedia;
+ EFI_BLOCK_IO_MEDIA *Media;
+ EFI_STATUS Status;
+
+ Media = &UsbMass->BlockIoMedia;
+ OldMedia = UsbMass->BlockIoMedia;
+
+ //
+ // First test whether the device is ready and get status
+ // If media changed or ready, need read the device's capacity
+ //
+ Status = UsbBootIsUnitReady (UsbMass);
+ if ((Status == EFI_SUCCESS && Media->MediaPresent) ||
+ (Status == EFI_MEDIA_CHANGED)) {
+ if ((UsbMass->Pdt != USB_PDT_CDROM) &&
+ (UsbMass->Pdt != USB_PDT_OPTICAL)) {
+ //
+ // Non CD-Rom device need ModeSenseCmd between InquiryCmd and ReadCapacityCmd
+ //
+ UsbBootModeSense (UsbMass);
+ }
+ DEBUG ((mUsbMscInfo, "UsbBootDetectMedia: Need Read Capacity\n"));
+ Status = UsbBootReadCapacity (UsbMass);
+ }
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Detect whether it is necessary to reinstall the BlockIO
+ //
+ if ((Media->MediaId != OldMedia.MediaId) ||
+ (Media->MediaPresent != OldMedia.MediaPresent) ||
+ (Media->ReadOnly != OldMedia.ReadOnly) ||
+ (Media->BlockSize != OldMedia.BlockSize) ||
+ (Media->LastBlock != OldMedia.LastBlock)) {
+ DEBUG ((mUsbMscInfo, "UsbBootDetectMedia: Need reinstall BlockIoProtocol\n"));
+ Media->MediaId++;
+ gBS->ReinstallProtocolInterface (
+ UsbMass->Controller,
+ &gEfiBlockIoProtocolGuid,
+ &UsbMass->BlockIo,
+ &UsbMass->BlockIo
+ );
+ //
+ // Check whether media present or media changed or write protected
+ //
+ if (Media->MediaPresent == FALSE) {
+ Status = EFI_NO_MEDIA;
+ }
+ if (Media->MediaId != OldMedia.MediaId) {
+ Status = EFI_MEDIA_CHANGED;
+ }
+ if (Media->ReadOnly != OldMedia.ReadOnly) {
+ Status = EFI_WRITE_PROTECTED;
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ Read some blocks from the device.
+
+ @param UsbMass The USB mass storage device to read from
+ @param Lba The start block number
+ @param TotalBlock Total block number to read
+ @param Buffer The buffer to read to
+
+ @retval EFI_SUCCESS Data are read into the buffer
+ @retval Others Failed to read all the data
+
+**/
+EFI_STATUS
+UsbBootReadBlocks (
+ IN USB_MASS_DEVICE *UsbMass,
+ IN UINT32 Lba,
+ IN UINTN TotalBlock,
+ OUT UINT8 *Buffer
+ )
+{
+ USB_BOOT_READ10_CMD ReadCmd;
+ EFI_STATUS Status;
+ UINT16 Count;
+ UINT32 BlockSize;
+ UINT32 ByteSize;
+ UINT32 Timeout;
+
+ BlockSize = UsbMass->BlockIoMedia.BlockSize;
+ Status = EFI_SUCCESS;
+
+ while (TotalBlock > 0) {
+ //
+ // Split the total blocks into smaller pieces to ease the pressure
+ // on the device. We must split the total block because the READ10
+ // command only has 16 bit transfer length (in the unit of block).
+ //
+ Count = (UINT16)((TotalBlock < USB_BOOT_IO_BLOCKS) ? TotalBlock : USB_BOOT_IO_BLOCKS);
+ ByteSize = (UINT32)Count * BlockSize;
+
+ //
+ // Optical device need longer timeout than other device
+ //
+ if (UsbMass->OpticalStorage == TRUE) {
+ Timeout = (UINT32)Count * USB_BOOT_OPTICAL_BLOCK_TIMEOUT;
+ } else {
+ Timeout = (UINT32)Count * USB_BOOT_GENERAL_BLOCK_TIMEOUT;
+ }
+
+ //
+ // Fill in the command then execute
+ //
+ ZeroMem (&ReadCmd, sizeof (USB_BOOT_READ10_CMD));
+
+ ReadCmd.OpCode = USB_BOOT_READ10_OPCODE;
+ ReadCmd.Lun = USB_BOOT_LUN (UsbMass->Lun);
+ UsbBootPutUint32 (ReadCmd.Lba, Lba);
+ UsbBootPutUint16 (ReadCmd.TransferLen, Count);
+
+ Status = UsbBootExecCmdWithRetry (
+ UsbMass,
+ &ReadCmd,
+ sizeof (USB_BOOT_READ10_CMD),
+ EfiUsbDataIn,
+ Buffer,
+ ByteSize,
+ Timeout
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Lba += Count;
+ Buffer += Count * BlockSize;
+ TotalBlock -= Count;
+ }
+
+ return Status;
+}
+
+
+/**
+ Write some blocks to the device.
+
+ @param UsbMass The USB mass storage device to write to
+ @param Lba The start block number
+ @param TotalBlock Total block number to write
+ @param Buffer The buffer to write to
+
+ @retval EFI_SUCCESS Data are written into the buffer
+ @retval Others Failed to write all the data
+
+**/
+EFI_STATUS
+UsbBootWriteBlocks (
+ IN USB_MASS_DEVICE *UsbMass,
+ IN UINT32 Lba,
+ IN UINTN TotalBlock,
+ OUT UINT8 *Buffer
+ )
+{
+ USB_BOOT_WRITE10_CMD WriteCmd;
+ EFI_STATUS Status;
+ UINT16 Count;
+ UINT32 BlockSize;
+ UINT32 ByteSize;
+ UINT32 Timeout;
+
+ BlockSize = UsbMass->BlockIoMedia.BlockSize;
+ Status = EFI_SUCCESS;
+
+ while (TotalBlock > 0) {
+ //
+ // Split the total blocks into smaller pieces to ease the pressure
+ // on the device. We must split the total block because the WRITE10
+ // command only has 16 bit transfer length (in the unit of block).
+ //
+ Count = (UINT16)((TotalBlock < USB_BOOT_IO_BLOCKS) ? TotalBlock : USB_BOOT_IO_BLOCKS);
+ ByteSize = (UINT32)Count * BlockSize;
+
+ //
+ // Optical device need longer timeout than other device
+ //
+ if (UsbMass->OpticalStorage == TRUE) {
+ Timeout = (UINT32)Count * USB_BOOT_OPTICAL_BLOCK_TIMEOUT;
+ } else {
+ Timeout = (UINT32)Count * USB_BOOT_GENERAL_BLOCK_TIMEOUT;
+ }
+
+ //
+ // Fill in the write10 command block
+ //
+ ZeroMem (&WriteCmd, sizeof (USB_BOOT_WRITE10_CMD));
+
+ WriteCmd.OpCode = USB_BOOT_WRITE10_OPCODE;
+ WriteCmd.Lun = USB_BOOT_LUN (UsbMass->Lun);
+ UsbBootPutUint32 (WriteCmd.Lba, Lba);
+ UsbBootPutUint16 (WriteCmd.TransferLen, Count);
+
+ Status = UsbBootExecCmdWithRetry (
+ UsbMass,
+ &WriteCmd,
+ sizeof (USB_BOOT_WRITE10_CMD),
+ EfiUsbDataOut,
+ Buffer,
+ ByteSize,
+ Timeout
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Lba += Count;
+ Buffer += Count * BlockSize;
+ TotalBlock -= Count;
+ }
+
+ return Status;
+}
+
+
+/**
+ Use the USB clear feature control transfer to clear the endpoint
+ stall condition.
+
+ @param UsbIo The USB IO protocol to use
+ @param EndpointAddr The endpoint to clear stall for
+
+ @retval EFI_SUCCESS The endpoint stall condtion is clear
+ @retval Others Failed to clear the endpoint stall condtion
+
+**/
+EFI_STATUS
+UsbClearEndpointStall (
+ IN EFI_USB_IO_PROTOCOL *UsbIo,
+ IN UINT8 EndpointAddr
+ )
+{
+ EFI_USB_DEVICE_REQUEST Request;
+ EFI_STATUS Status;
+ UINT32 CmdResult;
+ UINT32 Timeout;
+
+ Request.RequestType = 0x02;
+ Request.Request = USB_REQ_CLEAR_FEATURE;
+ Request.Value = USB_FEATURE_ENDPOINT_HALT;
+ Request.Index = EndpointAddr;
+ Request.Length = 0;
+ Timeout = USB_BOOT_GENERAL_CMD_TIMEOUT / USB_MASS_STALL_1_MS;
+
+ Status = UsbIo->UsbControlTransfer (
+ UsbIo,
+ &Request,
+ EfiUsbNoData,
+ Timeout,
+ NULL,
+ 0,
+ &CmdResult
+ );
+
+ return Status;
+}
diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.h b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.h new file mode 100644 index 0000000..cee70be --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.h @@ -0,0 +1,268 @@ +/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ UsbMassBoot.h
+
+Abstract:
+
+ The definition of command and data of the USB mass storage for
+ bootability command set.
+
+Revision History
+
+
+**/
+
+#ifndef _EFI_USB_MASS_BOOT_H_
+#define _EFI_USB_MASS_BOOT_H_
+
+enum {
+ //
+ // The opcodes of various usb boot commands:
+ // INQUIRY/REQUEST_SENSE are "No Timeout Commands" as specified
+ // by MMC command set. Others are "Group 1 Timeout Commands". That
+ // is they should be retried if driver is ready.
+ // We can't use the Peripheral Device Type in Inquiry data to
+ // determine the timeout used. For example, both floppy and flash
+ // are likely set their PDT to 0, or Direct Access Device.
+ //
+ USB_BOOT_INQUIRY_OPCODE = 0x12,
+ USB_BOOT_REQUEST_SENSE_OPCODE = 0x03,
+
+ USB_BOOT_MODE_SENSE10_OPCODE = 0x5a,
+ USB_BOOT_READ_CAPACITY_OPCODE = 0x25,
+ USB_BOOT_TEST_UNIT_READY_OPCODE = 0x00,
+ USB_BOOT_READ10_OPCODE = 0x28,
+ USB_BOOT_WRITE10_OPCODE = 0x2a,
+
+ //
+ // The Sense Key part of the sense data. Sense data has three levels:
+ // Sense key, Additional Sense Code and Additional Sense Code Qualifier
+ //
+ USB_BOOT_SENSE_NO_SENSE = 0x00, // No sense key
+ USB_BOOT_SENSE_RECOVERED = 0x01, // Last command succeed with recovery actions
+ USB_BOOT_SENSE_NOT_READY = 0x02, // Device not ready
+ USB_BOOT_SNESE_MEDIUM_ERROR = 0X03, // Failed probably because flaw in the media
+ USB_BOOT_SENSE_HARDWARE_ERROR = 0X04, // Non-recoverable hardware failure
+ USB_BOOT_SENSE_ILLEGAL_REQUEST = 0X05, // Illegal parameters in the request
+ USB_BOOT_SENSE_UNIT_ATTENTION = 0X06, // Removable medium may have been changed
+ USB_BOOT_SNESE_DATA_PROTECT = 0X07, // Write protected
+ USB_BOOT_SENSE_BLANK_CHECK = 0X08, // Blank/non-blank medium while reading/writing
+ USB_BOOT_SENSE_VENDOR = 0X09, // Vendor specific sense key
+ USB_BOOT_SENSE_ABORTED = 0X0B, // Command aborted by the device
+ USB_BOOT_SENSE_VOLUME_OVERFLOW = 0x0D, // Partition overflow
+ USB_BOOT_SENSE_MISCOMPARE = 0x0E, // Source data mis-match while verfying.
+
+ USB_BOOT_ASC_NOT_READY = 0x04,
+ USB_BOOT_ASC_MEDIA_UPSIDE_DOWN = 0x06,
+ USB_BOOT_ASC_NO_MEDIA = 0x3A,
+ USB_BOOT_ASC_MEDIA_CHANGE = 0x28,
+
+ USB_BOOT_ASCQ_IN_PROGRESS = 0x01,
+ USB_BOOT_ASCQ_DEVICE_BUSY = 0xFF,
+
+ //
+ // Other parameters
+ //
+ USB_BOOT_IO_BLOCKS = 64,
+
+ //
+ // Boot Retry times
+ //
+ USB_BOOT_COMMAND_RETRY = 5,
+ USB_BOOT_WAIT_RETRY = 5,
+
+ //
+ // Boot Stall time
+ //
+ USB_BOOT_UNIT_READY_STALL = 50 * USB_MASS_STALL_1_MS,
+
+ //
+ // Boot Transfer timeout
+ //
+ USB_BOOT_GENERAL_BLOCK_TIMEOUT = 200 * USB_MASS_STALL_1_MS,
+ USB_BOOT_OPTICAL_BLOCK_TIMEOUT = 1 * USB_MASS_STALL_1_S,
+ USB_BOOT_GENERAL_CMD_TIMEOUT = 1 * USB_MASS_STALL_1_S,
+ USB_BOOT_INQUIRY_CMD_TIMEOUT = 3 * USB_MASS_STALL_1_S,
+
+ //
+ // Supported PDT codes, or Peripheral Device Type
+ //
+ USB_PDT_DIRECT_ACCESS = 0x00, // Direct access device
+ USB_PDT_CDROM = 0x05, // CDROM
+ USB_PDT_OPTICAL = 0x07, // Non-CD optical disks
+ USB_PDT_SIMPLE_DIRECT = 0x0E, // Simplified direct access device
+};
+
+//
+// The required commands are INQUIRY, READ CAPACITY, TEST UNIT READY,
+// READ10, WRITE10, and REQUEST SENSE. The BLOCK_IO protocol uses LBA
+// so it isn't necessary to issue MODE SENSE / READ FORMAT CAPACITY
+// command to retrieve the disk gemotrics.
+//
+#pragma pack(1)
+typedef struct {
+ UINT8 OpCode;
+ UINT8 Lun; // Lun (high 3 bits)
+ UINT8 Reserved0[2];
+ UINT8 AllocLen;
+ UINT8 Reserved1;
+ UINT8 Pad[6];
+} USB_BOOT_INQUIRY_CMD;
+
+typedef struct {
+ UINT8 Pdt; // Peripheral Device Type (low 5 bits)
+ UINT8 Removable; // Removable Media (highest bit)
+ UINT8 Reserved0[2];
+ UINT8 AddLen; // Additional length
+ UINT8 Reserved1[3];
+ UINT8 VendorID[8];
+ UINT8 ProductID[16];
+ UINT8 ProductRevision[4];
+} USB_BOOT_INQUIRY_DATA;
+
+typedef struct {
+ UINT8 OpCode;
+ UINT8 Lun;
+ UINT8 Reserved0[8];
+ UINT8 Pad[2];
+} USB_BOOT_READ_CAPACITY_CMD;
+
+typedef struct {
+ UINT8 LastLba[4];
+ UINT8 BlockLen[4];
+} USB_BOOT_READ_CAPACITY_DATA;
+
+typedef struct {
+ UINT8 OpCode;
+ UINT8 Lun;
+ UINT8 Reserved[4];
+ UINT8 Pad[6];
+} USB_BOOT_TEST_UNIT_READY_CMD;
+
+typedef struct {
+ UINT8 OpCode;
+ UINT8 Lun;
+ UINT8 PageCode;
+ UINT8 Reserved0[4];
+ UINT8 ParaListLenMsb;
+ UINT8 ParaListLenLsb;
+ UINT8 Reserved1;
+ UINT8 Pad[2];
+} USB_BOOT_MODE_SENSE_CMD;
+
+typedef struct {
+ UINT8 ModeDataLenMsb;
+ UINT8 ModeDataLenLsb;
+ UINT8 Reserved0[4];
+ UINT8 BlkDesLenMsb;
+ UINT8 BlkDesLenLsb;
+} USB_BOOT_MODE_PARA_HEADER;
+
+typedef struct {
+ UINT8 OpCode;
+ UINT8 Lun; // Lun (High 3 bits)
+ UINT8 Lba[4]; // Logical block address
+ UINT8 Reserved0;
+ UINT8 TransferLen[2]; // Transfer length
+ UINT8 Reserverd1;
+ UINT8 Pad[2];
+} USB_BOOT_READ10_CMD;
+
+typedef struct {
+ UINT8 OpCode;
+ UINT8 Lun;
+ UINT8 Lba[4];
+ UINT8 Reserved0;
+ UINT8 TransferLen[2];
+ UINT8 Reserverd1;
+ UINT8 Pad[2];
+} USB_BOOT_WRITE10_CMD;
+
+typedef struct {
+ UINT8 OpCode;
+ UINT8 Lun; // Lun (High 3 bits)
+ UINT8 Reserved0[2];
+ UINT8 AllocLen; // Allocation length
+ UINT8 Reserved1;
+ UINT8 Pad[6];
+} USB_BOOT_REQUEST_SENSE_CMD;
+
+typedef struct {
+ UINT8 ErrorCode;
+ UINT8 Reserved0;
+ UINT8 SenseKey; // Sense key (low 4 bits)
+ UINT8 Infor[4];
+ UINT8 AddLen; // Additional Sense length, 10
+ UINT8 Reserved1[4];
+ UINT8 ASC; // Additional Sense Code
+ UINT8 ASCQ; // Additional Sense Code Qualifier
+ UINT8 Reserverd2[4];
+} USB_BOOT_REQUEST_SENSE_DATA;
+#pragma pack()
+
+//
+// Convert a LUN number to that in the command
+//
+#define USB_BOOT_LUN(Lun) ((Lun) << 5)
+
+//
+// Get the removable, PDT, and sense key bits from the command data
+//
+#define USB_BOOT_REMOVABLE(RmbByte) (((RmbByte) & 0x80) != 0)
+#define USB_BOOT_PDT(Pdt) ((Pdt) & 0x1f)
+#define USB_BOOT_SENSE_KEY(Key) ((Key) & 0x0f)
+
+//
+// Swap the byte sequence of a UINT32. Intel CPU uses little endian
+// in UEFI environment, but USB boot uses big endian.
+//
+#define USB_BOOT_SWAP32(Data32) \
+ ((((Data32) & 0x000000ff) << 24) | (((Data32) & 0xff000000) >> 24) | \
+ (((Data32) & 0x0000ff00) << 8) | (((Data32) & 0x00ff0000) >> 8))
+
+#define USB_BOOT_SWAP16(Data16) \
+ ((((Data16) & 0x00ff) << 8) | (((Data16) & 0xff00) >> 8))
+
+EFI_STATUS
+UsbBootGetParams (
+ IN USB_MASS_DEVICE *UsbMass
+ );
+
+EFI_STATUS
+UsbBootIsUnitReady (
+ IN USB_MASS_DEVICE *UsbMass
+ );
+
+EFI_STATUS
+UsbBootDetectMedia (
+ IN USB_MASS_DEVICE *UsbMass
+ );
+
+EFI_STATUS
+UsbBootReadBlocks (
+ IN USB_MASS_DEVICE *UsbMass,
+ IN UINT32 Lba,
+ IN UINTN TotalBlock,
+ OUT UINT8 *Buffer
+ );
+
+EFI_STATUS
+UsbBootWriteBlocks (
+ IN USB_MASS_DEVICE *UsbMass,
+ IN UINT32 Lba,
+ IN UINTN TotalBlock,
+ OUT UINT8 *Buffer
+ );
+#endif
+
diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBot.c b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBot.c new file mode 100644 index 0000000..0d6cca1 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBot.c @@ -0,0 +1,561 @@ +/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ UsbMassBot.c
+
+Abstract:
+
+ Implementation of the USB mass storage Bulk-Only Transport protocol.
+
+Revision History
+
+
+**/
+
+#include "UsbMass.h"
+#include "UsbMassBot.h"
+
+UINTN mUsbBotInfo = DEBUG_INFO;
+UINTN mUsbBotError = DEBUG_ERROR;
+
+STATIC
+EFI_STATUS
+UsbBotResetDevice (
+ IN VOID *Context,
+ IN BOOLEAN ExtendedVerification
+ );
+
+
+/**
+ Initialize the USB mass storage class BOT transport protocol.
+ It will save its context which is a USB_BOT_PROTOCOL structure
+ in the Context if Context isn't NULL.
+
+ @param UsbIo The USB IO protocol to use
+ @param Controller The controller to init
+ @param Context The variable to save the context to
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory
+ @retval EFI_UNSUPPORTED The transport protocol doesn't support the device.
+ @retval EFI_SUCCESS The device is supported and protocol initialized.
+
+**/
+STATIC
+EFI_STATUS
+UsbBotInit (
+ IN EFI_USB_IO_PROTOCOL * UsbIo,
+ IN EFI_HANDLE Controller,
+ OUT VOID **Context OPTIONAL
+ )
+{
+ USB_BOT_PROTOCOL *UsbBot;
+ EFI_USB_INTERFACE_DESCRIPTOR *Interface;
+ EFI_USB_ENDPOINT_DESCRIPTOR EndPoint;
+ EFI_STATUS Status;
+ UINT8 Index;
+
+ //
+ // Allocate the BOT context, append two endpoint descriptors to it
+ //
+ UsbBot = AllocateZeroPool (
+ sizeof (USB_BOT_PROTOCOL) + 2 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR)
+ );
+ if (UsbBot == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ UsbBot->UsbIo = UsbIo;
+
+ //
+ // Get the interface descriptor and validate that it
+ // is a USB MSC BOT interface.
+ //
+ Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &UsbBot->Interface);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((mUsbBotError, "UsbBotInit: Get invalid BOT interface (%r)\n", Status));
+ goto ON_ERROR;
+ }
+
+ Interface = &UsbBot->Interface;
+
+ if (Interface->InterfaceProtocol != USB_MASS_STORE_BOT) {
+ Status = EFI_UNSUPPORTED;
+ goto ON_ERROR;
+ }
+
+ //
+ // Locate and save the first bulk-in and bulk-out endpoint
+ //
+ for (Index = 0; Index < Interface->NumEndpoints; Index++) {
+ Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &EndPoint);
+
+ if (EFI_ERROR (Status) || !USB_IS_BULK_ENDPOINT (EndPoint.Attributes)) {
+ continue;
+ }
+
+ if (USB_IS_IN_ENDPOINT (EndPoint.EndpointAddress) &&
+ (UsbBot->BulkInEndpoint == NULL)) {
+
+ UsbBot->BulkInEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbBot + 1);
+ *UsbBot->BulkInEndpoint = EndPoint;
+ }
+
+ if (USB_IS_OUT_ENDPOINT (EndPoint.EndpointAddress) &&
+ (UsbBot->BulkOutEndpoint == NULL)) {
+
+ UsbBot->BulkOutEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbBot + 1) + 1;
+ *UsbBot->BulkOutEndpoint = EndPoint;
+ }
+ }
+
+ if ((UsbBot->BulkInEndpoint == NULL) || (UsbBot->BulkOutEndpoint == NULL)) {
+ DEBUG ((mUsbBotError, "UsbBotInit: In/Out Endpoint invalid\n"));
+ Status = EFI_UNSUPPORTED;
+ goto ON_ERROR;
+ }
+
+ //
+ // The USB BOT protocol uses dCBWTag to match the CBW and CSW.
+ //
+ UsbBot->CbwTag = 0x01;
+
+ if (Context != NULL) {
+ *Context = UsbBot;
+ } else {
+ gBS->FreePool (UsbBot);
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ gBS->FreePool (UsbBot);
+ return Status;
+}
+
+
+/**
+ Send the command to the device using Bulk-Out endpoint
+
+ @param UsbBot The USB BOT device
+ @param Cmd The command to transfer to device
+ @param CmdLen the length of the command
+ @param DataDir The direction of the data
+ @param TransLen The expected length of the data
+
+ @retval EFI_NOT_READY The device return NAK to the transfer
+ @retval EFI_SUCCESS The command is sent to the device.
+ @retval Others Failed to send the command to device
+
+**/
+STATIC
+EFI_STATUS
+UsbBotSendCommand (
+ IN USB_BOT_PROTOCOL *UsbBot,
+ IN UINT8 *Cmd,
+ IN UINT8 CmdLen,
+ IN EFI_USB_DATA_DIRECTION DataDir,
+ IN UINT32 TransLen
+ )
+{
+ USB_BOT_CBW Cbw;
+ EFI_STATUS Status;
+ UINT32 Result;
+ UINTN DataLen;
+ UINTN Timeout;
+
+ ASSERT ((CmdLen > 0) && (CmdLen <= USB_BOT_MAX_CMDLEN));
+
+ //
+ // Fill in the CSW. Only the first LUN is supported now.
+ //
+ Cbw.Signature = USB_BOT_CBW_SIGNATURE;
+ Cbw.Tag = UsbBot->CbwTag;
+ Cbw.DataLen = TransLen;
+ Cbw.Flag = ((DataDir == EfiUsbDataIn) ? 0x80 : 0);
+ Cbw.Lun = 0;
+ Cbw.CmdLen = CmdLen;
+
+ ZeroMem (Cbw.CmdBlock, USB_BOT_MAX_CMDLEN);
+ CopyMem (Cbw.CmdBlock, Cmd, CmdLen);
+
+ Result = 0;
+ DataLen = sizeof (USB_BOT_CBW);
+ Timeout = USB_BOT_CBW_TIMEOUT / USB_MASS_STALL_1_MS;
+
+ //
+ // Use the UsbIo to send the command to the device. The default
+ // time out is enough.
+ //
+ Status = UsbBot->UsbIo->UsbBulkTransfer (
+ UsbBot->UsbIo,
+ UsbBot->BulkOutEndpoint->EndpointAddress,
+ &Cbw,
+ &DataLen,
+ Timeout,
+ &Result
+ );
+ //
+ // Respond to Bulk-Out endpoint stall with a Reset Recovery,
+ // see the spec section 5.3.1
+ //
+ if (EFI_ERROR (Status)) {
+ if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL) && DataDir == EfiUsbDataOut) {
+ UsbBotResetDevice (UsbBot, FALSE);
+ } else if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) {
+ Status = EFI_NOT_READY;
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ Transfer the data between the device and host. BOT transfer
+ is composed of three phase, command, data, and status.
+
+ @param UsbBot The USB BOT device
+ @param DataDir The direction of the data
+ @param Data The buffer to hold data
+ @param TransLen The expected length of the data
+ @param Timeout The time to wait the command to complete
+
+ @retval EFI_SUCCESS The data is transferred
+ @retval Others Failed to transfer data
+
+**/
+STATIC
+EFI_STATUS
+UsbBotDataTransfer (
+ IN USB_BOT_PROTOCOL *UsbBot,
+ IN EFI_USB_DATA_DIRECTION DataDir,
+ IN OUT UINT8 *Data,
+ IN OUT UINTN *TransLen,
+ IN UINT32 Timeout
+ )
+{
+ EFI_USB_ENDPOINT_DESCRIPTOR *Endpoint;
+ EFI_STATUS Status;
+ UINT32 Result;
+
+ //
+ // It's OK if no data to transfer
+ //
+ if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Select the endpoint then issue the transfer
+ //
+ if (DataDir == EfiUsbDataIn) {
+ Endpoint = UsbBot->BulkInEndpoint;
+ } else {
+ Endpoint = UsbBot->BulkOutEndpoint;
+ }
+
+ Result = 0;
+ Timeout = Timeout / USB_MASS_STALL_1_MS;
+
+ Status = UsbBot->UsbIo->UsbBulkTransfer (
+ UsbBot->UsbIo,
+ Endpoint->EndpointAddress,
+ Data,
+ TransLen,
+ Timeout,
+ &Result
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((mUsbBotError, "UsbBotDataTransfer: (%r)\n", Status));
+ if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) {
+ DEBUG ((mUsbBotError, "UsbBotDataTransfer: DataIn Stall\n"));
+ UsbClearEndpointStall (UsbBot->UsbIo, Endpoint->EndpointAddress);
+ } else if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) {
+ Status = EFI_NOT_READY;
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ Get the command execution status from device. BOT transfer is
+ composed of three phase, command, data, and status.
+ This function return the transfer status of the BOT's CSW status,
+ and return the high level command execution result in Result. So
+ even it returns EFI_SUCCESS, the command may still have failed.
+
+ @param UsbBot The USB BOT device
+ @param TransLen The expected length of the data
+ @param Timeout The time to wait the command to complete
+ @param CmdStatus The result of the command execution.
+
+ @retval EFI_DEVICE_ERROR Failed to retrieve the command execute result
+ @retval EFI_SUCCESS Command execute result is retrieved and in the
+ Result.
+
+**/
+STATIC
+EFI_STATUS
+UsbBotGetStatus (
+ IN USB_BOT_PROTOCOL *UsbBot,
+ IN UINT32 TransLen,
+ OUT UINT8 *CmdStatus
+ )
+{
+ USB_BOT_CSW Csw;
+ UINTN Len;
+ UINT8 Endpoint;
+ EFI_STATUS Status;
+ UINT32 Result;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ UINT32 Index;
+ UINTN Timeout;
+
+ *CmdStatus = USB_BOT_COMMAND_ERROR;
+ Status = EFI_DEVICE_ERROR;
+ Endpoint = UsbBot->BulkInEndpoint->EndpointAddress;
+ UsbIo = UsbBot->UsbIo;
+ Timeout = USB_BOT_CSW_TIMEOUT / USB_MASS_STALL_1_MS;
+
+ for (Index = 0; Index < USB_BOT_GET_STATUS_RETRY; Index++) {
+ //
+ // Attemp to the read CSW from bulk in endpoint
+ //
+ ZeroMem (&Csw, sizeof (USB_BOT_CSW));
+ Result = 0;
+ Len = sizeof (USB_BOT_CSW);
+ Status = UsbIo->UsbBulkTransfer (
+ UsbIo,
+ Endpoint,
+ &Csw,
+ &Len,
+ Timeout,
+ &Result
+ );
+ if (EFI_ERROR(Status)) {
+ DEBUG ((mUsbBotError, "UsbBotGetStatus (%r)\n", Status));
+ if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) {
+ DEBUG ((mUsbBotError, "UsbBotGetStatus: DataIn Stall\n"));
+ UsbClearEndpointStall (UsbIo, Endpoint);
+ }
+ continue;
+ }
+
+ if (Csw.Signature != USB_BOT_CSW_SIGNATURE) {
+ //
+ // Invalid Csw need perform reset recovery
+ //
+ DEBUG ((mUsbBotError, "UsbBotGetStatus: Device return a invalid signature\n"));
+ Status = UsbBotResetDevice (UsbBot, FALSE);
+ } else if (Csw.CmdStatus == USB_BOT_COMMAND_ERROR) {
+ //
+ // Respond phase error need perform reset recovery
+ //
+ DEBUG ((mUsbBotError, "UsbBotGetStatus: Device return a phase error\n"));
+ Status = UsbBotResetDevice (UsbBot, FALSE);
+ } else {
+
+ *CmdStatus = Csw.CmdStatus;
+ break;
+ }
+ }
+ //
+ //The tag is increased even there is an error.
+ //
+ UsbBot->CbwTag++;
+
+ return Status;
+}
+
+
+/**
+ Call the Usb mass storage class transport protocol to issue
+ the command/data/status circle to execute the commands
+
+ @param Context The context of the BOT protocol, that is,
+ USB_BOT_PROTOCOL
+ @param Cmd The high level command
+ @param CmdLen The command length
+ @param DataDir The direction of the data transfer
+ @param Data The buffer to hold data
+ @param DataLen The length of the data
+ @param Timeout The time to wait command
+ @param CmdStatus The result of high level command execution
+
+ @retval EFI_DEVICE_ERROR Failed to excute command
+ @retval EFI_SUCCESS The command is executed OK, and result in CmdStatus
+
+**/
+STATIC
+EFI_STATUS
+UsbBotExecCommand (
+ IN VOID *Context,
+ IN VOID *Cmd,
+ IN UINT8 CmdLen,
+ IN EFI_USB_DATA_DIRECTION DataDir,
+ IN VOID *Data,
+ IN UINT32 DataLen,
+ IN UINT32 Timeout,
+ OUT UINT32 *CmdStatus
+ )
+{
+ USB_BOT_PROTOCOL *UsbBot;
+ EFI_STATUS Status;
+ UINTN TransLen;
+ UINT8 Result;
+
+ *CmdStatus = USB_MASS_CMD_FAIL;
+ UsbBot = (USB_BOT_PROTOCOL *) Context;
+
+ //
+ // Send the command to the device. Return immediately if device
+ // rejects the command.
+ //
+ Status = UsbBotSendCommand (UsbBot, Cmd, CmdLen, DataDir, DataLen);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((mUsbBotError, "UsbBotExecCommand: UsbBotSendCommand (%r)\n", Status));
+ return Status;
+ }
+
+ //
+ // Transfer the data. Don't return immediately even data transfer
+ // failed. The host should attempt to receive the CSW no matter
+ // whether it succeeds or failed.
+ //
+ TransLen = (UINTN) DataLen;
+ UsbBotDataTransfer (UsbBot, DataDir, Data, &TransLen, Timeout);
+
+ //
+ // Get the status, if that succeeds, interpret the result
+ //
+ Status = UsbBotGetStatus (UsbBot, DataLen, &Result);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((mUsbBotError, "UsbBotExecCommand: UsbBotGetStatus (%r)\n", Status));
+ return Status;
+ }
+
+ if (Result == 0) {
+ *CmdStatus = USB_MASS_CMD_SUCCESS;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Reset the mass storage device by BOT protocol
+
+ @param Context The context of the BOT protocol, that is,
+ USB_BOT_PROTOCOL
+
+ @retval EFI_SUCCESS The device is reset
+ @retval Others Failed to reset the device.
+
+**/
+STATIC
+EFI_STATUS
+UsbBotResetDevice (
+ IN VOID *Context,
+ IN BOOLEAN ExtendedVerification
+ )
+{
+ USB_BOT_PROTOCOL *UsbBot;
+ EFI_USB_DEVICE_REQUEST Request;
+ EFI_STATUS Status;
+ UINT32 Result;
+ UINT32 Timeout;
+
+ UsbBot = (USB_BOT_PROTOCOL *) Context;
+
+ if (ExtendedVerification) {
+ //
+ // If we need to do strictly reset, reset its parent hub port
+ //
+ Status = UsbBot->UsbIo->UsbPortReset (UsbBot->UsbIo);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // Issue a class specific "Bulk-Only Mass Storage Reset reqest.
+ // See the spec section 3.1
+ //
+ Request.RequestType = 0x21;
+ Request.Request = USB_BOT_RESET_REQUEST;
+ Request.Value = 0;
+ Request.Index = UsbBot->Interface.InterfaceNumber;
+ Request.Length = 0;
+ Timeout = USB_BOT_RESET_TIMEOUT / USB_MASS_STALL_1_MS;
+
+ Status = UsbBot->UsbIo->UsbControlTransfer (
+ UsbBot->UsbIo,
+ &Request,
+ EfiUsbNoData,
+ Timeout,
+ NULL,
+ 0,
+ &Result
+ );
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((mUsbBotError, "UsbBotResetDevice: (%r)\n", Status));
+ return Status;
+ }
+
+ //
+ // The device shall NAK the host's request until the reset is
+ // complete. We can use this to sync the device and host. For
+ // now just stall 100ms to wait the device.
+ //
+ gBS->Stall (USB_BOT_RESET_STALL);
+
+ //
+ // Clear the Bulk-In and Bulk-Out stall condition.
+ //
+ UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkInEndpoint->EndpointAddress);
+ UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkOutEndpoint->EndpointAddress);
+ return Status;
+}
+
+
+/**
+ Clean up the resource used by this BOT protocol
+
+ @param Context The context of the BOT protocol, that is,
+ USB_BOT_PROTOCOL
+
+ @retval EFI_SUCCESS The resource is cleaned up.
+
+**/
+STATIC
+EFI_STATUS
+UsbBotFini (
+ IN VOID *Context
+ )
+{
+ gBS->FreePool (Context);
+ return EFI_SUCCESS;
+}
+
+USB_MASS_TRANSPORT
+mUsbBotTransport = {
+ USB_MASS_STORE_BOT,
+ UsbBotInit,
+ UsbBotExecCommand,
+ UsbBotResetDevice,
+ UsbBotFini
+};
diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBot.h b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBot.h new file mode 100644 index 0000000..e11e9c5 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBot.h @@ -0,0 +1,102 @@ +/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ UsbMassBot.h
+
+Abstract:
+
+ Defination for the USB mass storage Bulk-Only Transport protocol.
+ This implementation is based on the "Universal Serial Bus Mass
+ Storage Class Bulk-Only Transport" Revision 1.0, September 31, 1999.
+
+Revision History
+
+
+**/
+
+#ifndef _EFI_USBMASS_BOT_H_
+#define _EFI_USBMASS_BOT_H_
+
+enum {
+ //
+ // Usb Bulk-Only class specfic request
+ //
+ USB_BOT_RESET_REQUEST = 0xFF, // Bulk-Only Mass Storage Reset
+ USB_BOT_GETLUN_REQUEST = 0xFE, // Get Max Lun
+ USB_BOT_CBW_SIGNATURE = 0x43425355, // dCBWSignature, tag the packet as CBW
+ USB_BOT_CSW_SIGNATURE = 0x53425355, // dCSWSignature, tag the packet as CSW
+ USB_BOT_MAX_LUN = 0x0F, // Lun number is from 0 to 15
+ USB_BOT_MAX_CMDLEN = 16, // Maxium number of command from command set
+
+ //
+ // Usb BOT command block status values
+ //
+ USB_BOT_COMMAND_OK = 0x00, // Command passed, good status
+ USB_BOT_COMMAND_FAILED = 0x01, // Command failed
+ USB_BOT_COMMAND_ERROR = 0x02, // Phase error, need to reset the device
+
+ //
+ // Usb Bot retry times
+ //
+ USB_BOT_GET_STATUS_RETRY = 3,
+
+ //
+ // Usb Bot stall time
+ //
+ USB_BOT_RESET_STALL = 100 * USB_MASS_STALL_1_MS,
+
+ //
+ // Usb Bot transfer timeout
+ //
+ USB_BOT_CBW_TIMEOUT = 1 * USB_MASS_STALL_1_S,
+ USB_BOT_CSW_TIMEOUT = 1 * USB_MASS_STALL_1_S,
+ USB_BOT_RESET_TIMEOUT = 3 * USB_MASS_STALL_1_S,
+};
+
+//
+// The CBW (Command Block Wrapper) and CSW (Command Status Wrapper)
+// structures used by the Usb BOT protocol.
+//
+#pragma pack(1)
+typedef struct {
+ UINT32 Signature;
+ UINT32 Tag;
+ UINT32 DataLen; // Length of data between CBW and CSW
+ UINT8 Flag; // Bit 7, 0 ~ Data-Out, 1 ~ Data-In
+ UINT8 Lun; // Lun number. Bits 0~3 are used
+ UINT8 CmdLen; // Length of the command. Bits 0~4 are used
+ UINT8 CmdBlock[USB_BOT_MAX_CMDLEN];
+} USB_BOT_CBW;
+
+typedef struct {
+ UINT32 Signature;
+ UINT32 Tag;
+ UINT32 DataResidue;
+ UINT8 CmdStatus;
+} USB_BOT_CSW;
+#pragma pack()
+
+//
+// Put Interface at the first field is to make it easy to get by Context, which
+// could be BOT/CBI Protocol instance
+//
+typedef struct {
+ EFI_USB_INTERFACE_DESCRIPTOR Interface;
+ EFI_USB_ENDPOINT_DESCRIPTOR *BulkInEndpoint;
+ EFI_USB_ENDPOINT_DESCRIPTOR *BulkOutEndpoint;
+ UINT32 CbwTag;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+} USB_BOT_PROTOCOL;
+
+extern USB_MASS_TRANSPORT mUsbBotTransport;
+#endif
diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassCbi.c b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassCbi.c new file mode 100644 index 0000000..1028e20 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassCbi.c @@ -0,0 +1,619 @@ +/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ UsbMassCbi.c
+
+Abstract:
+
+ Implementation of the USB mass storage Control/Bulk/Interrupt transpor.
+ Notice: it is being obseleted by the standard body in favor of the BOT
+ (Bulk-Only Transport).
+
+Revision History
+
+
+**/
+
+#include "UsbMass.h"
+#include "UsbMassCbi.h"
+
+UINTN mUsbCbiInfo = DEBUG_INFO;
+UINTN mUsbCbiError = DEBUG_ERROR;
+
+STATIC
+EFI_STATUS
+UsbCbiResetDevice (
+ IN VOID *Context,
+ IN BOOLEAN ExtendedVerification
+ );
+
+
+/**
+ Initialize the USB mass storage class CBI transport protocol.
+ If Context isn't NULL, it will save its context in it.
+
+ @param UsbIo The USB IO to use
+ @param Controller The device controller
+ @param Context The variable to save context in
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory
+ @retval EFI_UNSUPPORTED The device isn't supported
+ @retval EFI_SUCCESS The CBI protocol is initialized.
+
+**/
+STATIC
+EFI_STATUS
+UsbCbiInit (
+ IN EFI_USB_IO_PROTOCOL *UsbIo,
+ IN EFI_HANDLE Controller,
+ OUT VOID **Context OPTIONAL
+ )
+{
+ USB_CBI_PROTOCOL *UsbCbi;
+ EFI_USB_INTERFACE_DESCRIPTOR *Interface;
+ EFI_USB_ENDPOINT_DESCRIPTOR EndPoint;
+ EFI_STATUS Status;
+ UINT8 Index;
+
+ //
+ // Allocate the CBI context
+ //
+ UsbCbi = AllocateZeroPool (
+ sizeof (USB_CBI_PROTOCOL) + 3 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR)
+ );
+
+ if (UsbCbi == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ UsbCbi->UsbIo = UsbIo;
+
+ //
+ // Get the interface descriptor and validate that it is a USB mass
+ // storage class CBI interface.
+ //
+ Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &UsbCbi->Interface);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Interface = &UsbCbi->Interface;
+ if ((Interface->InterfaceProtocol != USB_MASS_STORE_CBI0)
+ && (Interface->InterfaceProtocol != USB_MASS_STORE_CBI1)) {
+ Status = EFI_UNSUPPORTED;
+ goto ON_ERROR;
+ }
+
+ //
+ // Locate and save the bulk-in, bulk-out, and interrupt endpoint
+ //
+ for (Index = 0; Index < Interface->NumEndpoints; Index++) {
+ Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &EndPoint);
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ if (USB_IS_BULK_ENDPOINT (EndPoint.Attributes)) {
+ //
+ // Use the first Bulk-In and Bulk-Out endpoints
+ //
+ if (USB_IS_IN_ENDPOINT (EndPoint.EndpointAddress) &&
+ (UsbCbi->BulkInEndpoint == NULL)) {
+
+ UsbCbi->BulkInEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1);
+ *UsbCbi->BulkInEndpoint = EndPoint;
+ }
+
+ if (USB_IS_OUT_ENDPOINT (EndPoint.EndpointAddress) &&
+ (UsbCbi->BulkOutEndpoint == NULL)) {
+
+ UsbCbi->BulkOutEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1) + 1;
+ *UsbCbi->BulkOutEndpoint = EndPoint;
+ }
+
+ } else if (USB_IS_INTERRUPT_ENDPOINT (EndPoint.Attributes)) {
+ //
+ // Use the first interrupt endpoint if it is CBI0
+ //
+ if ((Interface->InterfaceProtocol == USB_MASS_STORE_CBI0) &&
+ (UsbCbi->InterruptEndpoint == NULL)) {
+
+ UsbCbi->InterruptEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1) + 2;
+ *UsbCbi->InterruptEndpoint = EndPoint;
+ }
+ }
+ }
+
+ if ((UsbCbi->BulkInEndpoint == NULL)
+ || (UsbCbi->BulkOutEndpoint == NULL)
+ || ((Interface->InterfaceProtocol == USB_MASS_STORE_CBI0)
+ && (UsbCbi->InterruptEndpoint == NULL))) {
+ Status = EFI_UNSUPPORTED;
+ goto ON_ERROR;
+ }
+
+ if (Context != NULL) {
+ *Context = UsbCbi;
+ } else {
+ gBS->FreePool (UsbCbi);
+ }
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ gBS->FreePool (UsbCbi);
+ return Status;
+}
+
+
+
+/**
+ Send the command to the device using class specific control transfer.
+
+ @param UsbCbi The USB CBI protocol
+ @param Cmd The high level command to transfer to device
+ @param CmdLen The length of the command
+ @param Timeout The time to wait the command to finish
+
+ @retval EFI_SUCCESS The command is transferred to device
+ @retval Others The command failed to transfer to device
+
+**/
+STATIC
+EFI_STATUS
+UsbCbiSendCommand (
+ IN USB_CBI_PROTOCOL *UsbCbi,
+ IN UINT8 *Cmd,
+ IN UINT8 CmdLen,
+ IN UINT32 Timeout
+ )
+{
+ EFI_USB_DEVICE_REQUEST Request;
+ EFI_STATUS Status;
+ UINT32 TransStatus;
+ UINTN DataLen;
+ INTN Retry;
+
+ //
+ // Fill in the device request, CBI use the "Accept Device-Specific
+ // Cmd" (ADSC) class specific request to send commands
+ //
+ Request.RequestType = 0x21;
+ Request.Request = 0;
+ Request.Value = 0;
+ Request.Index = UsbCbi->Interface.InterfaceNumber;
+ Request.Length = CmdLen;
+
+ Status = EFI_SUCCESS;
+ Timeout = Timeout / USB_MASS_STALL_1_MS;
+
+ for (Retry = 0; Retry < USB_CBI_MAX_RETRY; Retry++) {
+ //
+ // Use the UsbIo to send the command to the device
+ //
+ TransStatus = 0;
+ DataLen = CmdLen;
+
+ Status = UsbCbi->UsbIo->UsbControlTransfer (
+ UsbCbi->UsbIo,
+ &Request,
+ EfiUsbDataOut,
+ Timeout,
+ Cmd,
+ DataLen,
+ &TransStatus
+ );
+ //
+ // The device can fail the command by STALL the control endpoint.
+ // It can delay the command by NAK the data or status stage, this
+ // is a "class-specific exemption to the USB specification". Retry
+ // if the command is NAKed.
+ //
+ if (EFI_ERROR (Status) && (TransStatus == EFI_USB_ERR_NAK)) {
+ continue;
+ }
+
+ break;
+ }
+
+ return Status;
+}
+
+
+/**
+ Transfer data between the device and host. The CBI contains three phase,
+ command, data, and status. This is data phase.
+
+ @param UsbCbi The USB CBI device
+ @param DataDir The direction of the data transfer
+ @param Data The buffer to hold the data
+ @param TransLen The expected transfer length
+ @param Timeout The time to wait the command to execute
+
+ @retval EFI_SUCCESS The data transfer succeeded
+ @retval Others Failed to transfer all the data
+
+**/
+STATIC
+EFI_STATUS
+UsbCbiDataTransfer (
+ IN USB_CBI_PROTOCOL *UsbCbi,
+ IN EFI_USB_DATA_DIRECTION DataDir,
+ IN OUT UINT8 *Data,
+ IN OUT UINTN *TransLen,
+ IN UINT32 Timeout
+ )
+{
+ EFI_USB_ENDPOINT_DESCRIPTOR *Endpoint;
+ EFI_STATUS Status;
+ UINT32 TransStatus;
+ UINTN Remain;
+ UINTN Increment;
+ UINT8 *Next;
+ UINTN Retry;
+
+ //
+ // It's OK if no data to transfer
+ //
+ if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Select the endpoint then issue the transfer
+ //
+ if (DataDir == EfiUsbDataIn) {
+ Endpoint = UsbCbi->BulkInEndpoint;
+ } else {
+ Endpoint = UsbCbi->BulkOutEndpoint;
+ }
+
+ Next = Data;
+ Remain = *TransLen;
+ Retry = 0;
+ Status = EFI_SUCCESS;
+ Timeout = Timeout / USB_MASS_STALL_1_MS;
+
+ //
+ // Transfer the data, if the device returns NAK, retry it.
+ //
+ while (Remain > 0) {
+ TransStatus = 0;
+
+ if (Remain > (UINTN) USB_CBI_MAX_PACKET_NUM * Endpoint->MaxPacketSize) {
+ Increment = USB_CBI_MAX_PACKET_NUM * Endpoint->MaxPacketSize;
+ } else {
+ Increment = Remain;
+ }
+
+ Status = UsbCbi->UsbIo->UsbBulkTransfer (
+ UsbCbi->UsbIo,
+ Endpoint->EndpointAddress,
+ Next,
+ &Increment,
+ Timeout,
+ &TransStatus
+ );
+ if (EFI_ERROR (Status)) {
+ if (TransStatus == EFI_USB_ERR_NAK) {
+ //
+ // The device can NAK the host if either the data/buffer isn't
+ // aviable or the command is in-progress. The data can be partly
+ // transferred. The transfer is aborted if several succssive data
+ // transfer commands are NAKed.
+ //
+ if (Increment == 0) {
+ if (++Retry > USB_CBI_MAX_RETRY) {
+ goto ON_EXIT;
+ }
+
+ } else {
+ Next += Increment;
+ Remain -= Increment;
+ Retry = 0;
+ }
+
+ continue;
+ }
+
+ //
+ // The device can fail the command by STALL the bulk endpoint.
+ // Clear the stall if that is the case.
+ //
+ if (TransStatus == EFI_USB_ERR_STALL) {
+ UsbClearEndpointStall (UsbCbi->UsbIo, Endpoint->EndpointAddress);
+ }
+
+ goto ON_EXIT;
+ }
+
+ Next += Increment;
+ Remain -= Increment;
+ }
+
+ON_EXIT:
+ *TransLen -= Remain;
+ return Status;
+}
+
+
+/**
+ Get the result of high level command execution from interrupt
+ endpoint. This function returns the USB transfer status, and
+ put the high level command execution result in Result.
+
+ @param UsbCbi The USB CBI protocol
+ @param Timeout The time to wait the command to execute
+ @param Result GC_TODO: add argument description
+
+ @retval EFI_SUCCESS The high level command execution result is
+ retrieved in Result.
+ @retval Others Failed to retrieve the result.
+
+**/
+STATIC
+EFI_STATUS
+UsbCbiGetStatus (
+ IN USB_CBI_PROTOCOL *UsbCbi,
+ IN UINT32 Timeout,
+ OUT USB_CBI_STATUS *Result
+ )
+{
+ UINTN Len;
+ UINT8 Endpoint;
+ EFI_STATUS Status;
+ UINT32 TransStatus;
+ INTN Retry;
+
+ Endpoint = UsbCbi->InterruptEndpoint->EndpointAddress;
+ Status = EFI_SUCCESS;
+ Timeout = Timeout / USB_MASS_STALL_1_MS;
+
+ //
+ // Attemp to the read the result from interrupt endpoint
+ //
+ for (Retry = 0; Retry < USB_CBI_MAX_RETRY; Retry++) {
+ TransStatus = 0;
+ Len = sizeof (USB_CBI_STATUS);
+
+ Status = UsbCbi->UsbIo->UsbSyncInterruptTransfer (
+ UsbCbi->UsbIo,
+ Endpoint,
+ Result,
+ &Len,
+ Timeout,
+ &TransStatus
+ );
+ //
+ // The CBI can NAK the interrupt endpoint if the command is in-progress.
+ //
+ if (EFI_ERROR (Status) && (TransStatus == EFI_USB_ERR_NAK)) {
+ continue;
+ }
+
+ break;
+ }
+
+ return Status;
+}
+
+
+/**
+ Execute USB mass storage command through the CBI0/CBI1 transport protocol
+
+ @param Context The USB CBI device
+ @param Cmd The command to transfer to device
+ @param CmdLen The length of the command
+ @param DataDir The direction of data transfer
+ @param Data The buffer to hold the data
+ @param DataLen The length of the buffer
+ @param Timeout The time to wait
+ @param CmdStatus The result of the command execution
+
+ @retval EFI_SUCCESS The command is executed OK and result in CmdStatus.
+ @retval EFI_DEVICE_ERROR Failed to execute the command
+
+**/
+STATIC
+EFI_STATUS
+UsbCbiExecCommand (
+ IN VOID *Context,
+ IN VOID *Cmd,
+ IN UINT8 CmdLen,
+ IN EFI_USB_DATA_DIRECTION DataDir,
+ IN VOID *Data,
+ IN UINT32 DataLen,
+ IN UINT32 Timeout,
+ OUT UINT32 *CmdStatus
+ )
+{
+ USB_CBI_PROTOCOL *UsbCbi;
+ USB_CBI_STATUS Result;
+ EFI_STATUS Status;
+ UINTN TransLen;
+
+ *CmdStatus = USB_MASS_CMD_SUCCESS;
+ UsbCbi = (USB_CBI_PROTOCOL *) Context;
+
+ //
+ // Send the command to the device. Return immediately if device
+ // rejects the command.
+ //
+ Status = UsbCbiSendCommand (UsbCbi, Cmd, CmdLen, Timeout);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((mUsbCbiError, "UsbCbiExecCommand: UsbCbiSendCommand (%r)\n",Status));
+ return Status;
+ }
+
+ //
+ // Transfer the data, return this status if no interrupt endpoint
+ // is used to report the transfer status.
+ //
+ TransLen = (UINTN) DataLen;
+
+ Status = UsbCbiDataTransfer (UsbCbi, DataDir, Data, &TransLen, Timeout);
+ if (UsbCbi->InterruptEndpoint == NULL) {
+ DEBUG ((mUsbCbiError, "UsbCbiExecCommand: UsbCbiDataTransfer (%r)\n",Status));
+ return Status;
+ }
+
+ //
+ // Get the status, if that succeeds, interpret the result
+ //
+ Status = UsbCbiGetStatus (UsbCbi, Timeout, &Result);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((mUsbCbiError, "UsbCbiExecCommand: UsbCbiGetStatus (%r)\n",Status));
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (UsbCbi->Interface.InterfaceSubClass == USB_MASS_STORE_UFI) {
+ //
+ // For UFI device, ASC and ASCQ are returned.
+ //
+ if (Result.Type != 0) {
+ *CmdStatus = USB_MASS_CMD_FAIL;
+ }
+
+ } else {
+ //
+ // Check page 27, CBI spec 1.1 for vaious reture status.
+ //
+ switch (Result.Value & 0x03) {
+ case 0x00:
+ //
+ // Pass
+ //
+ *CmdStatus = USB_MASS_CMD_SUCCESS;
+ break;
+
+ case 0x02:
+ //
+ // Phase Error, response with reset. Fall through to Fail.
+ //
+ UsbCbiResetDevice (UsbCbi, FALSE);
+
+ case 0x01:
+ //
+ // Fail
+ //
+ *CmdStatus = USB_MASS_CMD_FAIL;
+ break;
+
+ case 0x03:
+ //
+ // Persistent Fail, need to send REQUEST SENSE.
+ //
+ *CmdStatus = USB_MASS_CMD_PERSISTENT;
+ break;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Call the Usb mass storage class transport protocol to
+ reset the device. The reset is defined as a Non-Data
+ command. Don't use UsbCbiExecCommand to send the command
+ to device because that may introduce recursive loop.
+
+ @param Context The USB CBI device protocol
+
+ @retval EFI_SUCCESS the device is reset
+ @retval Others Failed to reset the device
+
+**/
+STATIC
+EFI_STATUS
+UsbCbiResetDevice (
+ IN VOID *Context,
+ IN BOOLEAN ExtendedVerification
+ )
+{
+ UINT8 ResetCmd[USB_CBI_RESET_CMD_LEN];
+ USB_CBI_PROTOCOL *UsbCbi;
+ USB_CBI_STATUS Result;
+ EFI_STATUS Status;
+ UINT32 Timeout;
+
+ UsbCbi = (USB_CBI_PROTOCOL *) Context;
+
+ //
+ // Fill in the reset command.
+ //
+ SetMem (ResetCmd, USB_CBI_RESET_CMD_LEN, 0xFF);
+
+ ResetCmd[0] = 0x1D;
+ ResetCmd[1] = 0x04;
+ Timeout = USB_CBI_RESET_TIMEOUT / USB_MASS_STALL_1_MS;
+
+ //
+ // Send the command to the device. Don't use UsbCbiExecCommand here.
+ //
+ Status = UsbCbiSendCommand (UsbCbi, ResetCmd, USB_CBI_RESET_CMD_LEN, Timeout);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Just retrieve the status and ignore that. Then stall
+ // 50ms to wait it complete
+ //
+ UsbCbiGetStatus (UsbCbi, Timeout, &Result);
+ gBS->Stall (50 * 1000);
+
+ //
+ // Clear the Bulk-In and Bulk-Out stall condition and
+ // init data toggle.
+ //
+ UsbClearEndpointStall (UsbCbi->UsbIo, UsbCbi->BulkInEndpoint->EndpointAddress);
+ UsbClearEndpointStall (UsbCbi->UsbIo, UsbCbi->BulkOutEndpoint->EndpointAddress);
+ return Status;
+}
+
+
+/**
+ Clean up the CBI protocol's resource
+
+ @param Context The CBI protocol
+
+ @retval EFI_SUCCESS The resource is cleaned up.
+
+**/
+STATIC
+EFI_STATUS
+UsbCbiFini (
+ IN VOID *Context
+ )
+{
+ gBS->FreePool (Context);
+ return EFI_SUCCESS;
+}
+
+USB_MASS_TRANSPORT
+mUsbCbi0Transport = {
+ USB_MASS_STORE_CBI0,
+ UsbCbiInit,
+ UsbCbiExecCommand,
+ UsbCbiResetDevice,
+ UsbCbiFini
+};
+
+USB_MASS_TRANSPORT
+mUsbCbi1Transport = {
+ USB_MASS_STORE_CBI1,
+ UsbCbiInit,
+ UsbCbiExecCommand,
+ UsbCbiResetDevice,
+ UsbCbiFini
+};
diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassCbi.h b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassCbi.h new file mode 100644 index 0000000..d4a9f70 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassCbi.h @@ -0,0 +1,64 @@ +/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ UsbMassCbi.h
+
+Abstract:
+
+ Defination for the USB mass storage Control/Bulk/Interrupt transpor.
+
+Revision History
+
+
+**/
+
+#ifndef _EFI_USBMASS_CBI_H_
+#define _EFI_USBMASS_CBI_H_
+
+enum {
+ USB_CBI_MAX_PACKET_NUM = 16,
+ USB_CBI_RESET_CMD_LEN = 12,
+
+ //
+ // Usb CBI retry times
+ //
+ USB_CBI_MAX_RETRY = 3,
+
+ //
+ // Usb Cbi transfer timeout
+ //
+ USB_CBI_RESET_TIMEOUT = 1 * USB_MASS_STALL_1_S,
+};
+
+//
+// Put Interface at the first field is to make it easy to get by Context, which
+// could be BOT/CBI Protocol instance
+//
+typedef struct {
+ EFI_USB_INTERFACE_DESCRIPTOR Interface;
+ EFI_USB_ENDPOINT_DESCRIPTOR *BulkInEndpoint;
+ EFI_USB_ENDPOINT_DESCRIPTOR *BulkOutEndpoint;
+ EFI_USB_ENDPOINT_DESCRIPTOR *InterruptEndpoint;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+} USB_CBI_PROTOCOL;
+
+#pragma pack(1)
+typedef struct {
+ UINT8 Type;
+ UINT8 Value;
+} USB_CBI_STATUS;
+#pragma pack()
+
+extern USB_MASS_TRANSPORT mUsbCbi0Transport;
+extern USB_MASS_TRANSPORT mUsbCbi1Transport;
+#endif
diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassImpl.c b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassImpl.c new file mode 100644 index 0000000..a7c3e82 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassImpl.c @@ -0,0 +1,640 @@ +/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ UsbMassImpl.c
+
+Abstract:
+
+ The implementation of USB mass storage class device driver.
+ The command set supported is "USB Mass Storage Specification
+ for Bootability".
+
+Revision History
+
+
+**/
+
+#include "UsbMassImpl.h"
+
+//
+// The underlying transport protocol. CBI support isn't included
+// in the current build. It is being obseleted by the standard
+// body. If you want to enable it, remove the if directive here,
+// then add the UsbMassCbi.c/.h to the driver's inf file.
+//
+STATIC
+USB_MASS_TRANSPORT *mUsbMassTransport[] = {
+ &mUsbCbi0Transport,
+ &mUsbCbi1Transport,
+ &mUsbBotTransport,
+ NULL
+};
+
+UINTN mUsbMscInfo = DEBUG_INFO;
+UINTN mUsbMscError = DEBUG_ERROR;
+
+
+/**
+ Retrieve the media parameters such as disk gemotric for the
+ device's BLOCK IO protocol.
+
+ @param UsbMass The USB mass storage device
+
+ @retval EFI_SUCCESS The media parameters is updated successfully.
+ @retval Others Failed to get the media parameters.
+
+**/
+EFI_STATUS
+UsbMassInitMedia (
+ IN USB_MASS_DEVICE *UsbMass
+ )
+{
+ EFI_BLOCK_IO_MEDIA *Media;
+ EFI_STATUS Status;
+ UINTN Index;
+
+ Media = &UsbMass->BlockIoMedia;
+
+ //
+ // Initialize the MediaPrsent/ReadOnly and others to the default.
+ // We are not forced to get it right at this time, check UEFI2.0
+ // spec for more information:
+ //
+ // MediaPresent: This field shows the media present status as
+ // of the most recent ReadBlocks or WriteBlocks call.
+ //
+ // ReadOnly : This field shows the read-only status as of the
+ // recent WriteBlocks call.
+ //
+ // but remember to update MediaId/MediaPresent/ReadOnly status
+ // after ReadBlocks and WriteBlocks
+ //
+ Media->MediaPresent = FALSE;
+ Media->LogicalPartition = FALSE;
+ Media->ReadOnly = FALSE;
+ Media->WriteCaching = FALSE;
+ Media->IoAlign = 0;
+
+ //
+ // Some device may spend several seconds before it is ready.
+ // Try several times before giving up. Wait 5s at most.
+ //
+ Status = EFI_SUCCESS;
+
+ for (Index = 0; Index < USB_BOOT_WAIT_RETRY; Index++) {
+
+ Status = UsbBootGetParams (UsbMass);
+ if ((Status != EFI_MEDIA_CHANGED)
+ && (Status != EFI_NOT_READY)
+ && (Status != EFI_TIMEOUT)) {
+ break;
+ }
+
+ Status = UsbBootIsUnitReady (UsbMass);
+ if (EFI_ERROR (Status)) {
+ gBS->Stall (USB_BOOT_UNIT_READY_STALL * (Index + 1));
+ }
+
+ }
+
+ return Status;
+}
+
+
+/**
+ Reset the block device. ExtendedVerification is ignored for this.
+
+ @param This The BLOCK IO protocol
+ @param ExtendedVerification Whether to execute extended verfication.
+
+ @retval EFI_SUCCESS The device is successfully resetted.
+ @retval Others Failed to reset the device.
+
+**/
+EFI_STATUS
+UsbMassReset (
+ IN EFI_BLOCK_IO_PROTOCOL *This,
+ IN BOOLEAN ExtendedVerification
+ )
+{
+ USB_MASS_DEVICE *UsbMass;
+
+ UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (This);
+ return UsbMass->Transport->Reset (UsbMass->Context, ExtendedVerification);
+}
+
+
+/**
+ Read some blocks of data from the block device.
+
+ @param This The Block IO protocol
+ @param MediaId The media's ID of the device for current request
+ @param Lba The start block number
+ @param BufferSize The size of buffer to read data in
+ @param Buffer The buffer to read data to
+
+ @retval EFI_SUCCESS The data is successfully read
+ @retval EFI_NO_MEDIA Media isn't present
+ @retval EFI_MEDIA_CHANGED The device media has been changed, that is,
+ MediaId changed
+ @retval EFI_INVALID_PARAMETER Some parameters are invalid, such as Buffer is
+ NULL.
+ @retval EFI_BAD_BUFFER_SIZE The buffer size isn't a multiple of media's block
+ size, or overflow the last block number.
+
+**/
+EFI_STATUS
+UsbMassReadBlocks (
+ IN EFI_BLOCK_IO_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ USB_MASS_DEVICE *UsbMass;
+ EFI_BLOCK_IO_MEDIA *Media;
+ EFI_STATUS Status;
+ UINTN TotalBlock;
+
+ UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (This);
+ Media = &UsbMass->BlockIoMedia;
+
+ //
+ // First, validate the parameters
+ //
+ if ((Buffer == NULL) || (BufferSize == 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // If it is a remoable media, such as CD-Rom or Usb-Floppy,
+ // if, need to detect the media before each rw, while Usb-Flash
+ // needn't. However, it's hard to identify Usb-Floppy between
+ // Usb-Flash by now, so detect media every time.
+ //
+ Status = UsbBootDetectMedia (UsbMass);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((mUsbMscError, "UsbMassReadBlocks: UsbBootDetectMedia (%r)\n", Status));
+ return Status;
+ }
+
+ //
+ // Make sure BlockSize and LBA is consistent with BufferSize
+ //
+ if ((BufferSize % Media->BlockSize) != 0) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ TotalBlock = BufferSize / Media->BlockSize;
+
+ if (Lba + TotalBlock - 1 > Media->LastBlock) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ Status = UsbBootReadBlocks (UsbMass, (UINT32) Lba, TotalBlock, Buffer);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((mUsbMscError, "UsbMassReadBlocks: UsbBootReadBlocks (%r) -> Reset\n", Status));
+ UsbMassReset (This, TRUE);
+ }
+
+ return Status;
+}
+
+
+/**
+ Write some blocks of data to the block device.
+
+ @param This The Block IO protocol
+ @param MediaId The media's ID of the device for current request
+ @param Lba The start block number
+ @param BufferSize The size of buffer to write data to
+ @param Buffer The buffer to write data to
+
+ @retval EFI_SUCCESS The data is successfully written
+ @retval EFI_NO_MEDIA Media isn't present
+ @retval EFI_MEDIA_CHANGED The device media has been changed, that is,
+ MediaId changed
+ @retval EFI_INVALID_PARAMETER Some parameters are invalid, such as Buffer is
+ NULL.
+ @retval EFI_BAD_BUFFER_SIZE The buffer size isn't a multiple of media's block
+ size,
+
+**/
+EFI_STATUS
+UsbMassWriteBlocks (
+ IN EFI_BLOCK_IO_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN UINTN BufferSize,
+ IN VOID *Buffer
+ )
+{
+ USB_MASS_DEVICE *UsbMass;
+ EFI_BLOCK_IO_MEDIA *Media;
+ EFI_STATUS Status;
+ UINTN TotalBlock;
+
+ UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (This);
+ Media = &UsbMass->BlockIoMedia;
+
+ //
+ // First, validate the parameters
+ //
+ if ((Buffer == NULL) || (BufferSize == 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // If it is a remoable media, such as CD-Rom or Usb-Floppy,
+ // if, need to detect the media before each rw, while Usb-Flash
+ // needn't. However, it's hard to identify Usb-Floppy between
+ // Usb-Flash by now, so detect media every time.
+ //
+ Status = UsbBootDetectMedia (UsbMass);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((mUsbMscError, "UsbMassWriteBlocks: UsbBootDetectMedia (%r)\n", Status));
+ return Status;
+ }
+
+ //
+ // Make sure BlockSize and LBA is consistent with BufferSize
+ //
+ if ((BufferSize % Media->BlockSize) != 0) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ TotalBlock = BufferSize / Media->BlockSize;
+
+ if (Lba + TotalBlock - 1 > Media->LastBlock) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ //
+ // Try to write the data even the device is marked as ReadOnly,
+ // and clear the status should the write succeed.
+ //
+ Status = UsbBootWriteBlocks (UsbMass, (UINT32) Lba, TotalBlock, Buffer);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((mUsbMscError, "UsbMassWriteBlocks: UsbBootWriteBlocks (%r) -> Reset\n", Status));
+ UsbMassReset (This, TRUE);
+ }
+
+ return Status;
+}
+
+
+/**
+ Flush the cached writes to disks. USB mass storage device doesn't
+ support write cache, so return EFI_SUCCESS directly.
+
+ @param This The BLOCK IO protocol
+
+ @retval EFI_SUCCESS Always returns success
+
+**/
+EFI_STATUS
+UsbMassFlushBlocks (
+ IN EFI_BLOCK_IO_PROTOCOL *This
+ )
+{
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Check whether the controller is a supported USB mass storage.
+
+ @param This The USB mass driver's driver binding.
+ @param Controller The device to test against.
+ @param RemainingDevicePath The remaining device path
+
+ @retval EFI_SUCCESS This device is a supported USB mass storage.
+ @retval EFI_UNSUPPORTED The device isn't supported
+ @retval Others Some error happened.
+
+**/
+EFI_STATUS
+EFIAPI
+USBMassDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ EFI_USB_INTERFACE_DESCRIPTOR Interface;
+ USB_MASS_TRANSPORT *Transport;
+ EFI_STATUS Status;
+ INTN Index;
+
+ //
+ // Check whether the controlelr support USB_IO
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiUsbIoProtocolGuid,
+ &UsbIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Get the interface to check the USB class and find a transport
+ // protocol handler.
+ //
+ Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = EFI_UNSUPPORTED;
+
+ if (Interface.InterfaceClass != USB_MASS_STORE_CLASS) {
+ goto ON_EXIT;
+ }
+
+ for (Index = 0; mUsbMassTransport[Index] != NULL; Index++) {
+ Transport = mUsbMassTransport[Index];
+ if (Interface.InterfaceProtocol == Transport->Protocol) {
+ Status = Transport->Init (UsbIo, Controller, NULL);
+ break;
+ }
+ }
+
+ DEBUG ((mUsbMscInfo, "Found a USB mass store device %r\n", Status));
+
+ON_EXIT:
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiUsbIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ return Status;
+}
+
+
+/**
+ Start the USB mass storage device on the controller. It will
+ install a BLOCK_IO protocol on the device if everything is OK.
+
+ @param This The USB mass storage driver binding.
+ @param Controller The USB mass storage device to start on
+ @param RemainingDevicePath The remaining device path.
+
+ @retval EFI_SUCCESS The driver has started on the device.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory
+ @retval Others Failed to start the driver on the device.
+
+**/
+EFI_STATUS
+EFIAPI
+USBMassDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ EFI_USB_INTERFACE_DESCRIPTOR Interface;
+ USB_MASS_DEVICE *UsbMass;
+ USB_MASS_TRANSPORT *Transport;
+ EFI_STATUS Status;
+ UINTN Index;
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiUsbIoProtocolGuid,
+ &UsbIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ UsbMass = AllocateZeroPool (sizeof (USB_MASS_DEVICE));
+ if (UsbMass == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Initialize the transport protocols
+ //
+ Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((mUsbMscError, "USBMassDriverBindingStart: UsbIo->UsbGetInterfaceDescriptor (%r)\n", Status));
+ goto ON_ERROR;
+ }
+
+ Status = EFI_UNSUPPORTED;
+
+ for (Index = 0; mUsbMassTransport[Index] != NULL; Index++) {
+ Transport = mUsbMassTransport[Index];
+
+ if (Interface.InterfaceProtocol == Transport->Protocol) {
+ UsbMass->Transport = Transport;
+ Status = Transport->Init (UsbIo, Controller, &UsbMass->Context);
+ break;
+ }
+ }
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((mUsbMscError, "USBMassDriverBindingStart: Transport->Init (%r)\n", Status));
+ goto ON_ERROR;
+ }
+
+ UsbMass->Signature = USB_MASS_SIGNATURE;
+ UsbMass->Controller = Controller;
+ UsbMass->UsbIo = UsbIo;
+ UsbMass->BlockIo.Media = &UsbMass->BlockIoMedia;
+ UsbMass->BlockIo.Reset = UsbMassReset;
+ UsbMass->BlockIo.ReadBlocks = UsbMassReadBlocks;
+ UsbMass->BlockIo.WriteBlocks = UsbMassWriteBlocks;
+ UsbMass->BlockIo.FlushBlocks = UsbMassFlushBlocks;
+ UsbMass->OpticalStorage = FALSE;
+
+ //
+ // Get the storage's parameters, such as last block number.
+ // then install the BLOCK_IO
+ //
+ Status = UsbMassInitMedia (UsbMass);
+ if (!EFI_ERROR (Status)) {
+ if ((UsbMass->Pdt != USB_PDT_DIRECT_ACCESS) &&
+ (UsbMass->Pdt != USB_PDT_CDROM) &&
+ (UsbMass->Pdt != USB_PDT_OPTICAL) &&
+ (UsbMass->Pdt != USB_PDT_SIMPLE_DIRECT)) {
+ DEBUG ((mUsbMscError, "USBMassDriverBindingStart: Found an unsupported peripheral type[%d]\n", UsbMass->Pdt));
+ goto ON_ERROR;
+ }
+ } else if (Status != EFI_NO_MEDIA){
+ DEBUG ((mUsbMscError, "USBMassDriverBindingStart: UsbMassInitMedia (%r)\n", Status));
+ goto ON_ERROR;
+ }
+
+ Status = gBS->InstallProtocolInterface (
+ &Controller,
+ &gEfiBlockIoProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &UsbMass->BlockIo
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ gBS->FreePool (UsbMass);
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiUsbIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ return Status;
+}
+
+
+/**
+ Stop controlling the device.
+
+ @param This The USB mass storage driver binding
+ @param Controller The device controller controlled by the driver.
+ @param NumberOfChildren The number of children of this device
+ @param ChildHandleBuffer The buffer of children handle.
+
+ @retval EFI_SUCCESS The driver stopped from controlling the device.
+ @retval Others Failed to stop the driver
+
+**/
+EFI_STATUS
+EFIAPI
+USBMassDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ USB_MASS_DEVICE *UsbMass;
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;
+
+ //
+ // First, get our context back from the BLOCK_IO
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiBlockIoProtocolGuid,
+ &BlockIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (BlockIo);
+
+ //
+ // Uninstall Block I/O protocol from the device handle,
+ // then call the transport protocol to stop itself.
+ //
+ Status = gBS->UninstallProtocolInterface (
+ Controller,
+ &gEfiBlockIoProtocolGuid,
+ &UsbMass->BlockIo
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiUsbIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ UsbMass->Transport->Fini (UsbMass->Context);
+ gBS->FreePool (UsbMass);
+
+ return EFI_SUCCESS;
+}
+
+EFI_DRIVER_BINDING_PROTOCOL gUSBMassDriverBinding = {
+ USBMassDriverBindingSupported,
+ USBMassDriverBindingStart,
+ USBMassDriverBindingStop,
+ 0x11,
+ NULL,
+ NULL
+};
+
+//@MT: EFI_DRIVER_ENTRY_POINT (USBMassStorageEntryPoint)
+
+EFI_STATUS
+EFIAPI
+USBMassStorageEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+/*++
+
+Routine Description:
+
+ The entry point for the driver, which will install the driver binding and
+ component name protocol
+
+Arguments:
+
+ ImageHandle - The image handle of this driver
+ SystemTable - The system table
+
+Returns:
+
+ EFI_SUCCESS - the protocols are installed OK
+ Others - Failed to install protocols.
+
+--*/
+{
+ EFI_STATUS Status;
+
+ //
+ // Install driver binding protocol
+ //
+ Status = EfiLibInstallAllDriverProtocols (
+ ImageHandle,
+ SystemTable,
+ &gUSBMassDriverBinding,
+ ImageHandle,
+ &gUsbMassStorageComponentName,
+ NULL,
+ NULL
+ );
+
+ return Status;
+}
diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassImpl.h b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassImpl.h new file mode 100644 index 0000000..39bc87c --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassImpl.h @@ -0,0 +1,57 @@ +/** @file
+
+Copyright (c) 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ UsbMassImpl.h
+
+Abstract:
+
+ The implementation of USB mass storage class device driver.
+
+Revision History
+
+
+**/
+
+#ifndef _EFI_USBMASS_IMPL_H_
+#define _EFI_USBMASS_IMPL_H_
+
+typedef struct _USB_MASS_DEVICE USB_MASS_DEVICE;
+
+#include "UsbMass.h"
+#include "UsbMassBot.h"
+#include "UsbMassCbi.h"
+#include "UsbMassBoot.h"
+
+enum {
+ USB_MASS_SIGNATURE= EFI_SIGNATURE_32 ('U', 's', 'b', 'K'),
+};
+
+typedef struct _USB_MASS_DEVICE {
+ UINT32 Signature;
+ EFI_HANDLE Controller;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ EFI_BLOCK_IO_PROTOCOL BlockIo;
+ EFI_BLOCK_IO_MEDIA BlockIoMedia;
+ BOOLEAN OpticalStorage;
+ UINT8 Lun; // Logical Unit Number
+ UINT8 Pdt; // Peripheral Device Type
+ USB_MASS_TRANSPORT *Transport; // USB mass storage transport protocol
+ VOID *Context; // Opaque storage for mass transport
+};
+
+#define USB_MASS_DEVICE_FROM_BLOCKIO(a) \
+ CR (a, USB_MASS_DEVICE, BlockIo, USB_MASS_SIGNATURE)
+
+extern EFI_COMPONENT_NAME_PROTOCOL gUsbMassStorageComponentName;
+
+#endif
diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf new file mode 100644 index 0000000..7636db7 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf @@ -0,0 +1,95 @@ +#/** @file
+# Component name for module UsbMassStorage
+#
+# Copyright (c) 2006, Intel Corporation. All right reserved.
+#
+# All rights reserved. This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+#**/
+
+################################################################################
+#
+# Defines Section - statements that will be processed to create a Makefile.
+#
+################################################################################
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = UsbMassStorageDxe
+ FILE_GUID = 9FB4B4A7-42C0-4bcd-8540-9BCC6711F83E
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ EDK_RELEASE_VERSION = 0x00020000
+ EFI_SPECIFICATION_VERSION = 0x00020000
+
+ ENTRY_POINT = USBMassStorageEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+################################################################################
+#
+# Sources Section - list of files that are required for the build to succeed.
+#
+################################################################################
+
+[Sources.common]
+ UsbMassBoot.h
+ UsbMassImpl.h
+ UsbMassBot.h
+ UsbMassBot.c
+ ComponentName.c
+ UsbMassImpl.c
+ UsbMassBoot.c
+ UsbMassCbi.h
+ UsbMass.h
+ UsbMassCbi.c
+
+################################################################################
+#
+# Package Dependency Section - list of Package files that are required for
+# this module.
+#
+################################################################################
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+################################################################################
+#
+# Library Class Section - list of Library Classes that are required for
+# this module.
+#
+################################################################################
+
+[LibraryClasses]
+ MemoryAllocationLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ BaseMemoryLib
+ DebugLib
+
+
+################################################################################
+#
+# Protocol C Name Section - list of Protocol and Protocol Notify C Names
+# that this module uses or produces.
+#
+################################################################################
+
+[Protocols]
+ gEfiUsbIoProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiBlockIoProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+
diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.msa b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.msa new file mode 100644 index 0000000..ca7f3db --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.msa @@ -0,0 +1,75 @@ +<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <MsaHeader>
+ <ModuleName>UsbMassStorageDxe</ModuleName>
+ <ModuleType>DXE_DRIVER</ModuleType>
+ <GuidValue>9FB4B4A7-42C0-4bcd-8540-9BCC6711F83E</GuidValue>
+ <Version>1.0</Version>
+ <Abstract>Component name for module UsbMassStorage</Abstract>
+ <Description>FIX ME!</Description>
+ <Copyright>Copyright (c) 2006, Intel Corporation. All right reserved.</Copyright>
+ <License>All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.</License>
+ <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052</Specification>
+ </MsaHeader>
+ <ModuleDefinitions>
+ <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>
+ <BinaryModule>false</BinaryModule>
+ <OutputFileBasename>UsbMassStorageDxe</OutputFileBasename>
+ </ModuleDefinitions>
+ <LibraryClassDefinitions>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>DebugLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>BaseMemoryLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiDriverEntryPoint</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiBootServicesTableLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>MemoryAllocationLib</Keyword>
+ </LibraryClass>
+ </LibraryClassDefinitions>
+ <SourceFiles>
+ <Filename>UsbMassCbi.c</Filename>
+ <Filename>UsbMass.h</Filename>
+ <Filename>UsbMassCbi.h</Filename>
+ <Filename>UsbMassBoot.c</Filename>
+ <Filename>UsbMassImpl.c</Filename>
+ <Filename>ComponentName.c</Filename>
+ <Filename>UsbMassBot.c</Filename>
+ <Filename>UsbMassBot.h</Filename>
+ <Filename>UsbMassImpl.h</Filename>
+ <Filename>UsbMassBoot.h</Filename>
+ </SourceFiles>
+ <PackageDependencies>
+ <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>
+ <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>
+ </PackageDependencies>
+ <Protocols>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiBlockIoProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiUsbIoProtocolGuid</ProtocolCName>
+ </Protocol>
+ </Protocols>
+ <Externs>
+ <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>
+ <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>
+ <Extern>
+ <ModuleEntryPoint>USBMassStorageEntryPoint</ModuleEntryPoint>
+ </Extern>
+ </Externs>
+</ModuleSurfaceArea>
\ No newline at end of file |