
///
/// @file i2cp.c
/// @authors k. Headley
/// @date 05 dec 2019

/// I2C primary bit-banged implementation

/// @sa doxygen-examples.c for more examples of Doxygen markup


/////////////////////////
// Terms of use
/////////////////////////
/*
 Copyright Information

 Copyright 2002-2019 MBARI
 Monterey Bay Aquarium Research Institute, all rights reserved.

 Terms of Use

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 3 of the License, or
 (at your option) any later version. You can access the GPLv3 license at
 http://www.gnu.org/licenses/gpl-3.0.html

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details
 (http://www.gnu.org/licenses/gpl-3.0.html)

 MBARI provides the documentation and software code "as is", with no warranty,
 express or implied, as to the software, title, non-infringement of third party
 rights, merchantability, or fitness for any particular purpose, the accuracy of
 the code, or the performance or results which you may obtain from its use. You
 assume the entire risk associated with use of the code, and you agree to be
 responsible for the entire cost of repair or servicing of the program with
 which you are using the code.

 In no event shall MBARI be liable for any damages, whether general, special,
 incidental or consequential damages, arising out of your use of the software,
 including, but not limited to, the loss or corruption of your data or damages
 of any kind resulting from use of the software, any prohibited use, or your
 inability to use the software. You agree to defend, indemnify and hold harmless
 MBARI and its officers, directors, and employees against any claim, loss,
 liability or expense, including attorneys' fees, resulting from loss of or
 damage to property or the injury to or death of any person arising out of the
 use of the software.

 The MBARI software is provided without obligation on the part of the
 Monterey Bay Aquarium Research Institute to assist in its use, correction,
 modification, or enhancement.

 MBARI assumes no responsibility or liability for any third party and/or
 commercial software required for the database or applications. Licensee agrees
 to obtain and maintain valid licenses for any additional third party software
 required.
 */

/////////////////////////
// Headers
/////////////////////////
#include <xc.h>
#include "i2cp.h"
#ifdef WITH_PIC24F_CURIOSITY
// LEDs for debugging
#include "leds.h"
#endif // WITH_PIC24F_CURIOSITY
/////////////////////////
// Macros
/////////////////////////

// These macros should only be defined for
// application main files rather than general C files
/*
/// @def PRODUCT
/// @brief header software product name
#define PRODUCT "carbonwg"

/// @def COPYRIGHT
/// @brief header software copyright info
#define COPYRIGHT "Copyright 2002-2019 MBARI Monterey Bay Aquarium Research Institute, all rights reserved."
/// @def NOWARRANTY
/// @brief header software terms of use
#define NOWARRANTY  \
"This program is distributed in the hope that it will be useful,\n"\
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"\
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"\
"GNU General Public License for more details (http://www.gnu.org/licenses/gpl-3.0.html)\n"
*/

///// @def I2C_HND_VALID
///// @brief return true if handle is valid index
///// @param hnd handle to test
#define I2CP_HND_VALID(hnd) ( (hnd>=0) && (hnd<I2CP_HND_COUNT) ? true : false)

/////////////////////////
// Declarations
/////////////////////////

/////////////////////////
// Imports
/////////////////////////

/////////////////////////
// Module Global Variables
/////////////////////////

/////////////////////////
// Function Definitions
/////////////////////////


//#ifdef WITH_I2C_BB

// bit-bang i2c implementation

#ifdef WITH_BB_I2C1
i2c_bb_bus_t i2c_bb_bus_1 = {
    &ANSB, &ODCB,
    &PORTB, &TRISB,
    I2CP_I2C1_SDC_PIN,
    I2CP_I2C1_SDA_PIN,
    0
};
#endif

#ifdef WITH_BB_I2C2
i2c_bb_bus_t i2c_bb_bus_2 = {
    &ANSB, &ODCB,
    &PORTB, &TRISB,
    I2CP_I2C2_SDC_PIN,
    I2CP_I2C2_SDA_PIN,
    0
};
#endif

i2c_bb_bus_t *i2c_bb_bus[]={
#ifdef WITH_BB_I2C1
    &i2c_bb_bus_1,
#else
    NULL,
#endif
#ifdef WITH_BB_I2C2
    &i2c_bb_bus_2,
#else
    NULL,
#endif
};

#if defined WITH_BB_I2C

// BB I2C implementation

