/*
 * r_i2c_masterx.c
 *
 *  Created on: 9/04/2021
 *      Author: alexraynepe196@gmail.com
 * ----------------------------------------------------------------------
 * драйвер устройства i2c с внутренней адресацией
 */

#include "r_i2c_masterx.h"
#include <stdbool.h>


//< handle i2s transfer by I2C_status() invoke
#define I2C_ISR_POLLING 0
//< handle i2s transfer by i2c ISR events
#define I2C_ISR_ACTIVE  1

#ifndef I2C_ISR_STYLE
#define I2C_ISR_STYLE   I2C_ISR_POLLING
#endif

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if 1
#include <assert.h>
#define ASSERT(...) assert(__VA_ARGS__)
#else
#define ASSERT(...)
#endif


//#include <trace.h>
//#include <tracelog.h>
#if (0)
#define I2C_puts(s)     debug_puts(s)
#define I2C_putchar(c)  debug_putchar(0, c)
#define I2C_printf(...) debug_printf(__VA_ARGS__)
#elif (0)
#define I2C_puts(s)     puts(s)
#define I2C_putchar(c)  putchar(c)
#define I2C_printf(...) printf(__VA_ARGS__)
#else
#define I2C_puts(s)
#define I2C_putchar(c)
#define I2C_printf(...)
#endif


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///
#include <trace_probes.h>
#ifndef trace_i2c_start
//* это пробы-сигналы тестовых пинов, для визуализации обмена на осцилографе
trace_need(i2c_start)
trace_need(i2c_work)
trace_need(i2c_irq)
#endif
#ifndef trace_i2c_adr
trace_need(i2c_adr)
#endif



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

#if I2CX_IS_SINGLE
// system have single i2c
#include "r_iic_master.h"

i2cx_descriptor      I2C_port;

#define SELFARG
#define SELF        (I2C_port)

#define API(method) (g_i2c_master_on_iic.method)

#else
// system use multiple i2c
#define SELFARG     i2c_descriptor* self,
#define SELF        (*self)

#define API(method) (*api->method)

#endif


#ifndef CODE_ISR
#ifdef __HOTFUNC
#define CODE_ISR    __HOTFUNC
#else
#define CODE_ISR
#endif
#endif

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

void I2C_OnEvent(i2c_master_callback_args_t *);

static
void i2c_reset(i2cx_descriptor* io){
    io->state   = i2cio_IDLE;
    io->err     = FSP_SUCCESS;
};

i2cHandle I2C_init_master(const i2c_master_instance_t* io, i2cx_descriptor* self){
    SELF.base.port = (const i2c_master_instance_t*)io;
    const i2c_master_api_t*   api = SELF.base.port->p_api;  (void)api;
    i2c_master_ctrl_t*  port= SELF.base.port->p_ctrl;

    SELF.ev = NULL;

    I2C_Status ok;      (void)ok;
    i2c_reset(&SELF);

    i2c_master_status_t status;
    API(statusGet)(port, &status);

    if (!status.open){
        ok = API(open)(port, SELF.base.port->p_cfg);
        ASSERT(ok == FSP_SUCCESS);
    }

    ok = API(callbackSet)(port, I2C_OnEvent, &SELF, NULL);
    ASSERT(ok == FSP_SUCCESS);

    return &(SELF.base);
}

I2C_Status I2C_close_master(i2cHandle h){
    if (I2C_status(h) != (int)I2C_BUSY)
        return I2C_OK;

    I2C_Status ok;
    ok = I2C_abort(h);

    i2cx_descriptor*     self = (i2cx_descriptor*)h;        (void)self;
    const i2c_master_api_t*   api = SELF.base.port->p_api;  (void)api;
    i2c_master_ctrl_t*  port= SELF.base.port->p_ctrl;

    ok = API(close)(port);

    return ok;
}


void I2C_assign_handler(i2cHandle h, I2C_on_event ev, void* ctx){
    i2cx_descriptor*     self = (i2cx_descriptor*)h;        (void)self;
    i2cx_descriptor*     io  = &SELF;

    io->ev      = ev;
    io->ev_ctx  = ctx;
}

static inline
void I2C_notify(i2cx_descriptor*     self, i2c_master_event_t ev){
    if ( SELF.ev != NULL)
        (SELF.ev)(&(SELF.base), ev, SELF.ev_ctx);
}

