text: /*******************************************************************************
    I2C Driver Implementation.
  
    Company:
      Microchip Technology Inc.
  
    File Name:
      drv_i2c.c
  
    Summary:
      Source code for the I2C driver dynamic implementation.
  
    Description:
      This file contains the source code for the dynamic implementation of the
      I2C driver.
  *******************************************************************************/
  
  //DOM-IGNORE-BEGIN
  /*******************************************************************************
  * Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries.
  *
  * Subject to your compliance with these terms, you may use Microchip software
  * and any derivatives exclusively with Microchip products. It is your
  * responsibility to comply with third party license terms applicable to your
  * use of third party software (including open source software) that may
  * accompany Microchip software.
  *
  * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
  * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
  * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
  * PARTICULAR PURPOSE.
  *
  * IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
  * INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
  * WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS
  * BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE
  * FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN
  * ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
  * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
  *******************************************************************************/
  //DOM-IGNORE-END
  
  // *****************************************************************************
  // *****************************************************************************
  // Section: Included Files
  // *****************************************************************************
  // *****************************************************************************
  #include "configuration.h"
  //#include "system/debug/sys_debug.h"
  #include "driver/i2c/drv_i2c.h"
  
  // *****************************************************************************
  // *****************************************************************************
  // Section: Global Data
  // *****************************************************************************
  // *****************************************************************************
  /* This is the driver instance object array. */
  static DRV_I2C_OBJ gDrvI2CObj[DRV_I2C_INSTANCES_NUMBER] ;
  
  // *****************************************************************************
  // *****************************************************************************
  // Section: File scope functions
  // *****************************************************************************
  // *****************************************************************************
  
  static inline uint32_t  _DRV_I2C_MAKE_HANDLE(uint16_t token, uint8_t drvIndex, uint8_t clientIndex)
  {
      return ((token << 16) | (drvIndex << 8) | clientIndex);
  }
  
  static inline uint16_t _DRV_I2C_UPDATE_TOKEN(uint16_t token)
  {
      token++;
      if (token >= DRV_I2C_TOKEN_MAX)
      {
          token = 1;
      }
  
      return token;
  }
  
  static DRV_I2C_CLIENT_OBJ* _DRV_I2C_DriverHandleValidate(DRV_HANDLE handle)
  {
      uint32_t drvInstance = 0;
      DRV_I2C_CLIENT_OBJ* client = (DRV_I2C_CLIENT_OBJ*)NULL;
  
      /* This function returns the pointer to the client object that is
         associated with this handle if the handle is valid. Returns NULL
         otherwise.
      */
  
      if((handle != DRV_HANDLE_INVALID) && (handle != 0))
      {
          /* Extract the instance value from the handle */
          drvInstance = ((handle & DRV_I2C_INSTANCE_INDEX_MASK) >> 8);
  
          if (drvInstance >= DRV_I2C_INSTANCES_NUMBER)
          {
              return (NULL);
          }
          if ((handle & DRV_I2C_CLIENT_INDEX_MASK) >= gDrvI2CObj[drvInstance].nClientsMax)
          {
              return (NULL);
          }
  
          client = &((DRV_I2C_CLIENT_OBJ *)gDrvI2CObj[drvInstance].clientObjPool)[handle & DRV_I2C_CLIENT_INDEX_MASK];
  
          if ((client->clientHandle != handle) || (client->inUse == false))
          {
              return (NULL);
          }
      }
      return(client);
  }
  
  static void _DRV_I2C_PLibCallbackHandler( uintptr_t contextHandle )
  {
      DRV_I2C_OBJ* dObj = (DRV_I2C_OBJ *)contextHandle;
      DRV_I2C_CLIENT_OBJ* clientObj = (DRV_I2C_CLIENT_OBJ*)NULL;
  
      clientObj = (DRV_I2C_CLIENT_OBJ*)dObj->activeClient;
  
      /* Update error into the client object*/
      clientObj->errors = dObj->i2cPlib->errorGet();
  
      if(clientObj->errors == DRV_I2C_ERROR_NONE)
      {
          dObj->transferStatus = DRV_I2C_TRANSFER_STATUS_COMPLETE;
      }
      else
      {
          dObj->transferStatus = DRV_I2C_TRANSFER_STATUS_ERROR;
      }
  
      /* Unblock the application thread */
      OSAL_SEM_PostISR( &dObj->transferDone);
  }
  
  SYS_MODULE_OBJ DRV_I2C_Initialize( const SYS_MODULE_INDEX drvIndex, const SYS_MODULE_INIT * const init )
  {
      DRV_I2C_OBJ* dObj     = (DRV_I2C_OBJ*)NULL;
      DRV_I2C_INIT* i2cInit = (DRV_I2C_INIT*)init;
  
      /* Validate the request */
      if(drvIndex >= DRV_I2C_INSTANCES_NUMBER)
      {
          SYS_DEBUG(SYS_ERROR_ERROR, "Invalid driver instance");
          return SYS_MODULE_OBJ_INVALID;
      }
  
      if(gDrvI2CObj[drvIndex].status == SYS_STATUS_READY)
      {
          SYS_DEBUG(SYS_ERROR_ERROR, "Instance already initialized");
          return SYS_MODULE_OBJ_INVALID;
      }
  
      /* Allocate the driver object */
      dObj = &gDrvI2CObj[drvIndex];
      dObj->inUse = true;
  
      /* Update the driver parameters */
      dObj->i2cPlib                           = i2cInit->i2cPlib;
      dObj->clientObjPool                     = i2cInit->clientObjPool;
      dObj->nClientsMax                       = i2cInit->numClients;
      dObj->nClients                          = 0;
      dObj->activeClient                      = (uintptr_t)NULL;
      dObj->i2cTokenCount                     = 1;
      dObj->isExclusive                       = false;
      dObj->initI2CClockSpeed                 = i2cInit->clockSpeed;
      dObj->currentTransferSetup.clockSpeed   = i2cInit->clockSpeed;
  
      if (OSAL_MUTEX_Create(&dObj->clientMutex) == OSAL_RESULT_FALSE)
      {
          /*  If the mutex was not created because the memory required to
              hold the mutex could not be allocated then NULL is returned. */
          return SYS_MODULE_OBJ_INVALID;
      }
  
      if (OSAL_MUTEX_Create(&dObj->transferMutex) == OSAL_RESULT_FALSE)
      {
          /*  If the mutex was not created because the memory required to
              hold the mutex could not be allocated then NULL is returned. */
          return SYS_MODULE_OBJ_INVALID;
      }
      if (OSAL_SEM_Create(&dObj->transferDone,OSAL_SEM_TYPE_BINARY, 0, 0) == OSAL_RESULT_FALSE)
      {
          /* There was insufficient heap memory available for the semaphore to
          be created successfully. */
          return SYS_MODULE_OBJ_INVALID;
      }
  
      /* Register a callback with PLIB.
       * dObj as a context parameter will be used to distinguish the events
       * from different instances. */
      dObj->i2cPlib->callbackRegister(_DRV_I2C_PLibCallbackHandler, (uintptr_t)dObj);
  
      /* Update the status */
      dObj->status = SYS_STATUS_READY;
  
      /* Return the object structure */
      return ( (SYS_MODULE_OBJ)drvIndex );
  }
  
  SYS_STATUS DRV_I2C_Status( const SYS_MODULE_OBJ object)
  {
      /* Validate the request */
      if((object == SYS_MODULE_OBJ_INVALID) || (object >= DRV_I2C_INSTANCES_NUMBER))
      {
          SYS_DEBUG(SYS_ERROR_ERROR, "Invalid system object handle");
          return SYS_STATUS_UNINITIALIZED;
      }
  
      return (gDrvI2CObj[object].status);
  }
  
  bool DRV_I2C_TransferSetup( const DRV_HANDLE handle, DRV_I2C_TRANSFER_SETUP* setup )
  {
      DRV_I2C_CLIENT_OBJ* clientObj = NULL;
  
      if(setup == NULL)
      {
          return false;
      }
  
      /* Validate the driver handle */
      clientObj = _DRV_I2C_DriverHandleValidate(handle);
  
      if(clientObj == NULL)
      {
          return false;
      }
  
      /* Save the client specific transfer setup */
      clientObj->transferSetup = *setup;
  
      return true;
  }
  
  DRV_I2C_ERROR DRV_I2C_ErrorGet( const DRV_HANDLE handle )
  {
      DRV_I2C_CLIENT_OBJ* clientObj = NULL;
      DRV_I2C_ERROR errors = DRV_I2C_ERROR_NONE;
  
      /* Validate the handle */
      clientObj = _DRV_I2C_DriverHandleValidate(handle);
  
      if(clientObj != NULL)
      {
          errors = clientObj->errors;
      }
  
      return errors;
  }
  
  DRV_HANDLE DRV_I2C_Open( const SYS_MODULE_INDEX drvIndex, const DRV_IO_INTENT ioIntent )
  {
      DRV_I2C_CLIENT_OBJ* clientObj = NULL;
      DRV_I2C_OBJ* dObj = NULL;
      uint32_t iClient;
  
      /* Validate the request */
      if (drvIndex >= DRV_I2C_INSTANCES_NUMBER)
      {
          SYS_DEBUG(SYS_ERROR_ERROR, "Invalid Driver Instance");
          return DRV_HANDLE_INVALID;
      }
  
      dObj = &gDrvI2CObj[drvIndex];
  
      if((dObj->status != SYS_STATUS_READY) || (dObj->inUse == false))
      {
          SYS_DEBUG(SYS_ERROR_ERROR, "Was the driver initialized?");
          return DRV_HANDLE_INVALID;
      }
  
      /* Acquire the instance specific mutex to protect the instance specific
       * client pool
       */
      if (OSAL_MUTEX_Lock(&dObj->clientMutex , OSAL_WAIT_FOREVER ) == OSAL_RESULT_FALSE)
      {
          return DRV_HANDLE_INVALID;
      }
  
      if(dObj->isExclusive)
      {
          /* This means the another client has opened the driver in exclusive
             mode. The driver cannot be opened again
          */
          OSAL_MUTEX_Unlock( &dObj->clientMutex);
          return DRV_HANDLE_INVALID;
      }
  
      if((dObj->nClients > 0) && (ioIntent & DRV_IO_INTENT_EXCLUSIVE))
      {
          /* This means the driver was already opened and another driver was
             trying to open it exclusively.  We cannot give exclusive access in
             this case
          */
          OSAL_MUTEX_Unlock( &dObj->clientMutex);
          return(DRV_HANDLE_INVALID);
      }
  
      /* Enter here only if the lock was obtained */
      for(iClient = 0; iClient != dObj->nClientsMax; iClient++)
      {
          if(false == ((DRV_I2C_CLIENT_OBJ *)dObj->clientObjPool)[iClient].inUse)
          {
              /* This means we have a free client object to use */
              clientObj = &((DRV_I2C_CLIENT_OBJ *)dObj->clientObjPool)[iClient];
  
              clientObj->inUse        = true;
  
              clientObj->hDriver      = dObj;
  
              clientObj->ioIntent     = ioIntent;
  
              clientObj->errors       = DRV_I2C_ERROR_NONE;
  
              clientObj->transferSetup.clockSpeed = dObj->initI2CClockSpeed;
  
              if(ioIntent & DRV_IO_INTENT_EXCLUSIVE)
              {
                  /* Set the driver exclusive flag */
                  dObj->isExclusive = true;
              }
  
              dObj->nClients ++;
  
              /* Save the generated client handle in the client object, which will
               * be then used to verify the validity of the client handle.
               */
              clientObj->clientHandle = _DRV_I2C_MAKE_HANDLE(dObj->i2cTokenCount, drvIndex, iClient);
  
              /* Increment the instance specific token counter */
              dObj->i2cTokenCount = _DRV_I2C_UPDATE_TOKEN(dObj->i2cTokenCount);
  
              break;
          }
      }
  
      OSAL_MUTEX_Unlock(&dObj->clientMutex);
  
      return clientObj ? ((DRV_HANDLE)clientObj->clientHandle) : DRV_HANDLE_INVALID;
  }
  
  void DRV_I2C_Close( const DRV_HANDLE handle )
  {
      /* This function closes the client, The client
         object is deallocated and returned to the
         pool.
      */
  
      DRV_I2C_CLIENT_OBJ* clientObj;
      DRV_I2C_OBJ* dObj;
  
      /* Validate the handle */
      clientObj = _DRV_I2C_DriverHandleValidate(handle);
  
      if(clientObj != NULL)
      {
          dObj = (DRV_I2C_OBJ*)clientObj->hDriver;
  
          /* Acquire the instance specific mutex to protect the instance specific
           * client pool
           */
          if (OSAL_MUTEX_Lock(&dObj->clientMutex , OSAL_WAIT_FOREVER ) == OSAL_RESULT_TRUE)
          {
              /* Reduce the number of clients */
              dObj->nClients --;
  
              /* Reset the exclusive flag */
              dObj->isExclusive = false;
  
              /* De-allocate the object */
              clientObj->inUse = false;
  
              /* Release the instance specific mutex */
              OSAL_MUTEX_Unlock( &dObj->clientMutex );
          }
      }
  }
  
  static bool _DRV_I2C_WriteReadTransfer (
      const DRV_HANDLE handle,
      uint16_t address,
      void* const writeBuffer,
      const size_t writeSize,
      void* const readBuffer,
      const size_t readSize,
      DRV_I2C_TRANSFER_OBJ_FLAGS transferFlags
  )
  {
      DRV_I2C_CLIENT_OBJ* clientObj = (DRV_I2C_CLIENT_OBJ *)NULL;
      DRV_I2C_OBJ* hDriver = (DRV_I2C_OBJ*)NULL;
      bool isSuccess = false;
      bool isReqAccepted = false;
  
      /* Validate the driver handle */
      clientObj = _DRV_I2C_DriverHandleValidate(handle);
  
      if (clientObj == NULL)
      {
          return isSuccess;
      }
  
      if (transferFlags == DRV_I2C_TRANSFER_OBJ_FLAG_READ)
      {
          if((readSize == 0) || (readBuffer == NULL))
          {
              return isSuccess;
          }
      }
      else if ((transferFlags == DRV_I2C_TRANSFER_OBJ_FLAG_WRITE) || (transferFlags == DRV_I2C_TRANSFER_OBJ_FLAG_WRITE_FORCED))
      {
          if((writeSize == 0) || (writeBuffer == NULL))
          {
              return isSuccess;
          }
      }
      else
      {
          if((writeSize == 0) || (writeBuffer == NULL) || (readSize == 0) || (readBuffer == NULL))
          {
              return isSuccess;
          }
      }
  
      hDriver = clientObj->hDriver;
  
  
      /* Block other threads from accessing the PLIB */
      if (OSAL_MUTEX_Lock(&hDriver->transferMutex, OSAL_WAIT_FOREVER ) == OSAL_RESULT_TRUE)
      {
          /* Error is cleared for every new transfer */
          clientObj->errors = DRV_I2C_ERROR_NONE;
  
          /* Errors if any, will be saved in the activeClient in the driver callback */
          hDriver->activeClient = (uintptr_t)clientObj;
  
          /* Check if the transfer setup for this client is different than the current transfer setup */
          if (hDriver->currentTransferSetup.clockSpeed != clientObj->transferSetup.clockSpeed)
          {
              /* Set the new transfer setup */
              hDriver->i2cPlib->transferSetup(&clientObj->transferSetup, 0);
  
              hDriver->currentTransferSetup.clockSpeed = clientObj->transferSetup.clockSpeed;
          }
  
          switch(transferFlags)
          {
              case DRV_I2C_TRANSFER_OBJ_FLAG_READ:
                  if (hDriver->i2cPlib->read(address, readBuffer, readSize) == true)
                  {
                      isReqAccepted = true;
                  }
                  break;
  
              case DRV_I2C_TRANSFER_OBJ_FLAG_WRITE:
                  if (hDriver->i2cPlib->write(address, writeBuffer, writeSize) == true)
                  {
                      isReqAccepted = true;
                  }
                  break;
  
  
              case DRV_I2C_TRANSFER_OBJ_FLAG_WRITE_READ:
                  if (hDriver->i2cPlib->writeRead(address, writeBuffer, writeSize, readBuffer, readSize) == true)
                  {
                      isReqAccepted = true;
                  }
                  break;
  
              default:
                  break;
          }
  
          if (isReqAccepted == true)
          {
              /* Wait till transfer completes. This semaphore is released from ISR */
              if (OSAL_SEM_Pend( &hDriver->transferDone, OSAL_WAIT_FOREVER ) == OSAL_RESULT_TRUE)
              {
                  if (hDriver->transferStatus == DRV_I2C_TRANSFER_STATUS_COMPLETE)
                  {
                      isSuccess = true;
                  }
              }
          }
  
          /* Release the mutex to allow other threads to access the PLIB */
          OSAL_MUTEX_Unlock(&hDriver->transferMutex);
      }
  
      return isSuccess;
  }
  
  bool DRV_I2C_ReadTransfer(
      const DRV_HANDLE handle,
      uint16_t address,
      void* const readBuffer,
      const size_t readSize
  )
  {
      return _DRV_I2C_WriteReadTransfer(
          handle,
          address,
          NULL,
          0,
          readBuffer,
          readSize,
          DRV_I2C_TRANSFER_OBJ_FLAG_READ
      );
  }
  
  bool DRV_I2C_WriteTransfer(
      const DRV_HANDLE handle,
      uint16_t address,
      void* const writeBuffer,
      const size_t writeSize
  )
  {
      return _DRV_I2C_WriteReadTransfer(
          handle,
          address,
          writeBuffer,
          writeSize,
          NULL,
          0,
          DRV_I2C_TRANSFER_OBJ_FLAG_WRITE
      );
  }
  
  
  bool DRV_I2C_WriteReadTransfer (
      const DRV_HANDLE handle,
      uint16_t address,
      void* const writeBuffer,
      const size_t writeSize,
      void* const readBuffer,
      const size_t readSize
  )
  {
      return _DRV_I2C_WriteReadTransfer(
          handle,
          address,
          writeBuffer,
          writeSize,
          readBuffer,
          readSize,
          DRV_I2C_TRANSFER_OBJ_FLAG_WRITE_READ
      );
  }
  /*******************************************************************************
   End of File
  */
  