static i2c_bb_bus_t *s_bb_instance(i2cp_hnd_t hnd);
int i2cp_wbit(i2cp_hnd_t hnd, byte b);
byte i2cp_rbit(i2cp_hnd_t hnd);
byte i2cp_rbit_r(i2cp_hnd_t hnd, byte *dest, uint32_t len, uint32_t by_ofs, uint32_t bi_ofs);

bool i2cp_wbyte(i2cp_hnd_t hnd, byte src, bool start, bool stop);
bool i2cp_wwbyte(i2cp_hnd_t hnd, byte src, bool start, bool stop);
bool i2cp_wrbyte(i2cp_hnd_t hnd, byte src, bool start, bool stop);
byte i2cp_rbyte(i2cp_hnd_t hnd, bool ack, bool stop);
bool i2cp_flag_isset(i2cp_hnd_t hnd, i2cx_flags_t mask);
bool i2cp_flag_isclr(i2cp_hnd_t hnd, i2cx_flags_t mask);
bool i2cp_test_scl(i2cp_hnd_t hnd);
int i2cp_start_condition(i2cp_hnd_t hnd);
void i2cp_stop_condition(i2cp_hnd_t hnd);

static i2c_bb_bus_t *s_bb_instance(i2cp_hnd_t hnd)
{
    i2c_bb_bus_t *instance=NULL;
    if(I2CP_HND_VALID(hnd)){
        if(hnd==I2CP_HND_BB1)
            instance = i2c_bb_bus[0];
        else if(hnd==I2CP_HND_BB2)
            instance = i2c_bb_bus[1];
    }
    return instance;
}


static int s_i2cp_cycle_clock(i2c_bb_bus_t *self)
{
    int retval=-1;
    if(NULL!=self){

        // release SCL (and wait for secondary to release)
        while(I2CX_SCL_IS_LOW(self)){
            I2CX_SET_SCL(self);
            I2CX_DELAY();
        }

        // read data when SCL high
        // set/clear flags
        if( I2CX_SDA_IS_LOW(self) ){
            I2CX_FLAG_CLR(self, I2CX_DATA);
            retval=0;
        }else{
            I2CX_FLAG_SET(self, I2CX_DATA);
            retval=1;
        }
        // take SCL low
        I2CX_CLR_SCL(self);
        // release SDA
        I2CX_SET_SDA(self);
        I2CX_DELAY();

    }// else invalid arg
    return retval;
}

void i2cp_bb_initialize(i2cp_hnd_t hnd)
{
    i2c_bb_bus_t *self = s_bb_instance(hnd);
    if(NULL!=self){
        // configure analog select (as digital IO)
        I2CX_ANSEL(self) &= ~( I2CX_SCL(self) | I2CX_SDA(self) );
        // configure open-drain control (enable for external pull-ups)
        I2CX_ODC(self) |= ( I2CX_SCL(self) |  I2CX_SDA(self) );
        // release SCL, SDA
        I2CX_SET_SCL(self);
        I2CX_SET_SDA(self);

        // 1-instruction cycle delay required
        // between direction change and w/r
        I2CX_DELAY();
    }// else invalid arg
}

void i2cp_bb_set_debug(i2cp_hnd_t hnd, bool enable)
{
    i2c_bb_bus_t *self = s_bb_instance(hnd);
    if(NULL!=self){
        // enable/disable debug
        if(enable){
            I2CX_FLAG_SET(self,I2CX_DEBUG);
        }else{
            I2CX_FLAG_CLR(self,I2CX_DEBUG);
        }
    }// invalid arg
}

bool i2cp_bb_get_debug(i2cp_hnd_t hnd)
{
    i2c_bb_bus_t *self = s_bb_instance(hnd);
    return (NULL!=self ? I2CX_FLAG_ISSET(self,I2CX_DEBUG) : false);
}

int i2cp_start_condition(i2cp_hnd_t hnd)
{
    int retval=0;

    i2c_bb_bus_t *self = s_bb_instance(hnd);
    if(NULL!=self){
        I2CX_FLAG_CLR(self, I2CX_ESTART);

        // release SCL, SDA
        I2CX_SET_SCL(self);
        I2CX_SET_SDA(self);
        I2CX_DELAY();

        if( !(I2CX_SCL_IS_LOW(self) || I2CX_SDA_IS_LOW(self)) ){
            I2CX_CLR_SDA(self);
            I2CX_DELAY();
            I2CX_CLR_SCL(self);
            I2CX_DELAY();
        }else{
            retval=-1;
            I2CX_FLAG_SET(self, I2CX_ESTART);
        }// error: scl/sda low
    }// invalid arg
    return retval;
}

