/*
 * Copyright (C) 2003-2004 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 arch/avr/dev/ahdlcavr.c
 * \brief AVR AHDLC driver.
 *
 * \verbatim
 * $Id: ahdlcavr.c 5472 2013-12-06 00:16:28Z olereinhardt $
 * \endverbatim
 */

#include <cfg/ahdlc.h>
#include <cfg/arch/avr.h>
#include <toolchain.h>

#include <sys/atom.h>
#include <sys/heap.h>
#include <sys/event.h>
#include <sys/timer.h>
#include <sys/thread.h>

#include <dev/irqreg.h>
#include <dev/uartavr.h>
#include <dev/ppp.h>
#include <dev/ahdlcavr.h>

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>

/*!
 * \addtogroup xgAhdlcAvr
 */
/*@{*/

/*!
* \name UART0 RTS Handshake Control
*
* UART0_RTS_BIT must be defined in arch/avr.h
*/
#ifdef UART0_RTS_BIT

#if (UART0_RTS_AVRPORT == AVRPORTB)
#define UART0_RTS_PORT  PORTB
#define UART0_RTS_DDR   DDRB

#elif (UART0_RTS_AVRPORT == AVRPORTD)
#define UART0_RTS_PORT  PORTD
#define UART0_RTS_DDR   DDRD

#elif (UART0_RTS_AVRPORT == AVRPORTE)
#define UART0_RTS_PORT  PORTE
#define UART0_RTS_DDR   DDRE

#elif (UART0_RTS_AVRPORT == AVRPORTF)
#define UART0_RTS_PORT  PORTF
#define UART0_RTS_DDR   DDRF

#elif (UART0_RTS_AVRPORT == AVRPORTG)
#define UART0_RTS_PORT  PORTG
#define UART0_RTS_DDR   DDRG

#elif (UART0_RTS_AVRPORT == AVRPORTH)
#define UART0_RTS_PORT  PORTH
#define UART0_RTS_DDR   DDRH

#endif

#endif /* UART0_RTS_BIT */

#ifdef UART0_DTR_BIT

#if (UART0_DTR_AVRPORT == AVRPORTB)
#define UART0_DTR_PORT  PORTB
#define UART0_DTR_DDR   DDRB

#elif (UART0_DTR_AVRPORT == AVRPORTD)
#define UART0_DTR_PORT  PORTD
#define UART0_DTR_DDR   DDRD

#elif (UART0_DTR_AVRPORT == AVRPORTE)
#define UART0_DTR_PORT  PORTE
#define UART0_DTR_DDR   DDRE

#elif (UART0_DTR_AVRPORT == AVRPORTF)
#define UART0_DTR_PORT  PORTF
#define UART0_DTR_DDR   DDRF

#elif (UART0_DTR_AVRPORT == AVRPORTG)
#define UART0_DTR_PORT  PORTG
#define UART0_DTR_DDR   DDRG

#elif (UART0_DTR_AVRPORT == AVRPORTH)
#define UART0_DTR_PORT  PORTH
#define UART0_DTR_DDR   DDRH

#endif

#endif /* UART0_DTR_BIT */

/*!
* \name UART1 RTS Handshake Control
*
* UART1_RTS_BIT must be defined in arch/avr.h
*/
#ifdef UART1_RTS_BIT

#if (UART1_RTS_AVRPORT == AVRPORTB)
#define UART1_RTS_PORT   PORTB
#define UART1_RTS_DDR    DDRB

#elif (UART1_RTS_AVRPORT == AVRPORTD)
#define UART1_RTS_PORT   PORTD
#define UART1_RTS_DDR    DDRD

#elif (UART1_RTS_AVRPORT == AVRPORTE)
#define UART1_RTS_PORT   PORTE
#define UART1_RTS_DDR    DDRE

#elif (UART1_RTS_AVRPORT == AVRPORTF)
#define UART1_RTS_PORT   PORTF
#define UART1_RTS_DDR    DDRF

#elif (UART1_RTS_AVRPORT == AVRPORTG)
#define UART1_RTS_PORT   PORTG
#define UART1_RTS_DDR    DDRG

#elif (UART1_RTS_AVRPORT == AVRPORTH)
#define UART1_RTS_PORT   PORTH
#define UART1_RTS_DDR    DDRH

#endif

#endif /* UART1_RTS_BIT */

#ifdef UART1_DTR_BIT

#if (UART1_DTR_AVRPORT == AVRPORTB)
#define UART1_DTR_PORT  PORTB
#define UART1_DTR_DDR   DDRB

#elif (UART1_DTR_AVRPORT == AVRPORTD)
#define UART1_DTR_PORT  PORTD
#define UART1_DTR_DDR   DDRD

#elif (UART1_DTR_AVRPORT == AVRPORTE)
#define UART1_DTR_PORT  PORTE
#define UART1_DTR_DDR   DDRE

#elif (UART1_DTR_AVRPORT == AVRPORTF)
#define UART1_DTR_PORT  PORTF
#define UART1_DTR_DDR   DDRF

#elif (UART1_DTR_AVRPORT == AVRPORTG)
#define UART1_DTR_PORT  PORTG
#define UART1_DTR_DDR   DDRG

#elif (UART1_DTR_AVRPORT == AVRPORTH)
#define UART1_DTR_PORT  PORTH
#define UART1_DTR_DDR   DDRH

#endif

#endif /* UART1_DTR_BIT */

/*
 * FCS lookup table located in program memory space.
 */