static
void I2C_finale(i2cx_descriptor*     self, i2c_master_event_t ev){
    //to allow reenant i2c operations in callback, need state accept before callback
    SELF.state = i2cio_IDLE;

    I2C_notify(self, ev);
}



I2C_Status I2C_xfer_start(i2cHandle h, unsigned char SLA);

static
void I2C_data_assign(i2cx_descriptor* io, void* data, unsigned dlen){
    io->xlen     = dlen;
    if (io->xlen > 0){
        ASSERT(data != NULL);
        io->xdata    = (unsigned char*)data;
    }
    else
        io->xdata    = NULL;
}

/** операции и2ц с выделеным адресом. адресная часть - передается как нормальные данные
 * */
I2C_Status I2C_xfer(i2cHandle h, unsigned char SLA
          , const void* addr, unsigned alen
          , void* data, unsigned dlen
          , i2cx_io_option mode
         )
{

    i2cx_descriptor*     self = (i2cx_descriptor*)h;        (void)self;
    i2cx_descriptor*     io  = &SELF;

    if ( UNLIKELY(io->state > i2cio_IDLE) )
        return I2C_BUSY;

    io->alen     = alen;
    if (alen > 0){
        ASSERT(addr != NULL);
        io->adata    = (const unsigned char*)addr;
    }
    else
        io->adata    = NULL;

    I2C_data_assign(io, data, dlen);
    io->mode     = mode;

    return I2C_xfer_start(h, SLA);
}



/// @brief this operations continues on last SLA, with no adress - device provide adress on it`s own
I2C_Status I2C_xfer_next(i2cHandle h, void* data, unsigned dlen, i2cx_io_option mode ){
    i2cx_descriptor*     self = (i2cx_descriptor*)h;        (void)self;
    i2cx_descriptor*     io  = &SELF;
    const i2c_master_api_t*   api = io->base.port->p_api;   (void)api;
    //const i2c_master_ctrl_t*  port= io->base.port->p_ctrl;


    if ( UNLIKELY(io->state > i2cio_IDLE) )
        return I2C_BUSY;

    io->adata    = NULL;
    //io->alen     = 0;
    io->mode     = mode;
    I2C_data_assign(io, data, dlen);

    return I2C_xfer_start(h, io->sla);
}



I2C_Status I2C_xfer2(i2cHandle h, uint32_t SLA_mode, uint32_t addr , void* data, unsigned dlen ){
    i2cx_descriptor*     self = (i2cx_descriptor*)h;        (void)self;
    i2cx_descriptor*     io  = &SELF;
    const i2c_master_api_t*   api = io->base.port->p_api;   (void)api;
    //i2c_master_ctrl_t*  port= io->base.port->p_ctrl;

    if ( UNLIKELY(io->state > i2cio_IDLE) )
        return I2C_BUSY;

    io->addr     = addr;
    io->adata    = (const unsigned char*)&(io->addr);
    uint8_t SLA  = (uint8_t)(SLA_mode >> 8);
    io->mode     = SLA_mode & 0xff;
    io->alen     = 1+ ((SLA_mode & i2co_ADDR)>> i2co_ADDR_Pos);
    I2C_data_assign(io, data, dlen);

    return I2C_xfer_start(h, SLA);
}

/// @brief this operations continues on last SLA
I2C_Status I2C_xfer_continue(i2cHandle h, uint32_t SLA_mode, uint32_t addr ,void* data, unsigned dlen ){
    i2cx_descriptor*     self = (i2cx_descriptor*)h;        (void)self;
    i2cx_descriptor*     io  = &SELF;
    const i2c_master_api_t*   api = io->base.port->p_api;   (void)api;
    //const i2c_master_ctrl_t*  port= io->base.port->p_ctrl;


    if ( UNLIKELY(io->state > i2cio_IDLE) )
        return I2C_BUSY;

    io->addr     = addr;
    io->adata    = (const unsigned char*)&(io->addr);
    //io->alen     = 1+ ((SLA_mode & i2co_ADDR)>> i2co_ADDR_Pos);
    io->mode     = SLA_mode & 0xff;
    I2C_data_assign(io, data, dlen);

    return I2C_xfer_start(h, io->sla);
}




