/*
 * Copyright (C) 2005-2007 by egnite Software GmbH. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holders nor the names of
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * For additional information see http://www.ethernut.de/
 *
 */

/*!
 * \file dev/twbbif.c
 * \brief Bit banging two wire interface.
 *
 * Tested with AT91R40008 only. Other ARMs or AVR may not work.
 *
 * \verbatim
 *
 * $Log$
 * Revision 1.11  2008/08/11 06:59:42  haraldkipp
 * BSD types replaced by stdint types (feature request #1282721).
 *
 * Revision 1.10  2008/02/15 17:04:49  haraldkipp
 * Spport for AT91SAM7SE512 added.
 *
 * Revision 1.9  2007/10/04 20:16:57  olereinhardt
 * Support for SAM7S256 added
 *
 * Revision 1.8  2007/07/09 13:38:50  olereinhardt
 * 2007-07-09  Ole Reinhardt <ole.reinhardt@embedded-it.de>
 *         * dev/twbbif.c: Added default TWI_DELAY for avr architecture
 *         to allow in-tree compilation
 *         * net/Makefile: Commented out igmp support as long as it does
 *         not exists to allow in-tree compilation
 *
 * Revision 1.7  2007/02/15 16:25:40  haraldkipp
 * Configurable port bits. Should work now on all AT91 MCUs.
 *
 * Revision 1.6  2006/10/08 16:48:09  haraldkipp
 * Documentation fixed
 *
 * Revision 1.5  2006/06/28 17:10:35  haraldkipp
 * Include more general header file for ARM.
 *
 * Revision 1.4  2006/05/25 09:30:23  haraldkipp
 * Compiles for AVR. Still not tested, though.
 *
 * Revision 1.3  2006/04/07 12:50:20  haraldkipp
 * Added additional delays in TwAck.
 * Clock and data forced to high before switching to input gives better
 * waveforms.
 * Additional delay added to set clock to 50% duty cycle. The PCF8563
 * seems to fail, if the clock's high time is much longer than the low time.
 *
 * Revision 1.2  2006/01/23 17:32:35  haraldkipp
 * Automatic initialization added.
 *
 * Revision 1.1  2005/10/24 10:21:57  haraldkipp
 * Initial check in.
 *
 *
 * \endverbatim
 */

#include <cfg/os.h>
#include <cfg/twi.h>
#include <cfg/arch/gpio.h>

#include <sys/heap.h>
#include <sys/event.h>
#include <string.h>

#include <dev/twif.h>

#if defined(__arm__)

#include <arch/arm.h>

/*!
 * \brief GPIO controller ID.
 *
 * Target specific defaults are set if the ID is not specified elsewhere.
 */
#if !defined(TWI_PIO_ID)
#if defined(MCU_AT91SAM7X) || defined(MCU_AT91SAM7S256) || defined(MCU_AT91SAM7SE512)
#define TWI_PIO_ID  PIOA_ID
#elif defined(MCU_AT91SAM9260)
#define TWI_PIO_ID  PIOB_ID
#else
#define TWI_PIO_ID  PIO_ID
#endif
#endif

/*!
 * \brief GPIO bit of bit banging TWI data line.
 *
 * Target specific defaults are used if the bit is not specified elsewhere.
 */
#ifndef TWI_SDA_BIT
#if defined(MCU_AT91SAM9260)
#define TWI_SDA_BIT     12
#else
#define TWI_SDA_BIT     10
#endif
#endif

/*!
 * \brief Clock delay.
 */
#ifndef TWI_DELAY
#if defined(MCU_AT91SAM9260)
#define TWI_DELAY   16
#else
#define TWI_DELAY   8
#endif
#endif

/*!
 * \brief GPIO bit of bit banging TWI clock line.
 *
 * Target specific defaults are used if the bit is not specified elsewhere.
 */
#ifndef TWI_SCL_BIT
#if defined(MCU_AT91SAM9260)
#define TWI_SCL_BIT     13
#else
#define TWI_SCL_BIT     11
#endif
#endif

/*!
 * \brief Bit banging TWI GPIO registers.
 *
 * Based on the GPIO controller ID.
 */
#if TWI_PIO_ID == PIOA_ID

