text: /*
   * FreeRTOS Common IO V0.1.1
   * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy of
   * this software and associated documentation files (the "Software"), to deal in
   * the Software without restriction, including without limitation the rights to
   * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
   * the Software, and to permit persons to whom the Software is furnished to do so,
   * subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in all
   * copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
   * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
   * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   *
   * http://aws.amazon.com/freertos
   * http://www.FreeRTOS.org
   */
  
  /**
   * @file iot_i2c.h
   * @brief File for the APIs of I2C called by application layer.
   */
  #ifndef _IOT_I2C_H_
  #define _IOT_I2C_H_
  
  /* Standard includes. */
  #include <stdint.h>
  #include <stddef.h>
  
  /**
   * @defgroup iot_i2c I2C Abstraction APIs.
   * @{
   */
  
  /**
   * The speeds supported by I2C bus.
   */
  #define IOT_I2C_STANDARD_MODE_BPS         ( 100000 )  /*!< Standard mode bits per second. */
  #define IOT_I2C_FAST_MODE_BPS             ( 400000 )  /*!< Fast mode bits per second. */
  #define IOT_I2C_FAST_MODE_PLUS_BPS        ( 1000000 ) /*!< Fast plus mode bits per second. */
  #define IOT_I2C_HIGH_SPEED_BPS            ( 3400000 ) /*!< High speed mode bits per second. */
  
  /**
   * The return codes for the functions in I2C.
   */
  #define IOT_I2C_SUCCESS                   ( 0 )     /*!< I2C operation completed successfully. */
  #define IOT_I2C_INVALID_VALUE             ( 1 )     /*!< At least one parameter is invalid. */
  #define IOT_I2C_BUSY                      ( 2 )     /*!< I2C bus is busy at current time. */
  #define IOT_I2C_WRITE_FAILED              ( 3 )     /*!< I2C driver returns error when performing write operation. */
  #define IOT_I2C_READ_FAILED               ( 4 )     /*!< I2C driver returns error when performing read operation. */
  #define IOT_I2C_NACK                      ( 5 )     /*!< Unexpected NACK is caught. */
  #define IOT_I2C_BUS_TIMEOUT               ( 6 )     /*!< I2C operation not completed within specified timeout. */
  #define IOT_I2C_NOTHING_TO_CANCEL         ( 7 )     /*!< No ongoing transaction when cancel operation is performed. */
  #define IOT_I2C_FUNCTION_NOT_SUPPORTED    ( 8 )     /*!< I2C operation is not supported. */
  #define IOT_I2C_SLAVE_ADDRESS_NOT_SET     ( 9 )     /*!< Slave address is not set before calling I2C read or write operation. */
  
  /**
   * @brief I2C Bus status
   */
  typedef enum
  {
      eI2CBusIdle = 0,            /*!< I2C bus is idle. */
      eI2cBusBusy = IOT_I2C_BUSY, /*!< I2C bus is busy. */
  } IotI2CBusStatus_t;
  
  /**
   * @brief I2C operation status.
   */
  typedef enum
  {
      eI2CCompleted = IOT_I2C_SUCCESS,         /*!< I2C operation completed successfully. */
      eI2CDriverFailed,                        /*!< I2C driver returns error during last operation. */
      eI2CNackFromSlave = IOT_I2C_NACK,        /*!< Unexpected NACK is caught. */
      eI2CMasterTimeout = IOT_I2C_BUS_TIMEOUT, /*!< I2C operation not completed within specified timeout. */
  } IotI2COperationStatus_t;
  
  /**
   * @brief I2C bus configuration
   */
  typedef struct IotI2CConfig
  {
      uint32_t ulMasterTimeout; /**<! Master timeout value in msec, to relinquish the bus if slave does not respond. This is an extended feature of SMBus which is not part of I2C protocol. */
      uint32_t ulBusFreq;       /**<! Bus frequency/baud rate */
  } IotI2CConfig_t;
  
  /**
   * @brief Ioctl request types.
   */
  typedef enum
  {
      eI2CSendNoStopFlag,  /**<! Set flag to not send stop after transaction */
                           /**<! Default is always stop for every transaction */
                           /**<! Flag will auto reset to stop after one transaction if you set no stop */
      eI2CSetSlaveAddr,    /**<! This can be either 7-bit address or 10-bit address. All the operations use this slave address after it is set. */
      eI2CSetMasterConfig, /**<! Sets the I2C bus frequency and timeout using the struct IotI2CConfig_t, default speed is Standard mode. */
      eI2CGetMasterConfig, /**<! Gets the I2C bus frequency and timeout set for the I2C master. */
      eI2CGetBusState,     /**<! Get the current I2C bus status. Returns eI2CBusIdle or eI2CBusy */
      eI2CBusReset,        /**<! Master resets the bus. */
      eI2CGetTxNoOfbytes,  /**<! Get the number of bytes sent in write operation. */
      eI2CGetRxNoOfbytes,  /**<! Get the number of bytes received in read operation. */
  } IotI2CIoctlRequest_t;
  
  /**
   * @brief The I2C descriptor type defined in the source file.
   * This is an anonymous struct that is vendor/driver specific.
   */
  struct                    IotI2CDescriptor;
  
  /**
   * @brief IotI2CHandle_t is the handle type returned by calling iot_i2c_open().
   *        This is initialized in open and returned to caller. The caller must pass
   *        this pointer to the rest of APIs.
   */
  typedef struct IotI2CDescriptor * IotI2CHandle_t;
  
  /**
   * @brief The callback function for completion of I2C operation.
   *
   * @param[out] xOpStatus    I2C asynchronous operation status.
   * @param[in] pvUserContext User Context passed when setting the callback.
   *                          This is not used or modified by the driver. The context
   *                          is provided by the caller when setting the callback, and is
   *                          passed back to the caller in the callback.
   *
   */
  typedef void (* IotI2CCallback_t) ( IotI2COperationStatus_t xOpStatus,
                                      void * pvUserContext );
  
  /**
   * @brief Initiates and reserves an I2C instance as master.
   *
   * One instance can communicate with one or more slave devices.
   * Slave addresses need to be changed between actions to different slave devices.
   *
   * @warning Once opened, the same I2C instance must be closed before calling open again.
   *
   * @param[in] lI2CInstance The instance of I2C to initialize. This is between 0 and the number of I2C instances on board - 1.
   *
   * @return
   * - 'the handle to the I2C port (not NULL)', on success.
   * - 'NULL', if
   *     - invalid instance number
   *     - open same instance more than once before closing it
   */
  IotI2CHandle_t iot_i2c_open( int32_t lI2CInstance );
  
  /**
   * @brief Sets the application callback to be called on completion of an operation.
   *
   * The callback is guaranteed to be invoked when the current asynchronous operation completes, either successful or failed.
   * This simply provides a notification mechanism to user's application. It has no impact if the callback is not set.
   *
   * @note This callback will not be invoked when synchronous operation completes.
   * @note This callback is per handle. Each instance has its own callback.
   * @note Single callback is used for both read_async and write_async. Newly set callback overrides the one previously set.
   * @warning If the input handle is invalid, this function silently takes no action.
   *
   * @param[in] pxI2CPeripheral The I2C peripheral handle returned in the open() call.
   * @param[in] xCallback The callback function to be called on completion of transaction.
   * @param[in] pvUserContext The user context to be passed back when callback is called.
   */
  void iot_i2c_set_callback( IotI2CHandle_t const pxI2CPeripheral,
                             IotI2CCallback_t xCallback,
                             void * pvUserContext );
  
  /**
   * @brief Starts the I2C master read operation in synchronous mode.
   *
   * This function attempts to read certain number of bytes from slave device to a pre-allocated buffer, in synchronous way.
   * Partial read might happen, e.g. no more data is available.
   * And the number of bytes that have been actually read can be obtained by calling iot_i2c_ioctl.
   *
   * @note Usually, the address of register needs to be written before calling this function.
   * @note If eI2CSendNoStopFlag is set and this function returns, whether the actual transaction has been started is undefined. This is board-specific behavior.
   *
   * @warning Prior to this function, slave address must be already configured.
   * @warning None of other read or write functions shall be called during this function.
   *
   * @param[in] pxI2CPeripheral The I2C handle returned in open() call.
   * @param[out] pucBuffer The receive buffer to read the data into. It must stay allocated before this function returns.
   * @param[in] xBytes The number of bytes to read.
   *
   * @return
   * - IOT_I2C_SUCCESS, on success (all the requested bytes have been read)
   * - IOT_I2C_INVALID_VALUE, if
   *     - pxI2CPeripheral is NULL
   *     - pxI2CPeripheral is not opened yet
   *     - pucBuffer is NULL
   *     - xBytes is 0
   * - IOT_I2C_SLAVE_ADDRESS_NOT_SET, if slave address is not set yet
   * - IOT_I2C_NACK or IOT_I2C_READ_FAILED, if
   *     - no device correspond to the slave address
   *     - the slave is unable to receive or transmit
   *     - the slave gets data or commands that it does not understand
   *     - there is some unknown driver error
   * - IOT_I2C_BUS_TIMEOUT, if timeout is supported and slave device does not respond within configured timeout.
   * - IOT_I2C_BUSY, if the bus is busy which means there is an ongoing transaction.
   *
   * <b>Example</b>
   * @code{c}
   *  // Declare an I2C handle.
   *  IotI2CHandle_t xI2CHandle;
   *
   *  // Return value of I2C functions.
   *  int32_t lRetVal = IOT_I2C_SUCCESS;
   *
   *  // Register address on I2C slave device.
   *  uint8_t xDeviceRegisterAddress = 0x73;
   *
   *  // Number of read/write bytes.
   *  uint16_t usReadBytes = 0;
   *  uint16_t usWriteBytes = 0;
   *
   *  uint8_t ucReadBuffer[2] = {0};
   *
   *  // Configurations of I2C master device.
   *  IotI2CConfig_t xI2CConfig =
   *  {
   *      .ulBusFreq       = IOT_I2C_FAST_MODE_BPS,
   *      .ulMasterTimeout = 500
   *  };
   *
   *  // Open one of the I2C instance and get a handle.
   *  xI2CHandle = iot_i2c_open( 1 );
   *
   *  if ( xI2CHandle != NULL )
   *  {
   *      // Set I2C configuration.
   *      lRetVal = iot_i2c_ioctl( xI2CHandle, eI2CSetMasterConfig, &xI2CConfig );
   *      // assert(lRetVal == IOT_I2C_SUCCESS);
   *
   *      // Set slave address.
   *      lRetVal = iot_i2c_ioctl( xI2CHandle, eI2CSetSlaveAddr, &uctestIotI2CSlaveAddr );
   *      // assert(lRetVal == IOT_I2C_SUCCESS);
   *
   *      // Write the register address as single byte, in a transaction.
   *      lRetVal = iot_i2c_write_sync( xI2CHandle, &xDeviceRegisterAddress, sizeof( xDeviceRegisterAddress ) );
   *
   *      if ( lRetVal == IOT_I2C_SUCCESS )
   *      {
   *          // Get the number of written bytes in last transaction.
   *          lRetVal = iot_i2c_ioctl( xI2CHandle, eI2CGetTxNoOfbytes, &usWriteBytes );
   *          // assert(lRetVal == IOT_I2C_SUCCESS);
   *          // assert(usWriteBytes == 1);
   *
   *          // Read two bytes of data to allocated buffer, in a transaction.
   *          lRetVal = iot_i2c_read_sync( xI2CHandle, &ucReadBuffer, sizeof( ucReadBuffer ) );
   *
   *          if ( lRetVal == IOT_I2C_SUCCESS )
   *          {
   *              // Get the number of read bytes in last transaction.
   *              lRetVal = iot_i2c_ioctl( xI2CHandle, eI2CGetRxNoOfbytes, &usReadBytes );
   *              // assert(lRetVal == IOT_I2C_SUCCESS);
   *              // assert(usReadBytes == 2);
   *          }
   *      }
   *
   *      lRetVal = iot_i2c_close( xI2CHandle );
   *      // assert(lRetVal == IOT_I2C_SUCCESS);
   *  }
   * @endcode
   */
  int32_t iot_i2c_read_sync( IotI2CHandle_t const pxI2CPeripheral,
                             uint8_t * const pucBuffer,
                             size_t xBytes );
  
  /**
   * @brief Starts the I2C master write operation in synchronous mode.
   *
   * This function attempts to write certain number of bytes from a pre-allocated buffer to a slave device, in synchronous way.
   * Partial write might happen, e.g. slave device unable to receive more data.
   * And the number of bytes that have been actually written can be obtained by calling iot_i2c_ioctl.
   *
   * @note Usually, the first byte is treated as the register address and the following bytes are treated as data to be written.
   * @note If eI2CSendNoStopFlag is set and this function returns, whether the actual transaction has been started is undefined. This is board-specific behavior.
   *
   * @warning Prior to this function, slave address must be already configured.
   * @warning None of other read or write functions shall be called during this function.
   *
   * @param[in] pxI2CPeripheral The I2C handle returned in open() call.
   * @param[in] pucBuffer The transmit buffer containing the data to be written. It must stay allocated before this function returns.
   * @param[in] xBytes The number of bytes to write.
   *
   * @return
   * - IOT_I2C_SUCCESS, on success (all the requested bytes have been written)
   * - IOT_I2C_INVALID_VALUE, if
   *     - pxI2CPeripheral is NULL
   *     - pxI2CPeripheral is not opened yet
   *     - pucBuffer is NULL
   *     - xBytes is 0
   * - IOT_I2C_SLAVE_ADDRESS_NOT_SET, if slave address is not set yet
   * - IOT_I2C_NACK or IOT_I2C_WRITE_FAILED, if
   *     - no device correspond to the slave address
   *     - the slave is unable to receive or transmit
   *     - the slave gets data or commands that it does not understand
   *     - there is some unknown driver error
   * - IOT_I2C_BUS_TIMEOUT, if timeout is supported and slave device does not respond within configured timeout.
   * - IOT_I2C_BUSY, if the bus is busy which means there is an ongoing transaction.
   */
  int32_t iot_i2c_write_sync( IotI2CHandle_t const pxI2CPeripheral,
                              uint8_t * const pucBuffer,
                              size_t xBytes );
  
  /**
   * @brief Starts the I2C master read operation in asynchronous mode.
   *
   * This function attempts to read certain number of bytes from a pre-allocated buffer, in asynchronous way.
   * It returns immediately when the operation is started and the status can be check by calling iot_i2c_ioctl.
   * Once the operation completes, successful or not, the user callback will be invoked.
   *
   * Partial read might happen, e.g. slave device unable to receive more data.
   * And the number of bytes that have been actually read can be obtained by calling iot_i2c_ioctl.
   *
   * @note Usually, the address of register needs to be written before calling this function.
   * @note If eI2CSendNoStopFlag is set and this function returns, whether the actual transaction has been started is undefined. This is board-specific behavior.
   * @note In order to get notification when the asynchronous call is completed, iot_i2c_set_callback must be called prior to this.
   *
   * @warning Prior to this function, slave address must be already configured.
   * @warning pucBuffer must be valid before callback is invoked.
   * @warning None of other read or write functions shall be called during this function or before user callback.
   *
   * @param[in] pxI2CPeripheral The I2C handle returned in open() call.
   * @param[out] pucBuffer The receive buffer to read the data into
   * @param[in] xBytes The number of bytes to read.
   *
   * @return
   * - IOT_I2C_SUCCESS, on success
   * - IOT_I2C_INVALID_VALUE, if
   *     - pxI2CPeripheral is NULL
   *     - pxI2CPeripheral is not opened yet
   *     - pucBuffer is NULL
   *     - xBytes is 0
   * - IOT_I2C_SLAVE_ADDRESS_NOT_SET, if slave address is not set yet
   * - IOT_I2C_NACK or IOT_I2C_READ_FAILED, if
   *     - no device correspond to the slave address
   *     - the slave is unable to receive or transmit
   *     - the slave gets data or commands that it does not understand
   *     - there is some unknown driver error
   */
  int32_t iot_i2c_read_async( IotI2CHandle_t const pxI2CPeripheral,
                              uint8_t * const pucBuffer,
                              size_t xBytes );
  
  /**
   * @brief Starts the I2C master write operation in asynchronous mode.
   *
   * This function attempts to write certain number of bytes from a pre-allocated buffer to a slave device, in asynchronous way.
   * It returns immediately when the operation is started and the status can be check by calling iot_i2c_ioctl.
   * Once the operation completes, successful or not, the user callback will be invoked.
   *
   * Partial write might happen, e.g. slave device unable to receive more data.
   * And the number of bytes that have been actually written can be obtained by calling iot_i2c_ioctl.
   *
   * @note Usually, the first byte is treated as the register address and the following bytes are treated as data to be written.
   * @note If eI2CSendNoStopFlag is set and this function returns, whether the actual transaction has been started is undefined. This is board-specific behavior.
   * @note In order to get notification when the asynchronous call is completed, iot_i2c_set_callback must be called prior to this.
   *
   * @warning Prior to this function, slave address must be already configured.
   * @warning None of other read or write functions shall be called during this function.
   *
   * @param[in] pxI2CPeripheral The I2C handle returned in open() call.
   * @param[in] pucBuffer The transmit buffer containing the data to be written. It must stay allocated before this function returns.
   * @param[in] xBytes The number of bytes to write.
   *
   * @return
   * - IOT_I2C_SUCCESS, on success
   * - IOT_I2C_INVALID_VALUE, if
   *     - pxI2CPeripheral is NULL
   *     - pxI2CPeripheral is not opened yet
   *     - pucBuffer is NULL
   *     - xBytes is 0
   * - IOT_I2C_SLAVE_ADDRESS_NOT_SET, if slave address is not set yet
   * - IOT_I2C_NACK or IOT_I2C_WRITE_FAILED, if
   *     - no device correspond to the slave address
   *     - the slave is unable to receive or transmit
   *     - the slave gets data or commands that it does not understand
   *     - there is some unknown driver error
   * - IOT_I2C_BUS_TIMEOUT, if timeout is supported and slave device does not respond within configured timeout.
   * - IOT_I2C_BUSY, if the bus is busy which means there is an ongoing transaction.
   */
  int32_t iot_i2c_write_async( IotI2CHandle_t const pxI2CPeripheral,
                               uint8_t * const pucBuffer,
                               size_t xBytes );
  
  /**
   * @brief Configures the I2C master with user configuration.
   *
   * @param[in] pxI2CPeripheral The I2C handle returned in open() call.
   * @param[in] xI2CRequest Should be one of I2C_Ioctl_Request_t.
   * @param[in,out] pvBuffer The configuration values for the IOCTL request.
   *
   * @note SetMasterConfig is expected only called once at beginning.
   * This request expects the buffer with size of IotI2CConfig_t.
   *
   * @note eI2CGetMasterConfig gets the current configuration for I2C master.
   * This request expects the buffer with size of IotI2CConfig_t.
   *
   * @note eI2CGetBusState gets the current bus state.
   * This request expects buffer with size of IotI2CBusStatus_t.
   *
   * @note eI2CSendNoStopFlag is called at every operation you want to not send stop condition.
   *
   * @note eI2CSetSlaveAddr sets either 7-bit address or 10-bit address, according to hardware's capability.
   * This request expects 2 bytes buffer (uint16_t)
   *
   * @note eI2CGetTxNoOfbytes returns the number of written bytes in last transaction.
   * This is supposed to be called in the caller task or application callback, right after last transaction completes.
   * This request expects 2 bytes buffer (uint16_t).
   *
   * - If the last transaction only did write, this returns the actual number of written bytes which might be smaller than the requested number (partial write).
   * - If the last transaction only did read, this returns 0.
   * - If the last transaction did both write and read, this returns the number of written bytes.
   *
   * @note eI2CGetRxNoOfbytes returns the number of read bytes in last transaction.
   * This is supposed to be called in the caller task or application callback, right after last transaction completes.
   * This request expects 2 bytes buffer (uint16_t).
   *
   * - If the last transaction only did read, this returns the actual number of read bytes which might be smaller than the requested number (partial read).
   * - If the last transaction only did write, this returns 0.
   * - If the last transaction did both write and read, this returns the number of read bytes.
   *
   *
   * @return
   * - IOT_I2C_SUCCESS, on success
   * - IOT_I2C_INVALID_VALUE, if
   *     - pxI2CPeripheral is NULL
   *     - pxI2CPeripheral is not opened yet
   *     - pucBuffer is NULL with requests which needs buffer
   * - IOT_I2C_FUNCTION_NOT_SUPPORTED, if this board doesn't support this feature.
   *     - eI2CSetSlaveAddr: 10-bit address is not supported
   *     - eI2CSendNoStopFlag: explicitly not sending stop condition is not supported
   *     - eI2CBusReset: reset bus is not supported
   */
  int32_t iot_i2c_ioctl( IotI2CHandle_t const pxI2CPeripheral,
                         IotI2CIoctlRequest_t xI2CRequest,
                         void * const pvBuffer );
  
  
  /**
   * @brief Stops the ongoing operation and de-initializes the I2C peripheral.
   *
   * @param[in] pxI2CPeripheral The I2C handle returned in open() call.
   *
   * @return
   * - IOT_I2C_SUCCESS, on success
   * - IOT_I2C_INVALID_VALUE, if
   *     - pxI2CPeripheral is NULL
   *     - pxI2CPeripheral is not opened yet
   */
  int32_t iot_i2c_close( IotI2CHandle_t const pxI2CPeripheral );
  
  /**
   * @brief This function is used to cancel the current operation in progress, if possible.
   *
   * @param[in] pxI2CPeripheral The I2C handle returned in open() call.
   *
   * @return
   * - IOT_I2C_SUCCESS, on success
   * - IOT_I2C_INVALID_VALUE, if
   *     - pxI2CPeripheral is NULL
   *     - pxI2CPeripheral is not opened yet
   * - IOT_I2C_NOTHING_TO_CANCEL, if there is no on-going transaction.
   * - IOT_I2C_FUNCTION_NOT_SUPPORTED, if this board doesn't support this operation.
   */
  int32_t iot_i2c_cancel( IotI2CHandle_t const pxI2CPeripheral );
  
  /**
   * @}
   */
  /* end of group iot_i2c */
  
  #endif /* ifndef _IOT_I2C_H_ */
  