static const char fcstab[512] PROGMEM = {
    0x00, 0x00, 0x11, 0x89, 0x23, 0x12, 0x32, 0x9b, 0x46, 0x24, 0x57, 0xad, 0x65, 0x36, 0x74, 0xbf,
    0x8c, 0x48, 0x9d, 0xc1, 0xaf, 0x5a, 0xbe, 0xd3, 0xca, 0x6c, 0xdb, 0xe5, 0xe9, 0x7e, 0xf8, 0xf7,
    0x10, 0x81, 0x01, 0x08, 0x33, 0x93, 0x22, 0x1a, 0x56, 0xa5, 0x47, 0x2c, 0x75, 0xb7, 0x64, 0x3e,
    0x9c, 0xc9, 0x8d, 0x40, 0xbf, 0xdb, 0xae, 0x52, 0xda, 0xed, 0xcb, 0x64, 0xf9, 0xff, 0xe8, 0x76,
    0x21, 0x02, 0x30, 0x8b, 0x02, 0x10, 0x13, 0x99, 0x67, 0x26, 0x76, 0xaf, 0x44, 0x34, 0x55, 0xbd,
    0xad, 0x4a, 0xbc, 0xc3, 0x8e, 0x58, 0x9f, 0xd1, 0xeb, 0x6e, 0xfa, 0xe7, 0xc8, 0x7c, 0xd9, 0xf5,
    0x31, 0x83, 0x20, 0x0a, 0x12, 0x91, 0x03, 0x18, 0x77, 0xa7, 0x66, 0x2e, 0x54, 0xb5, 0x45, 0x3c,
    0xbd, 0xcb, 0xac, 0x42, 0x9e, 0xd9, 0x8f, 0x50, 0xfb, 0xef, 0xea, 0x66, 0xd8, 0xfd, 0xc9, 0x74,

    0x42, 0x04, 0x53, 0x8d, 0x61, 0x16, 0x70, 0x9f, 0x04, 0x20, 0x15, 0xa9, 0x27, 0x32, 0x36, 0xbb,
    0xce, 0x4c, 0xdf, 0xc5, 0xed, 0x5e, 0xfc, 0xd7, 0x88, 0x68, 0x99, 0xe1, 0xab, 0x7a, 0xba, 0xf3,
    0x52, 0x85, 0x43, 0x0c, 0x71, 0x97, 0x60, 0x1e, 0x14, 0xa1, 0x05, 0x28, 0x37, 0xb3, 0x26, 0x3a,
    0xde, 0xcd, 0xcf, 0x44, 0xfd, 0xdf, 0xec, 0x56, 0x98, 0xe9, 0x89, 0x60, 0xbb, 0xfb, 0xaa, 0x72,
    0x63, 0x06, 0x72, 0x8f, 0x40, 0x14, 0x51, 0x9d, 0x25, 0x22, 0x34, 0xab, 0x06, 0x30, 0x17, 0xb9,
    0xef, 0x4e, 0xfe, 0xc7, 0xcc, 0x5c, 0xdd, 0xd5, 0xa9, 0x6a, 0xb8, 0xe3, 0x8a, 0x78, 0x9b, 0xf1,
    0x73, 0x87, 0x62, 0x0e, 0x50, 0x95, 0x41, 0x1c, 0x35, 0xa3, 0x24, 0x2a, 0x16, 0xb1, 0x07, 0x38,
    0xff, 0xcf, 0xee, 0x46, 0xdc, 0xdd, 0xcd, 0x54, 0xb9, 0xeb, 0xa8, 0x62, 0x9a, 0xf9, 0x8b, 0x70,

    0x84, 0x08, 0x95, 0x81, 0xa7, 0x1a, 0xb6, 0x93, 0xc2, 0x2c, 0xd3, 0xa5, 0xe1, 0x3e, 0xf0, 0xb7,
    0x08, 0x40, 0x19, 0xc9, 0x2b, 0x52, 0x3a, 0xdb, 0x4e, 0x64, 0x5f, 0xed, 0x6d, 0x76, 0x7c, 0xff,
    0x94, 0x89, 0x85, 0x00, 0xb7, 0x9b, 0xa6, 0x12, 0xd2, 0xad, 0xc3, 0x24, 0xf1, 0xbf, 0xe0, 0x36,
    0x18, 0xc1, 0x09, 0x48, 0x3b, 0xd3, 0x2a, 0x5a, 0x5e, 0xe5, 0x4f, 0x6c, 0x7d, 0xf7, 0x6c, 0x7e,
    0xa5, 0x0a, 0xb4, 0x83, 0x86, 0x18, 0x97, 0x91, 0xe3, 0x2e, 0xf2, 0xa7, 0xc0, 0x3c, 0xd1, 0xb5,
    0x29, 0x42, 0x38, 0xcb, 0x0a, 0x50, 0x1b, 0xd9, 0x6f, 0x66, 0x7e, 0xef, 0x4c, 0x74, 0x5d, 0xfd,
    0xb5, 0x8b, 0xa4, 0x02, 0x96, 0x99, 0x87, 0x10, 0xf3, 0xaf, 0xe2, 0x26, 0xd0, 0xbd, 0xc1, 0x34,
    0x39, 0xc3, 0x28, 0x4a, 0x1a, 0xd1, 0x0b, 0x58, 0x7f, 0xe7, 0x6e, 0x6e, 0x5c, 0xf5, 0x4d, 0x7c,

    0xc6, 0x0c, 0xd7, 0x85, 0xe5, 0x1e, 0xf4, 0x97, 0x80, 0x28, 0x91, 0xa1, 0xa3, 0x3a, 0xb2, 0xb3,
    0x4a, 0x44, 0x5b, 0xcd, 0x69, 0x56, 0x78, 0xdf, 0x0c, 0x60, 0x1d, 0xe9, 0x2f, 0x72, 0x3e, 0xfb,
    0xd6, 0x8d, 0xc7, 0x04, 0xf5, 0x9f, 0xe4, 0x16, 0x90, 0xa9, 0x81, 0x20, 0xb3, 0xbb, 0xa2, 0x32,
    0x5a, 0xc5, 0x4b, 0x4c, 0x79, 0xd7, 0x68, 0x5e, 0x1c, 0xe1, 0x0d, 0x68, 0x3f, 0xf3, 0x2e, 0x7a,
    0xe7, 0x0e, 0xf6, 0x87, 0xc4, 0x1c, 0xd5, 0x95, 0xa1, 0x2a, 0xb0, 0xa3, 0x82, 0x38, 0x93, 0xb1,
    0x6b, 0x46, 0x7a, 0xcf, 0x48, 0x54, 0x59, 0xdd, 0x2d, 0x62, 0x3c, 0xeb, 0x0e, 0x70, 0x1f, 0xf9,
    0xf7, 0x8f, 0xe6, 0x06, 0xd4, 0x9d, 0xc5, 0x14, 0xb1, 0xab, 0xa0, 0x22, 0x92, 0xb9, 0x83, 0x30,
    0x7b, 0xc7, 0x6a, 0x4e, 0x58, 0xd5, 0x49, 0x5c, 0x3d, 0xe3, 0x2c, 0x6a, 0x1e, 0xf1, 0x0f, 0x78
};

/*!
 * Checks the 32-bit ACCM to see if the byte needs un-escaping
 */
#define IN_ACC_MAP(c, m) in_acc_map(c, &(m))
/* Trust me, this is a whole lot smaller when compiled than it looks in C.
 * It is written explicitly so that gcc can make good AVR asm out of it. */
static NUT_INLINE_FUNC uint8_t in_acc_map(u_char ch, void * esc_mask) NUT_FORCE_INLINE;  /* gcc specific attribute */
static NUT_INLINE_FUNC uint8_t in_acc_map(u_char ch, void * esc_mask)
{
    const uint8_t shift_mask = 0x07;
    const uint8_t index_mask = 0x18;
    const uint8_t over_mask = ~(shift_mask|index_mask);

    uint8_t shift, index, emask;
    uint8_t * emskp = esc_mask;

    if (over_mask & ch) {
        return 0;
    }

    shift = shift_mask & ch;

    index = ch >> (uint8_t)3;

    /* NOTE:  This assumes that the esc bit field is little endian,
     * which it should have been switched to before being set. */

    emask = emskp[index];
    return emask & ( ((uint8_t)1) << shift );
}

#ifndef NUT_THREAD_AHDLCRXSTACK
#define NUT_THREAD_AHDLCRXSTACK     512
#endif

#if !defined(MCU_AT90USB1287)
/*
 * Handle AVR UART0 transmit complete interrupts.
 */