I2C_Status I2C_xfer_start(i2cHandle h, unsigned char SLA)
{
    (void)h;
    I2C_Status res = I2C_OK;
    i2cx_descriptor*     self = (i2cx_descriptor*)h;        (void)self;
    i2cx_descriptor*     io  = &SELF;
    const i2c_master_api_t*   api = io->base.port->p_api;   (void)api;
    i2c_master_ctrl_t*        port= io->base.port->p_ctrl;
    i2c_master_status_t       io_status;

    if ( UNLIKELY(io->state > i2cio_IDLE) )
        return I2C_BUSY;

    trace_i2c_start_on();
    trace_i2c_adr_off();
    I2C_putchar('<');

    i2c_master_xfer_mode_t restart = 0;
    if (io->mode & i2co_LockBus)
        restart = I2C_MASTER_RESTART;

    // TODO: provide support 10bit SLA
    res = API(slaveAddressSet)(port, SLA, I2C_MASTER_ADDR_MODE_7BIT);
    io->sla = SLA;
    SLA = (uint8_t)(SLA <<1);

    io->err = 0;
    if ((io->mode & i2co_XSEND) != 0) {
        //только отсылаем data
        SLA &= (uint8_t)~1;
        if (io->adata != NULL) {
            //отсылаем адрес
            io->state    = i2cio_ADDR;
            if (io->xlen > 0)
                restart = I2C_MASTER_BUS_KEEP;
            trace_i2c_adr_on();
            res = API(write)(port, io->adata, io->alen, restart );
        }
        else /* if (io->xdata != NULL) */ {
            io->state    = i2cio_XSEND;
            res = API(write)(port, io->xdata, io->xlen, restart );
        }
    }
    else {
        if (io->adata != NULL) {
            SLA &= (uint8_t)~1u;
            //отсылаем адрес
            io->state    = i2cio_ADDR;
            if (io->xlen > 0)
                restart = I2C_MASTER_RESTART;
            trace_i2c_adr_on();
            res = API(write)(port, io->adata, io->alen, restart );
        }
        else /* if (io->xdata != NULL) */ {
            //только читаем data
            SLA |= 1;
            io->state    = i2cio_XRECV;
            res = API(read)(port, io->xdata, io->xlen, restart );
        }
    }

    I2C_printf("@%02x", SLA);
    trace_i2c_start_off();

    if (res != FSP_SUCCESS) {
        I2C_putchar('#');
        io->err = res;
        if (io->state == i2cio_ADDR) {
            res = I2C_NOSLA;
        }
        io->state = i2cio_IDLE;
        I2C_finale(io, I2C_MASTER_EVENT_ABORTED);
    } // if (res != FSP_SUCCESS)
    else {
        //if (io->state <= i2cio_IDLE)
        //    res = I2C_BUSY;
        trace_i2c_work_on();

        // startup timeout
        if (self->bytes_tick > 0) {
            self->start_time = clock_now();
            API(statusGet)(port, &io_status);
            self->least_bytes = io_status.pended;
            self->start_state = io->state;
        }
    }

    I2C_putchar('>');
    return res;
}




CODE_ISR
I2C_Status I2C_ISR_xfer(i2cx_descriptor* self){
    (void)self;

    i2cx_descriptor*     io  = &SELF;
    ASSERT( (io != NULL) && (io->base.port != NULL) );
    const i2c_master_api_t*   api = io->base.port->p_api;   (void)api;
    i2c_master_ctrl_t*  port= io->base.port->p_ctrl;
    ASSERT( (api != NULL) && (port != NULL) );

    i2cx_io_state        state = io->state;
    int res;
    i2c_master_xfer_mode_t restart = 0;

    trace_i2c_adr_off();

    I2C_putchar('^');
    I2C_putchar( (char)('0'+(unsigned)(state)) );

    switch (state){
        case i2cio_IDLE: return io->err;

        case i2cio_XSLA: {

            if (io->mode & i2co_LockBus)
                restart = I2C_MASTER_RESTART;

            ASSERT(io->xdata != NULL);

            if ((io->mode & i2co_XSEND) != 0){
                io->state = i2cio_XSEND;
                // TODO: move it i2C_status() ?
                // last operation have sent SLA
                res = API(write)(port, io->xdata, io->xlen, restart | I2C_MASTER_CONTINUE );

                I2C_printf("$>%02x", io->xlen);
            }
            else{
                io->state = i2cio_XRECV;
                res = API(read)(port, io->xdata, io->xlen, restart );

                I2C_printf("$<%02x", io->xlen);
            }
            io->err = res;

            if (res == FSP_SUCCESS)
                res = I2C_BUSY;
            else {
                trace_i2c_work_off();
                I2C_finale(io, I2C_MASTER_EVENT_ABORTED);
            }

            return res;
        }

        //case i2cio_ADDR:
        //case i2cio_XRECV:
        //case i2cio_XSEND:
        default:
            return I2C_BUSY;
    }; // switch (state)

}



