/*
 * Copyright (C) 2001-2003 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/uartavr.c
 * \brief AVR fast UART driver.
 *
 * \verbatim
 * $Id: uartavr.c 5472 2013-12-06 00:16:28Z olereinhardt $
 * \endverbatim
 */

#include <string.h>

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

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

#include <fcntl.h>

/*
 * Not nice because stdio already defined them. But in order to save memory,
 * we do the whole buffering and including stdio here would be more weird.
 */
#ifndef _IOFBF
#define _IOFBF      0x00
#define _IOLBF      0x01
#define _IONBF      0x02
#endif

/*!
 * \addtogroup xgUartAvr
 */
/*@{*/

/*
 * Handle AVR UART transmit complete.
 */
static void TxComplete(void *arg)
{
    NUTDEVICE *dev = (NUTDEVICE *) arg;
    IFSTREAM *ifs = dev->dev_icb;
    UARTDCB *dcb;

    if (ifs->if_tx_idx != ifs->if_wr_idx) {
#ifdef UDR1
        if (dev->dev_base)
            UDR1 = ifs->if_tx_buf[ifs->if_tx_idx];
        else
#endif
            outb(UDR, ifs->if_tx_buf[ifs->if_tx_idx]);
        ifs->if_tx_idx++;
    } else {
        ifs->if_tx_act = 0;
        dcb = dev->dev_dcb;
        NutEventPostFromIrq(&dcb->dcb_tx_rdy);
    }
}

/*
 * Handle AVR UART receive complete.
 *
 * Note, that this function modifies the receive index in
 * interrupt context. This requires, that any non atomic
 * access of this index requires interrupts being disabled.
 * Thanks to Mike Cornelius, who pointed this out.
 */
static void RxComplete(void *arg)
{
    NUTDEVICE *dev = (NUTDEVICE *) arg;
    IFSTREAM *ifs = dev->dev_icb;
    UARTDCB *dcb;

#ifdef UDR1
    if (dev->dev_base)
        ifs->if_rx_buf[ifs->if_rx_idx] = inb(UDR1);
    else
#endif
        ifs->if_rx_buf[ifs->if_rx_idx] = inb(UDR);

    if (ifs->if_rd_idx == ifs->if_rx_idx) {
        dcb = dev->dev_dcb;
        NutEventPostFromIrq(&dcb->dcb_rx_rdy);
    }
    /* Late increment fixes ICCAVR bug on volatile variables. */
    ifs->if_rx_idx++;
}

/*!
 * \brief Wait for input.
 *
 * This function checks the input buffer for any data. If
 * the buffer is empty, the calling \ref xrThread "thread"
 * will be blocked until at least one new character is
 * received or a timeout occurs.
 *
 * \param dev Indicates the UART device.
 *
 * \return 0 on success, -1 on timeout.
 */
int UartAvrInput(NUTDEVICE * dev)
{
    int rc = 0;
    int empty;
    IFSTREAM *ifs = dev->dev_icb;
    UARTDCB *dcb =  dev->dev_dcb;

    NutEnterCritical();
    empty = ifs->if_rd_idx == ifs->if_rx_idx;
    NutExitCritical();

    if (empty) {

        /*
         * Changing if into a while loop fixes a serious bug:
         * Previous receiver events without any waiting thread
         * set the event handle to the signaled state. So the
         * wait returns immediately. Unfortunately the calling
         * routines rely on a filled buffer when we return 0.
         * Thanks to Mike Cornelius, who found this bug.
         *
         * MR (2005-07-14): Reduced critical sections
         */
        do {

            rc = NutEventWait(&dcb->dcb_rx_rdy, dcb->dcb_rtimeout);

            NutEnterCritical();
            empty = ifs->if_rd_idx == ifs->if_rx_idx;
            NutExitCritical();

        } while (rc == 0 && empty);
    }

    return rc;
}

