From e237e7ae9fc23f32a25040d49cc9a16f2a7f3b4c Mon Sep 17 00:00:00 2001 From: vanjeff Date: Wed, 11 Jul 2007 08:47:37 +0000 Subject: 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 --- .../Bus/Usb/UsbMassStorageDxe/ComponentName.c | 153 ++++ MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMass.h | 149 ++++ .../Bus/Usb/UsbMassStorageDxe/UsbMassBoot.c | 886 +++++++++++++++++++++ .../Bus/Usb/UsbMassStorageDxe/UsbMassBoot.h | 268 +++++++ .../Bus/Usb/UsbMassStorageDxe/UsbMassBot.c | 561 +++++++++++++ .../Bus/Usb/UsbMassStorageDxe/UsbMassBot.h | 102 +++ .../Bus/Usb/UsbMassStorageDxe/UsbMassCbi.c | 619 ++++++++++++++ .../Bus/Usb/UsbMassStorageDxe/UsbMassCbi.h | 64 ++ .../Bus/Usb/UsbMassStorageDxe/UsbMassImpl.c | 640 +++++++++++++++ .../Bus/Usb/UsbMassStorageDxe/UsbMassImpl.h | 57 ++ .../Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf | 95 +++ .../Usb/UsbMassStorageDxe/UsbMassStorageDxe.msa | 75 ++ 12 files changed, 3669 insertions(+) create mode 100644 MdeModulePkg/Bus/Usb/UsbMassStorageDxe/ComponentName.c create mode 100644 MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMass.h create mode 100644 MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.c create mode 100644 MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.h create mode 100644 MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBot.c create mode 100644 MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBot.h create mode 100644 MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassCbi.c create mode 100644 MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassCbi.h create mode 100644 MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassImpl.c create mode 100644 MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassImpl.h create mode 100644 MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf create mode 100644 MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.msa (limited to 'MdeModulePkg/Bus/Usb/UsbMassStorageDxe') diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/ComponentName.c b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/ComponentName.c new file mode 100644 index 0000000000..bd0f939225 --- /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 + +// +// The Library classes this module consumes +// +#include + +// +// 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 0000000000..c48dee5a7b --- /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 +// +// The protocols, PPI and GUID defintions for this module +// +#include +#include +// +// The Library classes this module consumes +// +#include +#include +#include +#include +#include +#include + +#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 0000000000..980f8b2895 --- /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 0000000000..cee70be412 --- /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 0000000000..0d6cca167b --- /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 0000000000..e11e9c5108 --- /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 0000000000..1028e201c2 --- /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 0000000000..d4a9f700ad --- /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 0000000000..a7c3e82f98 --- /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 0000000000..39bc87ca9e --- /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 0000000000..7636db7028 --- /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 0000000000..ca7f3db911 --- /dev/null +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.msa @@ -0,0 +1,75 @@ + + + UsbMassStorageDxe + DXE_DRIVER + 9FB4B4A7-42C0-4bcd-8540-9BCC6711F83E + 1.0 + Component name for module UsbMassStorage + FIX ME! + 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. + FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052 + + + IA32 X64 IPF EBC + false + UsbMassStorageDxe + + + + DebugLib + + + BaseMemoryLib + + + UefiDriverEntryPoint + + + UefiBootServicesTableLib + + + UefiLib + + + MemoryAllocationLib + + + + UsbMassCbi.c + UsbMass.h + UsbMassCbi.h + UsbMassBoot.c + UsbMassImpl.c + ComponentName.c + UsbMassBot.c + UsbMassBot.h + UsbMassImpl.h + UsbMassBoot.h + + + + + + + + gEfiBlockIoProtocolGuid + + + gEfiUsbIoProtocolGuid + + + + EFI_SPECIFICATION_VERSION 0x00020000 + EDK_RELEASE_VERSION 0x00020000 + + USBMassStorageEntryPoint + + + \ No newline at end of file -- cgit v1.2.3