#ifndef TWI_SDA_PE_REG
#define TWI_SDA_PE_REG  PIOA_PER
#endif
#ifndef TWI_SDA_OE_REG
#define TWI_SDA_OE_REG  PIOA_OER
#endif
#ifndef TWI_SDA_OD_REG
#define TWI_SDA_OD_REG  PIOA_ODR
#endif
#ifndef TWI_SDA_COD_REG
#define TWI_SDA_COD_REG PIOA_CODR
#endif
#ifndef TWI_SDA_SOD_REG
#define TWI_SDA_SOD_REG PIOA_SODR
#endif
#ifndef TWI_SDA_PDS_REG
#define TWI_SDA_PDS_REG PIOA_PDSR
#endif

#ifndef TWI_SCL_PE_REG
#define TWI_SCL_PE_REG  PIOA_PER
#endif
#ifndef TWI_SCL_OE_REG
#define TWI_SCL_OE_REG  PIOA_OER
#endif
#ifndef TWI_SCL_OD_REG
#define TWI_SCL_OD_REG  PIOA_ODR
#endif
#ifndef TWI_SCL_COD_REG
#define TWI_SCL_COD_REG PIOA_CODR
#endif
#ifndef TWI_SCL_SOD_REG
#define TWI_SCL_SOD_REG PIOA_SODR
#endif
#ifndef TWI_SCL_PDS_REG
#define TWI_SCL_PDS_REG PIOA_PDSR
#endif

#elif TWI_PIO_ID == PIOB_ID

#ifndef TWI_SDA_PE_REG
#define TWI_SDA_PE_REG  PIOB_PER
#endif
#ifndef TWI_SDA_OE_REG
#define TWI_SDA_OE_REG  PIOB_OER
#endif
#ifndef TWI_SDA_OD_REG
#define TWI_SDA_OD_REG  PIOB_ODR
#endif
#ifndef TWI_SDA_COD_REG
#define TWI_SDA_COD_REG PIOB_CODR
#endif
#ifndef TWI_SDA_SOD_REG
#define TWI_SDA_SOD_REG PIOB_SODR
#endif
#ifndef TWI_SDA_PDS_REG
#define TWI_SDA_PDS_REG PIOB_PDSR
#endif

#ifndef TWI_SCL_PE_REG
#define TWI_SCL_PE_REG  PIOB_PER
#endif
#ifndef TWI_SCL_OE_REG
#define TWI_SCL_OE_REG  PIOB_OER
#endif
#ifndef TWI_SCL_OD_REG
#define TWI_SCL_OD_REG  PIOB_ODR
#endif
#ifndef TWI_SCL_COD_REG
#define TWI_SCL_COD_REG PIOB_CODR
#endif
#ifndef TWI_SCL_SOD_REG
#define TWI_SCL_SOD_REG PIOB_SODR
#endif
#ifndef TWI_SCL_PDS_REG
#define TWI_SCL_PDS_REG PIOB_PDSR
#endif

#elif TWI_PIO_ID == PIOC_ID

#ifndef TWI_SDA_PE_REG
#define TWI_SDA_PE_REG  PIOC_PER
#endif
#ifndef TWI_SDA_OE_REG
#define TWI_SDA_OE_REG  PIOC_OER
#endif
#ifndef TWI_SDA_OD_REG
#define TWI_SDA_OD_REG  PIOC_ODR
#endif
#ifndef TWI_SDA_COD_REG
#define TWI_SDA_COD_REG PIOC_CODR
#endif
#ifndef TWI_SDA_SOD_REG
#define TWI_SDA_SOD_REG PIOC_SODR
#endif
#ifndef TWI_SDA_PDS_REG
#define TWI_SDA_PDS_REG PIOC_PDSR
#endif

#ifndef TWI_SCL_PE_REG
#define TWI_SCL_PE_REG  PIOC_PER
#endif
#ifndef TWI_SCL_OE_REG
#define TWI_SCL_OE_REG  PIOC_OER
#endif
#ifndef TWI_SCL_OD_REG
#define TWI_SCL_OD_REG  PIOC_ODR
#endif
#ifndef TWI_SCL_COD_REG
#define TWI_SCL_COD_REG PIOC_CODR
#endif
#ifndef TWI_SCL_SOD_REG
#define TWI_SCL_SOD_REG PIOC_SODR
#endif
#ifndef TWI_SCL_PDS_REG
#define TWI_SCL_PDS_REG PIOC_PDSR
#endif

#else