/*!
 * \brief Initiate output.
 *
 * This function checks the output buffer for any data. If
 * the buffer contains at least one character, the transmitter
 * is started, if not already running. The function returns
 * immediately, without waiting for the character being
 * completely transmitted. Any remaining characters in the
 * output buffer are transmitted in the background.
 *
 * \param dev Indicates the UART device.
 *
 * \return 0 on success, -1 otherwise.
 */
int UartAvrOutput(NUTDEVICE * dev)
{
    IFSTREAM *ifs = dev->dev_icb;

    if (ifs->if_tx_act == 0 && ifs->if_tx_idx != ifs->if_wr_idx) {
        ifs->if_tx_act = 1;
#ifdef UDR1
        if (dev->dev_base)
            UDR1 = ifs->if_tx_buf[ifs->if_tx_idx];
        else
#endif
            outb(UDR, ifs->if_tx_buf[ifs->if_tx_idx]);
        ifs->if_tx_idx++;
    }
    return 0;
}

/*!
 * \brief Wait for output buffer empty.
 *
 * If the output buffer contains any data, the calling
 * thread is suspended until all data has been transmitted.
 *
 * \param dev Indicates the UART device.
 *
 * \return 0 on success, -1 otherwise.
 */
int UartAvrFlush(NUTDEVICE * dev)
{
    IFSTREAM *ifs = dev->dev_icb;
    UARTDCB *dcb = dev->dev_dcb;

    /*
     * Start any pending output.
     */
    if (UartAvrOutput(dev))
        return -1;

    /*
     * Wait until output buffer empty.
     */
    NutEnterCritical();
    while (ifs->if_tx_idx != ifs->if_wr_idx)
        NutEventWait(&dcb->dcb_tx_rdy, 100);
    NutExitCritical();

    return 0;
}

/*
 *
 * \param dev Indicates the UART device.
 *
 * \return 0 on success, -1 otherwise.
 */
static int UartAvrGetStatus(NUTDEVICE * dev, uint32_t * status)
{
    IFSTREAM *ifs = dev->dev_icb;
    uint8_t us;

    *status = 0;

#ifdef UDR1
    if (dev->dev_base)
        us = inb(UCSR1A);
    else
#endif
        us = inb(USR);
    if (us & FE)
        *status |= UART_FRAMINGERROR;
    if (us & DOR)
        *status |= UART_OVERRUNERROR;
    if (ifs->if_tx_idx == ifs->if_wr_idx)
        *status |= UART_TXBUFFEREMPTY;
    if (ifs->if_rd_idx == ifs->if_rx_idx)
        *status |= UART_RXBUFFEREMPTY;
    return 0;
}

/*
 * Carefully enable UART functions.
 */
static void UartAvrEnable(uint16_t base)
{
    NutEnterCritical();
#ifdef UCSR1B
    if (base)
        UCSR1B = BV(RXCIE) | BV(TXCIE) | BV(RXEN) | BV(TXEN);
    else
#endif
        UCR = BV(RXCIE) | BV(TXCIE) | BV(RXEN) | BV(TXEN);
    NutExitCritical();
}

/*
 * Carefully disable UART functions.
 */
static void UartAvrDisable(uint16_t base)
{
    /*
     * Disable UART interrupts.
     */
    NutEnterCritical();
#ifdef UCSR1B
    if (base)
        UCSR1B &= ~(BV(RXCIE) | BV(TXCIE));
    else
#endif
        UCR &= ~(BV(RXCIE) | BV(TXCIE));
    NutExitCritical();

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

    /*
     * Now disable UART functions.
     */
#ifdef UCSR1B
    if (base)
        UCSR1B &= ~(BV(RXEN) | BV(TXEN));
    else
#endif
        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
 *             - UART_GETSTATUS
 *             - 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).
 *
 * \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. To disable timeout,
 *          set the parameter to NUT_WAIT_INFINITE.
 *
 * \note For ATmega103, only 8 data bits, 1 stop bit and no parity are allowed.
 *
 */