static void Tx0Complete(void *arg)
{
    AHDLCDCB *dcb = arg;

    if (dcb->dcb_tx_idx != dcb->dcb_wr_idx) {
#ifdef UART0_CTS_BIT
        if (bit_is_set(UART0_CTS_PIN, UART0_CTS_BIT)) {
            cbi(UCR, UDRIE);
            return;
        }
#endif
        outb(UDR, dcb->dcb_tx_buf[dcb->dcb_tx_idx]);
        dcb->dcb_tx_idx++;
    } else {
        cbi(UCR, UDRIE);
        NutEventPostFromIrq(&dcb->dcb_tx_rdy);
    }
}

#ifdef UART0_CTS_BIT
/*
 * Handle AVR UART0 transmit handshake interrupts.
 */
static void Cts0Interrupt(void *arg)
{
    sbi(UCR, UDRIE);
}
#endif
#endif /*!USB1287*/

#ifdef __AVR_ENHANCED__
/*
 * Handle AVR UART1 transmit complete interrupts.
 */
static void Tx1Complete(void *arg)
{
    register AHDLCDCB *dcb = arg;

    if (dcb->dcb_tx_idx != dcb->dcb_wr_idx) {
#ifdef UART1_CTS_BIT
        if (bit_is_set(UART1_CTS_PIN, UART1_CTS_BIT)) {
            cbi(UCSR1B, UDRIE);
            return;
        }
#endif
        outb(UDR1, dcb->dcb_tx_buf[dcb->dcb_tx_idx]);
        dcb->dcb_tx_idx++;
    } else {
        cbi(UCSR1B, UDRIE);
        NutEventPostFromIrq(&dcb->dcb_tx_rdy);
    }
}

#ifdef UART1_CTS_BIT
/*
 * Handle AVR UART1 transmit handshake interrupts.
 */
static void Cts1Interrupt(void *arg)
{
    sbi(UCSR1B, UDRIE);
}
#endif

#endif                          /* __AVR_ENHANCED__ */

#if !defined(MCU_AT90USB1287)
/*
 * Handle AVR UART0 receive complete interrupts.
 */
static void Rx0Complete(void *arg)
{
    AHDLCDCB *dcb = arg;

    dcb->dcb_rx_buf[dcb->dcb_rx_idx] = inb(UDR);
    if (dcb->dcb_rd_idx == dcb->dcb_rx_idx)
        NutEventPostFromIrq(&dcb->dcb_rx_rdy);
    /* Late increment fixes ICCAVR bug on volatile variables. */
    dcb->dcb_rx_idx++;
}
#endif /*!USB1287*/

#ifdef __AVR_ENHANCED__
/*
 * Handle AVR UART1 receive complete interrupts.
 */
static void Rx1Complete(void *arg)
{
    AHDLCDCB *dcb = arg;

    dcb->dcb_rx_buf[dcb->dcb_rx_idx] = inb(UDR1);
    if (dcb->dcb_rd_idx == dcb->dcb_rx_idx)
        NutEventPostFromIrq(&dcb->dcb_rx_rdy);
    /* Late increment fixes ICCAVR bug on volatile variables. */
    dcb->dcb_rx_idx++;
}
#endif                          /* __AVR_ENHANCED__ */

/*
 * \return 0 on success, -1 in case of any errors.
 */
static int SendRawByte(AHDLCDCB * dcb, uint8_t ch, uint8_t flush)
{
    /*
     * If transmit buffer is full, wait until interrupt routine
     * signals an empty buffer or until a timeout occurs.
     */
    while ((uint8_t) (dcb->dcb_wr_idx + 1) == dcb->dcb_tx_idx) {
        if (NutEventWait(&dcb->dcb_tx_rdy, dcb->dcb_wtimeout))
            break;
    }

    /*
     * If transmit buffer is still full, we have a write timeout.
     */
    if ((uint8_t) (dcb->dcb_wr_idx + 1) == dcb->dcb_tx_idx) {
        return -1;
    }

    /*
     * Buffer has room for more data. Put the byte in the buffer
     * and increment the write index.
     */
    dcb->dcb_tx_buf[dcb->dcb_wr_idx] = ch;
    dcb->dcb_wr_idx++;

    /*
     * If transmit buffer has become full and the transmitter
     * is not active, then activate it.
     */
    if (flush || (uint8_t) (dcb->dcb_wr_idx + 1) == dcb->dcb_tx_idx) {

        /*
         * TODO: Check handshake.
         */
        NutEnterCritical();
#ifdef __AVR_ENHANCED__
        if (dcb->dcb_base)
            sbi(UCSR1B, UDRIE);
        else
#endif
            sbi(UCR, UDRIE);
        NutExitCritical();
    }
    return 0;
}

/*
 * Characters are properly escaped and checksum is updated.
 *
 * \return 0 on success, -1 in case of any errors.
 */
static int SendHdlcData(AHDLCDCB * dcb, const uint8_t * data, uint16_t len, uint16_t * txfcs)
{
    uint16_t tbx;
    register uint16_t fcs;

    if (txfcs)
        fcs = *txfcs;
    else
        fcs = 0;
    while (len) {
        tbx = (uint16_t) ((uint8_t) fcs ^ *data) << 1;
        fcs >>= 8;
        fcs ^= ((uint16_t) PRG_RDB(fcstab + tbx) << 8) | PRG_RDB(fcstab + tbx + 1);
        if (IN_ACC_MAP(*data, dcb->dcb_tx_accm) || *data == AHDLC_FLAG || *data == AHDLC_ESCAPE) {
            if (SendRawByte(dcb, AHDLC_ESCAPE, 0))
                return -1;
            if (SendRawByte(dcb, *data ^ AHDLC_TRANS, 0))
                return -1;
        } else if (SendRawByte(dcb, *data, 0))
            return -1;
        data++;
        len--;
    }
    if (txfcs)
        *txfcs = fcs;

    return 0;
}

/*!
 * \brief Send HDLC frame.
 *
 * \param dev   Identifies the device to use.
 * \param nb    Network buffer structure containing the packet to be sent.
 *              The structure must have been allocated by a previous
 *              call NutNetBufAlloc().
 *
 * \return 0 on success, -1 in case of any errors.
 */
int AhdlcOutput(NUTDEVICE * dev, NETBUF * nb)
{
    uint16_t txfcs;
    AHDLCDCB *dcb = dev->dev_dcb;
    uint16_t sz;

    /*
     * If we are in RAW mode we are not allowed to send AHDLC output.
     * We just emulate packet loss behaviour in here.
     */
    if (dcb->dcb_modeflags & UART_MF_RAWMODE) {
        return 0;
    }

    /*
     * Calculate the number of bytes to be send. Do not
     * send packets larger than transmit mru.
     */
    sz = nb->nb_dl.sz + nb->nb_nw.sz + nb->nb_tp.sz + nb->nb_ap.sz;

    if (sz > dcb->dcb_tx_mru) {
        return -1;
    }

    /*
     * TODO: If transmitter is running, we may omit the flag.
     */
    SendRawByte(dcb, AHDLC_FLAG, 0);

    /* Initialize the checksum and send the NETBUF. */
    txfcs = AHDLC_INITFCS;
    if (SendHdlcData(dcb, nb->nb_dl.vp, nb->nb_dl.sz, &txfcs))
        return -1;
    if (SendHdlcData(dcb, nb->nb_nw.vp, nb->nb_nw.sz, &txfcs))
        return -1;
    if (SendHdlcData(dcb, nb->nb_tp.vp, nb->nb_tp.sz, &txfcs))
        return -1;
    if (SendHdlcData(dcb, nb->nb_ap.vp, nb->nb_ap.sz, &txfcs))
        return -1;

    /* Send the checksum and the final flag. */
    txfcs ^= 0xffff;
    if (SendHdlcData(dcb, (uint8_t *) & txfcs, 2, 0))
        return -1;
    SendRawByte(dcb, AHDLC_FLAG, 1);

    return 0;
}