#ifndef TWI_SDA_PE_REG
#define TWI_SDA_PE_REG  PIO_PER
#endif
#ifndef TWI_SDA_OE_REG
#define TWI_SDA_OE_REG  PIO_OER
#endif
#ifndef TWI_SDA_OD_REG
#define TWI_SDA_OD_REG  PIO_ODR
#endif
#ifndef TWI_SDA_COD_REG
#define TWI_SDA_COD_REG PIO_CODR
#endif
#ifndef TWI_SDA_SOD_REG
#define TWI_SDA_SOD_REG PIO_SODR
#endif
#ifndef TWI_SDA_PDS_REG
#define TWI_SDA_PDS_REG PIO_PDSR
#endif

#ifndef TWI_SCL_PE_REG
#define TWI_SCL_PE_REG  PIO_PER
#endif
#ifndef TWI_SCL_OE_REG
#define TWI_SCL_OE_REG  PIO_OER
#endif
#ifndef TWI_SCL_OD_REG
#define TWI_SCL_OD_REG  PIO_ODR
#endif
#ifndef TWI_SCL_COD_REG
#define TWI_SCL_COD_REG PIO_CODR
#endif
#ifndef TWI_SCL_SOD_REG
#define TWI_SCL_SOD_REG PIO_SODR
#endif
#ifndef TWI_SCL_PDS_REG
#define TWI_SCL_PDS_REG PIO_PDSR
#endif

#endif

#define TWI_ENABLE() { \
    outr(TWI_SDA_COD_REG, _BV(TWI_SDA_BIT)); \
    outr(TWI_SCL_COD_REG, _BV(TWI_SCL_BIT)); \
    outr(TWI_SDA_PE_REG, _BV(TWI_SDA_BIT)); \
    outr(TWI_SCL_PE_REG, _BV(TWI_SCL_BIT)); \
}

#define SDA_LOW() { \
    outr(TWI_SDA_COD_REG, _BV(TWI_SDA_BIT)); \
    outr(TWI_SDA_OE_REG, _BV(TWI_SDA_BIT)); \
}

#define SDA_HIGH() { \
    outr(TWI_SDA_SOD_REG, _BV(TWI_SDA_BIT)); \
    outr(TWI_SDA_OD_REG, _BV(TWI_SDA_BIT)); \
}

#define SDA_STAT()      (inr(TWI_SDA_PDS_REG) & _BV(TWI_SDA_BIT))

#define SCL_LOW() { \
    outr(TWI_SCL_COD_REG, _BV(TWI_SCL_BIT)); \
    outr(TWI_SCL_OE_REG, _BV(TWI_SCL_BIT)); \
}

#define SCL_HIGH() { \
    outr(TWI_SCL_SOD_REG, _BV(TWI_SCL_BIT)); \
    outr(TWI_SCL_OD_REG, _BV(TWI_SCL_BIT)); \
}

#elif defined(__AVR__)

/*
 * AVR not yet tested.
 */
#include <cfg/arch/avr.h>

#ifndef TWI_SDA_BIT
#define TWI_SDA_BIT     0
#endif

#if (TWI_SDA_AVRPORT == AVRPORTD)
#define TWI_SDA_PORT    PORTD
#define TWI_SDA_PIN     PIND
#define TWI_SDA_DDR     DDRD
#elif (TWI_SDA_AVRPORT == AVRPORTE)
#define TWI_SDA_PORT    PORTE
#define TWI_SDA_PIN     PINE
#define TWI_SDA_DDR     DDRE
#elif (TWI_SDA_AVRPORT == AVRPORTF)
#define TWI_SDA_PORT    PORTF
#define TWI_SDA_PIN     PINF
#define TWI_SDA_DDR     DDRF
#else
#define TWI_SDA_PORT    PORTB
#define TWI_SDA_PIN     PINB
#define TWI_SDA_DDR     DDRB
#endif

#ifndef TWI_SCL_BIT
#define TWI_SCL_BIT     1
#endif

#if (TWI_SCL_AVRPORT == AVRPORTD)
#define TWI_SCL_PORT    PORTD
#define TWI_SCL_DDR     DDRD
#elif (TWI_SCL_AVRPORT == AVRPORTE)
#define TWI_SCL_PORT    PORTE
#define TWI_SCL_DDR     DDRE
#elif (TWI_SCL_AVRPORT == AVRPORTF)
#define TWI_SCL_PORT    PORTF
#define TWI_SCL_DDR     DDRF
#else
#define TWI_SCL_PORT    PORTB
#define TWI_SCL_DDR     DDRB
#endif

#define TWI_ENABLE() {              \
    cbi(TWI_SDA_PORT, TWI_SDA_BIT); \
    cbi(TWI_SCL_PORT, TWI_SCL_BIT); \
}

