/*++ Copyright (c) 2006 - 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: UsbMassStorage.c Abstract: USB Mass Storage Driver Revision History --*/ #include "UsbMassStorage.h" #include "UsbMassStorageHelper.h" // // Block I/O Protocol Interface // STATIC EFI_STATUS EFIAPI USBFloppyReset ( IN EFI_BLOCK_IO_PROTOCOL *This, IN BOOLEAN ExtendedVerification ); STATIC EFI_STATUS EFIAPI USBFloppyReadBlocks ( IN EFI_BLOCK_IO_PROTOCOL *This, IN UINT32 MediaId, IN EFI_LBA LBA, IN UINTN BufferSize, OUT VOID *Buffer ); STATIC EFI_STATUS EFIAPI USBFloppyWriteBlocks ( IN EFI_BLOCK_IO_PROTOCOL *This, IN UINT32 MediaId, IN EFI_LBA LBA, IN UINTN BufferSize, IN VOID *Buffer ); STATIC EFI_STATUS EFIAPI USBFloppyFlushBlocks ( IN EFI_BLOCK_IO_PROTOCOL *This ); // // USB Floppy Driver Global Variables // EFI_DRIVER_BINDING_PROTOCOL gUSBFloppyDriverBinding = { USBFloppyDriverBindingSupported, USBFloppyDriverBindingStart, USBFloppyDriverBindingStop, 0xa, NULL, NULL }; EFI_STATUS EFIAPI USBFloppyDriverBindingSupported ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) /*++ Routine Description: Test to see if this driver supports ControllerHandle. Any ControllerHandle that has UsbHcProtocol installed will be supported. Arguments: This - Protocol instance pointer. Controller - Handle of device to test RemainingDevicePath - Not used Returns: EFI_SUCCESS - This driver supports this device. EFI_UNSUPPORTED - This driver does not support this device. --*/ { EFI_STATUS OpenStatus; EFI_USB_ATAPI_PROTOCOL *AtapiProtocol; // // check whether EFI_USB_ATAPI_PROTOCOL exists, if it does, // then the controller must be a USB Mass Storage Controller // OpenStatus = gBS->OpenProtocol ( Controller, &gEfiUsbAtapiProtocolGuid, (VOID **) &AtapiProtocol, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (OpenStatus)) { return OpenStatus; } gBS->CloseProtocol ( Controller, &gEfiUsbAtapiProtocolGuid, This->DriverBindingHandle, Controller ); return EFI_SUCCESS; } EFI_STATUS EFIAPI USBFloppyDriverBindingStart ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) /*++ Routine Description: Starting the Usb Bus Driver Arguments: This - Protocol instance pointer. Controller - Handle of device to test RemainingDevicePath - Not used Returns: EFI_SUCCESS - This driver supports this device. EFI_UNSUPPORTED - This driver does not support this device. EFI_DEVICE_ERROR - This driver cannot be started due to device Error EFI_OUT_OF_RESOURCES- Can't allocate memory resources EFI_ALREADY_STARTED - Thios driver has been started --*/ { EFI_STATUS Status; EFI_USB_ATAPI_PROTOCOL *AtapiProtocol; USB_FLOPPY_DEV *UsbFloppyDevice; UsbFloppyDevice = NULL; // // Check whether Usb Atapi Protocol attached on the controller handle. // Status = gBS->OpenProtocol ( Controller, &gEfiUsbAtapiProtocolGuid, (VOID **) &AtapiProtocol, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status)) { return Status; } Status = gBS->AllocatePool ( EfiBootServicesData, sizeof (USB_FLOPPY_DEV), (VOID **) &UsbFloppyDevice ); if (EFI_ERROR (Status)) { gBS->CloseProtocol ( Controller, &gEfiUsbAtapiProtocolGuid, This->DriverBindingHandle, Controller ); return Status; } ZeroMem (UsbFloppyDevice, sizeof (USB_FLOPPY_DEV)); UsbFloppyDevice->Handle = Controller; UsbFloppyDevice->BlkIo.Media = &UsbFloppyDevice->BlkMedia; UsbFloppyDevice->Signature = USB_FLOPPY_DEV_SIGNATURE; UsbFloppyDevice->BlkIo.Reset = USBFloppyReset; UsbFloppyDevice->BlkIo.ReadBlocks = USBFloppyReadBlocks; UsbFloppyDevice->BlkIo.WriteBlocks = USBFloppyWriteBlocks; UsbFloppyDevice->BlkIo.FlushBlocks = USBFloppyFlushBlocks; UsbFloppyDevice->AtapiProtocol = AtapiProtocol; // // Identify drive type and retrieve media information. // Status = USBFloppyIdentify (UsbFloppyDevice); if (EFI_ERROR (Status)) { if (UsbFloppyDevice->SenseData != NULL) { gBS->FreePool (UsbFloppyDevice->SenseData); } gBS->FreePool (UsbFloppyDevice); gBS->CloseProtocol ( Controller, &gEfiUsbAtapiProtocolGuid, This->DriverBindingHandle, Controller ); return Status; } // // Install Block I/O protocol for the usb floppy device. // Status = gBS->InstallProtocolInterface ( &Controller, &gEfiBlockIoProtocolGuid, EFI_NATIVE_INTERFACE, &UsbFloppyDevice->BlkIo ); if (EFI_ERROR (Status)) { if (UsbFloppyDevice->SenseData != NULL) { gBS->FreePool (UsbFloppyDevice->SenseData); } gBS->FreePool (UsbFloppyDevice); gBS->CloseProtocol ( Controller, &gEfiUsbAtapiProtocolGuid, This->DriverBindingHandle, Controller ); return Status; } return EFI_SUCCESS; } EFI_STATUS EFIAPI USBFloppyDriverBindingStop ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer ) /*++ Routine Description: Stop this driver on ControllerHandle. Support stoping any child handles created by this driver. Arguments: This - Protocol instance pointer. Controller - Handle of device to stop driver on NumberOfChildren - Number of Children in the ChildHandleBuffer ChildHandleBuffer - List of handles for the children we need to stop. Returns: EFI_SUCCESS EFI_DEVICE_ERROR others --*/ { EFI_STATUS Status; USB_FLOPPY_DEV *UsbFloppyDevice; EFI_BLOCK_IO_PROTOCOL *BlkIo; // // First find USB_FLOPPY_DEV // gBS->OpenProtocol ( Controller, &gEfiBlockIoProtocolGuid, (VOID **) &BlkIo, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); UsbFloppyDevice = USB_FLOPPY_DEV_FROM_THIS (BlkIo); // // Uninstall Block I/O protocol from the device handle // Status = gBS->UninstallProtocolInterface ( Controller, &gEfiBlockIoProtocolGuid, &UsbFloppyDevice->BlkIo ); if (EFI_ERROR (Status)) { return Status; } // // Stop using EFI_USB_ATAPI_PROTOCOL // gBS->CloseProtocol ( Controller, &gEfiUsbAtapiProtocolGuid, This->DriverBindingHandle, Controller ); if (UsbFloppyDevice->SenseData != NULL) { gBS->FreePool (UsbFloppyDevice->SenseData); } gBS->FreePool (UsbFloppyDevice); return EFI_SUCCESS; } STATIC EFI_STATUS EFIAPI USBFloppyReset ( IN EFI_BLOCK_IO_PROTOCOL *This, IN BOOLEAN ExtendedVerification ) /*++ Routine Description: Implements EFI_BLOCK_IO_PROTOCOL.Reset() function. Arguments: This The EFI_BLOCK_IO_PROTOCOL instance. ExtendedVerification Indicates that the driver may perform a more exhaustive verification operation of the device during reset. (This parameter is ingored in this driver.) Returns: EFI_SUCCESS - Success --*/ { USB_FLOPPY_DEV *UsbFloppyDevice; EFI_USB_ATAPI_PROTOCOL *UsbAtapiInterface; EFI_STATUS Status; EFI_TPL OldTpl; OldTpl = gBS->RaiseTPL (EFI_TPL_CALLBACK); UsbFloppyDevice = USB_FLOPPY_DEV_FROM_THIS (This); UsbAtapiInterface = UsbFloppyDevice->AtapiProtocol; // // directly calling EFI_USB_ATAPI_PROTOCOL.Reset() to implement reset. // Status = UsbAtapiInterface->UsbAtapiReset (UsbAtapiInterface, ExtendedVerification); gBS->RestoreTPL (OldTpl); return Status; } STATIC EFI_STATUS EFIAPI USBFloppyReadBlocks ( IN EFI_BLOCK_IO_PROTOCOL *This, IN UINT32 MediaId, IN EFI_LBA LBA, IN UINTN BufferSize, OUT VOID *Buffer ) /*++ Routine Description: Implements EFI_BLOCK_IO_PROTOCOL.ReadBlocks() function. Arguments: This The EFI_BLOCK_IO_PROTOCOL instance. MediaId The media id that the read request is for. LBA The starting logical block address to read from on the device. BufferSize The size of the Buffer in bytes. This must be a multiple of the intrinsic block size of the device. Buffer A pointer to the destination buffer for the data. The caller is responsible for either having implicit or explicit ownership of the buffer. Returns: EFI_INVALID_PARAMETER - Parameter is error EFI_SUCCESS - Success EFI_DEVICE_ERROR - Hardware Error EFI_NO_MEDIA - No media EFI_MEDIA_CHANGED - Media Change EFI_BAD_BUFFER_SIZE - Buffer size is bad --*/ { USB_FLOPPY_DEV *UsbFloppyDevice; EFI_STATUS Status; EFI_BLOCK_IO_MEDIA *Media; UINTN BlockSize; UINTN NumberOfBlocks; BOOLEAN MediaChange; EFI_TPL OldTpl; Status = EFI_SUCCESS; MediaChange = FALSE; UsbFloppyDevice = USB_FLOPPY_DEV_FROM_THIS (This); // // Check parameters // if (Buffer == NULL) { return EFI_INVALID_PARAMETER; } if (BufferSize == 0) { return EFI_SUCCESS; } OldTpl = gBS->RaiseTPL (EFI_TPL_CALLBACK); UsbFloppyTestUnitReady (UsbFloppyDevice); Status = UsbFloppyDetectMedia (UsbFloppyDevice, &MediaChange); if (EFI_ERROR (Status)) { Status = EFI_DEVICE_ERROR; goto Done; } if (MediaChange) { gBS->ReinstallProtocolInterface ( UsbFloppyDevice->Handle, &gEfiBlockIoProtocolGuid, &UsbFloppyDevice->BlkIo, &UsbFloppyDevice->BlkIo ); } Media = UsbFloppyDevice->BlkIo.Media; BlockSize = Media->BlockSize; NumberOfBlocks = BufferSize / BlockSize; if (!(Media->MediaPresent)) { Status = EFI_NO_MEDIA; goto Done; } if (MediaId != Media->MediaId) { Status = EFI_MEDIA_CHANGED; goto Done; } if (BufferSize % BlockSize != 0) { Status = EFI_BAD_BUFFER_SIZE; goto Done; } if (LBA > Media->LastBlock) { Status = EFI_INVALID_PARAMETER; goto Done; } if ((LBA + NumberOfBlocks - 1) > Media->LastBlock) { Status = EFI_INVALID_PARAMETER; goto Done; } if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) { Status = EFI_INVALID_PARAMETER; goto Done; } while (NumberOfBlocks > 0) { if (NumberOfBlocks > BLOCK_UNIT) { Status = USBFloppyRead10 (UsbFloppyDevice, Buffer, LBA, BLOCK_UNIT); } else { Status = USBFloppyRead10 (UsbFloppyDevice, Buffer, LBA, NumberOfBlocks); } if (EFI_ERROR (Status)) { This->Reset (This, TRUE); Status = EFI_DEVICE_ERROR; goto Done; } if (NumberOfBlocks > BLOCK_UNIT) { NumberOfBlocks -= BLOCK_UNIT; LBA += BLOCK_UNIT; Buffer = (UINT8 *) Buffer + This->Media->BlockSize * BLOCK_UNIT; } else { NumberOfBlocks -= NumberOfBlocks; LBA += NumberOfBlocks; Buffer = (UINT8 *) Buffer + This->Media->BlockSize * NumberOfBlocks; } } Done: gBS->RestoreTPL (OldTpl); return Status; } STATIC EFI_STATUS EFIAPI USBFloppyWriteBlocks ( IN EFI_BLOCK_IO_PROTOCOL *This, IN UINT32 MediaId, IN EFI_LBA LBA, IN UINTN BufferSize, IN VOID *Buffer ) /*++ Routine Description: Implements EFI_BLOCK_IO_PROTOCOL.WriteBlocks() function. Arguments: This The EFI_BLOCK_IO_PROTOCOL instance. MediaId The media id that the write request is for. LBA The starting logical block address to be written. The caller is responsible for writing to only legitimate locations. BufferSize The size of the Buffer in bytes. This must be a multiple of the intrinsic block size of the device. Buffer A pointer to the source buffer for the data. The caller is responsible for either having implicit or explicit ownership of the buffer. Returns: EFI_INVALID_PARAMETER - Parameter is error EFI_SUCCESS - Success EFI_DEVICE_ERROR - Hardware Error EFI_NO_MEDIA - No media EFI_MEDIA_CHANGED - Media Change EFI_BAD_BUFFER_SIZE - Buffer size is bad --*/ { USB_FLOPPY_DEV *UsbFloppyDevice; EFI_STATUS Status; EFI_BLOCK_IO_MEDIA *Media; UINTN BlockSize; UINTN NumberOfBlocks; BOOLEAN MediaChange; EFI_TPL OldTpl; Status = EFI_SUCCESS; MediaChange = FALSE; UsbFloppyDevice = USB_FLOPPY_DEV_FROM_THIS (This); // // Check parameters // if (Buffer == NULL) { return EFI_INVALID_PARAMETER; } if (BufferSize == 0) { return EFI_SUCCESS; } OldTpl = gBS->RaiseTPL (EFI_TPL_CALLBACK); UsbFloppyTestUnitReady (UsbFloppyDevice); Status = UsbFloppyDetectMedia (UsbFloppyDevice, &MediaChange); if (EFI_ERROR (Status)) { Status = EFI_DEVICE_ERROR; goto Done; } if (MediaChange) { gBS->ReinstallProtocolInterface ( UsbFloppyDevice->Handle, &gEfiBlockIoProtocolGuid, &UsbFloppyDevice->BlkIo, &UsbFloppyDevice->BlkIo ); } Media = UsbFloppyDevice->BlkIo.Media; BlockSize = Media->BlockSize; NumberOfBlocks = BufferSize / BlockSize; if (!(Media->MediaPresent)) { Status = EFI_NO_MEDIA; goto Done; } if (MediaId != Media->MediaId) { Status = EFI_MEDIA_CHANGED; goto Done; } if (BufferSize % BlockSize != 0) { Status = EFI_BAD_BUFFER_SIZE; goto Done; } if (LBA > Media->LastBlock) { Status = EFI_INVALID_PARAMETER; goto Done; } if ((LBA + NumberOfBlocks - 1) > Media->LastBlock) { Status = EFI_INVALID_PARAMETER; goto Done; } if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) { Status = EFI_INVALID_PARAMETER; goto Done; } if (UsbFloppyDevice->BlkMedia.ReadOnly) { Status = EFI_WRITE_PROTECTED; goto Done; } while (NumberOfBlocks > 0) { if (NumberOfBlocks > BLOCK_UNIT) { Status = USBFloppyWrite10 (UsbFloppyDevice, Buffer, LBA, BLOCK_UNIT); } else { Status = USBFloppyWrite10 (UsbFloppyDevice, Buffer, LBA, NumberOfBlocks); } if (EFI_ERROR (Status)) { This->Reset (This, TRUE); Status = EFI_DEVICE_ERROR; goto Done; } if (NumberOfBlocks > BLOCK_UNIT) { NumberOfBlocks -= BLOCK_UNIT; LBA += BLOCK_UNIT; Buffer = (UINT8 *) Buffer + This->Media->BlockSize * BLOCK_UNIT; } else { NumberOfBlocks -= NumberOfBlocks; LBA += NumberOfBlocks; Buffer = (UINT8 *) Buffer + This->Media->BlockSize * NumberOfBlocks; } } Done: gBS->RestoreTPL (OldTpl); return Status; } STATIC EFI_STATUS EFIAPI USBFloppyFlushBlocks ( IN EFI_BLOCK_IO_PROTOCOL *This ) /*++ Routine Description: Implements EFI_BLOCK_IO_PROTOCOL.FlushBlocks() function. (In this driver, this function just returns EFI_SUCCESS.) Arguments: This The EFI_BLOCK_IO_PROTOCOL instance. Returns: EFI_SUCCESS - Success --*/ { return EFI_SUCCESS; }