void i2cp_stop_condition(i2cp_hnd_t hnd)
{
    i2c_bb_bus_t *self = s_bb_instance(hnd);
    if(NULL!=self){
        // take SCL, SDA low
        I2CX_CLR_SCL(self);
        I2CX_CLR_SDA(self);
        I2CX_DELAY();
        // release SCL, SDA
        I2CX_SET_SCL(self);
        I2CX_DELAY();
        I2CX_SET_SDA(self);
        I2CX_DELAY();
    }// invalid arg
}

int i2cp_wbit(i2cp_hnd_t hnd, byte b)
{
    int retval=-1;
    i2c_bb_bus_t *self = s_bb_instance(hnd);
    if(NULL!=self){
        if( b != 0 ){
            // release SDA (high)
            I2CX_SET_SDA(self);
        }else{
            // clear SDA (low)
            I2CX_CLR_SDA(self);
        }
        I2CX_DELAY();
        retval=s_i2cp_cycle_clock(self);
    }// invalid arg
    return retval;
}

byte i2cp_rbit_r(i2cp_hnd_t hnd, byte *dest, uint32_t len, uint32_t by_ofs, uint32_t bi_ofs)
{
    byte retval=0;
    i2c_bb_bus_t *self = s_bb_instance(hnd);
    if(NULL!=self){
        I2CX_SET_SDA(self);
        I2CX_DELAY();
        I2CX_SET_SCL(self);
        I2CX_DELAY();

        if( I2CX_SDA_IS_LOW(self) ){
            retval = 0x00;
        }else{
            retval = 0x01;
        }

        I2CX_CLR_SCL(self);

        // write result to destination, if provided
        // buffer may be arbitrary length (i.e. capture bit sequence)
        if( (NULL != dest) && (by_ofs < len)){
            dest[by_ofs] &= ((retval==0x00 ? 0 : 1) << bi_ofs);
        }
    }// invalid arg

    return retval;
}

byte i2cp_rbit(i2cp_hnd_t hnd)
{
    return i2cp_rbit_r(hnd, NULL, 0, 0, 0);
}


bool  i2cp_wbyte(i2cp_hnd_t hnd, byte src, bool start, bool stop)
{
    bool retval=true;
    i2c_bb_bus_t *self = s_bb_instance(hnd);
    if(NULL!=self){

        // clear write error flag
        I2CX_FLAG_CLR(self, I2CX_EWRITE);

#ifdef WITH_PIC24F_CURIOSITY
        LED_Toggle(LED_LD2);
#endif
        if( start ){
            // assert start condition
            i2cp_start_condition(hnd);
        }

        int i;
        for( i = 0; i < 8; i++ ){
            // transmit bits
            i2cp_wbit( hnd, (src & 0x80) );   // write the most-significant bit
            src <<= 1;
        }

        // get ACK
        byte ack=i2cp_rbit(hnd);
        if(ack==1){
            // NACK - set error flag
            retval=false;
            I2CX_FLAG_SET(self, I2CX_EWRITE);
        }

        if( stop ){
            // assert stop condition
            i2cp_stop_condition(hnd);
        }
#ifdef WITH_PIC24F_CURIOSITY
        LED_Toggle(LED_LD2);
#endif

    }// invalid arg

    return retval;
}

bool i2cp_wwbyte(i2cp_hnd_t hnd, byte src, bool start, bool stop)
{
    return i2cp_wbyte(hnd, I2CX_ADDR_WR(src), start, stop);
}

bool i2cp_wrbyte(i2cp_hnd_t hnd, byte src, bool start, bool stop)
{
    return i2cp_wbyte(hnd, I2CX_ADDR_RD(src), start, stop);
}

byte i2cp_rbyte(i2cp_hnd_t hnd, bool ack, bool stop)
{
    byte retval=0;
    i2c_bb_bus_t *self = s_bb_instance(hnd);
    if(NULL!=self){

        // clear read error flag
        I2CX_FLAG_CLR(self, I2CX_EREAD);
        int i=0;

        for( i = 0; i < 8; i++ ){
            // clock in bits (read by cycle clock)
            int test =  s_i2cp_cycle_clock(self);
            if(test>=0){
                // bit valid, add to output
                retval <<= 1;
                retval |= test;
            }else{
                // error - set read error flag
                I2CX_FLAG_SET(self, I2CX_EREAD);
            }
        }

        if( ack && I2CX_FLAG_ISCLR(self, I2CX_EREAD)){
            // read OK and ACK enabled
            // send ACK
             i2cp_wbit(hnd, 0);
        }else{
            // error or ACK disabled
            // send NACK
            i2cp_wbit(hnd, 1);
        }

        if( stop ){
            // assert stop condition
            i2cp_stop_condition(hnd);
        }

    }// invalid arg
    return retval;
}

