/********************************************************************************
Copyright (C) 2016 Marvell International Ltd.
Copyright (c) 2021-2022, Rockchip Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent

*******************************************************************************/

#include <Uefi.h>

#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/ShellCommandLib.h>
#include <Library/ShellLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/PrintLib.h>
#include <Library/HiiLib.h>

#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/UefiBootServicesTableLib.h>

#include <Protocol/I2cDemo.h>
#include <Protocol/I2c.h>

CONST CHAR16 ShellI2cDemoFileName[] = L"I2cDemoTestShellCommand";
EFI_HANDLE ShellI2cDemoHiiHandle = NULL;

STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
  {L"read", TypeFlag},
  {L"write", TypeFlag},
  {L"list", TypeFlag},
  {L"help", TypeFlag},
  {NULL , TypeMax}
  };

/**
  Return the file name of the help text file if not using HII.

  @return The string pointer to the file name.
**/
STATIC
CONST CHAR16*
EFIAPI
ShellCommandGetManFileNameI2cDemo (
  VOID
  )
{
  return ShellI2cDemoFileName;
}

STATIC
VOID
Usage (
  VOID
  )
{
  Print (L"I2C DEMO TEST commands:\n"
         "i2cdemo [read] [write] [list] [<Bus>][<Address>] [<Length>] [<RegAddress>] [<RegAddressLength>] [<Data>] \n"
         "All modes except 'list' require Address, Length and Chip set.\n\n"
         "read    - read from i2cdemo device\n"
         "write   - write Data to i2cdemo device\\n"
         "list    - list available i2cdemo devices\n\n"
         "Bus    - I2C bus address\n"
	 "Address    - i2cdemo bus address\n"
         "Length	  - data byte length to read/write\\n"
         "RegAddress - address in i2cdemo to read/write\n"
         "RegAddressLength - address in i2cdemo length\n"
         "Data    - data byte to be written\n"
         "Examples:\n"
         "List devices:\n"
         " i2cdemo list\n"
         "Read 2 bytes from address 0x10 in chip 0x51@bus2:\n"
         " i2cdemo read 2 0x51 2 0x10 1\n"
         "Fill 16 bytes with 0xab at address 0x0 in chip 0x57:\n"
         " i2cdemo write 2 0x51 1 0x10 1 0x00\n"
  );
}

STATIC
EFI_STATUS
I2cDemoList (
    )
{
  EFI_HANDLE *HandleBuffer;
  EFI_STATUS Status;
  UINTN ProtocolCount;
  ROCKCHIP_I2CDEMO_PROTOCOL *I2cDemoProtocol;
  UINTN i;
  Status = gBS->LocateHandleBuffer ( ByProtocol,
                                     &gRockchipI2cDemoProtocolGuid,
                                     NULL,
                                     &ProtocolCount,
                                     &HandleBuffer
                                   );
  if (ProtocolCount == 0) {
    Print (L"0 devices found.\n");
  } else {
    Print (L"%u devices found: ", ProtocolCount);
  }

  for (i = 0; i < ProtocolCount; i++) {
    Status = gBS->OpenProtocol (
                    HandleBuffer[i],
                    &gRockchipI2cDemoProtocolGuid,
                    (VOID **) &I2cDemoProtocol,
                    gImageHandle,
                    NULL,
                    EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL );
    Print (L"0x%x at bus %d\n", I2C_DEVICE_ADDRESS(I2cDemoProtocol->Identifier),
        I2C_DEVICE_BUS(I2cDemoProtocol->Identifier));
    Status = gBS->CloseProtocol ( HandleBuffer[i],
                                  &gRockchipI2cDemoProtocolGuid,
                                  gImageHandle,
                                  NULL );
  }
  Print (L"\n");
  return Status;
}