CODE_ISR
void I2C_OnEvent(i2c_master_callback_args_t* ev)
{
    trace_i2c_irq_twist();
    i2cx_descriptor*     self= (i2cx_descriptor*)ev->p_context;   (void)self;
    i2cx_descriptor*     io  = &SELF;
    //const i2c_master_api_t* api = io->base.port->p_api; (void)api;
    //i2c_master_ctrl_t*  port= io->base.port->p_ctrl;


    i2cx_io_state        state = io->state;
    I2C_putchar( (char)('0'+(unsigned)(state)) );

    i2c_master_event_t event = ev->event;

    if (event <= I2C_MASTER_EVENT_ABORTED){
        I2C_putchar( '!' );
        if (event == I2C_MASTER_EVENT_NOSLA)
            io->err = I2C_NOSLA;
        else
            io->err = I2C_ABORT;
    }
    else
    switch (state){

    case i2cio_ADDR:
        if (UNLIKELY(ev->event != I2C_MASTER_EVENT_TX_COMPLETE)){
            io->err = I2C_NOSLA;
            break;
        }

        if ( (((unsigned)(io->xdata)) == 0) || (io->xlen == 0) ) {
            io->err = I2C_OK;
            break;
        }
        else {
            io->state = i2cio_XSLA;
#if I2C_ISR_STYLE == I2C_ISR_ACTIVE
            I2C_ISR_xfer(io);
            return;
#else
            I2C_notify(io, I2C_MASTER_EVENT_BUSY);
            return;
#endif
        }
        // fallthrough

    case i2cio_XRECV:
    case i2cio_XSEND:
        trace_i2c_work_off();
        io->err = I2C_OK;
        break;

    case i2cio_IDLE:
    default:
        return;
    }; // switch (state)

    I2C_finale(io, ev->event);
}

I2C_Status I2C_abort(i2cHandle h)
{
    i2cx_descriptor*     self = (i2cx_descriptor*)h;            (void)self;
    i2cx_descriptor*     io  = &SELF;
    const i2c_master_api_t*   api = io->base.port->p_api;       (void)api;
    i2c_master_ctrl_t*  port= io->base.port->p_ctrl;


    int ok = API(abort)(port);
    (void)ok;

    if (io->state == i2cio_IDLE)
        return ok;

    trace_i2c_work_off();
    io->err = FSP_ERR_ABORTED;
    I2C_finale(io, I2C_MASTER_EVENT_ABORTED);

    return I2C_OK;
}


/// @brief отпускает шину если она занята последней операцией
///   эквивалетно I2C_xfer с пустыми параметрами
I2C_Status I2C_xfer_release(i2cHandle h){
    i2cx_descriptor*     self = (i2cx_descriptor*)h;            (void)self;
    i2cx_descriptor*     io  = &SELF;
    const i2c_master_api_t*   api = io->base.port->p_api;       (void)api;
    i2c_master_ctrl_t*  port= io->base.port->p_ctrl;

    I2C_Status res = API(read)(port, NULL, 0, I2C_MASTER_RELEASE | I2C_MASTER_CONTINUE );
    return res;
}