bool i2cp_bb_send_bytes(i2cp_hnd_t hnd, byte addr, byte *src, uint32_t len, uint32_t *bw)
{
    bool retval = false;
    i2c_bb_bus_t *self = s_bb_instance(hnd);
    if(NULL!=self){
        uint32_t i = 0;
       // start, send address, write
       if( i2cp_wwbyte(hnd, addr, true, false ) || i2cp_get_debug(hnd) ){
            while(i<len){
                // send data, get ACK
                if( i2cp_wbyte(hnd, src[i], false, false ) == false ){
                    if(i2cp_get_debug(hnd)==false)
                    // quit if write fails
                    break;
                }
                i++;
                if(NULL!=bw){
                    // increment bytes written (optional output)
                    *bw=i;
                }
            }
        }
        // send SP
        i2cp_stop_condition(hnd);
        // return true if all bytes sent
        if( i == len ){
            retval=true;
        }
    }// invalid arg
    return retval;
}

bool i2cp_bb_recv_bytes(i2cp_hnd_t hnd, byte addr, byte *dest, uint32_t len, uint32_t *br)
{
    bool retval = false;
    i2c_bb_bus_t *self = s_bb_instance(hnd);
    if(NULL!=self){
        uint32_t i = 0;
        // start, send address, read
        if( i2cp_wrbyte(hnd, addr, true, false ) || i2cp_get_debug(hnd))
        {
            while(i<len){
                // read data, send ACK
                if( i2cp_rbyte(hnd, true, false ) == false ){
                    // ignore error in debug mode
                    if(i2cp_get_debug(hnd)==false){
                        // quit if read fails
                        break;
                    }
                }
                i++;
                if(NULL!=br){
                    // increment bytes read (optional output)
                    *br=i;
                }
            }

        }
        // send SP
        i2cp_stop_condition(hnd);
        // return true if all bytes read
        if( i == len ){
            retval=true;
        }

        }// invalid arg
    return retval;
}

//bool i2cp_flag_isset(i2cp_hnd_t hnd, i2cx_flags_t mask)
//{
//
//    i2c_bus_t *self = s_hnd_instance(hnd);
//    return (self!=NULL ? I2CX_FLAG_ISSET(self, mask) : false);
//}

//bool i2cp_flag_isclr(i2cp_hnd_t hnd, i2cx_flags_t mask)
//{
//    i2c_bus_t *self = s_hnd_instance(hnd);
//    return (self!=NULL ? I2CX_FLAG_ISCLR(self, mask) : false);
//}

//bool i2cp_test_scl(i2cp_hnd_t hnd)
//{
//    i2c_bus_t *self = s_hnd_instance(hnd);
//    return ( NULL!=self ? I2CX_SCL_IS_HIGH(self) : false);
//}

#endif // WITH_BB_I2C


// I2C1 bus resources
#ifdef WITH_MMC_I2C1
uint8_t i2c1_trb_buffer[TRB_BUFFER_BYTES]={0};

i2c_mcc_bus_t i2c_mcc_bus_1={
    I2CP_HND_MMC1,
    {0,0,i2c1_trb_buffer},
    I2C_MESSAGE_FAIL,
    0x0
};
#endif

// I2C2 bus resources
#ifdef WITH_MMC_I2C1
uint8_t i2c2_trb_buffer[TRB_BUFFER_BYTES]={0};

i2c_mcc_bus_t i2c_mcc_bus_2={
    I2CP_HND_MMC2,
    {0,0,i2c2_trb_buffer},
    I2C_MESSAGE_FAIL,
    0x0
};
#endif

i2c_mcc_bus_t *i2c_mcc_bus[]={
#ifdef WITH_MMC_I2C1
    &i2c_mcc_bus_1,
#else
    NULL,
#endif
#ifdef WITH_MMC_I2C2
    &i2c_mcc_bus_2,
#else
    NULL,
#endif
};

#if defined WITH_MMC_I2C

// MMC I2C implentation

static void s_primary_initialize(i2cp_hnd_t hnd)
{
    if(hnd==I2CP_HND_MMC1){
#ifdef WITH_MMC_I2C1
        I2C1_Initialize();
#endif
    }else if(hnd==I2CP_HND_MMC2){
#ifdef WITH_MMC_I2C2
        I2C2_Initialize();
#endif
    }
}