STATIC
EFI_STATUS
I2cDemoLocateProtocol (
 IN  UINT32 Identifier,
 OUT EFI_HANDLE *FoundHandle,
 OUT ROCKCHIP_I2CDEMO_PROTOCOL **FoundI2cDemoProtocol
 )
{
  EFI_HANDLE *HandleBuffer;
  EFI_STATUS Status;
  UINTN ProtocolCount;
  ROCKCHIP_I2CDEMO_PROTOCOL *I2cDemoProtocol;
  UINTN i;
  Status = gBS->LocateHandleBuffer ( ByProtocol,
                                     &gRockchipI2cDemoProtocolGuid,
                                     NULL,
                                     &ProtocolCount,
                                     &HandleBuffer
                                   );
  if (EFI_ERROR(Status)) {
    return Status;
  }
  for (i = 0; i < ProtocolCount; i++) {
    Status = gBS->OpenProtocol (
                    HandleBuffer[i],
                    &gRockchipI2cDemoProtocolGuid,
                    (VOID **) &I2cDemoProtocol,
                    gImageHandle,
                    NULL,
                    EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL );

    if (I2cDemoProtocol->Identifier == Identifier) {
      *FoundI2cDemoProtocol = I2cDemoProtocol;
      *FoundHandle = HandleBuffer[i];
      return EFI_SUCCESS;
    }
    Status = gBS->CloseProtocol ( HandleBuffer[i],
                                  &gRockchipI2cDemoProtocolGuid,
                                  gImageHandle,
                                  NULL );
  }
  *FoundI2cDemoProtocol = NULL;

  return EFI_UNSUPPORTED;
}