I2C_Status I2C_status(i2cHandle h){
    i2cx_descriptor*     self = (i2cx_descriptor*)h;            (void)self;
    i2cx_descriptor*     io  = &SELF;
    i2c_master_status_t  io_status;
    //const i2c_master_api_t*   api = io->base.port->p_api;
    //i2c_master_ctrl_t*  port= io->base.port->p_ctrl;

    if (io->state == i2cio_IDLE)
        return io->err;

    I2C_putchar('?');
    I2C_putchar( (char)('0'+(unsigned)(io->state)) );

    I2C_Status ok = I2C_ISR_xfer(io);

    // check timeout for pending bytes are decreases
    if (ok == I2C_BUSY)
    if (self->bytes_tick > 0){
        os_time_t hrnow = clock_now();
        int passes = hrnow - self->start_time;
        if (passes > 1){
            i2c_master_ctrl_t*        port= io->base.port->p_ctrl;
            const i2c_master_api_t*   api = io->base.port->p_api;   (void)api;
            /*ok =*/ API(statusGet)(port, &io_status);

            if ( self->start_state != io->state){
                self->start_state   = io->state;
                self->least_bytes   = io_status.pended + (io->state<<16);
                self->start_time    = hrnow;
            }
            else if ( self->least_bytes != io_status.pended) {   // ( self->least_bytes > io_status.pended)
                self->least_bytes   = io_status.pended + (io->state<<16);
                self->start_time    = hrnow;
            }
            else{
                // xfer hungs, abort and timeout error
                ok = I2C_abort(h);
                io->err = FSP_ERR_TIMEOUT;
                if (ok == I2C_OK){
                    return FSP_ERR_TIMEOUT;
                }
            }
        }
    }

    return ok;
}


#ifdef __cplusplus

/** это врап линуховых файловых операций на И2Цпорту.
 * они используют собственный SLA и не пересекаются с I2C_write/read
 * */
int read(i2cHandle port, void* data, int len){
    i2cx_descriptor& io = I2C_port;
    I2C_Status res = I2C_xfer(port, io.file_sla, NULL, 0, data, len, i2co_XRECV);
    if (res == I2C_OK)
        return len;
    else if (res == I2C_NOSLA)
        return 0;
    else
        return len - io.xlen;
}

int write(i2cHandle port, const void* data, int len){
    i2cx_descriptor& io = I2C_port;
    I2C_Status res = I2C_xfer(port, io.file_sla, NULL, 0, (void*)data, len, i2co_XSEND);
    if (res == I2C_OK)
        return len;
    else if (res == I2C_NOSLA)
        return 0;
    else
        return len - io.xlen;
}

int ioctl(i2cHandle port, I2C_ioctl_code code, int data){
    i2cx_descriptor& io = I2C_port;
    io.file_sla = data;
    return 0;
}

#endif // __cplusplus




///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///                         SSP
#include "r_ssp_i2cx.h"
#include "OsProcess.h"


///////////////////////////////////////////////////////////////////////////
#include <trace_probes.h>
#ifndef trace_ssp_i2c_irq
trace_need(ssp_i2c_irq)
// on during spi operation starting
trace_need(ssp_i2c_start)
#endif


PTResult    ssp_i2cx_aquire_dummy(struct SSP_IOPort* this, unsigned cs_id);
DevResult   ssp_i2cx_release_dummy(struct SSP_IOPort* this);

DevResult ssp_i2cx_trx(SSP_IOPort* self, SSPMessage* msg);
PTResult  ssp_i2cx_wait(struct SSP_IOPort* self, SSPMessage* msg, unsigned to /*= toInfinite*/);
DevResult ssp_i2cx_abort(struct SSP_IOPort* self, SSPMessage* msg);
void ssp_i2cx_isr_handle(i2cHandle h, i2c_master_event_t ev, void* ctx);



DevResult ssp_i2cx_init(SSP_I2CXPort* this, const i2c_master_instance_t* io) //, i2cHandle i2x
{
    ASSERT(io != NULL);

    i2cHandle h = I2C_init_master(io, &this->i2c);

    this->spi.aquire    = ssp_i2cx_aquire_dummy;
    this->spi.release   = ssp_i2cx_release_dummy;
    this->spi.trx       = ssp_i2cx_trx;
    this->spi.wait      = ssp_i2cx_wait;
    this->spi.abort     = ssp_i2cx_abort;
    this->spi.msg_signal= NULL;

    ASSERT(h != NULL);
    I2C_assign_handler(h, (ssp_i2cx_isr_handle), this );

    return DEV_OK;
}

static inline
i2cHandle ssp_i2cx_handle(SSP_I2CXPort* this){ return &this->i2c.base; }


PTResult    ssp_i2cx_aquire_dummy(struct SSP_IOPort* ssp, unsigned cs_id) {
    SSP_I2CXPort* this = (SSP_I2CXPort*) ssp;
    this->i2c.sla = (uint8_t)cs_id;
    return ptOK;
}