static void s_primary_write(i2cp_hnd_t hnd, uint8_t *pdata, uint8_t length, i2c_addr_t address, i2c_status_t *pstatus)
{
    if(hnd==I2CP_HND_MMC1){
#ifdef WITH_MMC_I2C1
        I2C1_MasterWrite(pdata, length, address, (I2C1_MESSAGE_STATUS *)pstatus);
#endif
    }else if(hnd==I2CP_HND_MMC2){
#ifdef WITH_MMC_I2C2
        I2C2_MasterWrite(pdata, length, address, (I2C2_MESSAGE_STATUS *)pstatus);
#endif
    }
}

static void s_primary_read(i2cp_hnd_t hnd, uint8_t *pdata, uint8_t length, i2c_addr_t address, i2c_status_t *pstatus)
{
    if(hnd==I2CP_HND_MMC1){
#ifdef WITH_MMC_I2C1
        I2C1_MasterRead(pdata, length, address, (I2C1_MESSAGE_STATUS *)pstatus);
#endif
    }else if(hnd==I2CP_HND_MMC2){
#ifdef WITH_MMC_I2C2
        I2C2_MasterRead(pdata, length, address, (I2C2_MESSAGE_STATUS *)pstatus);
#endif
    }
}

static void s_primary_trb_insert(i2cp_hnd_t hnd, uint8_t count, i2c_trb_t *ptrb_list, i2c_status_t *pstatus)
{
    if(hnd==I2CP_HND_MMC1){
#ifdef WITH_MMC_I2C1
        I2C1_MasterTRBInsert(count, (I2C1_TRANSACTION_REQUEST_BLOCK *)ptrb_list, (I2C1_MESSAGE_STATUS *)pstatus);
#endif
    }else if(hnd==I2CP_HND_MMC2){
#ifdef WITH_MMC_I2C2
        I2C2_MasterTRBInsert(count, (I2C2_TRANSACTION_REQUEST_BLOCK *)ptrb_list, (I2C2_MESSAGE_STATUS *)pstatus);
#endif
    }
}

static void s_primary_read_trb_build(i2cp_hnd_t hnd, i2c_trb_t *ptrb, uint8_t *pdata, uint8_t length, i2c_addr_t address)
{
    if(hnd==I2CP_HND_MMC1){
#ifdef WITH_MMC_I2C1
        I2C1_MasterReadTRBBuild((I2C1_TRANSACTION_REQUEST_BLOCK *)ptrb, pdata, length, address);
#endif
    }else if(hnd==I2CP_HND_MMC2){
#ifdef WITH_MMC_I2C2
        I2C2_MasterReadTRBBuild((I2C2_TRANSACTION_REQUEST_BLOCK *)ptrb, pdata, length, address);
#endif
    }
}

static void s_primary_write_trb_build(i2cp_hnd_t hnd, i2c_trb_t *ptrb, uint8_t *pdata, uint8_t length, i2c_addr_t address)
{
    if(hnd==I2CP_HND_MMC1){
#ifdef WITH_MMC_I2C1
        I2C1_MasterWriteTRBBuild((I2C1_TRANSACTION_REQUEST_BLOCK *)ptrb, pdata, length, address);
#endif
    }else if(hnd==I2CP_HND_MMC2){
#ifdef WITH_MMC_I2C2
        I2C2_MasterWriteTRBBuild((I2C2_TRANSACTION_REQUEST_BLOCK *)ptrb, pdata, length, address);
#endif
    }
}

static void s_primary_queue_is_empty(i2cp_hnd_t hnd)
{
    if(hnd==I2CP_HND_MMC1){
#ifdef WITH_MMC_I2C1
        I2C1_MasterQueueIsEmpty();
#endif
    }else if(hnd==I2CP_HND_MMC2){
#ifdef WITH_MMC_I2C2
        I2C2_MasterQueueIsEmpty();
#endif
    }
}

static void s_primary_queue_is_full(i2cp_hnd_t hnd)
{
    if(hnd==I2CP_HND_MMC1){
#ifdef WITH_MMC_I2C1
        I2C1_MasterQueueIsFull();
#endif
    }else if(hnd==I2CP_HND_MMC2){
#ifdef WITH_MMC_I2C2
        I2C2_MasterQueueIsFull();
#endif
    }
}


// Generic API functions
// map to BB or MCC unique per-bus functions and data types