/*! \fn AhdlcRx(void *arg)
 * \brief Asynchronous HDLC receiver thread.
 *
 *
 * Running at high priority.
 */
THREAD(AhdlcRx, arg)
{
    NUTDEVICE *dev = arg;
    NUTDEVICE *netdev;
    AHDLCDCB *dcb = dev->dev_dcb;
    IFNET *ifn;
    NETBUF *nb;
    uint8_t *rxbuf;
    uint8_t *rxptr;
    uint16_t rxcnt;
    uint8_t ch;
    uint16_t tbx;
    uint8_t inframe;
    uint8_t escaped;
    uint16_t rxfcs;

    NutThreadSetPriority(9);
    for (;;) {
        /*
         * Reset variables to their initial state
         */
        rxptr = 0;
        rxcnt = 0;
        escaped = 0;
        rxfcs = AHDLC_INITFCS;
        inframe = 0;

        for (;;) {
            /*
             * Wait until the network interface has been attached.
             * This will be initiated by the application calling
             * NutNetIfConfig(), which in turn calls a HDLC_SETIFNET
             * ioctl() to store the NUTDEVICE pointer of the network
             * device in dev_icb and trigger an event on dcb_mf_evt.
             */
            while ((netdev = dev->dev_icb) == 0) {
                NutEventWait(&dcb->dcb_mf_evt, 1000);
            }
            ifn = netdev->dev_icb;
            dcb->dcb_rtimeout = 1000;
            inframe = 0;

            /*
             * Allocate the receive buffer, if this fails, we are in a
             * low memory situation. Take a nap and see, if the
             * situation improved.
             * Note that we also put the FCS into the receive buffer.
             */
            if ((rxbuf = NutHeapAlloc(dcb->dcb_rx_mru + 2)) != 0) {
                break;
            }
            NutSleep(1000);
        }

        /*
         * Signal the link driver that we are up.
         */
        ifn->if_send = AhdlcOutput;
        netdev->dev_ioctl(netdev, LCP_LOWERUP, 0);

        for (;;) {
            /*
             * If we are still connected to a network, fetch the next
             * character from the buffer.
             */
            while (dcb->dcb_rd_idx == dcb->dcb_rx_idx) {
                if (dev->dev_icb == 0)
                    break;
                /* TODO: Check for idle timeout. */
                if (NutEventWait(&dcb->dcb_rx_rdy, dcb->dcb_rtimeout)) {
                    continue;
                }
            }

            /*
             * Leave loop if network interface is detached
             */
            if (dev->dev_icb == 0)
                break;

            /*
             * If RAW mode is active, we are not allowing any data encapsulation
             * processing. So we just sleep for a while.
             */
            if (dcb->dcb_modeflags & UART_MF_RAWMODE) {
                /*
                 * It is a must to sleep here, because if we just yield it could create
                 * too much processing in here and stall processing elsewhere. This gives
                 * opportunity to other threads to process incoming data from USART.
                 */
                NutSleep(100);
                continue;
            }

            /*
             * Read next character from input buffer
             */
            ch = dcb->dcb_rx_buf[dcb->dcb_rd_idx++];

            if (inframe) {
                if (ch != AHDLC_FLAG) {
                    if (ch == AHDLC_ESCAPE) {
                        escaped = 1;
                        continue;
                    }
                    if (escaped) {
                        ch ^= AHDLC_TRANS;
                        escaped = 0;
                    }

                    /*
                     * Unless the peer lied to us about the negotiated MRU,
                     * we should never get a frame which is too long. If it
                     * happens, toss it away and grab the next incoming one.
                     */
                    if (rxcnt++ < dcb->dcb_rx_mru + 2) {
                        /* Update calculated checksum and store character in buffer. */
                        tbx = (uint16_t) ((uint8_t) rxfcs ^ ch) << 1;
                        rxfcs >>= 8;
                        rxfcs ^= ((uint16_t) PRG_RDB(fcstab + tbx) << 8) | PRG_RDB(fcstab + tbx + 1);
                        *rxptr++ = ch;
                    } else
                        inframe = 0;
                    continue;
                }

                if (rxcnt >= 2 && rxfcs == AHDLC_GOODFCS) {
                    /*
                     * If the frame checksum is valid, create a NETBUF
                     * and pass it to the network specific receive handler.
                     */
                    rxcnt -= 2;
                    if ((nb = NutNetBufAlloc(0, NBAF_DATALINK, rxcnt)) != 0) {
                        memcpy(nb->nb_dl.vp, rxbuf, rxcnt);
                        (*ifn->if_recv) (netdev, nb);
                    }
                }
            }

            /*
             * If frame flag is received, resync frame processing
             */
            if (ch == AHDLC_FLAG) {
                inframe = 1;
                escaped = 0;
                rxptr = rxbuf;
                rxcnt = 0;
                rxfcs = AHDLC_INITFCS;
            }
        }

        /* Signal the link driver that we are down. */
        netdev->dev_ioctl(netdev, LCP_LOWERDOWN, 0);

        /* Disconnected, clean up. */
        if (rxbuf) {
            NutHeapFree(rxbuf);
            rxbuf = 0;
        }
    }
}

/*
 * \param dev Indicates the UART device.
 *
 * \return 0 on success, -1 otherwise.
 */
static int AhdlcAvrGetStatus(NUTDEVICE * dev, uint32_t * status)
{
    AHDLCDCB *dcb = dev->dev_dcb;
    uint8_t us;

    *status = 0;

#ifdef __AVR_ENHANCED__
    if (dev->dev_base) {
#ifdef UART1_CTS_BIT
        if (bit_is_set(UART1_CTS_PIN, UART1_CTS_BIT))
            *status |= UART_CTSDISABLED;
        else
            *status |= UART_CTSENABLED;
#endif
#ifdef UART1_RTS_BIT
        if (bit_is_set(UART1_RTS_PORT, UART1_RTS_BIT))
            *status |= UART_RTSDISABLED;
        else
            *status |= UART_RTSENABLED;
#endif
#ifdef UART1_DTR_BIT
        if (bit_is_set(UART1_DTR_PORT, UART1_DTR_BIT))
            *status |= UART_DTRDISABLED;
        else
            *status |= UART_DTRENABLED;
#endif
        us = inb(UCSR1A);
    } else
#endif                          /* __AVR_ENHANCED__ */
    {
#ifdef UART0_CTS_BIT
        if (bit_is_set(UART0_CTS_PIN, UART0_CTS_BIT))
            *status |= UART_CTSDISABLED;
        else
            *status |= UART_CTSENABLED;
#endif
#ifdef UART0_RTS_BIT
        if (bit_is_set(UART0_RTS_PORT, UART0_RTS_BIT))
            *status |= UART_RTSDISABLED;
        else
            *status |= UART_RTSENABLED;
#endif
#ifdef UART0_DTR_BIT
        if (bit_is_set(UART0_DTR_PORT, UART0_DTR_BIT))
            *status |= UART_DTRDISABLED;
        else
            *status |= UART_DTRENABLED;
#endif
        us = inb(USR);
    }
    if (us & FE)
        *status |= UART_FRAMINGERROR;
    if (us & DOR)
        *status |= UART_OVERRUNERROR;
    if (dcb->dcb_tx_idx == dcb->dcb_wr_idx)
        *status |= UART_TXBUFFEREMPTY;
    if (dcb->dcb_rd_idx == dcb->dcb_rx_idx)
        *status |= UART_RXBUFFEREMPTY;

    return 0;
}