SHELL_STATUS
EFIAPI
ShellCommandRunI2cDemo (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS              Status;
  LIST_ENTRY              *CheckPackage;
  CHAR16                  *ProblemParam;
  CONST CHAR16            *ValueStr;
  UINTN                   Bus, Address, XferLength, RegAddress, RegAddressLength, Source;
  UINT8                   *Buffer;
  BOOLEAN                 ReadMode, WriteMode;
  EFI_HANDLE              Handle, ProtHandle;
  ROCKCHIP_I2CDEMO_PROTOCOL *I2cDemoProtocol = NULL;
  UINTN                   HandleSize, i;
  UINTN                   TxData;

  Handle = NULL;
  Source = 0;
  HandleSize = 2 * sizeof (EFI_HANDLE);

  Status = gBS->LocateHandle (ByProtocol, &gRockchipI2cDemoProtocolGuid, NULL,
    &HandleSize, &ProtHandle);
  if (EFI_ERROR(Status)) {
    Print (L"No I2cDemo protocol, connect I2C stack\n");
    Status = gBS->LocateHandle (ByProtocol, &gEfiI2cMasterProtocolGuid, NULL,
      &HandleSize, &ProtHandle);
    if (EFI_ERROR(Status)) {
      Print (L"Failed to locate I2cMaster protocol, abort!\n");
      return SHELL_ABORTED;
    }
    Status = gBS->ConnectController (ProtHandle, NULL, NULL, TRUE);
    if (EFI_ERROR(Status)) {
      Print (L"Cannot connect I2C stack, abort!\n");
      return SHELL_ABORTED;
    }
  }

  Status = ShellInitialize ();
  if (EFI_ERROR (Status)) {
    Print (L"Error - failed to initialize shell\n");
    return SHELL_ABORTED;
  }

  Status = ShellCommandLineParse (ParamList, &CheckPackage, &ProblemParam, TRUE);
  if (EFI_ERROR (Status)) {
    Print (L"Error - failed to parse command line\n");
    return SHELL_ABORTED;
  }

  if (ShellCommandLineGetFlag (CheckPackage, L"list")) {
    I2cDemoList();
    return SHELL_SUCCESS;
  }

  if (ShellCommandLineGetFlag (CheckPackage, L"help")) {
    Usage();
    return SHELL_SUCCESS;
  }

  ReadMode = ShellCommandLineGetFlag (CheckPackage, L"read");
  WriteMode = ShellCommandLineGetFlag (CheckPackage, L"write");

  if (!ReadMode && !WriteMode) {
    Print (L"Not support mode given.\n");
    Usage();
    return SHELL_ABORTED;
  }

  if (ShellCommandLineGetCount(CheckPackage) != 6 && ReadMode) {
    Print (L"Not enough arguments given.\n");
    Usage();
    return SHELL_ABORTED;
  }

  if (ShellCommandLineGetCount(CheckPackage) != 7 && WriteMode) {
    Print (L"Not enough arguments given.\n");
    Usage();
    return SHELL_ABORTED;
  }

  ValueStr = ShellCommandLineGetRawValue(CheckPackage, 1);
  Bus = ShellHexStrToUintn (ValueStr);

  ValueStr = ShellCommandLineGetRawValue(CheckPackage, 2);
  Address = ShellHexStrToUintn (ValueStr);

  ValueStr = ShellCommandLineGetRawValue(CheckPackage, 3);
  XferLength = ShellHexStrToUintn (ValueStr);

  ValueStr = ShellCommandLineGetRawValue(CheckPackage, 4);
  RegAddress = ShellHexStrToUintn (ValueStr);

  ValueStr = ShellCommandLineGetRawValue(CheckPackage, 5);
  RegAddressLength = ShellHexStrToUintn (ValueStr);

  if (WriteMode) {
    ValueStr = ShellCommandLineGetRawValue(CheckPackage, 6);
    TxData = ShellHexStrToUintn (ValueStr);
  }

  I2cDemoLocateProtocol (I2C_DEVICE_INDEX(Bus, Address), &Handle, &I2cDemoProtocol);
  if (I2cDemoProtocol == NULL) {
    Print (L"Failed to locate I2CDEMO protocol.\n");
    return SHELL_INVALID_PARAMETER;
  }

    Buffer = AllocateZeroPool (XferLength);
    if (Buffer == NULL) {
      Status = SHELL_OUT_OF_RESOURCES;
      Print (L"Error - out of resources.\n");
      goto out_close;
    }

  if (ReadMode) {
    Status = I2cDemoProtocol->Read(I2cDemoProtocol, (UINT8 *)&RegAddress, RegAddressLength,  Buffer, XferLength);
    if (!EFI_ERROR(Status)) {
      Print (L"Read data[0 ~ %d]:", XferLength - 1);
      for (i = 0; i <XferLength; i++ ) {
        Print (L" 0x%x", Buffer[i]);
      }
      Print (L"\n");
    }
  } else {
    Status = I2cDemoProtocol->Write(I2cDemoProtocol, (UINT8 *)&RegAddress, RegAddressLength,  (UINT8 *)&TxData, XferLength);
  }

  if (EFI_ERROR(Status)) {
    Print (L"I2c Operation failed %d.\n", Status);
  } else {
    Print (L"I2c Operation successfully.\n");
  }

  Status = SHELL_SUCCESS;

    FreePool(Buffer);
out_close:
  gBS->CloseProtocol ( Handle,
                       &gRockchipI2cDemoProtocolGuid,
                       gImageHandle,
                       NULL );

  return Status;
}

EFI_STATUS
EFIAPI
ShellI2cDemoTestLibConstructor (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{

  Print (L"~Filed to add Hii package\n");
  ShellI2cDemoHiiHandle = NULL;

  ShellI2cDemoHiiHandle = HiiAddPackages (
                          &gShellI2cDemoHiiGuid, gImageHandle,
                          UefiShellI2cDemoLibStrings, NULL
                          );
  if (ShellI2cDemoHiiHandle == NULL) {
    Print (L"Filed to add Hii package\n");
    return EFI_DEVICE_ERROR;
  }
  ShellCommandRegisterCommandName (
     L"i2cdemo", ShellCommandRunI2cDemo, ShellCommandGetManFileNameI2cDemo, 0,
     L"i2cdemo", TRUE , ShellI2cDemoHiiHandle, STRING_TOKEN (STR_GET_HELP_I2CDEMO)
     );

  return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
ShellI2cDemoTestLibDestructor (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{

  if (ShellI2cDemoHiiHandle != NULL) {
    HiiRemovePackages (ShellI2cDemoHiiHandle);
  }
  return EFI_SUCCESS;
}
