/* * 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/ * * The 9-bit communication had been contributed by Brett Abbott, * Digital Telemetry Limited. * * Dave Smart contributed the synchronous mode support. */ /*! * \file arch/avr/dev/usartavr.c * \brief AVR USART support. * * \verbatim * $Id: usartavr.c 5472 2013-12-06 00:16:28Z olereinhardt $ * \endverbatim */ #include #include #include #include #include #ifdef NUTTRACER #include #endif /*! * \addtogroup xgUsartAvr */ /*@{*/ /* \brief ASCII code for software flow control, starts transmitter. */ #define ASCII_XON 0x11 /* \brief ASCII code for software flow control, stops transmitter. */ #define ASCII_XOFF 0x13 /* \brief XON transmit pending flag. */ #define XON_PENDING 0x10 /* \brief XOFF transmit pending flag. */ #define XOFF_PENDING 0x20 /* \brief XOFF sent flag. */ #define XOFF_SENT 0x40 /* \brief XOFF received flag. */ #define XOFF_RCVD 0x80 /*! * \brief Receiver error flags. */ static ureg_t rx_errors; /*! * \brief Enables software flow control if not equal zero. */ static ureg_t flow_control; /*! * \brief Transmit address frame, if not zero. */ static ureg_t tx_aframe; #ifdef UART_HDX_BIT /* define in cfg/modem.h */ #ifdef UART_HDX_FLIP_BIT /* same as RTS toggle by Windows NT driver */ #define UART_HDX_TX cbi #define UART_HDX_RX sbi #else /* previous usage by Ethernut */ #define UART_HDX_TX sbi #define UART_HDX_RX cbi #endif #endif #ifdef UART_HDX_BIT /*! * \brief Enables half duplex control if not equal zero. * * This variable exists only if the hardware configuration defines a * port bit to switch between receive and transmit mode. */ static ureg_t hdx_control; #endif #ifdef UART_RTS_BIT /*! * \brief Enables RTS control if not equal zero. * * This variable exists only if the hardware configuration defines a * port bit to control the RTS signal. */ static ureg_t rts_control; #endif #ifdef UART_CTS_BIT /*! * \brief Enables CTS sense if not equal zero. * * This variable exists only if the hardware configuration defines a * port bit to sense the CTS signal. */ static ureg_t cts_sense; #endif #ifdef UART_CTS_BIT /*! * \brief USARTn CTS sense interrupt handler. * * This interrupt routine is called when the CTS line level is low. * Typical line drivers negate the signal, thus driving our port * low when CTS is active. * * This routine exists only if the hardware configuration defines a * port bit to sense the CTS signal. */ static void AvrUsartCts(void *arg) { /* Enable transmit interrupt. */ sbi(UCSRnB, UDRIE); /* Disable CTS sense interrupt. */ cbi(EIMSK, UART_CTS_BIT); } #endif #ifdef UART_HDX_BIT /* * \brief USARTn transmit complete interrupt handler. * * Used with half duplex communication to switch from tranmit to receive * mode after the last character has been transmitted. * * This routine exists only if the hardware configuration defines a * port bit to switch between receive and transmit mode. * * \param arg Pointer to the transmitter ring buffer. */ static void AvrUsartTxComplete(void *arg) { register RINGBUF *rbf = (RINGBUF *) arg; /* * Check if half duplex mode has been enabled and if all characters * had been sent out. */ if (hdx_control && rbf->rbf_cnt == 0) { /* Switch to receiver mode. */ UART_HDX_RX(UART_HDX_PORT, UART_HDX_BIT); } } #endif /* * \brief USARTn transmit data register empty interrupt handler. * * \param arg Pointer to the transmitter ring buffer. */ #ifdef USE_USART SIGNAL( SIG_AVRUART_DATA ) { register RINGBUF *rbf = &dcb_usart.dcb_tx_rbf; #else static void AvrUsartTxEmpty(void *arg) { register RINGBUF *rbf = (RINGBUF *) arg; #endif register uint8_t *cp = rbf->rbf_tail; #ifdef NUTTRACER TRACE_ADD_ITEM(TRACE_TAG_INTERRUPT_ENTER,TRACE_INT_UART_TXEMPTY); #endif #ifndef UART_NO_SW_FLOWCONTROL /* * Process pending software flow controls first. */ if (flow_control & (XON_PENDING | XOFF_PENDING)) { if (flow_control & XON_PENDING) { outb(UDRn, ASCII_XOFF); flow_control |= XOFF_SENT; } else { outb(UDRn, ASCII_XON); flow_control &= ~XOFF_SENT; } flow_control &= ~(XON_PENDING | XOFF_PENDING); #ifdef NUTTRACER TRACE_ADD_ITEM(TRACE_TAG_INTERRUPT_EXIT,TRACE_INT_UART_TXEMPTY); #endif return; } if (flow_control & XOFF_RCVD) { /* * If XOFF has been received, we disable the transmit interrupts * and return without sending anything. */ cbi(UCSRnB, UDRIE); #ifdef NUTTRACER TRACE_ADD_ITEM(TRACE_TAG_INTERRUPT_EXIT,TRACE_INT_UART_TXEMPTY); #endif return; } #endif /* UART_NO_SW_FLOWCONTROL */ if (rbf->rbf_cnt) { #ifdef UART_CTS_BIT /* * If CTS has been disabled, we disable the transmit interrupts * and return without sending anything. */ if (cts_sense && bit_is_set(UART_CTS_PIN, UART_CTS_BIT)) { cbi(UCSRnB, UDRIE); sbi(EIMSK, UART_CTS_BIT); #ifdef NUTTRACER TRACE_ADD_ITEM(TRACE_TAG_INTERRUPT_EXIT,TRACE_INT_UART_TXEMPTY); #endif return; } #endif rbf->rbf_cnt--; /* * The data sheet doesn't exactly tell us, if this bit is retained * or cleared after the character has been sent out. So we do it * the save way. */ if (tx_aframe) { sbi(UCSRnB, TXB8); } else { cbi(UCSRnB, TXB8); } /* * Start transmission of the next character. */ outb(UDRn, *cp); /* * Wrap around the buffer pointer if we reached its end. */ if (++cp == rbf->rbf_last) { cp = rbf->rbf_start; } rbf->rbf_tail = cp; if (rbf->rbf_cnt == rbf->rbf_lwm) { NutEventPostFromIrq(&rbf->rbf_que); NutSelectWakeupFromIrq(rbf->wq_list, WQ_FLAG_WRITE); } } /* * Nothing left to transmit, disable interrupt. */ else { cbi(UCSRnB, UDRIE); rbf->rbf_cnt = 0; NutEventPostFromIrq(&rbf->rbf_que); NutSelectWakeupFromIrq(rbf->wq_list, WQ_FLAG_WRITE); } #ifdef NUTTRACER TRACE_ADD_ITEM(TRACE_TAG_INTERRUPT_EXIT,TRACE_INT_UART_TXEMPTY); #endif } /* * \brief USARTn receive complete interrupt handler. * * * \param arg Pointer to the receiver ring buffer. */ #ifdef USE_USART SIGNAL( SIG_AVRUART_RECV ){ register RINGBUF *rbf = &dcb_usart.dcb_rx_rbf; #else static void AvrUsartRxComplete(void *arg) { register RINGBUF *rbf = (RINGBUF *) arg; #endif register size_t cnt; register uint8_t ch; #ifdef NUTTRACER TRACE_ADD_ITEM(TRACE_TAG_INTERRUPT_ENTER,TRACE_INT_UART_RXCOMPL); #endif #ifdef UART_READMULTIBYTE register uint8_t postEvent = 0; do { #endif /* * We read the received character as early as possible to avoid overflows * caused by interrupt latency. However, reading the error flags must come * first, because reading the ATmega128 data register clears the status. */ rx_errors |= inb(UCSRnA); ch = inb(UDRn); #ifndef UART_NO_SW_FLOWCONTROL /* * Handle software handshake. We have to do this before checking the * buffer, because flow control must work in write-only mode, where * there is no receive buffer. */ if (flow_control) { /* XOFF character disables transmit interrupts. */ if (ch == ASCII_XOFF) { cbi(UCSRnB, UDRIE); flow_control |= XOFF_RCVD; #ifdef NUTTRACER TRACE_ADD_ITEM(TRACE_TAG_INTERRUPT_EXIT,TRACE_INT_UART_RXCOMPL); #endif return; } /* XON enables transmit interrupts. */ else if (ch == ASCII_XON) { sbi(UCSRnB, UDRIE); flow_control &= ~XOFF_RCVD; #ifdef NUTTRACER TRACE_ADD_ITEM(TRACE_TAG_INTERRUPT_EXIT,TRACE_INT_UART_RXCOMPL); #endif return; } } #endif /* * Check buffer overflow. */ cnt = rbf->rbf_cnt; if (cnt >= rbf->rbf_siz) { rx_errors |= _BV(DOR); #ifdef NUTTRACER TRACE_ADD_ITEM(TRACE_TAG_INTERRUPT_EXIT,TRACE_INT_UART_RXCOMPL); #endif return; } /* Wake up waiting threads if this is the first byte in the buffer. */ if (cnt++ == 0){ #ifdef UART_READMULTIBYTE // we do this later, to get the other bytes in time.. postEvent = 1; #else NutEventPostFromIrq(&rbf->rbf_que); NutSelectWakeupFromIrq(rbf->wq_list, WQ_FLAG_READ); #endif } #ifndef UART_NO_SW_FLOWCONTROL /* * Check the high watermark for software handshake. If the number of * buffered bytes is above this mark, then send XOFF. */ else if (flow_control) { if(cnt >= rbf->rbf_hwm) { if((flow_control & XOFF_SENT) == 0) { if (inb(UCSRnA) & _BV(UDRE)) { outb(UDRn, ASCII_XOFF); flow_control |= XOFF_SENT; flow_control &= ~XOFF_PENDING; } else { flow_control |= XOFF_PENDING; } } } } #endif #ifdef UART_RTS_BIT /* * Check the high watermark for hardware handshake. If the number of * buffered bytes is above this mark, then disable RTS. */ else if (rts_control && cnt >= rbf->rbf_hwm) { sbi(UART_RTS_PORT, UART_RTS_BIT); } #endif /* * Store the character and increment and the ring buffer pointer. */ *rbf->rbf_head++ = ch; if (rbf->rbf_head == rbf->rbf_last) { rbf->rbf_head = rbf->rbf_start; } /* Update the ring buffer counter. */ rbf->rbf_cnt = cnt; #ifdef UART_READMULTIBYTE } while ( inb(UCSRnA) & _BV(RXC) ); // byte in buffer? // Eventually post event to wake thread if (postEvent) { NutEventPostFromIrq(&rbf->rbf_que); NutSelectWakeupFromIrq(rbf->wq_list, WQ_FLAG_READ); } #endif #ifdef NUTTRACER TRACE_ADD_ITEM(TRACE_TAG_INTERRUPT_EXIT,TRACE_INT_UART_RXCOMPL); #endif } /*! * \brief Carefully enable USART hardware functions. * * Always enable transmitter and receiver, even on read-only or * write-only mode. So we can support software flow control. */ static void AvrUsartEnable(void) { NutEnterCritical(); outb(UCSRnB, _BV(RXCIE) | _BV(UDRIE) | _BV(RXEN) | _BV(TXEN)); #ifdef UART_HDX_BIT if (hdx_control) { /* Enable transmit complete interrupt. */ sbi(UCSRnB, TXCIE); } #endif NutExitCritical(); } /*! * \brief Carefully disable USART hardware functions. */ static void AvrUsartDisable(void) { /* * Disable USART interrupts. */ NutEnterCritical(); cbi(UCSRnB, RXCIE); cbi(UCSRnB, TXCIE); cbi(UCSRnB, UDRIE); NutExitCritical(); /* * Allow incoming or outgoing character to finish. */ NutDelay(10); /* * Disable USART transmit and receive. */ cbi(UCSRnB, RXEN); cbi(UCSRnB, TXEN); } /*! * \brief Query the USART hardware for the selected speed. * * This function is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \return The currently selected baudrate. */ static uint32_t AvrUsartGetSpeed(void) { uint32_t fct; uint16_t sv = (uint16_t) inb(UBRRnL); #ifdef __AVR_ENHANCED__ sv |= ((uint16_t) inb(UBRRnH) << 8); /* Synchronous mode. */ if (bit_is_set(UCSRnC, UMSEL)) { fct = 2UL; } /* Double rate mode. */ else if (bit_is_set(UCSRnA, U2X)) { fct = 8UL; } /* Normal mode. */ else { fct = 16UL; } #else fct = 16UL; #endif return NutGetCpuClock() / (fct * ((uint32_t) sv + 1UL)); } /*! * \brief Set the USART hardware bit rate. * * This function is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \param rate Number of bits per second. * * \return 0 on success, -1 otherwise. */ static int AvrUsartSetSpeed(uint32_t rate) { uint16_t sv; AvrUsartDisable(); /* * Modified Robert Hildebrand's refined calculation. */ #ifdef __AVR_ENHANCED__ if (bit_is_clear(UCSRnC, UMSEL)) { if (bit_is_set(UCSRnA, U2X)) { rate <<= 2; } else { rate <<= 3; } } #else rate <<= 3; #endif sv = (uint16_t) ((NutGetCpuClock() / rate + 1UL) / 2UL) - 1; outb(UBRRnL, (uint8_t) sv); #ifdef __AVR_ENHANCED__ outb(UBRRnH, (uint8_t) (sv >> 8)); #endif AvrUsartEnable(); return 0; } /*! * \brief Query the USART hardware for the number of data bits. * * This function is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \return The number of data bits set. */ static uint8_t AvrUsartGetDataBits(void) { if (bit_is_set(UCSRnB, UCSZ2)) { return 9; } #ifdef __AVR_ENHANCED__ if (bit_is_set(UCSRnC, UCSZ1)) { if (bit_is_set(UCSRnC, UCSZ0)) { return 8; } else { return 7; } } else if (bit_is_set(UCSRnC, UCSZ0)) { return 6; } return 5; #else return 8; #endif } /*! * \brief Set the USART hardware to the number of data bits. * * This function is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \return 0 on success, -1 otherwise. */ static int AvrUsartSetDataBits(uint8_t bits) { AvrUsartDisable(); cbi(UCSRnB, UCSZ2); #ifdef __AVR_ENHANCED__ cbi(UCSRnC, UCSZ0); cbi(UCSRnC, UCSZ1); switch (bits) { case 6: sbi(UCSRnC, UCSZ0); break; case 9: sbi(UCSRnB, UCSZ2); case 8: sbi(UCSRnC, UCSZ0); case 7: sbi(UCSRnC, UCSZ1); break; } #else if(bits == 9) { sbi(UCSRnB, UCSZ2); } #endif AvrUsartEnable(); /* * Verify the result. */ if (AvrUsartGetDataBits() != bits) { return -1; } return 0; } /*! * \brief Query the USART hardware for the parity mode. * * This routine is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \return Parity mode, either 0 (disabled), 1 (odd) or 2 (even). */ static uint8_t AvrUsartGetParity(void) { #ifdef __AVR_ENHANCED__ if (bit_is_set(UCSRnC, UPM1)) { if (bit_is_set(UCSRnC, UPM0)) { return 1; } else { return 2; } } #endif return 0; } /*! * \brief Set the USART hardware to the specified parity mode. * * This routine is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \param mode 0 (disabled), 1 (odd) or 2 (even) * * \return 0 on success, -1 otherwise. */ static int AvrUsartSetParity(uint8_t mode) { #ifdef __AVR_ENHANCED__ AvrUsartDisable(); switch (mode) { case 0: cbi(UCSRnC, UPM0); cbi(UCSRnC, UPM1); break; case 1: sbi(UCSRnC, UPM0); sbi(UCSRnC, UPM1); break; case 2: cbi(UCSRnC, UPM0); sbi(UCSRnC, UPM1); break; } AvrUsartEnable(); #endif /* * Verify the result. */ if (AvrUsartGetParity() != mode) { return -1; } return 0; } /*! * \brief Query the USART hardware for the number of stop bits. * * This routine is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \return The number of stop bits set, either 1 or 2. */ static uint8_t AvrUsartGetStopBits(void) { #ifdef __AVR_ENHANCED__ if (bit_is_set(UCSRnC, USBS)) { return 2; } #endif return 1; } /*! * \brief Set the USART hardware to the number of stop bits. * * This routine is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \return 0 on success, -1 otherwise. */ static int AvrUsartSetStopBits(uint8_t bits) { #ifdef __AVR_ENHANCED__ AvrUsartDisable(); if (bits == 1) { cbi(UCSRnC, USBS); } else if (bits == 2) { sbi(UCSRnC, USBS); } AvrUsartEnable(); #endif /* * Verify the result. */ if (AvrUsartGetStopBits() != bits) { return -1; } return 0; } /*! * \brief Query the USART hardware status. * * \return Status flags. */ static uint32_t AvrUsartGetStatus(void) { uint32_t rc = 0; /* * Set receiver error flags. */ if ((rx_errors & _BV(FE)) != 0) { rc |= UART_FRAMINGERROR; } if ((rx_errors & _BV(DOR)) != 0) { rc |= UART_OVERRUNERROR; } #ifdef __AVR_ENHANCED__ if ((rx_errors & _BV(UPE)) != 0) { rc |= UART_PARITYERROR; } #endif /* * Determine software handshake status. The flow control status may * change during interrupt, but this doesn't really hurt us. */ if (flow_control) { if (flow_control & XOFF_SENT) { rc |= UART_RXDISABLED; } if (flow_control & XOFF_RCVD) { rc |= UART_TXDISABLED; } } #ifdef UART_RTS_BIT /* * Determine hardware handshake control status. */ if (bit_is_set(UART_RTS_PORT, UART_RTS_BIT)) { rc |= UART_RTSDISABLED; if (rts_control) { rc |= UART_TXDISABLED; } } else { rc |= UART_RTSENABLED; } #endif #ifdef UART_CTS_BIT /* * Determine hardware handshake sense status. */ if (bit_is_set(UART_CTS_PIN, UART_CTS_BIT)) { rc |= UART_CTSDISABLED; if (cts_sense) { rc |= UART_RXDISABLED; } } else { rc |= UART_CTSENABLED; } #endif #ifdef UART_DTR_BIT /* * Determine DTS status. */ if ( bit_is_set( UART_DTR_PORT, UART_DTR_BIT ) ) { rc |= UART_DTRENABLED; } else { rc |= UART_DTRDISABLED; } #endif /* * If transmitter and receiver haven't been detected disabled by any * of the checks above, then they are probably enabled. */ if ((rc & UART_RXDISABLED) == 0) { rc |= UART_RXENABLED; } if ((rc & UART_TXDISABLED) == 0) { rc |= UART_TXENABLED; } /* * Process multidrop setting. */ if (tx_aframe) { rc |= UART_TXADDRFRAME; } else { rc |= UART_TXNORMFRAME; } #ifdef __AVR_ENHANCED__ if (bit_is_set(UCSRnA, MPCM)) { rc |= UART_RXADDRFRAME; } else { rc |= UART_RXNORMFRAME; } #else rc |= UART_RXNORMFRAME; #endif return rc; } /*! * \brief Set the USART hardware status. * * \param flags Status flags. * * \return 0 on success, -1 otherwise. */ static int AvrUsartSetStatus(uint32_t flags) { /* * Process software handshake control. */ if (flow_control) { /* Access to the flow control status must be atomic. */ NutEnterCritical(); /* * Enabling or disabling the receiver means to behave like * having sent a XON or XOFF character resp. */ if (flags & UART_RXENABLED) { flow_control &= ~XOFF_SENT; } else if (flags & UART_RXDISABLED) { flow_control |= XOFF_SENT; } /* * Enabling or disabling the transmitter means to behave like * having received a XON or XOFF character resp. */ if (flags & UART_TXENABLED) { flow_control &= ~XOFF_RCVD; } else if (flags & UART_TXDISABLED) { flow_control |= XOFF_RCVD; } NutExitCritical(); } #ifdef UART_RTS_BIT /* * Process hardware handshake control. */ if (rts_control) { if (flags & UART_RXDISABLED) { sbi(UART_RTS_PORT, UART_RTS_BIT); } if (flags & UART_RXENABLED) { cbi(UART_RTS_PORT, UART_RTS_BIT); } } if (flags & UART_RTSDISABLED) { sbi(UART_RTS_PORT, UART_RTS_BIT); } if (flags & UART_RTSENABLED) { cbi(UART_RTS_PORT, UART_RTS_BIT); } #endif #ifdef UART_DTR_BIT if ( flags & UART_DTRDISABLED ) { sbi(UART_DTR_DDR, UART_DTR_BIT); sbi(UART_DTR_PORT, UART_DTR_BIT); } if ( flags & UART_DTRENABLED ) { sbi(UART_DTR_DDR, UART_DTR_BIT); cbi(UART_DTR_PORT, UART_DTR_BIT); } #endif /* * Process multidrop setting. */ if (flags & UART_TXADDRFRAME) { tx_aframe = 1; } if (flags & UART_TXNORMFRAME) { tx_aframe = 0; } #ifdef __AVR_ENHANCED__ if (flags & UART_RXADDRFRAME) { sbi(UCSRnA, MPCM); } if (flags & UART_RXNORMFRAME) { cbi(UCSRnA, MPCM); } #endif /* * Clear UART receive errors. */ if (flags & UART_FRAMINGERROR) { rx_errors &= ~_BV(FE); } if (flags & UART_OVERRUNERROR) { rx_errors &= ~_BV(DOR); } #ifdef __AVR_ENHANCED__ if (flags & UART_PARITYERROR) { rx_errors &= ~_BV(UPE); } #endif /* * Verify the result. */ if ((AvrUsartGetStatus() & ~UART_ERRORS) != flags) { return -1; } return 0; } /*! * \brief Query the USART hardware for synchronous mode. * * This function is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \return Or-ed combination of \ref UART_SYNC, \ref UART_MASTER, * \ref UART_NCLOCK and \ref UART_HIGHSPEED. */ static uint8_t AvrUsartGetClockMode(void) { uint8_t rc = 0; #ifdef __AVR_ENHANCED__ if (bit_is_set(UCSRnC, UMSEL)) { rc |= UART_SYNC; if (bit_is_set(DDRE, 2)) { rc |= UART_MASTER; } if (bit_is_set(UCSRnC, UCPOL)) { rc |= UART_NCLOCK; } } else if (bit_is_set(UCSRnA, U2X)) { rc |= UART_HIGHSPEED; } #endif return rc; } /*! * \brief Set asynchronous or synchronous mode. * * This function is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \param mode Must be an or-ed combination of USART_SYNC, USART_MASTER, * USART_NCLOCK and USART_HIGHSPEED. * * \return 0 on success, -1 otherwise. */ static int AvrUsartSetClockMode(uint8_t mode) { #ifdef __AVR_ENHANCED__ AvrUsartDisable(); /* * Handle synchronous mode. */ if (mode & UART_SYNC) { if (mode & UART_MASTER) { /* Enable master mode. */ sbi(DDRE, 2); } else { /* Disable master mode. */ cbi(DDRE, 2); } if (mode & UART_NCLOCK) { /* Enable negated clock. */ sbi(UCSRnC, UCPOL); } else { /* Disable negated clock. */ cbi(UCSRnC, UCPOL); } /* Disable high speed. */ cbi(UCSRnA, U2X); /* Enable synchronous mode. */ sbi(UCSRnC, UMSEL); } /* * Handle asynchronous mode. */ else { if (mode & UART_HIGHSPEED) { /* Enable high speed. */ sbi(UCSRnA, U2X); } else { /* Disable high speed. */ cbi(UCSRnA, U2X); } /* Disable negated clock. */ cbi(UCSRnC, UCPOL); /* Disable synchronous mode. */ cbi(UCSRnC, UMSEL); } AvrUsartEnable(); #endif /* * Verify the result. */ if (AvrUsartGetClockMode() != mode) { return -1; } return 0; } /*! * \brief Query flow control mode. * * This routine is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \return See UsartIOCtl(). */ static uint32_t AvrUsartGetFlowControl(void) { uint32_t rc = 0; if (flow_control) { rc |= USART_MF_XONXOFF; } else { rc &= ~USART_MF_XONXOFF; } #ifdef UART_RTS_BIT if (rts_control) { rc |= USART_MF_RTSCONTROL; } else { rc &= ~USART_MF_RTSCONTROL; } #endif #ifdef UART_CTS_BIT if (cts_sense) { rc |= USART_MF_CTSSENSE; } else { rc &= ~USART_MF_CTSSENSE; } #endif #ifdef UART_HDX_BIT if (hdx_control) { rc |= USART_MF_HALFDUPLEX; } else { rc &= ~USART_MF_HALFDUPLEX; } #endif return rc; } /*! * \brief Set flow control mode. * * This function is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \param flags See UsartIOCtl(). * * \return 0 on success, -1 otherwise. */ static int AvrUsartSetFlowControl(uint32_t flags) { /* * Set software handshake mode. */ if (flags & USART_MF_XONXOFF) { if(flow_control == 0) { NutEnterCritical(); flow_control = 1 | XOFF_SENT; /* force XON to be sent on next read */ NutExitCritical(); } } else { NutEnterCritical(); flow_control = 0; NutExitCritical(); } #ifdef UART_RTS_BIT /* * Set RTS control mode. */ if (flags & USART_MF_RTSCONTROL) { sbi(UART_RTS_PORT, UART_RTS_BIT); sbi(UART_RTS_DDR, UART_RTS_BIT); rts_control = 1; } else if (rts_control) { rts_control = 0; cbi(UART_RTS_DDR, UART_RTS_BIT); } #endif #ifdef UART_CTS_BIT /* * Set CTS sense mode. */ if (flags & USART_MF_CTSSENSE) { /* Register CTS sense interrupt. */ if (NutRegisterIrqHandler(&UART_CTS_SIGNAL, AvrUsartCts, 0)) { return -1; } sbi(UART_CTS_PORT, UART_CTS_BIT); cbi(UART_CTS_DDR, UART_CTS_BIT); cts_sense = 1; } else if (cts_sense) { cts_sense = 0; /* Deregister CTS sense interrupt. */ NutRegisterIrqHandler(&UART_CTS_SIGNAL, 0, 0); cbi(UART_CTS_DDR, UART_CTS_BIT); } #endif #ifdef UART_HDX_BIT /* * Set half duplex mode. */ if (flags & USART_MF_HALFDUPLEX) { /* Register transmit complete interrupt. */ if (NutRegisterIrqHandler(&sig_UART_TRANS, AvrUsartTxComplete, &dcb_usart.dcb_tx_rbf)) { return -1; } /* Initially enable the receiver. */ UART_HDX_RX(UART_HDX_PORT, UART_HDX_BIT); sbi(UART_HDX_DDR, UART_HDX_BIT); hdx_control = 1; /* Enable transmit complete interrupt. */ sbi(UCSRnB, TXCIE); } else if (hdx_control) { hdx_control = 0; /* disable transmit complete interrupt */ cbi(UCSRnB, TXCIE); /* Deregister transmit complete interrupt. */ NutRegisterIrqHandler(&sig_UART_TRANS, 0, 0); cbi(UART_HDX_DDR, UART_HDX_BIT); } #endif /* * Verify the result. */ if (AvrUsartGetFlowControl() != flags) { return -1; } return 0; } /*! * \brief Start the USART transmitter hardware. * * The upper level USART driver will call this function through the * USARTDCB jump table each time it added one or more bytes to the * transmit buffer. */ static void AvrUsartTxStart(void) { #ifdef UART_HDX_BIT if (hdx_control) { /* Enable half duplex transmitter. */ UART_HDX_TX(UART_HDX_PORT, UART_HDX_BIT); } #endif /* Enable transmit interrupts. */ sbi(UCSRnB, UDRIE); } /*! * \brief Start the USART receiver hardware. * * The upper level USART driver will call this function through the * USARTDCB jump table each time it removed enough bytes from the * receive buffer. Enough means, that the number of bytes left in * the buffer is below the low watermark. */ static void AvrUsartRxStart(void) { /* * Do any required software flow control. */ if (flow_control && (flow_control & XOFF_SENT) != 0) { NutEnterCritical(); if (inb(UCSRnA) & _BV(UDRE)) { outb(UDRn, ASCII_XON); flow_control &= ~XON_PENDING; } else { flow_control |= XON_PENDING; } flow_control &= ~(XOFF_SENT | XOFF_PENDING); NutExitCritical(); } #ifdef UART_RTS_BIT if (rts_control) { /* Enable RTS. */ cbi(UART_RTS_PORT, UART_RTS_BIT); } #endif } /* * \brief Initialize the USART hardware driver. * * This function is called during device registration by the upper level * USART driver through the USARTDCB jump table. * * \return 0 on success, -1 otherwise. */ static int AvrUsartInit(void) { #ifndef USE_USART /* * Register receive and transmit interrupts. */ if (NutRegisterIrqHandler(&sig_UART_RECV, AvrUsartRxComplete, &dcb_usart.dcb_rx_rbf)) return -1; if (NutRegisterIrqHandler(&sig_UART_DATA, AvrUsartTxEmpty, &dcb_usart.dcb_tx_rbf)) { NutRegisterIrqHandler(&sig_UART_RECV, 0, 0); return -1; } #endif #ifdef UART_RTS_BIT sbi(UART_RTS_DDR, UART_RTS_BIT); #endif #ifdef UART_DTR_BIT sbi(UART_DTR_DDR, UART_DTR_BIT); #endif return 0; } /* * \brief Deinitialize the USART hardware driver. * * This function is called during device deregistration by the upper * level USART driver through the USARTDCB jump table. * * \return 0 on success, -1 otherwise. */ static int AvrUsartDeinit(void) { #ifndef USE_USART /* Deregister receive and transmit interrupts. */ NutRegisterIrqHandler(&sig_UART_RECV, 0, 0); NutRegisterIrqHandler(&sig_UART_DATA, 0, 0); #endif /* * Disabling flow control shouldn't be required here, because it's up * to the upper level to do this on the last close or during * deregistration. */ #ifdef UART_HDX_BIT /* Deregister transmit complete interrupt. */ if (hdx_control) { hdx_control = 0; NutRegisterIrqHandler(&sig_UART_TRANS, 0, 0); } #endif #ifdef UART_CTS_BIT if (cts_sense) { cts_sense = 0; cbi(UART_CTS_DDR, UART_CTS_BIT); /* Deregister CTS sense interrupt. */ NutRegisterIrqHandler(&UART_CTS_SIGNAL, 0, 0); } #endif #ifdef UART_RTS_BIT if (rts_control) { rts_control = 0; cbi(UART_RTS_DDR, UART_RTS_BIT); } #endif #ifdef UART_DTR_BIT cbi(UART_DTR_DDR, UART_DTR_BIT); #endif return 0; } /*@}*/