int UartAvrIOCtl(NUTDEVICE * dev, int req, void *conf)
{
    int rc = 0;
    UARTDCB *dcb;
    uint32_t *lvp = (uint32_t *) conf;
    uint32_t lv = *lvp;
    uint8_t bv = (uint8_t) lv;
    uint16_t sv = 0;
    uint8_t devnum;

    if (dev == 0)
        dev = &devUart0;

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

    switch (req) {
    case UART_SETSPEED:
        UartAvrDisable(devnum);
#ifdef __AVR_ENHANCED__
        if (devnum) {
            if (bit_is_set(UCSR1A, U2X1)) {
                lv <<= 2;
            } else {
                lv <<= 3;
            }
#if !defined(MCU_AT90USB1287)
        } else {
            if (bit_is_set(UCSR0A, U2X0)) {
                lv <<= 2;
            } else {
                lv <<= 3;
            }
#endif
        }
#else
        lv <<= 3;
#endif
        sv = (uint16_t) ((NutGetCpuClock() / lv + 1UL) / 2UL) - 1;
#ifdef UBRR1H
        if (devnum) {
            UBRR1L = (uint8_t) sv;
            UBRR1H = (uint8_t) (sv >> 8);
#if !defined(MCU_AT90USB1287)
        } else {
            UBRR0L = (uint8_t) sv;
            UBRR0H = (uint8_t) (sv >> 8);
#endif
        }
#else
        UBRR = (uint8_t) sv;
#endif
        UartAvrEnable(devnum);
        break;

    case UART_GETSPEED:
#ifdef UBRR1H
        if (devnum) {
            if (bit_is_set(UCSR1A, U2X1))
                lv = 8UL;
            else
                lv = 16UL;
            sv = (uint16_t) (UBRR1H) << 8 | UBRR1L;
        }
#if !defined(MCU_AT90USB1287)
        else
        {
            if (bit_is_set(UCSR0A, U2X0))
                lv = 8UL;
            else
                lv = 16UL;
            sv = (uint16_t) (UBRR0H) << 8 | UBRR0L;
        }
#endif
#else
        sv = UBRR;
        lv = 16UL;
#endif
        *lvp = NutGetCpuClock() / (lv * (uint32_t) (sv + 1));
        break;

    case UART_SETDATABITS:
        UartAvrDisable(devnum);
#ifdef UCSR1C
        if (bv >= 5 && bv <= 8) {
            bv = (bv - 5) << 1;
            if (devnum) {
                UCSR1C = (UCSR1C & 0xF9) | bv;
                UCSR1B &= 0xFB;
#if !defined(MCU_AT90USB1287)
            } else {
                UCSR0C = (UCSR0C & 0xF9) | bv;
                UCSR0B &= 0xFB;
#endif
            }
        } else
            rc = -1;
#else
        if (bv != 8)
            rc = -1;
#endif
        UartAvrEnable(devnum);
        break;

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

    case UART_SETPARITY:
        UartAvrDisable(devnum);
#ifdef UCSR1C
        if (bv <= 2) {
            if (bv == 1)
                bv = 3;
            bv <<= 4;
            if (devnum)
                UCSR1C = (UCSR1C & 0xCF) | bv;
#if !defined(MCU_AT90USB1287)
            else
                UCSR0C = (UCSR0C & 0xCF) | bv;
#endif
        } else
            rc = -1;
#endif
        if (bv)
            rc = -1;
        UartAvrEnable(devnum);
        break;

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

    case UART_SETSTOPBITS:
        UartAvrDisable(devnum);
#ifdef UCSR1C
        if (bv == 1 || bv == 2) {
            bv = (bv - 1) << 3;
            if (devnum)
                UCSR1C = (UCSR1C & 0xF7) | bv;
#if !defined(MCU_AT90USB1287)
            else
                UCSR0C = (UCSR0C & 0xF7) | bv;
#endif
        } else
            rc = -1;
#else
        if (bv != 1)
            rc = -1;
#endif
        UartAvrEnable(devnum);
        break;

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

    case UART_GETSTATUS:
        UartAvrGetStatus(dev, lvp);
        break;
    case UART_SETSTATUS:
        rc = -1;
        break;

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

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

    case UART_SETLOCALECHO:
        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:
        if (bv)
            dcb->dcb_modeflags |= UART_MF_LOCALECHO;
        else
            dcb->dcb_modeflags &= ~UART_MF_LOCALECHO;
        break;
    case UART_GETFLOWCONTROL:
        break;

    case UART_SETCOOKEDMODE:
        if (bv)
            dcb->dcb_modeflags |= UART_MF_COOKEDMODE;
        else
            dcb->dcb_modeflags &= ~UART_MF_COOKEDMODE;
        break;
    case UART_GETCOOKEDMODE:
        if (dcb->dcb_modeflags & UART_MF_COOKEDMODE)
            *lvp = 1;
        else
            *lvp = 0;
        break;

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

/*!
 * \brief Initialize on chip uart device.
 *
 * Prepares the device for subsequent reading or writing.
 * Enables UART transmitter and receiver interrupts.
 *
 * \param dev  Identifies the device to initialize.
 *
 * \return 0 on success, -1 otherwise.
 */
int UartAvrInit(NUTDEVICE * dev)
{
    IFSTREAM *ifs;
    UARTDCB *dcb;
    uint32_t baudrate = 9600;

    /*
     * We only support character devices for on-chip UARTs.
     */
    if (dev->dev_type != IFTYP_STREAM || dev->dev_irq != 0 ||
        (dev->dev_base != 0 && dev->dev_base != 1))
        return -1;

    /*
     * Initialize interface control block.
     */
    ifs = dev->dev_icb;
    memset(ifs, 0, sizeof(IFSTREAM));
    ifs->if_input = UartAvrInput;
    ifs->if_output = UartAvrOutput;
    ifs->if_flush = UartAvrFlush;

    /*
     * Initialize driver control block.
     */
    dcb = dev->dev_dcb;
    memset(dcb, 0, sizeof(UARTDCB));
    dcb->dcb_modeflags = UART_MF_NOBUFFER;

    /*
     * Register interrupt handler.
     */
    if (dev->dev_base) {
#ifdef UDR1
        if (NutRegisterIrqHandler(&sig_UART1_RECV, RxComplete, dev))
            return -1;
        if (NutRegisterIrqHandler(&sig_UART1_TRANS, TxComplete, dev))
#endif
            return -1;
    } else {
        if (NutRegisterIrqHandler(&sig_UART0_RECV, RxComplete, dev))
            return -1;
        if (NutRegisterIrqHandler(&sig_UART0_TRANS, TxComplete, dev))
            return -1;
    }

    /*
     * Set baudrate and handshake default. This will also
     * enable the UART functions.
     */
    UartAvrIOCtl(dev, UART_SETSPEED, &baudrate);

    return 0;
}

/*!
 * \brief Read from device.
 */
int UartAvrRead(NUTFILE * fp, void *buffer, int size)
{
    int rc;
    NUTDEVICE *dev;
    IFSTREAM *ifs;
    UARTDCB *dcb;
    uint8_t elmode;
    uint8_t ch;
    uint8_t *cp = buffer;

    dev = fp->nf_dev;
    ifs = (IFSTREAM *) dev->dev_icb;
    dcb = dev->dev_dcb;

    if (dcb->dcb_modeflags & UART_MF_COOKEDMODE)
        elmode = 1;
    else
        elmode = 0;

    /*
     * Call without data pointer discards receive buffer.
     */
    if (buffer == 0) {
        ifs->if_rd_idx = ifs->if_rx_idx;
        return 0;
    }

    /*
     * Get characters from receive buffer.
     */
    for (rc = 0; rc < size;) {
        if (ifs->if_rd_idx == ifs->if_rx_idx) {
            if (rc)
                break;
            while (ifs->if_rd_idx == ifs->if_rx_idx) {
                if (UartAvrInput(dev)) {
                    return 0;
        }
        }
        }
        ch = ifs->if_rx_buf[ifs->if_rd_idx++];
        if (elmode && (ch == '\r' || ch == '\n')) {
            if (ifs->if_last_eol == 0 || ifs->if_last_eol == ch) {
                ifs->if_last_eol = ch;
                *cp++ = '\n';
                rc++;
            }
        } else {
            ifs->if_last_eol = 0;
            *cp++ = ch;
            rc++;
        }
    }
    return rc;
}

/*!
 * \brief Write to device.
 */
int UartAvrPut(NUTDEVICE * dev, const void *buffer, int len, int pflg)
{
    int rc;
    IFSTREAM *ifs;
    UARTDCB *dcb;
    const uint8_t *cp;
    uint8_t lbmode;
    uint8_t elmode;
    uint8_t ch;

    ifs = dev->dev_icb;
    dcb = dev->dev_dcb;

    if (dcb->dcb_modeflags & UART_MF_LINEBUFFER)
        lbmode = 1;
    else
        lbmode = 0;

    if (dcb->dcb_modeflags & UART_MF_COOKEDMODE)
        elmode = 1;
    else
        elmode = 0;

    /*
     * Call without data pointer starts transmission.
     */
    if (buffer == 0) {
        rc = UartAvrFlush(dev);
        return rc;
    }

    /*
     * Put characters in transmit buffer.
     */
    cp = buffer;
    for (rc = 0; rc < len;) {
        if ((uint8_t) (ifs->if_wr_idx + 1) == ifs->if_tx_idx) {
            if (UartAvrFlush(dev)) {
                return -1;
            }
        }
        ch = pflg ? PRG_RDB(cp) : *cp;
        if (elmode == 1 && ch == '\n') {
            elmode = 2;
            if (lbmode == 1)
                lbmode = 2;
            ch = '\r';
        } else {
            if (elmode == 2)
                elmode = 1;
            cp++;
            rc++;
        }
        ifs->if_tx_buf[ifs->if_wr_idx++] = ch;
    }

    if (lbmode > 1 || (dcb->dcb_modeflags & UART_MF_NOBUFFER) != 0) {
        if (UartAvrFlush(dev))
            rc = -1;
    }
    return rc;
}

int UartAvrWrite(NUTFILE * fp, const void *buffer, int len)
{
    return UartAvrPut(fp->nf_dev, buffer, len, 0);
}

int UartAvrWrite_P(NUTFILE * fp, PGM_P buffer, int len)
{
    return UartAvrPut(fp->nf_dev, (const char *) buffer, len, 1);
}


/*!
 * \brief Open a device or file.
 */
NUTFILE *UartAvrOpen(NUTDEVICE * dev, const char *name, int mode, int acc)
{
    NUTFILE *fp = NutHeapAlloc(sizeof(NUTFILE));
    UARTDCB *dcb;

    if (fp == 0)
        return NUTFILE_EOF;

    dcb = dev->dev_dcb;
    if (mode & _O_BINARY)
        dcb->dcb_modeflags &= ~UART_MF_COOKEDMODE;
    else
        dcb->dcb_modeflags |= UART_MF_COOKEDMODE;

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

    return fp;
}

/*!
 * \brief Close a device or file.
 */
int UartAvrClose(NUTFILE * fp)
{
    NutHeapFree(fp);

    return 0;
}

/*!
 * \brief Request file size.
 */
long UartAvrSize(NUTFILE * fp)
{
    NUTDEVICE *dev;
    IFSTREAM *ifs;

    dev = fp->nf_dev;
    ifs = (IFSTREAM *) dev->dev_icb;
    return ((uint8_t)(ifs->if_rx_idx - ifs->if_rd_idx));
}


/*@}*/