DevResult   ssp_i2cx_release_dummy(struct SSP_IOPort* ssp) {
    (void)ssp;
    return DEV_OK;

#if 0
    SSP_I2CXPort* this = (SSP_I2CXPort*) ssp;
    I2C_Status ok = I2C_abort( ssp_i2cx_handle(this) );

    if (ok == I2C_OK)
        return DEV_OK;
    else
        return DEV_FAIL+ok;
#endif

}

static
DevResult   I2C_ASDEVRESULT(I2C_Status ok){
    if (ok == I2C_OK)
        return DEV_OK;
    else if (ok >= 0) {
        if (ok == I2C_NOSLA)
            return SSP_errNOACK;
        else if (ok ==  I2C_ABORT)
            return SSP_errABORTED;
        return 1000u + ok;
    }
    else
        return ok;
}


DevResult ssp_i2cx_trx(SSP_IOPort* ssp, SSPMessage* msg){
    SSP_I2CXPort* this = (SSP_I2CXPort*) ssp;

    if (this->i2c.state > i2cio_IDLE)
        return DEV_BUSY;

    if (this->msg != NULL)
        return SSP_errBUSY;

    trace_ssp_i2c_start_on();

    // запомню процесс запустивший транзакцию - он будет просигнализирован после её исполнения
    ssp_signal_to_me(ssp);

    this->msg = msg;

    i2cx_descriptor*     self = &this->i2c;  (void)self;
    i2cx_descriptor*     io  = &SELF;

    unsigned char SLA   = (unsigned char)ssp_msCS(msg->mode);

    unsigned mode = 0;
    if (( msg->mode & SSP_msCS_HOLD) != 0)
        mode |= i2co_LockBus;

    io->alen     = msg->head_count;
    if (msg->head_count > 0){
        ASSERT(msg->head != NULL);
        io->adata    = (const unsigned char*)msg->head;
    }
    else
        io->adata    = NULL;

    if (msg->dst != NULL){
        I2C_data_assign(io, msg->dst, msg->word_count);
        io->mode     = mode | i2co_XRECV;
    }
    else if (msg->src != NULL){
        I2C_data_assign(io, (void*) msg->src, msg->word_count);
        io->mode     = mode | i2co_XSEND;
    }
    else {
        // DUMMY sla touch
        I2C_data_assign(io, NULL, 0);
        io->mode     = mode | (msg->mode & SSPI2C_TOUCH);
    }

    I2C_Status ok = I2C_xfer_start( ssp_i2cx_handle(this) , SLA);

    trace_ssp_i2c_start_off();

    if (ok == I2C_OK)
        return DEV_BUSY;

    return I2C_ASDEVRESULT(ok);
}

PTResult  ssp_i2cx_wait(SSP_IOPort* ssp, SSPMessage* msg, unsigned to /*= toInfinite*/){
    (void)msg; (void)to;
    SSP_I2CXPort* this = (SSP_I2CXPort*) ssp;

    if (this->msg == NULL)
        return ptOK;

    I2C_Status ok = I2C_status( ssp_i2cx_handle(this) );
    if  ( ok == I2C_BUSY )
        return ptWAITING;

    // транзакция завершена, и опрошена -> освобожу порт.
    this->msg = NULL;

    return (PTResult)I2C_ASDEVRESULT(ok);
}

DevResult ssp_i2cx_abort(SSP_IOPort* ssp, SSPMessage* msg){
    (void)msg;
    SSP_I2CXPort* this = (SSP_I2CXPort*) ssp;
    I2C_Status ok = I2C_abort( ssp_i2cx_handle(this) );
    return I2C_ASDEVRESULT(ok);
}


static
void ssp_i2cx_finished(SSP_I2CXPort* this){
    if ((this->i2c.mode & i2co_LockBus) == 0){
        this->spi.release(&this->spi);
    }
}

void ssp_i2cx_isr_handle(i2cHandle h, i2c_master_event_t ev, void* ctx){
    (void)h; (void)ev;
    trace_ssp_i2c_irq_on();

    SSP_I2CXPort* this = (SSP_I2CXPort*) ctx;

    if (this->spi.msg_signal)
        process_poll( (struct process *)this->spi.msg_signal );

    if (this->i2c.state <= i2cio_IDLE) {// finished message transaction
        ssp_i2cx_finished(this);

        SSPMessage* msg;
        msg = this->msg;
        if (msg)
        if (msg->on_complete )
            (msg->on_complete)(&this->spi, msg);
    }

    trace_ssp_i2c_irq_off();

}