#define SDA_LOW()   sbi(TWI_SDA_DDR, TWI_SDA_BIT)
#define SDA_HIGH()  cbi(TWI_SDA_DDR, TWI_SDA_BIT)
#define SDA_STAT()  bit_is_set(TWI_SDA_PIN, TWI_SDA_BIT)

#define SCL_LOW()   sbi(TWI_SCL_DDR, TWI_SCL_BIT)
#define SCL_HIGH()  cbi(TWI_SCL_DDR, TWI_SCL_BIT)

#ifndef TWI_DELAY
#define TWI_DELAY   8
#endif

#endif                          /* __AVR__ */


static int twibb_initialized;

/*
 * Short delay.
 *
 * Our bit banging code relies on pull-up resistors. The I/O ports mimic
 * open collector outputs by switching to input mode for high level and
 * switching to output mode for low level. This is much slower than
 * switching an output between low to high. Thus we need some delay.
 */
static void TwDelay(int nops)
{
    while (nops--) {
        _NOP();
    }
}

/*
 * Falling edge on the data line while the clock line is high indicates
 * a start condition.
 *
 * Entry: SCL any, SDA any
 * Exit: SCL low, SDA low
 */
static void TwStart(void)
{
    SDA_HIGH();
    TwDelay(TWI_DELAY);
    SCL_HIGH();
    TwDelay(TWI_DELAY);
    SDA_LOW();
    TwDelay(TWI_DELAY);
    SCL_LOW();
    TwDelay(TWI_DELAY);
}

/*
 * Rising edge on the data line while the clock line is high indicates
 * a stop condition.
 *
 * Entry: SCL low, SDA any
 * Exit: SCL high, SDA high
 */
static void TwStop(void)
{
    SDA_LOW();
    TwDelay(TWI_DELAY);
    SCL_HIGH();
    TwDelay(2 * TWI_DELAY);
    SDA_HIGH();
    TwDelay(8 * TWI_DELAY);
}

/*
 * Toggles out a single byte in master mode.
 *
 * Entry: SCL low, SDA any
 * Exit: SCL low, SDA high
 */
static int TwPut(uint8_t octet)
{
    int i;

    for (i = 0x80; i; i >>= 1) {
        /* Set the data bit. */
        if (octet & i) {
            SDA_HIGH();
        } else {
            SDA_LOW();
        }
        /* Wait for data to stabelize. */
        TwDelay(TWI_DELAY);
        /* Toggle the clock. */
        SCL_HIGH();
        TwDelay(2 * TWI_DELAY);
        SCL_LOW();
        TwDelay(TWI_DELAY);
    }

    /* Set data line high to receive the ACK bit. */
    SDA_HIGH();

    /* ACK should appear shortly after the clock's rising edge. */
    SCL_HIGH();
    TwDelay(2 * TWI_DELAY);
    if (SDA_STAT()) {
        i = -1;
    } else {
        i = 0;
    }
    SCL_LOW();

    return i;
}

/*
 * Toggles in a single byte in master mode.
 *
 * Entry: SCL low, SDA any
 * Exit: SCL low, SDA high
 */
static uint8_t TwGet(void)
{
    uint8_t rc = 0;
    int i;

    /* SDA is input. */
    SDA_HIGH();
    TwDelay(TWI_DELAY);
    for (i = 0x80; i; i >>= 1) {
        TwDelay(TWI_DELAY);
        /* Data should appear shortly after the clock's rising edge. */
        SCL_HIGH();
        TwDelay(2 * TWI_DELAY);
        /* SDA read. */
        if (SDA_STAT()) {
            rc |= i;
        }
        SCL_LOW();
    }
    return rc;
}

/*
 * Toggles out an acknowledge bit in master mode.
 *
 * Entry: SCL low, SDA any
 * Exit: SCL low, SDA high
 */
static void TwAck(void)
{
    SDA_LOW();
    TwDelay(TWI_DELAY);
    SCL_HIGH();
    TwDelay(2 * TWI_DELAY);
    SCL_LOW();
    TwDelay(TWI_DELAY);
    SDA_HIGH();
}