static i2c_mcc_bus_t *s_mmc_instance(i2cp_hnd_t hnd)
{
    i2c_mcc_bus_t *instance=NULL;
    if(I2CP_HND_VALID(hnd)){
        if(hnd==I2CP_HND_MMC1)
            instance = i2c_mcc_bus[0];
        if(hnd==I2CP_HND_MMC2)
            instance = i2c_mcc_bus[2];
    }
    return instance;
}

void i2cp_mmc_initialize(i2cp_hnd_t hnd)
{
    s_primary_initialize(hnd);
}

void i2cp_mmc_set_debug(i2cp_hnd_t hnd, bool enable)
{
    i2c_mcc_bus_t *self = s_mmc_instance(hnd);
    if(NULL!=self){
        // enable/disable debug
        if(enable){
            I2CX_FLAG_SET(self,I2CX_DEBUG);
        }else{
            I2CX_FLAG_CLR(self,I2CX_DEBUG);
        }
    }// invalid arg
}

bool i2cp_mmc_get_debug(i2cp_hnd_t hnd)
{
    i2c_mcc_bus_t *self = s_mmc_instance(hnd);
    return (NULL!=self ? I2CX_FLAG_ISSET(self,I2CX_DEBUG) : false);
}

bool i2cp_mmc_send_bytes(i2cp_hnd_t hnd, i2c_addr_t addr, byte *src, uint32_t len, uint32_t *bw)
{
    bool retval=false;
    i2c_mcc_bus_t *self = s_mmc_instance(hnd);
    if(NULL!=self){
        s_primary_write(hnd, src, 1, addr, &self->status);
    }
    return retval;
}

bool i2cp_mmc_recv_bytes(i2cp_hnd_t hnd, i2c_addr_t addr, byte *dest, uint32_t len, uint32_t *br)
{
    bool retval=false;
    i2c_mcc_bus_t *self = s_mmc_instance(hnd);
    if(NULL!=self){
        s_primary_read(hnd, dest, 1, addr, &self->status);
    }
    return retval;
}
#endif //WITH_MMC_I2C


// API Functions
void i2cp_initialize(i2cp_hnd_t hnd)
{
#if defined WITH_BB_I2C
    if(I2CP_HND_IS_BB(hnd))
        i2cp_bb_initialize(hnd);
#endif
#if defined WITH_MMC_I2C
    if(I2CP_HND_IS_MMC(hnd))
        i2cp_mmc_initialize(hnd);
#endif
}

void i2cp_set_debug(i2cp_hnd_t hnd, bool enable)
{
#if defined WITH_BB_I2C
    if(I2CP_HND_IS_BB(hnd))
        i2cp_bb_set_debug(hnd, enable);
#endif
#if defined WITH_MMC_I2C
    if(I2CP_HND_IS_MMC(hnd))
        i2cp_mmc_set_debug(hnd, enable);
#endif
}

bool i2cp_get_debug(i2cp_hnd_t hnd)
{
    bool retval=false;
#if defined WITH_BB_I2C
    if(I2CP_HND_IS_BB(hnd))
        retval=i2cp_bb_get_debug(hnd);
#endif
#if defined WITH_MMC_I2C
    if(I2CP_HND_IS_MMC(hnd))
        retval=i2cp_mmc_get_debug(hnd);
#endif
    return retval;
}

bool i2cp_send_bytes(i2cp_hnd_t hnd, i2c_addr_t addr, byte *src, uint32_t len, uint32_t *bw)
{
    bool retval=false;
#if defined WITH_BB_I2C
    if(I2CP_HND_IS_BB(hnd))
        retval=i2cp_bb_send_bytes(hnd, I2CX_LSB16(addr), src, len, bw);
#endif
#if defined WITH_MMC_I2C
    if(I2CP_HND_IS_MMC(hnd))
        retval=i2cp_mmc_send_bytes(hnd, addr, src, len, bw);
#endif
    return retval;
}

bool i2cp_recv_bytes(i2cp_hnd_t hnd, i2c_addr_t addr, byte *dest, uint32_t len, uint32_t *br)
{
    bool retval=false;
#if defined WITH_BB_I2C
    if(I2CP_HND_IS_BB(hnd))
        retval=i2cp_bb_recv_bytes(hnd, I2CX_LSB16(addr), dest, len, br);
#endif
#if defined WITH_MMC_I2C
    if(I2CP_HND_IS_MMC(hnd))
        retval=i2cp_mmc_recv_bytes(hnd, addr, dest, len, br);
#endif
    return retval;
}