/*
 * \param dev Indicates the UART device.
 *
 * \return 0 on success, -1 otherwise.
 */
static int AhdlcAvrSetStatus(NUTDEVICE * dev, uint32_t status)
{
#ifdef __AVR_ENHANCED__
    if (dev->dev_base) {
#ifdef UART1_RTS_BIT
        if (status & UART_RTSDISABLED)
            sbi(UART1_RTS_PORT, UART1_RTS_BIT);
        else if (status & UART_RTSENABLED)
            cbi(UART1_RTS_PORT, UART1_RTS_BIT);
#endif
#ifdef UART1_DTR_BIT
        if (status & UART_DTRDISABLED)
            sbi(UART1_DTR_PORT, UART1_DTR_BIT);
        else if (status & UART_DTRENABLED)
            cbi(UART1_DTR_PORT, UART1_DTR_BIT);
#endif
    } else
#endif                          /* __AVR_ENHANCED__ */
    {
#ifdef UART0_RTS_BIT
        if (status & UART_RTSDISABLED)
            sbi(UART0_RTS_PORT, UART0_RTS_BIT);
        else if (status & UART_RTSENABLED)
            cbi(UART0_RTS_PORT, UART0_RTS_BIT);
#endif
#ifdef UART0_DTR_BIT
        if (status & UART_DTRDISABLED)
            sbi(UART0_DTR_PORT, UART0_DTR_BIT);
        else if (status & UART_DTRENABLED)
            cbi(UART0_DTR_PORT, UART0_DTR_BIT);
#endif
    }
    return 0;
}

/*
 * Carefully enable UART functions.
 */
static void AhdlcAvrEnable(uint16_t base)
{
    NutEnterCritical();

#ifdef __AVR_ENHANCED__
    if (base) {
#ifdef UART1_CTS_BIT
        sbi(EIMSK, UART1_CTS_BIT);
#endif
        outb(UCSR1B, BV(RXCIE) | BV(RXEN) | BV(TXEN));
    } else
#endif                          /* __AVR_ENHANCED__ */
    {
#ifdef UART0_CTS_BIT
        sbi(EIMSK, UART0_CTS_BIT);
#endif
        outb(UCR, BV(RXCIE) | BV(RXEN) | BV(TXEN));
    }
    NutExitCritical();
}

/*
 * Carefully disable UART functions.
 */
static void AhdlcAvrDisable(uint16_t base)
{
    /*
     * Disable UART interrupts.
     */
    NutEnterCritical();
#ifdef __AVR_ENHANCED__
    if (base) {
#ifdef UART1_CTS_BIT
        cbi(EIMSK, UART1_CTS_BIT);
#endif
        outb(UCSR1B, inb(UCSR1B) & ~(BV(RXCIE) | BV(UDRIE)));
    } else
#endif                          /* __AVR_ENHANCED__ */
    {
#ifdef UART0_CTS_BIT
        cbi(EIMSK, UART0_CTS_BIT);
#endif
        outb(UCR, inb(UCR) & ~(BV(RXCIE) | BV(UDRIE)));
    }
    NutExitCritical();

    /*
     * Allow incoming or outgoing character to finish.
     */
    NutDelay(10);

    /*
     * Now disable UART functions.
     */
#ifdef __AVR_ENHANCED__
    if (base)
        outb(UCSR1B, inb(UCSR1B) & ~(BV(RXEN) | BV(TXEN)));
    else
#endif                          /* __AVR_ENHANCED__ */
        outb(UCR, inb(UCR) & ~(BV(RXEN) | BV(TXEN)));
}

/*!
 * \brief Perform on-chip UART control functions.
 *
 * \param dev  Identifies the device that receives the device-control
 *             function.
 * \param req  Requested control function. May be set to one of the
 *             following constants:
 *             - UART_SETSPEED, conf points to an uint32_t value containing the baudrate.
 *             - UART_GETSPEED, conf points to an uint32_t value receiving the current baudrate.
 *             - UART_SETDATABITS, conf points to an uint32_t value containing the number of data bits, 5, 6, 7 or 8.
 *             - UART_GETDATABITS, conf points to an uint32_t value receiving the number of data bits, 5, 6, 7 or 8.
 *             - UART_SETPARITY, conf points to an uint32_t value containing the parity, 0 (no), 1 (odd) or 2 (even).
 *             - UART_GETPARITY, conf points to an uint32_t value receiving the parity, 0 (no), 1 (odd) or 2 (even).
 *             - UART_SETSTOPBITS, conf points to an uint32_t value containing the number of stop bits 1 or 2.
 *             - UART_GETSTOPBITS, conf points to an uint32_t value receiving the number of stop bits 1 or 2.
 *             - UART_SETSTATUS, conf points to an uint32_t value containing the changes for the control lines.
 *             - UART_GETSTATUS, conf points to an uint32_t value receiving the state of the control lines and the device.
 *             - UART_SETREADTIMEOUT, conf points to an uint32_t value containing the read timeout.
 *             - UART_GETREADTIMEOUT, conf points to an uint32_t value receiving the read timeout.
 *             - UART_SETWRITETIMEOUT, conf points to an uint32_t value containing the write timeout.
 *             - UART_GETWRITETIMEOUT, conf points to an uint32_t value receiving the write timeout.
 *             - UART_SETLOCALECHO, conf points to an uint32_t value containing 0 (off) or 1 (on).
 *             - UART_GETLOCALECHO, conf points to an uint32_t value receiving 0 (off) or 1 (on).
 *             - UART_SETFLOWCONTROL, conf points to an uint32_t value containing combined UART_FCTL_ values.
 *             - UART_GETFLOWCONTROL, conf points to an uint32_t value containing receiving UART_FCTL_ values.
 *             - UART_SETCOOKEDMODE, conf points to an uint32_t value containing 0 (off) or 1 (on).
 *             - UART_GETCOOKEDMODE, conf points to an uint32_t value receiving 0 (off) or 1 (on).
 *             - UART_SETRAWMODE, conf points to an uint32_t value containing 0 (off) or 1 (on).
 *             - UART_GETRAWMODE, conf points to an uint32_t value receiving 0 (off) or 1 (on).
 *             - HDLC_SETIFNET, conf points to a pointer containing the address of the network device's NUTDEVICE structure.
 *             - HDLC_GETIFNET, conf points to a pointer receiving the address of the network device's NUTDEVICE structure.
 *             - HDLC_SETTXACCM, conf points to an uint32_t value containing the ACC Map in host endian.
 *             - HDLC_GETTXACCM, conf points to an uint32_t value receiving the ACC Map in host endian.
 *
 * \param conf Points to a buffer that contains any data required for
 *             the given control function or receives data from that
 *             function.
 * \return 0 on success, -1 otherwise.
 *
 * \warning Timeout values are given in milliseconds and are limited to
 *          the granularity of the system timer.
 *
 * \note For ATmega103, only 8 data bits, 1 stop bit and no parity are allowed.
 *
 */