/*!
 * \brief Transmit and/or receive data as a master.
 *
 * The two-wire serial interface must have been initialized by calling
 * TwInit() before this function can be used.
 *
 * \param sla    Slave address of the destination. This slave address
 *               must be specified as a 7-bit address. For example, the
 *               PCF8574A may be configured to slave addresses from 0x38
 *               to 0x3F.
 * \param txdata Points to the data to transmit. Ignored, if the number
 *               of data bytes to transmit is zero.
 * \param txlen  Number of data bytes to transmit. If zero, then the
 *               interface will not send any data to the slave device
 *               and will directly enter the master receive mode.
 * \param rxdata Points to a buffer, where the received data will be
 *               stored. Ignored, if the maximum number of bytes to
 *               receive is zero.
 * \param rxsiz  Maximum number of bytes to receive. Set to zero, if
 *               no bytes are expected from the slave device.
 * \param tmo    Timeout in milliseconds. To disable timeout, set this
 *               parameter to NUT_WAIT_INFINITE.
 *
 * \return The number of bytes received, -1 in case of an error or timeout.
 *
 * \note Timeout is not used in the bit banging version.
 */
int NutTwiMasterTranceive(NUTTWIBUS *bus, uint8_t sla, const void *txdata, uint16_t txlen, void *rxdata, uint16_t rxsiz, uint32_t tmo)

{
    int rc = 0;
    uint8_t *cp;
    NUTTWIICB *icb = bus->bus_icb;

    if (!twibb_initialized) {
        TwInit(0);
    }

    /* This routine is marked reentrant, so lock the interface. */
    if (NutEventWait(&bus->bus_mutex, tmo)) {
        icb->tw_mm_error = TWERR_IF_LOCKED;
        return -1;
    }

    TwStart();
    /* Send SLA+W and check for ACK. */
    if ((rc = TwPut(sla << 1)) == 0) {
        for (cp = (uint8_t *)txdata; txlen--; cp++) {
            if ((rc = TwPut(*cp)) != 0) {
                break;
            }
        }
    }
    if (rc == 0 && rxsiz) {
        TwStart();
        /* Send SLA+R and check for ACK. */
        if ((rc = TwPut((sla << 1) | 1)) == 0) {
            for (cp = rxdata;; cp++) {
                *cp = TwGet();
                if (++rc >= rxsiz) {
                    break;
                }
                TwAck();
            }
        }
    }
    TwStop();

    if (rc == -1) {
        icb->tw_mm_error = TWERR_SLA_NACK;
    }

    /* Release the interface. */
    NutEventPost(&bus->bus_mutex);

    return rc;
}

/*!
 * \brief Get last master mode error.
 *
 * You may call this function to determine the specific cause
 * of an error after twi transaction failed.
 *
 */
int NutTwiMasterError(NUTTWIBUS *bus)
{
    int rc = bus->bus_icb->tw_mm_error;
    bus->bus_icb->tw_mm_error = 0;
    return rc;
}

/*!
 * \brief Listen for incoming data from a master.
 *
 * If this function returns without error, the bus is blocked. The caller
 * must immediately process the request and return a response by calling
 * TwSlaveRespond().
 *
 * \note Slave mode is not implemented in the bitbanging driver.
 *       Thus the function always returns -1.
 *
 * \param sla    Points to a byte variable, which receives the slave
 *               address sent by the master. This can be used by the
 *               caller to determine whether the the interface has been
 *               addressed by a general call or its individual address.
 * \param rxdata Points to a data buffer where the received data bytes
 *               are stored.
 * \param rxsiz  Specifies the maximum number of data bytes to receive.
 * \param tmo    Timeout in milliseconds. To disable timeout,
 *               set this parameter to NUT_WAIT_INFINITE.
 *
 * \return The number of bytes received, -1 in case of an error or timeout.
 *
 */
int NutTwiSlaveListen(NUTTWIBUS *bus, uint8_t *sla, void *rxdata, uint16_t rxsiz, uint32_t tmo)
{
    return -1;
}

/*!
 * \brief Send response to a master.
 *
 * This function must be called as soon as possible after TwSlaveListen()
 * returned successfully, even if no data needs to be returned. Not doing
 * so will completely block the bus.
 *
 * \note Slave mode is not implemented in the bitbanging driver.
 *       Thus the function always returns -1.
 *
 * \param txdata Points to the data to transmit. Ignored, if the
 *               number of bytes to transmit is zero.
 * \param txlen  Number of data bytes to transmit.
 * \param tmo    Timeout in milliseconds. To disable timeout,
 *               set this parameter to NUT_WAIT_INFINITE.
 *
 * \return The number of bytes transmitted, -1 in case of an error or timeout.
 */
extern int NutTwiSlaveRespond(NUTTWIBUS *bus, void *txdata, uint16_t txlen, uint32_t tmo)
{
    return -1;
}

