text: /** @file
    Fat File System driver routines that support EFI driver model.
  
  Copyright (c) 2005 - 2014, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent
  
  **/
  
  #include "Fat.h"
  
  /**
  
    Register Driver Binding protocol for this driver.
  
    @param  ImageHandle           - Handle for the image of this driver.
    @param  SystemTable           - Pointer to the EFI System Table.
  
    @retval EFI_SUCCESS           - Driver loaded.
    @return other                 - Driver not loaded.
  
  **/
  EFI_STATUS
  EFIAPI
  FatEntryPoint (
    IN EFI_HANDLE        ImageHandle,
    IN EFI_SYSTEM_TABLE  *SystemTable
    );
  
  /**
  
    Unload function for this image. Uninstall DriverBinding protocol.
  
    @param ImageHandle           - Handle for the image of this driver.
  
    @retval EFI_SUCCESS           - Driver unloaded successfully.
    @return other                 - Driver can not unloaded.
  
  **/
  EFI_STATUS
  EFIAPI
  FatUnload (
    IN EFI_HANDLE  ImageHandle
    );
  
  /**
  
    Test to see if this driver can add a file system to ControllerHandle.
    ControllerHandle must support both Disk IO and Block IO protocols.
  
    @param  This                  - Protocol instance pointer.
    @param  ControllerHandle      - Handle of device to test.
    @param  RemainingDevicePath   - Not used.
  
    @retval EFI_SUCCESS           - This driver supports this device.
    @retval EFI_ALREADY_STARTED   - This driver is already running on this device.
    @return other                 - This driver does not support this device.
  
  **/
  EFI_STATUS
  EFIAPI
  FatDriverBindingSupported (
    IN EFI_DRIVER_BINDING_PROTOCOL  *This,
    IN EFI_HANDLE                   Controller,
    IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
    );
  
  /**
  
    Start this driver on ControllerHandle by opening a Block IO and Disk IO
    protocol, reading Device Path. Add a Simple File System protocol to
    ControllerHandle if the media contains a valid file system.
  
    @param  This                  - Protocol instance pointer.
    @param  ControllerHandle      - Handle of device to bind driver to.
    @param  RemainingDevicePath   - Not used.
  
    @retval EFI_SUCCESS           - This driver is added to DeviceHandle.
    @retval EFI_ALREADY_STARTED   - This driver is already running on DeviceHandle.
    @retval EFI_OUT_OF_RESOURCES  - Can not allocate the memory.
    @return other                 - This driver does not support this device.
  
  **/
  EFI_STATUS
  EFIAPI
  FatDriverBindingStart (
    IN EFI_DRIVER_BINDING_PROTOCOL  *This,
    IN EFI_HANDLE                   Controller,
    IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
    );
  
  /**
  
    Stop this driver on ControllerHandle.
  
    @param  This                  - Protocol instance pointer.
    @param  ControllerHandle      - Handle of device to stop driver on.
    @param  NumberOfChildren      - Not used.
    @param  ChildHandleBuffer     - Not used.
  
    @retval EFI_SUCCESS           - This driver is removed DeviceHandle.
    @return other                 - This driver was not removed from this device.
  
  **/
  EFI_STATUS
  EFIAPI
  FatDriverBindingStop (
    IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
    IN  EFI_HANDLE                   Controller,
    IN  UINTN                        NumberOfChildren,
    IN  EFI_HANDLE                   *ChildHandleBuffer
    );
  
  //
  // DriverBinding protocol instance
  //
  EFI_DRIVER_BINDING_PROTOCOL  gFatDriverBinding = {
    FatDriverBindingSupported,
    FatDriverBindingStart,
    FatDriverBindingStop,
    0xa,
    NULL,
    NULL
  };
  
  /**
  
    Register Driver Binding protocol for this driver.
  
    @param  ImageHandle           - Handle for the image of this driver.
    @param  SystemTable           - Pointer to the EFI System Table.
  
    @retval EFI_SUCCESS           - Driver loaded.
    @return other                 - Driver not loaded.
  
  **/
  EFI_STATUS
  EFIAPI
  FatEntryPoint (
    IN EFI_HANDLE        ImageHandle,
    IN EFI_SYSTEM_TABLE  *SystemTable
    )
  {
    EFI_STATUS  Status;
  
    //
    // Initialize the EFI Driver Library
    //
    Status = EfiLibInstallDriverBindingComponentName2 (
               ImageHandle,
               SystemTable,
               &gFatDriverBinding,
               ImageHandle,
               &gFatComponentName,
               &gFatComponentName2
               );
    ASSERT_EFI_ERROR (Status);
  
    return Status;
  }
  
  /**
  
    Unload function for this image. Uninstall DriverBinding protocol.
  
    @param ImageHandle           - Handle for the image of this driver.
  
    @retval EFI_SUCCESS           - Driver unloaded successfully.
    @return other                 - Driver can not unloaded.
  
  **/
  EFI_STATUS
  EFIAPI
  FatUnload (
    IN EFI_HANDLE  ImageHandle
    )
  {
    EFI_STATUS  Status;
    EFI_HANDLE  *DeviceHandleBuffer;
    UINTN       DeviceHandleCount;
    UINTN       Index;
    VOID        *ComponentName;
    VOID        *ComponentName2;
  
    Status = gBS->LocateHandleBuffer (
                    AllHandles,
                    NULL,
                    NULL,
                    &DeviceHandleCount,
                    &DeviceHandleBuffer
                    );
    if (EFI_ERROR (Status)) {
      return Status;
    }
  
    for (Index = 0; Index < DeviceHandleCount; Index++) {
      Status = EfiTestManagedDevice (DeviceHandleBuffer[Index], ImageHandle, &gEfiDiskIoProtocolGuid);
      if (!EFI_ERROR (Status)) {
        Status = gBS->DisconnectController (
                        DeviceHandleBuffer[Index],
                        ImageHandle,
                        NULL
                        );
        if (EFI_ERROR (Status)) {
          break;
        }
      }
    }
  
    if (Index == DeviceHandleCount) {
      //
      // Driver is stopped successfully.
      //
      Status = gBS->HandleProtocol (ImageHandle, &gEfiComponentNameProtocolGuid, &ComponentName);
      if (EFI_ERROR (Status)) {
        ComponentName = NULL;
      }
  
      Status = gBS->HandleProtocol (ImageHandle, &gEfiComponentName2ProtocolGuid, &ComponentName2);
      if (EFI_ERROR (Status)) {
        ComponentName2 = NULL;
      }
  
      if (ComponentName == NULL) {
        if (ComponentName2 == NULL) {
          Status = gBS->UninstallMultipleProtocolInterfaces (
                          ImageHandle,
                          &gEfiDriverBindingProtocolGuid,
                          &gFatDriverBinding,
                          NULL
                          );
        } else {
          Status = gBS->UninstallMultipleProtocolInterfaces (
                          ImageHandle,
                          &gEfiDriverBindingProtocolGuid,
                          &gFatDriverBinding,
                          &gEfiComponentName2ProtocolGuid,
                          ComponentName2,
                          NULL
                          );
        }
      } else {
        if (ComponentName2 == NULL) {
          Status = gBS->UninstallMultipleProtocolInterfaces (
                          ImageHandle,
                          &gEfiDriverBindingProtocolGuid,
                          &gFatDriverBinding,
                          &gEfiComponentNameProtocolGuid,
                          ComponentName,
                          NULL
                          );
        } else {
          Status = gBS->UninstallMultipleProtocolInterfaces (
                          ImageHandle,
                          &gEfiDriverBindingProtocolGuid,
                          &gFatDriverBinding,
                          &gEfiComponentNameProtocolGuid,
                          ComponentName,
                          &gEfiComponentName2ProtocolGuid,
                          ComponentName2,
                          NULL
                          );
        }
      }
    }
  
    if (DeviceHandleBuffer != NULL) {
      FreePool (DeviceHandleBuffer);
    }
  
    return Status;
  }
  
  /**
  
    Test to see if this driver can add a file system to ControllerHandle.
    ControllerHandle must support both Disk IO and Block IO protocols.
  
    @param  This                  - Protocol instance pointer.
    @param  ControllerHandle      - Handle of device to test.
    @param  RemainingDevicePath   - Not used.
  
    @retval EFI_SUCCESS           - This driver supports this device.
    @retval EFI_ALREADY_STARTED   - This driver is already running on this device.
    @return other                 - This driver does not support this device.
  
  **/
  EFI_STATUS
  EFIAPI
  FatDriverBindingSupported (
    IN EFI_DRIVER_BINDING_PROTOCOL  *This,
    IN EFI_HANDLE                   ControllerHandle,
    IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
    )
  {
    EFI_STATUS            Status;
    EFI_DISK_IO_PROTOCOL  *DiskIo;
  
    //
    // Open the IO Abstraction(s) needed to perform the supported test
    //
    Status = gBS->OpenProtocol (
                    ControllerHandle,
                    &gEfiDiskIoProtocolGuid,
                    (VOID **)&DiskIo,
                    This->DriverBindingHandle,
                    ControllerHandle,
                    EFI_OPEN_PROTOCOL_BY_DRIVER
                    );
  
    if (EFI_ERROR (Status)) {
      return Status;
    }
  
    //
    // Close the I/O Abstraction(s) used to perform the supported test
    //
    gBS->CloseProtocol (
           ControllerHandle,
           &gEfiDiskIoProtocolGuid,
           This->DriverBindingHandle,
           ControllerHandle
           );
  
    //
    // Open the IO Abstraction(s) needed to perform the supported test
    //
    Status = gBS->OpenProtocol (
                    ControllerHandle,
                    &gEfiBlockIoProtocolGuid,
                    NULL,
                    This->DriverBindingHandle,
                    ControllerHandle,
                    EFI_OPEN_PROTOCOL_TEST_PROTOCOL
                    );
  
    return Status;
  }
  
  /**
  
    Start this driver on ControllerHandle by opening a Block IO and Disk IO
    protocol, reading Device Path. Add a Simple File System protocol to
    ControllerHandle if the media contains a valid file system.
  
    @param  This                  - Protocol instance pointer.
    @param  ControllerHandle      - Handle of device to bind driver to.
    @param  RemainingDevicePath   - Not used.
  
    @retval EFI_SUCCESS           - This driver is added to DeviceHandle.
    @retval EFI_ALREADY_STARTED   - This driver is already running on DeviceHandle.
    @retval EFI_OUT_OF_RESOURCES  - Can not allocate the memory.
    @return other                 - This driver does not support this device.
  
  **/
  EFI_STATUS
  EFIAPI
  FatDriverBindingStart (
    IN EFI_DRIVER_BINDING_PROTOCOL  *This,
    IN EFI_HANDLE                   ControllerHandle,
    IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
    )
  {
    EFI_STATUS             Status;
    EFI_BLOCK_IO_PROTOCOL  *BlockIo;
    EFI_DISK_IO_PROTOCOL   *DiskIo;
    EFI_DISK_IO2_PROTOCOL  *DiskIo2;
    BOOLEAN                LockedByMe;
  
    LockedByMe = FALSE;
    //
    // Acquire the lock.
    // If caller has already acquired the lock, cannot lock it again.
    //
    Status = FatAcquireLockOrFail ();
    if (!EFI_ERROR (Status)) {
      LockedByMe = TRUE;
    }
  
    Status = InitializeUnicodeCollationSupport (This->DriverBindingHandle);
    if (EFI_ERROR (Status)) {
      goto Exit;
    }
  
    //
    // Open our required BlockIo and DiskIo
    //
    Status = gBS->OpenProtocol (
                    ControllerHandle,
                    &gEfiBlockIoProtocolGuid,
                    (VOID **)&BlockIo,
                    This->DriverBindingHandle,
                    ControllerHandle,
                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
                    );
    if (EFI_ERROR (Status)) {
      goto Exit;
    }
  
    Status = gBS->OpenProtocol (
                    ControllerHandle,
                    &gEfiDiskIoProtocolGuid,
                    (VOID **)&DiskIo,
                    This->DriverBindingHandle,
                    ControllerHandle,
                    EFI_OPEN_PROTOCOL_BY_DRIVER
                    );
    if (EFI_ERROR (Status)) {
      goto Exit;
    }
  
    Status = gBS->OpenProtocol (
                    ControllerHandle,
                    &gEfiDiskIo2ProtocolGuid,
                    (VOID **)&DiskIo2,
                    This->DriverBindingHandle,
                    ControllerHandle,
                    EFI_OPEN_PROTOCOL_BY_DRIVER
                    );
    if (EFI_ERROR (Status)) {
      DiskIo2 = NULL;
    }
  
    //
    // Allocate Volume structure. In FatAllocateVolume(), Resources
    // are allocated with protocol installed and cached initialized
    //
    Status = FatAllocateVolume (ControllerHandle, DiskIo, DiskIo2, BlockIo);
  
    //
    // When the media changes on a device it will Reinstall the BlockIo interface.
    // This will cause a call to our Stop(), and a subsequent reentrant call to our
    // Start() successfully. We should leave the device open when this happen.
    //
    if (EFI_ERROR (Status)) {
      Status = gBS->OpenProtocol (
                      ControllerHandle,
                      &gEfiSimpleFileSystemProtocolGuid,
                      NULL,
                      This->DriverBindingHandle,
                      ControllerHandle,
                      EFI_OPEN_PROTOCOL_TEST_PROTOCOL
                      );
      if (EFI_ERROR (Status)) {
        gBS->CloseProtocol (
               ControllerHandle,
               &gEfiDiskIoProtocolGuid,
               This->DriverBindingHandle,
               ControllerHandle
               );
        gBS->CloseProtocol (
               ControllerHandle,
               &gEfiDiskIo2ProtocolGuid,
               This->DriverBindingHandle,
               ControllerHandle
               );
      }
    }
  
  Exit:
    //
    // Unlock if locked by myself.
    //
    if (LockedByMe) {
      FatReleaseLock ();
    }
  
    return Status;
  }
  
  /**
  
    Stop this driver on ControllerHandle.
  
    @param  This                  - Protocol instance pointer.
    @param  ControllerHandle      - Handle of device to stop driver on.
    @param  NumberOfChildren      - Not used.
    @param  ChildHandleBuffer     - Not used.
  
    @retval EFI_SUCCESS           - This driver is removed DeviceHandle.
    @return other                 - This driver was not removed from this device.
  
  **/
  EFI_STATUS
  EFIAPI
  FatDriverBindingStop (
    IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
    IN  EFI_HANDLE                   ControllerHandle,
    IN  UINTN                        NumberOfChildren,
    IN  EFI_HANDLE                   *ChildHandleBuffer
    )
  {
    EFI_STATUS                       Status;
    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  *FileSystem;
    FAT_VOLUME                       *Volume;
    EFI_DISK_IO2_PROTOCOL            *DiskIo2;
  
    DiskIo2 = NULL;
    //
    // Get our context back
    //
    Status = gBS->OpenProtocol (
                    ControllerHandle,
                    &gEfiSimpleFileSystemProtocolGuid,
                    (VOID **)&FileSystem,
                    This->DriverBindingHandle,
                    ControllerHandle,
                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
                    );
  
    if (!EFI_ERROR (Status)) {
      Volume  = VOLUME_FROM_VOL_INTERFACE (FileSystem);
      DiskIo2 = Volume->DiskIo2;
      Status  = FatAbandonVolume (Volume);
    }
  
    if (!EFI_ERROR (Status)) {
      if (DiskIo2 != NULL) {
        Status = gBS->CloseProtocol (
                        ControllerHandle,
                        &gEfiDiskIo2ProtocolGuid,
                        This->DriverBindingHandle,
                        ControllerHandle
                        );
        ASSERT_EFI_ERROR (Status);
      }
  
      Status = gBS->CloseProtocol (
                      ControllerHandle,
                      &gEfiDiskIoProtocolGuid,
                      This->DriverBindingHandle,
                      ControllerHandle
                      );
      ASSERT_EFI_ERROR (Status);
    }
  
    return Status;
  }
  