int AhdlcAvrIOCtl(NUTDEVICE * dev, int req, void *conf)
{
    int rc = 0;
    AHDLCDCB *dcb;
    void **ppv = (void **) conf;
    uint32_t *lvp = (uint32_t *) conf;
    uint8_t bv = 0;
    uint16_t sv = 0;
    uint8_t devnum;

#if !defined(MCU_AT90USB1287)
    if (dev == 0)
        dev = &devUart0;
#else
    if (dev == 0)
        return -1;
#endif

    devnum = dev->dev_base;
    dcb = dev->dev_dcb;

    switch (req) {
    case UART_SETSPEED:
        AhdlcAvrDisable(devnum);
        sv = (uint16_t) ((((2UL * NutGetCpuClock()) / (*lvp * 16UL)) + 1UL) / 2UL) - 1;
#ifdef __AVR_ENHANCED__
        if (devnum) {
            outb(UBRR1L, (uint8_t) sv);
            outb(UBRR1H, (uint8_t) (sv >> 8));
#if !defined(MCU_AT90USB1287)
        } else {
            outb(UBRR0L, (uint8_t) sv);
            outb(UBRR0H, (uint8_t) (sv >> 8));
#endif
        }
#else
        outb(UBRR, (uint8_t) sv);
#endif
        AhdlcAvrEnable(devnum);
        break;

    case UART_GETSPEED:
#ifdef __AVR_ENHANCED__
        if (devnum)
            sv = (uint16_t) inb(UBRR1H) << 8 | inb(UBRR1L);
#if !defined(MCU_AT90USB1287)
        else
            sv = (uint16_t) inb(UBRR0H) << 8 | inb(UBRR0L);
#endif
#else
        sv = inb(UBRR);
#endif
        *lvp = NutGetCpuClock() / (16UL * (uint32_t) (sv + 1));
        break;

    case UART_SETDATABITS:
        AhdlcAvrDisable(devnum);
        bv = (uint8_t)(*lvp);
#ifdef __AVR_ENHANCED__
        if (bv >= 5 && bv <= 8) {
            bv = (bv - 5) << 1;
            if (devnum) {
                outb(UCSR1C, (inb(UCSR1C) & 0xF9) | bv);
                outb(UCSR1B, inb(UCSR1B) & 0xFB);
#if !defined(MCU_AT90USB1287)
            } else {
                outb(UCSR0C, (inb(UCSR0C) & 0xF9) | bv);
                outb(UCSR0B, inb(UCSR0B) & 0xFB);
#endif
            }
        } else
            rc = -1;
#else
        if (bv != 8)
            rc = -1;
#endif
        AhdlcAvrEnable(devnum);
        break;

    case UART_GETDATABITS:
#ifdef __AVR_ENHANCED__
        if (devnum)
            *lvp = ((inb(UCSR1C) & 0x06) >> 1) + 5;
#if !defined(MCU_AT90USB1287)
        else
            *lvp = ((inb(UCSR0C) & 0x06) >> 1) + 5;
#endif
#else
        *lvp = 8;
#endif
        break;

    case UART_SETPARITY:
        AhdlcAvrDisable(devnum);
        bv = (uint8_t)(*lvp);
#ifdef __AVR_ENHANCED__
        if (bv <= 2) {
            if (bv == 1)
                bv = 3;
            bv <<= 4;
            if (devnum)
                outb(UCSR1C, (inb(UCSR1C) & 0xCF) | bv);
#if !defined(MCU_AT90USB1287)
            else
                outb(UCSR0C, (inb(UCSR0C) & 0xCF) | bv);
#endif
        } else
            rc = -1;
#endif
        if (bv)
            rc = -1;
        AhdlcAvrEnable(devnum);
        break;

    case UART_GETPARITY:
#ifdef __AVR_ENHANCED__
        if (devnum)
            bv = (inb(UCSR1C) & 0x30) >> 4;
#if !defined(MCU_AT90USB1287)
        else
            bv = (inb(UCSR0C) & 0x30) >> 4;
#endif
        if (bv == 3)
            bv = 1;
#else
        bv = 0;
#endif
        *lvp = bv;
        break;

    case UART_SETSTOPBITS:
        AhdlcAvrDisable(devnum);
        bv = (uint8_t)(*lvp);
#ifdef __AVR_ENHANCED__
        if (bv == 1 || bv == 2) {
            bv = (bv - 1) << 3;
            if (devnum)
                outb(UCSR1C, (inb(UCSR1C) & 0xF7) | bv);
#if !defined(MCU_AT90USB1287)
            else
                outb(UCSR0C, (inb(UCSR0C) & 0xF7) | bv);
#endif
        } else
            rc = -1;
#else
        if (bv != 1)
            rc = -1;
#endif
        AhdlcAvrEnable(devnum);
        break;

    case UART_GETSTOPBITS:
#ifdef __AVR_ENHANCED__
        if (devnum)
            *lvp = ((inb(UCSR1C) & 0x08) >> 3) + 1;
#if !defined(MCU_AT90USB1287)
        else
            *lvp = ((inb(UCSR0C) & 0x08) >> 3) + 1;
#endif
#else
        *lvp = 1;
#endif
        break;

    case UART_GETSTATUS:
        AhdlcAvrGetStatus(dev, lvp);
        break;
    case UART_SETSTATUS:
        AhdlcAvrSetStatus(dev, *lvp);
        break;

    case UART_SETREADTIMEOUT:
        dcb->dcb_rtimeout = *lvp;
        break;
    case UART_GETREADTIMEOUT:
        *lvp = dcb->dcb_rtimeout;
        break;

    case UART_SETWRITETIMEOUT:
        dcb->dcb_wtimeout = *lvp;
        break;
    case UART_GETWRITETIMEOUT:
        *lvp = dcb->dcb_wtimeout;
        break;

    case UART_SETLOCALECHO:
        bv = (uint8_t)(*lvp);
        if (bv)
            dcb->dcb_modeflags |= UART_MF_LOCALECHO;
        else
            dcb->dcb_modeflags &= ~UART_MF_LOCALECHO;
        break;
    case UART_GETLOCALECHO:
        if (dcb->dcb_modeflags & UART_MF_LOCALECHO)
            *lvp = 1;
        else
            *lvp = 0;
        break;

    case UART_SETFLOWCONTROL:
        bv = (uint8_t)(*lvp);
        if (bv)
            dcb->dcb_modeflags |= UART_MF_LOCALECHO;
        else
            dcb->dcb_modeflags &= ~UART_MF_LOCALECHO;
        break;
    case UART_GETFLOWCONTROL:
        break;

    case UART_SETRAWMODE:
        bv = (uint8_t)(*lvp);
        if (bv)
            dcb->dcb_modeflags |= UART_MF_RAWMODE;
        else
            dcb->dcb_modeflags &= ~UART_MF_RAWMODE;
        break;

    case UART_GETRAWMODE:
        if (dcb->dcb_modeflags & UART_MF_RAWMODE)
            *lvp = 1;
        else
            *lvp = 0;
        break;

    case HDLC_SETIFNET:
        if (ppv && (*ppv != 0)) {
            dev->dev_icb = *ppv;
            dev->dev_type = IFTYP_NET;
            dcb->dcb_rx_mru = dcb->dcb_tx_mru = ((IFNET *)(((NUTDEVICE *)ppv)->dev_icb))->if_mtu;
            NutEventPost(&dcb->dcb_mf_evt);
        } else {
            dev->dev_type = IFTYP_CHAR;

            if (dev->dev_icb != 0)
            {
                dev->dev_icb = 0;

                /*
                 * Signal AHDLC Thread, so it can change it's state instantly
                 */
                NutEventPost(&dcb->dcb_rx_rdy);
            }
        }
        break;
    case HDLC_GETIFNET:
        *ppv = dev->dev_icb;
        break;

    /* AVR host endian is little endian, and the ACCM should have been switched
     * to host endian when the value was read in. */
    case HDLC_SETTXACCM:
        dcb->dcb_tx_accm = (*lvp);
        break;

    case HDLC_GETTXACCM:
        (*lvp) = dcb->dcb_tx_accm;
        break;

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

/*!
 * \brief Initialize asynchronous HDLC device.
 *
 * This function will be called during device registration. It
 * initializes the hardware, registers all required interrupt
 * handlers and initializes all internal data structures used by
 * this driver.
 *
 * \param dev  Identifies the device to initialize.
 *
 * \return 0 on success, -1 otherwise.
 */
int AhdlcAvrInit(NUTDEVICE * dev)
{
    int rc = 0;
    AHDLCDCB *dcb;
    uint32_t baudrate = 9600;

    /* Disable UART. */
    AhdlcAvrDisable(dev->dev_base);

    /* Initialize driver control block. */
    dcb = dev->dev_dcb;
    memset(dcb, 0, sizeof(AHDLCDCB));
    dcb->dcb_base = dev->dev_base;
    dcb->dcb_rx_buf = NutHeapAlloc(256);
    dcb->dcb_tx_buf = NutHeapAlloc(256);
    dcb->dcb_rx_mru = dcb->dcb_tx_mru = ((IFNET *)dev->dev_icb)->if_mtu;
    dcb->dcb_tx_accm = 0xFFFFFFFF;

    /*
     * Initialize UART handshake hardware and register interrupt handlers.
     */
    if (dev->dev_base) {
#ifdef __AVR_ENHANCED__

#ifdef UART1_CTS_BIT
        sbi(UART1_CTS_PORT, UART1_CTS_BIT);
        cbi(UART1_CTS_DDR, UART1_CTS_BIT);
        /* Falling edge will generate interrupts. */
#if UART1_CTS_BIT == 4
        sbi(EICR, 1);
#elif UART1_CTS_BIT == 5
        sbi(EICR, 3);
#elif UART1_CTS_BIT == 6
        sbi(EICR, 5);
#elif UART1_CTS_BIT == 7
        sbi(EICR, 7);
#endif
#endif
#ifdef UART1_RTS_BIT
        sbi(UART1_RTS_PORT, UART1_RTS_BIT);
        sbi(UART1_RTS_DDR, UART1_RTS_BIT);
#endif
#ifdef UART1_DTR_BIT
        sbi(UART1_DTR_PORT, UART1_DTR_BIT);
        sbi(UART1_DTR_DDR, UART1_DTR_BIT);
#endif

        if (NutRegisterIrqHandler(&sig_UART1_RECV, Rx1Complete, dcb))
            rc = -1;
        else if (NutRegisterIrqHandler(&sig_UART1_DATA, Tx1Complete, dcb))
#ifdef UART1_CTS_BIT
            rc = -1;
        else if (NutRegisterIrqHandler(&UART1_CTS_SIGNAL, Cts1Interrupt, dev))
#endif
#endif                          /* __AVR_ENHANCED__ */
            rc = -1;

    } else {

#if defined(MCU_AT90USB1287)
        rc = -1;
#else
#ifdef UART0_CTS_BIT
        sbi(UART0_CTS_PORT, UART0_CTS_BIT);
        cbi(UART0_CTS_DDR, UART0_CTS_BIT);
#if UART0_CTS_BIT == 4
        sbi(EICR, 1);
#elif UART0_CTS_BIT == 5
        sbi(EICR, 3);
#elif UART0_CTS_BIT == 6
        sbi(EICR, 5);
#elif UART0_CTS_BIT == 7
        sbi(EICR, 7);
#endif
#endif
#ifdef UART0_RTS_BIT
        sbi(UART0_RTS_PORT, UART0_RTS_BIT);
        sbi(UART0_RTS_DDR, UART0_RTS_BIT);
#endif
#ifdef UART0_DTR_BIT
        sbi(UART0_DTR_PORT, UART0_DTR_BIT);
        sbi(UART0_DTR_DDR, UART0_DTR_BIT);
#endif

        if (NutRegisterIrqHandler(&sig_UART0_RECV, Rx0Complete, dcb))
            rc = -1;
        else if (NutRegisterIrqHandler(&sig_UART0_DATA, Tx0Complete, dcb))
            rc = -1;
#ifdef UART0_CTS_BIT
        else if (NutRegisterIrqHandler(&UART0_CTS_SIGNAL, Cts0Interrupt, dev))
            rc = -1;
#endif
#endif
    }


    /*
     * If we have been successful so far, start the HDLC receiver thread,
     * set the initial baudrate and enable the UART.
     */
    if (rc == 0 && NutThreadCreate("ahdlcrx", AhdlcRx, dev,
        (NUT_THREAD_AHDLCRXSTACK * NUT_THREAD_STACK_MULT) + NUT_THREAD_STACK_ADD)) {
        AhdlcAvrIOCtl(dev, UART_SETSPEED, &baudrate);

        return 0;
    }

    /* We failed, clean up. */
    if (dcb->dcb_rx_buf)
        NutHeapFree((void *) dcb->dcb_rx_buf);
    if (dcb->dcb_tx_buf)
        NutHeapFree((void *) dcb->dcb_tx_buf);

    return -1;
}

/*!
 * \brief Read from the asynchronous HDLC device.
 *
 * This function is called by the low level input routines of the
 * \ref xrCrtLowio "C runtime library", using the _NUTDEVICE::dev_read
 * entry.
 *
 * The function may block the calling thread until at least one
 * character has been received or a timeout occurs.
 *
 * It is recommended to set a proper read timeout with software handshake.
 * In this case a timeout may occur, if the communication peer lost our
 * last XON character. The application may then use ioctl() to disable the
 * receiver and do the read again. This will send out another XON.
 *
 * \param fp     Pointer to a \ref _NUTFILE structure, obtained by a
 *               previous call to AhdlcOpen().
 * \param buffer Pointer to the buffer that receives the data. If zero,
 *               then all characters in the input buffer will be
 *               removed.
 * \param size   Maximum number of bytes to read.
 *
 * \return The number of bytes read, which may be less than the number
 *         of bytes specified. A return value of -1 indicates an error,
 *         while zero is returned in case of a timeout.
 */
int AhdlcAvrRead(NUTFILE * fp, void *buffer, int size)
{
    int rc = 0;
    AHDLCDCB *dcb = fp->nf_dev->dev_dcb;
    uint8_t *cp = buffer;

    /*
     * Get characters from receive buffer.
     */
    if (buffer) {
        while (rc < size) {
            if (dcb->dcb_rd_idx != dcb->dcb_rx_idx) {
                *cp++ = dcb->dcb_rx_buf[dcb->dcb_rd_idx++];
                rc++;
            } else if (rc || NutEventWait(&dcb->dcb_rx_rdy, dcb->dcb_rtimeout))
                break;
        }
    }

    /*
     * Call without data buffer discards receive buffer.
     */
    else
        dcb->dcb_rd_idx = dcb->dcb_rx_idx;

    return rc;
}

/*!
 * \brief Write to the asynchronous HDLC device.
 *
 * \param dev    Pointer to a previously registered NUTDEVICE structure.
 * \param buffer Pointer the data to write.
 * \param len    Number of data bytes to write.
 * \param pflg   If this flag is set, then the buffer is located in program
 *               space.
 *
 * \return The number of bytes written. In case of a write timeout, this
 *         may be less than the specified length.
 */
int AhdlcAvrPut(NUTDEVICE * dev, const void *buffer, int len, int pflg)
{
    int rc = 0;
    AHDLCDCB *dcb = dev->dev_dcb;
    const uint8_t *cp = buffer;

    /*
     * Put characters in transmit buffer.
     */
    if (buffer) {
        while (rc < len) {
            if (SendRawByte(dcb, pflg ? PRG_RDB(cp) : *cp, 0))
                break;
            cp++;
            rc++;
        }
    }

    /*
     * Call without data pointer starts transmission.
     */
    else {
        /*
         * TODO: Check handshake.
         */
#ifdef __AVR_ENHANCED__
        if (dev->dev_base)
            sbi(UCSR1B, UDRIE);
        else
#endif
            sbi(UCR, UDRIE);
    }
    return rc;
}

/*!
 * \brief Write to the asynchronous HDLC device.
 *
 * This function is called by the low level output routines of the
 * \ref xrCrtLowio "C runtime library", using the
 * \ref _NUTDEVICE::dev_write entry.
 *
 * The function may block the calling thread.
 *
 * \param fp     Pointer to a _NUTFILE structure, obtained by a previous
 *               call to AhldcOpen().
 * \param buffer Pointer to the data to be written. If zero, then the
 *               output buffer will be flushed.
 * \param len    Number of bytes to write.
 *
 * \return The number of bytes written, which may be less than the number
 *         of bytes specified if a timeout occured. A return value of -1
 *         indicates an error.
 */
int AhdlcAvrWrite(NUTFILE * fp, const void *buffer, int len)
{
    return AhdlcAvrPut(fp->nf_dev, buffer, len, 0);
}

/*!
 * \brief Write to the asynchronous HDLC device.
 *
 * Similar to AhdlcWrite() except that the data is located in program
 * memory.
 *
 * This function is called by the low level output routines of the
 * \ref xrCrtLowio "C runtime library", using the _NUTDEVICE::dev_write_P
 * entry.
 *
 * The function may block the calling thread.
 *
 * \param fp     Pointer to a NUTFILE structure, obtained by a previous
 *               call to AhdlcOpen().
 * \param buffer Pointer to the data in program space to be written.
 * \param len    Number of bytes to write.
 *
 * \return The number of bytes written, which may be less than the number
 *         of bytes specified if a timeout occured. A return value of -1
 *         indicates an error.
 */
int AhdlcAvrWrite_P(NUTFILE * fp, PGM_P buffer, int len)
{
    return AhdlcAvrPut(fp->nf_dev, (const char *) buffer, len, 1);
}

/*!
 * \brief Open the asynchronous HDLC device.
 *
 * This function is called by the low level open routine of the C runtime
 * library, using the _NUTDEVICE::dev_open entry.
 *
 * \param dev Pointer to the NUTDEVICE structure.
 * \param name Ignored, should point to an empty string.
 * \param mode Operation mode. Any of the following values may be or-ed:
 * - \ref _O_BINARY
 * - \ref _O_RDONLY
 * - \ref _O_WRONLY
 * \param acc Ignored, should be zero.
 *
 * \return Pointer to a NUTFILE structure if successful or NUTFILE_EOF otherwise.
 */
NUTFILE *AhdlcAvrOpen(NUTDEVICE * dev, const char *name, int mode, int acc)
{
    NUTFILE *fp;

    if ((fp = NutHeapAlloc(sizeof(NUTFILE))) == 0)
        return NUTFILE_EOF;

    fp->nf_dev = dev;
    fp->nf_fcb = NULL;

    /* Enable handshake outputs. */
#ifdef __AVR_ENHANCED__
    if (dev->dev_base) {
#ifdef UART1_RTS_BIT
        cbi(UART1_RTS_PORT, UART1_RTS_BIT);
#endif
#ifdef UART1_DTR_BIT
        cbi(UART1_DTR_PORT, UART1_DTR_BIT);
#endif
    } else
#endif                          /* __AVR_ENHANCED__ */
    {
#ifdef UART0_RTS_BIT
        cbi(UART0_RTS_PORT, UART0_RTS_BIT);
#endif
#ifdef UART0_DTR_BIT
        cbi(UART0_DTR_PORT, UART0_DTR_BIT);
#endif
    }
    return fp;
}

/*!
 * \brief Close the asynchronous HDLC device.
 *
 * This function is called by the low level close routine of the C runtime
 * library, using the _NUTDEVICE::dev_close entry.
 *
 * \param fp Pointer to a _NUTFILE structure, obtained by a previous call
 *           to UsartOpen().
 *
 * \return 0 on success or -1 otherwise.
 *
 * \todo We may support shared open and use dev_irq as an open counter.
 */
int AhdlcAvrClose(NUTFILE * fp)
{
    if (fp && fp != NUTFILE_EOF) {
        /* Disable handshake outputs. */
#ifdef __AVR_ENHANCED__
        if (fp->nf_dev->dev_base) {
#ifdef UART1_RTS_BIT
            sbi(UART1_RTS_PORT, UART1_RTS_BIT);
#endif
#ifdef UART1_DTR_BIT
            sbi(UART1_DTR_PORT, UART1_DTR_BIT);
#endif
        } else
#endif                          /* __AVR_ENHANCED__ */
        {
#ifdef UART0_RTS_BIT
            sbi(UART0_RTS_PORT, UART0_RTS_BIT);
#endif
#ifdef UART0_DTR_BIT
            sbi(UART0_DTR_PORT, UART0_DTR_BIT);
#endif
        }
        NutHeapFree(fp);
        return 0;
    }
    return -1;
}

/*@}*/
