/*
 * r_iic_master_devaddr.h
 *
 *  Created on: 9/04/2021
 *      Author: alexraynepe196@gmail.com
 * ----------------------------------------------------------------------
 * драйвер устройства i2c с внутренней адресацией
 *
 * TODO: masterx использует почти идентичный функциона что и SSP_Client. имеет смысл
 *      реализовать базовым SSP_I2CXPort, а  этот АПИ прокинуть через него.
 */

#ifndef BSP_R_I2C_MASTERX_H_
#define BSP_R_I2C_MASTERX_H_

#include <c_compat.h>
#include <mcu-chip.h>
// FSP generated sources
#include "r_i2c_master_api.h"
// provide: os_hrtime_t
#include "OsTime.h"


#ifdef __cplusplus
namespace i2c {
extern "C" {
#endif



///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///

enum {
    // это событие передается после обновления внутреней машины I2C, I2C_status поменялся.
    //      требуется I2C_wait для продолжения работы,
    I2C_MASTER_EVENT_BUSY     = 4,  ///< A transfer has events, and need to poll
};


typedef fsp_err_t I2C_Status;
typedef enum I2CStatusID{
    I2C_OK        = FSP_SUCCESS ,
    I2C_BUSY      = 700 ,
    I2C_NOSLA     = 701 ,   //SLA not acknowledges
    I2C_BADDATA,            // device return damaged data
    I2C_ABORT     = FSP_ERR_ABORTED,
    //, I2C_ERROR    = 0x01
    //, I2C_TIMEOUT  = 0x03
    //, I2C_ERRCRC   = 0x05   //use for aside api
}  I2CStatusID;


typedef struct {
    const i2c_master_instance_t*  port;
    //uint32_t        rate;
    //unsigned long   TOinterval;
} I2C_basic;

typedef I2C_basic* i2cHandle;

I2C_Status I2C_status(i2cHandle port);
I2C_Status I2C_abort(i2cHandle port);

#define  I2C_wait(...)  I2C_status(__VA_ARGS__)

/// @brief handle on i2c transfer finish
typedef void (* I2C_on_event)(i2cHandle h, i2c_master_event_t ev, void* ctx);

void I2C_assign_handler(i2cHandle port, I2C_on_event, void* ctx);


typedef enum {

    i2co_DIR        = 1 ,   //transfer direction
    i2co_XRECV      = 0 ,
    i2co_XSEND      = 1 ,

    //!< делает XRECV без рестарта (отменяет рестарт фрейма) после передачи адреса.
    //! TODO: RA FSP i2c_master can't implement it
    //i2co_OneFrame   = 4,

    //!< XSEND отсылает фрейм данных после рестрата (форсирует рестарт фрейма) после адресной части
    //! TODO: RA FSP i2c_master всегда делает только так
    //i2co_XFrame     = 8 ,

    //!< подавляет освобождение шины после транзакции. обычный запрос освобождает шину после передачи данных.
    //! @note RA FSP i2c_master позволяет это делать рестартом
    i2co_LockBus    = 0x10 ,

    //< I2C_xfer2 provide Addr-len here
    i2co_ADDR       = 0xc0 ,
    i2co_ADDR_Pos   = 6 ,
    i2co_ADDR1      = 0<< i2co_ADDR_Pos,
    i2co_ADDR2      = 1<< i2co_ADDR_Pos,
    i2co_ADDR3      = 2<< i2co_ADDR_Pos,
    i2co_ADDR4      = 3<< i2co_ADDR_Pos,
} i2cx_io_option;



/// @brief запускает операцию I2C, завершение ожидается  I2C_status
/// @return I2C_OK - операция успешно запущена
I2C_Status I2C_xfer(i2cHandle port, unsigned char SLA
          , const void* addr, unsigned alen
          , void* data, unsigned dlen
          , i2cx_io_option mode
          );

/// @brief отпускает шину если она занята последней операцией
///   эквивалетно I2C_xfer с пустыми параметрами
I2C_Status I2C_xfer_release(i2cHandle port);

/** операции и2ц с выделеным адресом. адресная часть - передается как нормальные данные
 * */
/** тоже самое что и miic_transfer(port, SLA, addr, alen, dara, dlen)
 * \sa miic_transfer
 */
INLINE
I2C_Status I2C_read(i2cHandle port, unsigned char SLA
          , const void* addr, unsigned alen
          , void* data, unsigned dlen
          )
{
    return I2C_xfer(port, SLA, addr, alen, data, dlen, i2co_XRECV);
}

INLINE
I2C_Status I2C_write(i2cHandle port, unsigned char SLA
          , const void* addr, unsigned alen
          , void* data, unsigned dlen
          )
{
    return I2C_xfer(port, SLA, addr, alen, data, dlen, i2co_XSEND);
}



/// @brief this operations continues on last SLA, with no adress - device provide adress on it`s own
I2C_Status I2C_xfer_next(i2cHandle port, void* data, unsigned dlen, i2cx_io_option mode );

INLINE
I2C_Status I2C_read_next(i2cHandle port, void* data, unsigned dlen )
{
    return I2C_xfer_next(port, data, dlen, i2co_XRECV);
}

INLINE
I2C_Status I2C_write_next(i2cHandle port, void* data, unsigned dlen)
{
    return I2C_xfer_next(port, data, dlen, i2co_XSEND);
}



/// @brief alternative simplified API for regs adress in 4bytes
///
static inline
uint32_t I2C_MODE( uint8_t sla, uint8_t mode) {
    return (sla << 8) | mode ;
}

I2C_Status I2C_xfer2(i2cHandle port, uint32_t SLA_mode, uint32_t addr , void* data, unsigned dlen );



/// @brief API for registers set with 1byte target register index
INLINE
I2C_Status I2C_read_regs(i2cHandle port, unsigned char SLA, uint8_t addr, void* data, unsigned dlen )
{
    return I2C_xfer2(port, I2C_MODE(SLA, (i2co_XRECV|i2co_ADDR1) ), addr, data, dlen);
}

INLINE
I2C_Status I2C_write_regs(i2cHandle port, unsigned char SLA  , uint8_t addr, void* data, unsigned dlen)
{
    return I2C_xfer2(port, I2C_MODE(SLA, (i2co_XSEND|i2co_ADDR1) ), addr, data, dlen);
}




/// @brief this operations continues on last SLA
I2C_Status I2C_xfer_continue(i2cHandle port, uint32_t SLA_mode, uint32_t addr ,void* data, unsigned dlen );

INLINE
I2C_Status I2C_read_continue(i2cHandle port, uint8_t addr, void* data, unsigned dlen )
{
    return I2C_xfer_continue(port, I2C_MODE(0, (i2co_XRECV|i2co_ADDR1) ), addr, data, dlen);
}

INLINE
I2C_Status I2C_write_continue(i2cHandle port, uint8_t addr, void* data, unsigned dlen)
{
    return I2C_xfer_continue(port, I2C_MODE(0, (i2co_XSEND|i2co_ADDR1) ), addr, data, dlen);
}



///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///         I2CX port instantiations

struct i2cx_descriptor;
typedef struct i2cx_descriptor i2cx_descriptor;


enum i2cx_io_state{
      i2cio_IDLE
    , i2cio_ADDR
    , i2cio_XSLA
    , i2cio_XRECV
    , i2cio_XSEND
    //, i2cio_BREAK
};
typedef enum i2cx_io_state i2cx_io_state;


enum i2cx_ioDModes{
      i2co_IDLE       = 0
    , i2co_XMIT       = 2
    , i2co_DRECV      = i2co_XRECV | i2co_XMIT
    , i2co_DSEND      = i2co_XSEND | i2co_XMIT
};


struct i2cx_descriptor
{
    I2C_basic       base;

    unsigned char   file_sla;   //< stdio::read/write sla
    unsigned char   sla;
    uint32_t        addr;

    //автомат I2C_xfer
    i2cx_io_state    state;
    i2cx_io_option   mode;
    int             err;

    const unsigned char*    adata;
    unsigned                alen;
    unsigned char*          xdata;
    unsigned                xlen;

    I2C_on_event    ev;
    void*           ev_ctx;

    // anti-hung polling timeout. This timeout checks by I2CStatus()
    unsigned            bytes_tick;     // [bytes/1tick]
    //unsigned            to_locked;        // locked line timeout
    os_time_t           start_time;
    unsigned            least_bytes;
    i2cx_io_state       start_state;
};


i2cHandle I2C_init_master( const i2c_master_instance_t* io, i2cx_descriptor* self);
I2C_Status I2C_close_master(i2cHandle h);

#ifndef I2CX_IS_SINGLE
#define I2CX_IS_SINGLE  1
#endif

#if I2CX_IS_SINGLE

extern i2cx_descriptor      I2C_port;

#endif




#ifdef __cplusplus
}
} // namespace i2c {
#endif



#endif /* BSP_R_I2C_MASTERX_H_ */
