/** @file 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). ;****************************************************************************** ;* Copyright (c) 2012 - 2018, Insyde Software Corp. All Rights Reserved. ;* ;* You may not reproduce, distribute, publish, display, perform, modify, adapt, ;* transmit, broadcast, present, recite, release, license or otherwise exploit ;* any part of this publication in any form, by any means, without the prior ;* written permission of Insyde Software Corporation. ;* ;****************************************************************************** */ #include "UsbMassCbi.h" #ifndef MDEPKG_NDEBUG UINTN mUsbCbiInfo = EFI_D_INFO; UINTN mUsbCbiError = EFI_D_ERROR; #endif // // Function prototypes // STATIC EFI_STATUS UsbCbi0Init ( IN EFI_USB_IO_PROTOCOL *UsbIo, IN EFI_HANDLE Controller, OUT VOID **Context ); STATIC EFI_STATUS UsbCbi1Init ( IN EFI_USB_IO_PROTOCOL *UsbIo, IN EFI_HANDLE Controller, OUT VOID **Context ); EFI_STATUS EFIAPI UsbMassBotDriverEntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ); STATIC EFI_GUID mUsbMassTransportProtocolGuid = USB_MASS_TRANSPORT_PROTOCOL_GUID; STATIC EFI_USB_CORE_PROTOCOL *mUsbCore; STATIC BOOLEAN mInSmram; STATIC USB_MASS_TRANSPORT_PROTOCOL *mUsbCbi0Transport; STATIC USB_MASS_TRANSPORT_PROTOCOL *mUsbCbi1Transport; /** Register protocol for this driver. @param ImageHandle EFI_HANDLE @param SystemTable EFI_SYSTEM_TABLE pointer @retval EFI_SUCCESS Driver loaded @retval other Driver not loaded **/ EFI_STATUS EFIAPI UsbMassCbiDriverEntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_HANDLE Handle; UINTN Index; UINTN HandleCount; EFI_HANDLE *HandleBuffer; USB_MASS_TRANSPORT_PROTOCOL *Transport; // // Locate UsbCore protocol // Status = gBS->LocateProtocol ( &gEfiUsbCoreProtocolGuid, NULL, (VOID **)&mUsbCore ); if (EFI_ERROR (Status)) { return EFI_UNSUPPORTED; } // // Check the phase of instance // mUsbCore->IsInSmm (&mInSmram); if (!mInSmram) { // // Now in boot service, install instance // Status = mUsbCore->AllocateBuffer ( sizeof (USB_MASS_TRANSPORT_PROTOCOL) * 2, ALIGNMENT_32, (VOID **)&mUsbCbi0Transport ); if (EFI_ERROR (Status)) { return Status; } mUsbCbi0Transport->Protocol = USB_MASS_STORE_CBI0; mUsbCbi0Transport->Init = UsbCbi0Init; mUsbCbi0Transport->ExecCommand = UsbCbiExecCommand; mUsbCbi0Transport->Reset = UsbCbiResetDevice; mUsbCbi0Transport->Fini = UsbCbiFini; mUsbCbi1Transport = mUsbCbi0Transport + 1; mUsbCbi1Transport->Protocol = USB_MASS_STORE_CBI1; mUsbCbi1Transport->Init = UsbCbi1Init; mUsbCbi1Transport->ExecCommand = UsbCbiExecCommand; mUsbCbi1Transport->Reset = UsbCbiResetDevice; mUsbCbi1Transport->Fini = UsbCbiFini; // // Pass in a NULL to install to a new handle // Handle = NULL; Status = gBS->InstallProtocolInterface ( &Handle, &mUsbMassTransportProtocolGuid, EFI_NATIVE_INTERFACE, mUsbCbi0Transport ); if (EFI_ERROR (Status)) { return Status; } Handle = NULL; Status = gBS->InstallProtocolInterface ( &Handle, &mUsbMassTransportProtocolGuid, EFI_NATIVE_INTERFACE, mUsbCbi1Transport ); // // Register module in DXE instance // mUsbCore->ModuleRegistration (ImageHandle); } else { // // SMM instance, disable DEBUG message out // #ifndef MDEPKG_NDEBUG mUsbCbiInfo = 0; mUsbCbiError = 0; #endif // // Now in SMM, get instance installed by boot service // Status = gBS->LocateHandleBuffer ( ByProtocol, &mUsbMassTransportProtocolGuid, NULL, &HandleCount, &HandleBuffer ); if (EFI_ERROR(Status)) { return Status; } Transport = NULL; for (Index = 0; Index < HandleCount; Index ++) { Status = gBS->HandleProtocol ( HandleBuffer[Index], &mUsbMassTransportProtocolGuid, (VOID **)&Transport ); if (EFI_ERROR(Status)) { continue; } // // Setup appropriate variable in the Smm region // if (Transport->Protocol == USB_MASS_STORE_CBI0) { mUsbCbi0Transport = Transport; } else if (Transport->Protocol == USB_MASS_STORE_CBI1) { mUsbCbi1Transport = Transport; } } gBS->FreePool (HandleBuffer); // // Setup UsbCore for use by SMM instance // mUsbCore->AddressConvert ( SMM_ADDRESS, (VOID*)(UINTN)mUsbCore, (VOID**)&mUsbCore ); // // Register module in SMM instance // mUsbCore->ModuleRegistration (ImageHandle); // // Setup Smm address convert table for Smm security policy // mUsbCore->InsertAddressConvertTable ( ACT_FUNCTION_POINTER, &mUsbCbi0Transport->Init, 4 ); mUsbCore->InsertAddressConvertTable ( ACT_FUNCTION_POINTER, &mUsbCbi1Transport->Init, 4 ); mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_BODY, mUsbCbi0Transport, sizeof (USB_MASS_TRANSPORT_PROTOCOL) * 2 ); mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_POINTER, &mUsbCbi0Transport, 1 ); mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_POINTER, &mUsbCbi1Transport, 1 ); } return Status; } /** This function is used to manage a USB device with a control transfer pipe. @param This Indicates calling context. @param Request A pointer to the USB device request that will be sent to the USB device. @param Direction Indicates the data direction. @param Data A pointer to the buffer of data that will be transmitted to USB device or received from USB device. @param Timeout Indicates the transfer should be completed within this time frame. @param DataLength The size, in bytes, of the data buffer specified by Data. @param Status A pointer to the result of the USB transfer. @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER @retval EFI_OUT_OF_RESOURCES @retval EFI_TIMEOUT @retval EFI_DEVICE_ERROR **/ STATIC EFI_STATUS EFIAPI UsbControlTransfer ( IN EFI_USB_IO_PROTOCOL *This, IN EFI_USB_DEVICE_REQUEST *Request, IN EFI_USB_DATA_DIRECTION Direction, IN UINT32 Timeout, IN OUT VOID *Data, OPTIONAL IN UINTN DataLength, OPTIONAL OUT UINT32 *Status ) { EFI_USB_IO_CONTROL_TRANSFER UsbControlTransferFunc = This->UsbControlTransfer; if (mInSmram) { mUsbCore->AddressConvert ( SMM_ADDRESS, (VOID*)(UINTN)UsbControlTransferFunc, (VOID**)&UsbControlTransferFunc ); } return UsbControlTransferFunc ( This, Request, Direction, Timeout, Data, DataLength, Status ); } /** This function is used to manage a USB device with the bulk transfer pipe. @param This Indicates calling context. @param DeviceEndpoint The destination USB device endpoint to which the device request is being sent. @param Data A pointer to the buffer of data that will be transmitted to USB device or received from USB device. @param DataLength On input, the size, in bytes, of the data buffer specified by Data. On output, the number of bytes that were actually transferred. @param Timeout Indicates the transfer should be completed within this time frame. @param Status This parameter indicates the USB transfer status. @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER @retval EFI_OUT_OF_RESOURCES @retval EFI_TIMEOUT @retval EFI_DEVICE_ERROR **/ EFI_STATUS EFIAPI UsbBulkTransfer ( IN EFI_USB_IO_PROTOCOL *This, IN UINT8 DeviceEndpoint, IN OUT VOID *Data, IN OUT UINTN *DataLength, IN UINTN Timeout, OUT UINT32 *Status ) { EFI_USB_IO_BULK_TRANSFER UsbBulkTransferFunc = This->UsbBulkTransfer; if (mInSmram) { mUsbCore->AddressConvert ( SMM_ADDRESS, (VOID*)(UINTN)UsbBulkTransferFunc, (VOID**)&UsbBulkTransferFunc ); } return UsbBulkTransferFunc ( This, DeviceEndpoint, Data, DataLength, Timeout, Status ); } /*++ Usb Sync Interrupt Transfer @param This Indicates calling context. @param DeviceEndpoint The destination USB device endpoint to which the device request is being sent. @param Data A pointer to the buffer of data that will be transmitted to USB device or received from USB device. @param DataLength On input, the size, in bytes, of the data buffer specified by Data. On output, the number of bytes that were actually transferred. @param Timeout Indicates the transfer should be completed within this time frame. @param Status This parameter indicates the USB transfer status. @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER @retval EFI_OUT_OF_RESOURCES @retval EFI_TIMEOUT @retval EFI_DEVICE_ERROR --*/ EFI_STATUS EFIAPI UsbSyncInterruptTransfer ( IN EFI_USB_IO_PROTOCOL *This, IN UINT8 DeviceEndpoint, IN OUT VOID *Data, IN OUT UINTN *DataLength, IN UINTN Timeout, OUT UINT32 *Status ) { EFI_USB_IO_SYNC_INTERRUPT_TRANSFER UsbSyncInterruptTransferFunc = This->UsbSyncInterruptTransfer; if (mInSmram) { mUsbCore->AddressConvert ( SMM_ADDRESS, (VOID*)(UINTN)UsbSyncInterruptTransferFunc, (VOID**)&UsbSyncInterruptTransferFunc ); } return UsbSyncInterruptTransferFunc ( This, DeviceEndpoint, Data, DataLength, Timeout, Status ); } /** Retrieves the interface Descriptor for that controller. @param This Indicates the calling context. @param InterfaceDescriptor A pointer to the caller allocated USB interface Descriptor. @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER @retval EFI_NOT_FOUND **/ STATIC EFI_STATUS EFIAPI UsbGetInterfaceDescriptor ( IN EFI_USB_IO_PROTOCOL *This, OUT EFI_USB_INTERFACE_DESCRIPTOR *InterfaceDescriptor ) { EFI_USB_IO_GET_INTERFACE_DESCRIPTOR UsbGetInterfaceDescriptorFunc = This->UsbGetInterfaceDescriptor; if (mInSmram) { mUsbCore->AddressConvert ( SMM_ADDRESS, (VOID*)(UINTN)UsbGetInterfaceDescriptorFunc, (VOID**)&UsbGetInterfaceDescriptorFunc ); } return UsbGetInterfaceDescriptorFunc ( This, InterfaceDescriptor ); } /** Retrieves the endpoint Descriptor for a given endpoint. @param This Indicates the calling context. @param EndpointIndex Indicates which endpoint descriptor to retrieve. The valid range is 0..15. @param EndpointDescriptor A pointer to the caller allocated USB Endpoint Descriptor of a USB controller. @retval EFI_SUCCESS The endpoint descriptor was retrieved successfully. @retval EFI_INVALID_PARAMETER EndpointIndex is not valid. EndpointDescriptor is NULL. @retval EFI_NOT_FOUND The endpoint descriptor cannot be found. The device may not be correctly configured. **/ STATIC EFI_STATUS EFIAPI UsbGetEndpointDescriptor ( IN EFI_USB_IO_PROTOCOL *This, IN UINT8 EndpointIndex, OUT EFI_USB_ENDPOINT_DESCRIPTOR *EndpointDescriptor ) { EFI_USB_IO_GET_ENDPOINT_DESCRIPTOR UsbGetEndpointDescriptorFunc = This->UsbGetEndpointDescriptor; if (mInSmram) { mUsbCore->AddressConvert ( SMM_ADDRESS, (VOID*)(UINTN)UsbGetEndpointDescriptorFunc, (VOID**)&UsbGetEndpointDescriptorFunc ); } return UsbGetEndpointDescriptorFunc ( This, EndpointIndex, EndpointDescriptor ); } /** 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 UINT8 InterfaceProtocol, 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 // Status = mUsbCore->AllocateBuffer( sizeof (USB_CBI_PROTOCOL) + 3 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR), ALIGNMENT_32, (VOID **)&UsbCbi ); 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 = UsbGetInterfaceDescriptor (UsbIo, &UsbCbi->Interface); if (EFI_ERROR (Status)) { goto ON_ERROR; } Interface = &UsbCbi->Interface; // // Locate and save the bulk-in, bulk-out, and interrupt endpoint // for (Index = 0; Index < Interface->NumEndpoints; Index++) { Status = 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 ((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) || ((InterfaceProtocol == USB_MASS_STORE_CBI0) && (UsbCbi->InterruptEndpoint == NULL))) { Status = EFI_UNSUPPORTED; goto ON_ERROR; } if (Context != NULL) { // // Setup the data for UsbLegacy // CopyMem( &UsbCbi->Transport, (InterfaceProtocol == USB_MASS_STORE_CBI0) ? mUsbCbi0Transport : mUsbCbi1Transport, sizeof(USB_MASS_TRANSPORT_PROTOCOL) ); // // Setup Smm address convert table for Smm security policy // mUsbCore->InsertAddressConvertTable ( ACT_FUNCTION_POINTER, &UsbCbi->Transport.Init, 4 ); mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_BODY, UsbCbi, sizeof (USB_CBI_PROTOCOL) + 3 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR) ); mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbCbi->BulkInEndpoint, 1 ); mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbCbi->BulkOutEndpoint, 1 ); mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbCbi->InterruptEndpoint, 1 ); mUsbCore->InsertAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbCbi->UsbIo, 1 ); *Context = UsbCbi; } else { // // Free the instance due to the calling purpose is just to check is the device matches the protocol // Status = EFI_SUCCESS; goto ON_ERROR; } return EFI_SUCCESS; ON_ERROR: mUsbCore->FreeBuffer ( sizeof (USB_CBI_PROTOCOL) + 3 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR), UsbCbi ); return Status; } /** 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 UsbCbi0Init ( IN EFI_USB_IO_PROTOCOL *UsbIo, IN EFI_HANDLE Controller, OUT VOID **Context OPTIONAL ) { return UsbCbiInit( USB_MASS_STORE_CBI0, UsbIo, Controller, Context ); } /** 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 UsbCbi1Init ( IN EFI_USB_IO_PROTOCOL *UsbIo, IN EFI_HANDLE Controller, OUT VOID **Context OPTIONAL ) { return UsbCbiInit( USB_MASS_STORE_CBI1, UsbIo, Controller, Context ); } /** 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++) { // // Check is device been detached // if (mUsbCore->CheckDeviceDetached(UsbCbi->UsbIo) == EFI_SUCCESS) { return EFI_NOT_FOUND; } // // Use the UsbIo to send the command to the device // TransStatus = 0; DataLen = CmdLen; Status = 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)) { // // Stall 0.5ms for specific device compatibility(0.3ms minimum) // mUsbCore->Stall(500); 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) { // // Check is device been detached // if (mUsbCore->CheckDeviceDetached(UsbCbi->UsbIo) == EFI_SUCCESS) { return EFI_NOT_FOUND; } TransStatus = 0; if (Remain > (UINTN) USB_CBI_MAX_PACKET_NUM * Endpoint->MaxPacketSize) { Increment = USB_CBI_MAX_PACKET_NUM * Endpoint->MaxPacketSize; } else { Increment = Remain; } Status = 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) { mUsbCore->UsbClearDeviceFeature( UsbCbi->UsbIo, USB_TARGET_ENDPOINT, USB_FEATURE_ENDPOINT_HALT, Endpoint->EndpointAddress, &TransStatus ); } 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 Result status @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++) { // // Check is device been detached // if (mUsbCore->CheckDeviceDetached(UsbCbi->UsbIo) == EFI_SUCCESS) { return EFI_NOT_FOUND; } TransStatus = 0; Len = sizeof (USB_CBI_STATUS); Status = 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 (EFI_ERROR (Status) || UsbCbi->InterruptEndpoint == NULL) { DEBUG ((mUsbCbiError, "UsbCbiExecCommand: UsbCbiDataTransfer (%r)\n",Status)); return Status; } if (UsbCbi->Transport.Protocol == USB_MASS_STORE_CBI1) { // // No completion interrupt required // return EFI_SUCCESS; } // // Get the status for CBI0, 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); *CmdStatus = USB_MASS_CMD_PHASE_ERROR; break; 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; UINT32 ClearResult; UsbCbi = (USB_CBI_PROTOCOL *) Context; // // Check is device been detached // if (mUsbCore->CheckDeviceDetached(UsbCbi->UsbIo) == EFI_SUCCESS) { return EFI_NOT_FOUND; } // // 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); mUsbCore->Stall (50 * 1000); // // Clear the Bulk-In and Bulk-Out stall condition and // init data toggle. // mUsbCore->UsbClearDeviceFeature( UsbCbi->UsbIo, USB_TARGET_ENDPOINT, USB_FEATURE_ENDPOINT_HALT, UsbCbi->BulkInEndpoint->EndpointAddress, &ClearResult ); mUsbCore->UsbClearDeviceFeature( UsbCbi->UsbIo, USB_TARGET_ENDPOINT, USB_FEATURE_ENDPOINT_HALT, UsbCbi->BulkOutEndpoint->EndpointAddress, &ClearResult ); 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 ) { USB_CBI_PROTOCOL *UsbCbi; UsbCbi = (USB_CBI_PROTOCOL *) Context; mUsbCore->FreeBuffer ( sizeof (USB_CBI_PROTOCOL) + 3 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR), UsbCbi ); // // Remove Smm address convert table // mUsbCore->RemoveAddressConvertTable ( ACT_FUNCTION_POINTER, &UsbCbi->Transport.Init ); mUsbCore->RemoveAddressConvertTable ( ACT_INSTANCE_BODY, UsbCbi ); mUsbCore->RemoveAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbCbi->BulkInEndpoint ); mUsbCore->RemoveAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbCbi->BulkOutEndpoint ); mUsbCore->RemoveAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbCbi->InterruptEndpoint ); mUsbCore->RemoveAddressConvertTable ( ACT_INSTANCE_POINTER, &UsbCbi->UsbIo ); return EFI_SUCCESS; }