/*!
 * \brief Get last slave mode error.
 *
 * You may call this function to determine the specific cause
 * of an error after TwSlaveListen() or TwSlaveRespond() failed.
 *
 * \return Error code or 0 if no error occurred.
 *
 * \note Slave mode is not implemented in the bitbanging driver.
 *       Thus the function always returns TWERR_BUS.
 */
extern int NutTwiSlaveError(NUTTWIBUS *bus)
{
    return TWERR_BUS;
}


/*!
 * \brief Set Speed of I2C Interface.
 *
 * Setup Interface Speed
 */
int NutTwiSetSpeed( NUTTWIBUS *bus, uint32_t speed)
{
    int rc = -1;

    if (speed > 400000) {
        /* Speed out of range */
        return rc;
    }

    if (bus==NULL) {
        /* No bus selected */
        return rc;
    }

    return 0;
}

/*!
 * \brief Request Current Speed of I2C Interface.
 *
 * \return always 10000 which is just a dummy value
 */
uint32_t NutTwiGetSpeed( NUTTWIBUS *bus)
{
    return 10000;
}

/*!
 * \brief Perform TWI control functions.
 *
 * Not implemented in the bit banging version.
 *
 * \param req  Requested control function.
 * \param conf Points to a buffer that contains any data required for
 *         the given control function or receives data from that
 *         function.
 *
 * \return Always 0.
 */
int NutTwiIOCtl( NUTTWIBUS *bus, int req, void *conf )
{
    int rc = 0;

    switch (req) {

    case TWI_SETSPEED:
        rc = NutTwiSetSpeed(bus, *((uint32_t *)conf));
        break;

    case TWI_GETSPEED:
        *((uint32_t *)conf) = NutTwiGetSpeed(bus);
        break;

    case TWI_GETSTATUS:
        rc = 0;
        break;

    case TWI_SETSTATUS:
        rc = 0;
        break;

    default:
        rc = -1;
        break;
    }
    return rc;
}

/*!
 * \brief Initialize TWI interface.
 *
 * The specified slave address is used only, if the local system
 * is running as a slave. Anyway, care must be taken that it doesn't
 * conflict with another connected device.
 *
 * \param sla Slave address, must be specified as a 7-bit address,
 *            always lower than 128.
 *
 * \return Always 0.
 *
 */
int TwBbifInit(void)
{
    SDA_HIGH();
    SCL_HIGH();
    TWI_ENABLE();
    twibb_initialized = 1;

    return 0;
}

/*!
 * \brief Initialize TWI interface bus.
 *
 * The specified slave address is not used here as we don't support twi-slave
 * on the bitbanging interface
 *
 * \param sla Slave address, must be specified as a 7-bit address,
 *            always lower than 128.
 */
int NutRegisterTwiBus( NUTTWIBUS *bus, uint8_t sla )
{
    int rc = 0;
    NUTTWIICB *icb = NULL;

    /* Check if bus was already registered */
    if( bus->bus_icb) {
        return 0;
    }

    /* Allocate ICB for this bus */
    icb = NutHeapAlloc(sizeof(NUTTWIICB));
    if( icb == NULL) {
        return rc;
    }
    memset( icb, 0, sizeof(NUTTWIICB));

    /* Link bus and ICB */
    bus->bus_icb = icb;

    /* Initialize interface hardware */
    if (bus->bus_initbus) {
        rc = bus->bus_initbus();
    }

    /* Initialize mutex semaphores. */
    NutEventPost(&bus->bus_mutex);

    return rc;
}

int NutDestroyTwiBus( NUTTWIBUS *bus)
{
    if (bus->bus_icb) {
        NutHeapFree( bus->bus_icb);
    }

    return 0;
}


/*!
 * \brief TWI/I2C bus structure.
 */
NUTTWIBUS TwBbifBus = {
  /*.bus_base =   */  0,           /* Bus base address. */
  /*.bus_sig_ev = */  NULL,        /* Bus data and event interrupt handler. */
  /*.bus_sig_er = */  NULL,        /* Bus error interrupt handler. */
  /*.bus_mutex =  */  NULL,        /* Bus lock queue. */
  /*.bus_icb   =  */  NULL,        /* Bus Runtime Data Pointer */
  /*.bus_dma_tx = */  0,           /* DMA channel for TX direction. */
  /*.bus_dma_rx = */  0,           /* DMA channel for RX direction. */
  /*.bus_initbus =*/  TwBbifInit,  /* Initialize bus controller. */
  /*.bus_recover =*/  NULL,        /* Recover bus controller */
};
