/* * 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 dev/usart.c * \brief Hardware independent part of the U(S)ART driver. * * \verbatim * $Id: usart.c 5526 2014-01-07 18:17:36Z u_bonnes $ * \endverbatim */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * 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 xgUsart */ /*@{*/ /*! * \brief Initialize the USART device. * \internal * * This function is called by NutRegisterDevice(), using the * _NUTDEVICE::dev_init entry. It will call the low level * driver's _USARTDCB::dcb_init routine to initialize the hardware. * * \param dev Identifies the device to initialize. * * \return 0 on success, -1 otherwise. * * \todo Read initial settings from EEPROM. */ int UsartInit(NUTDEVICE * dev) { int rc; USARTDCB *dcb = dev->dev_dcb; /* Initialize the low level hardware driver. */ rc = (*dcb->dcb_init) (); if (rc == 0) { /* Ignore errors on initial configuration. */ (*dcb->dcb_set_speed) (USART_INITSPEED); } return rc; } /*! * \brief Reset a USART ring buffer. * * \param rbf Pointer to the ring buffer structure. * \param size Buffer size. * \param lowm Low watermark. * \param hiwm High watermark. * * \return 0 on success, -1 if changing the buffer size fails. */ static int UsartResetBuffer(RINGBUF * rbf, size_t size, size_t lowm, size_t hiwm) { uint8_t *xbp = rbf->rbf_start; size_t xsz = rbf->rbf_siz; /* Disable further buffer usage. */ NutEnterCritical(); rbf->rbf_siz = 0; NutExitCritical(); /* Resize the buffer, if required. */ if (xsz != size) { if (xsz) { free(xbp); } if (size) { xbp = malloc(size); if (xbp == NULL) { return -1; } } } /* Update ring buffer status. */ if (size) { rbf->rbf_start = xbp; rbf->rbf_head = xbp; rbf->rbf_tail = xbp; rbf->rbf_last = xbp + size; rbf->rbf_lwm = lowm; rbf->rbf_hwm = hiwm; rbf->rbf_cnt = 0; /* Re-enable buffer usage. */ NutEnterCritical(); rbf->rbf_siz = size; NutExitCritical(); } return 0; } /*! * \brief Read from device. * \internal * * 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 UsartOpen(). * \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 UsartRead(NUTFILE * fp, void *buffer, int size) { size_t rc; size_t avail; size_t taken = 0; uint8_t ch; uint8_t *cp = buffer; NUTDEVICE *dev; USARTDCB *dcb; RINGBUF *rbf; NUTASSERT(fp != NULL && fp != NUTFILE_EOF); dev = fp->nf_dev; dcb = dev->dev_dcb; rbf = &dcb->dcb_rx_rbf; if (dcb->dcb_modeflags & USART_MF_BLOCKREAD) { rbf->rbf_blockptr = buffer; rbf->rbf_blockcnt = size; (*dcb->dcb_rx_start) (); return size; } /* * No buffer allocated, this device is read only. */ if (rbf->rbf_siz == 0) { return -1; } /* * Call without data pointer discards receive buffer. */ if (buffer == NULL) { UsartResetBuffer(rbf, rbf->rbf_siz, rbf->rbf_lwm, rbf->rbf_hwm); (*dcb->dcb_rx_start) (); return 0; } /* * Wait until at least one character is buffered or until a read * timeout occured. */ for (;;) { /* Atomic access to the ring buffer counter. */ NutEnterCritical(); avail = rbf->rbf_cnt; NutExitCritical(); if (avail) { break; } /* * This will enable RTS hardware handshake or re-enable the * remote transmitter by sending a XON character. */ (*dcb->dcb_rx_start) (); if (NutEventWait(&rbf->rbf_que, dcb->dcb_rtimeout)) { return 0; } /* Device closed by another thread. */ if (rbf->rbf_siz == 0) { return -1; } } /* * Get cooked characters from receive buffer. */ if (dcb->dcb_modeflags & USART_MF_COOKEDMODE) { for (rc = 0; rc < (size_t) size;) { if (taken >= avail) { break; } ch = *rbf->rbf_tail++; if (rbf->rbf_tail == rbf->rbf_last) { rbf->rbf_tail = rbf->rbf_start; } taken++; if (ch == '\r' || ch == '\n') { if (dcb->dcb_last_eol == 0 || dcb->dcb_last_eol == ch) { dcb->dcb_last_eol = ch; *cp++ = '\n'; rc++; } } else { dcb->dcb_last_eol = 0; *cp++ = ch; rc++; } } } /* * Get raw characters from receive buffer. */ else { rc = size; if (rc > avail) { rc = avail; } for (taken = 0; taken < rc; taken++) { *cp++ = *rbf->rbf_tail++; if (rbf->rbf_tail == rbf->rbf_last) { rbf->rbf_tail = rbf->rbf_start; } } } if (taken) { NutEnterCritical(); rbf->rbf_cnt -= taken; NutExitCritical(); if (rbf->rbf_cnt < rbf->rbf_lwm) { (*dcb->dcb_rx_start) (); } } return (int) rc; } /*! * \brief Flush output buffer. * * The current thread will be blocked until all characters up to a * specified count have been written or until a timeout occurred. * * \param dcb Pointer to the device's control block structure. * \param added Number of bytes to add to the ring buffer counter. The * characters must have been added without updating the * counter. Because access to the counter has to be atomic, * this parameter simplifies the calling routine a bit. * \param left The maximum number of bytes left in the buffer before * this function returns. * * \return Number of bytes left in the output buffer, which is greater * than the specified value in case of a timeout. */ static size_t UsartFlushOutput(USARTDCB *dcb, size_t added, size_t left) { size_t rc; RINGBUF *rbf = &dcb->dcb_tx_rbf; /* * Add the new characters to the buffer count. */ NutEnterCritical(); rbf->rbf_cnt += added; rc = rbf->rbf_cnt; NutExitCritical(); while (rc > left) { /* Start transmitter and wait for the next event. */ (*dcb->dcb_tx_start) (); if (NutEventWait(&rbf->rbf_que, dcb->dcb_wtimeout)) { break; } /* Refresh the count. */ NutEnterCritical(); rc = rbf->rbf_cnt; NutExitCritical(); }; return rc; } /*! * \brief Write to device. * * \param dev Pointer to a previously registered NUTDEVICE structure. * \param buffer Pointer to 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. Used by Harvard architectures only. * * \return The number of bytes written. In case of a write timeout, this * may be less than the specified length. */ static int UsartPut(NUTDEVICE * dev, const void *buffer, int len, int pflg) { int rc; const uint8_t *cp; uint8_t lbmode; ureg_t cooked; uint8_t ch; size_t cnt; size_t added; USARTDCB *dcb = dev->dev_dcb; RINGBUF *rbf = &dcb->dcb_tx_rbf; if (dcb->dcb_modeflags & USART_MF_BLOCKWRITE) { rbf->rbf_blockptr = (void*)buffer; rbf->rbf_blockcnt = len; (*dcb->dcb_tx_start) (); rc = NutEventWaitNext( &rbf->rbf_que, dcb->dcb_wtimeout); if (rc == 0) { rc = len; } return rc; } /* * No output ring buffer allocated, this device is read only. */ if (rbf->rbf_siz == 0) { return -1; } /* * Call without data pointer flushes the buffer. In this case a return * value not equal zero indicates write timeout. */ if (buffer == NULL) { return UsartFlushOutput(dcb, 0, 0); } if (dcb->dcb_modeflags & USART_MF_LINEBUFFER) { lbmode = 1; } else { lbmode = 0; } if (dcb->dcb_modeflags & USART_MF_COOKEDMODE) { cooked = 1; } else { cooked = 0; } /* * Get the number of buffered bytes. The transmit interrupt will modify * this value, so make the query atomic. */ NutEnterCritical(); cnt = rbf->rbf_cnt; NutExitCritical(); /* * Move bytes to the transmit buffer. */ cp = buffer; added = 0; for (rc = 0; rc < len;) { /* * If we reached the high watermark, then kick the hardware driver * to start transmission and wait until the low watermark is reached. */ if (cnt + added >= rbf->rbf_hwm) { cnt = UsartFlushOutput(dcb, added, rbf->rbf_lwm); added = 0; /* If still above the mark, then a timeout occured. */ if(cnt > rbf->rbf_lwm) { break; } } /* * Get the next data byte. If the pflg parameter is set, then data * is located in program space. */ ch = pflg ? PRG_RDB(cp) : *cp; /* * In cooked mode we prepend a carriage return to any linefeed * character. */ if (cooked == 1 && ch == '\n') { cooked = 2; ch = '\r'; if (lbmode == 1) { lbmode = 2; } } else { if (cooked == 2) { cooked = 1; } cp++; rc++; } *rbf->rbf_head++ = ch; if (rbf->rbf_head == rbf->rbf_last) { rbf->rbf_head = rbf->rbf_start; } added++; } if (added) { NutEnterCritical(); rbf->rbf_cnt += added; NutExitCritical(); (*dcb->dcb_tx_start) (); } return rc; } /*! * \brief Write a device or file. * \internal * * 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 UsartOpen(). * \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 occurred. A return value of -1 * indicates an error. * * \note Using a NULL pointer to flush the output buffer is a Nut/OS * specific extension and will most probably not work on other * systems. */ int UsartWrite(NUTFILE * fp, const void *buffer, int len) { NUTASSERT(fp != NULL && fp != NUTFILE_EOF); return UsartPut(fp->nf_dev, buffer, len, 0); } #ifdef __HARVARD_ARCH__ /*! * \brief Write a device or file. * \internal * * Similar to UsartWrite() 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. Used by Harvard architectures only. * * The function may block the calling thread. * * \param fp Pointer to a NUTFILE structure, obtained by a previous * call to UsartOpen(). * \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 occurred. A return value of -1 * indicates an error. */ int UsartWrite_P(NUTFILE * fp, PGM_P buffer, int len) { NUTASSERT(fp != NULL && fp != NUTFILE_EOF); return UsartPut(fp->nf_dev, (const char *) buffer, len, 1); } #endif /*! * \brief Close a USART device. * \internal * * 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 UsartClose(NUTFILE * fp) { int rc = 0; NUTDEVICE *dev; USARTDCB *dcb; NUTASSERT(fp != NULL && fp != NUTFILE_EOF); dev = fp->nf_dev; dcb = (USARTDCB *) dev->dev_dcb; /* Flush the complete output buffer. If this fails, let the caller know, that not all bytes had been transmitted. */ if (UsartFlushOutput(dcb, 0, 0)) { rc = -1; } (*dcb->dcb_set_status) (UART_RTSDISABLED); free(fp); UsartResetBuffer(&dcb->dcb_tx_rbf, 0, 0, 0); UsartResetBuffer(&dcb->dcb_rx_rbf, 0, 0, 0); /* Wake-up all threads waiting for incoming data. */ NutEventBroadcast(&dcb->dcb_rx_rbf.rbf_que); NutSelectWakeup(dcb->dcb_rx_rbf.wq_list, WQ_FLAG_READ); NutSelectWakeup(dcb->dcb_tx_rbf.wq_list, WQ_FLAG_WRITE); return rc; } /*! * \brief Open a USART device. * \internal * * 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. * * \todo We may support shared open and use dev_irq as an open counter. */ NUTFILE *UsartOpen(NUTDEVICE * dev, const char *name, int mode, int acc) { USARTDCB *dcb = dev->dev_dcb; NUTFILE *fp; /* * Create the transmit buffer unless this is used for read only. */ if ((mode & 0x0003) != _O_RDONLY) { if (UsartResetBuffer(&dcb->dcb_tx_rbf, USART_TXBUFSIZ, USART_TXLOWMARK, USART_TXHIWMARK)) { return NUTFILE_EOF; } } /* * Create the receive buffer unless this is used for write only. */ if ((mode & 0x0003) != _O_WRONLY) { if (UsartResetBuffer(&dcb->dcb_rx_rbf, USART_RXBUFSIZ, USART_RXLOWMARK, USART_RXHIWMARK)) { free(dcb->dcb_tx_rbf.rbf_start); return NUTFILE_EOF; } } /* * Allocate memory for the file structure. */ fp = malloc(sizeof(*fp)); if (fp == NULL) { free(dcb->dcb_tx_rbf.rbf_start); free(dcb->dcb_rx_rbf.rbf_start); return NUTFILE_EOF; } /* Set proper device modes. */ if ((mode & 0xC000) == _O_BINARY) { dcb->dcb_modeflags &= ~USART_MF_COOKEDMODE; } else { dcb->dcb_modeflags |= USART_MF_COOKEDMODE; } /* * For now we do the initialization here. Later we may implement * a file creation routine to get a linked list of all opened * files in the system. */ fp->nf_dev = dev; fp->nf_fcb = NULL; if ((mode & 0x0003) != _O_WRONLY) { (*dcb->dcb_rx_start) (); } return fp; } /*! * \brief Perform USART control functions. * \internal * * This function is called by the _ioctl() function of the C runtime * library. * * \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: * - \ref UART_SETSPEED * - \ref UART_GETSPEED * - \ref UART_SETDATABITS * - \ref UART_GETDATABITS * - \ref UART_SETPARITY * - \ref UART_GETPARITY * - \ref UART_SETSTOPBITS * - \ref UART_GETSTOPBITS * - \ref UART_SETSTATUS * - \ref UART_GETSTATUS * - \ref UART_SETREADTIMEOUT * - \ref UART_GETREADTIMEOUT * - \ref UART_SETWRITETIMEOUT * - \ref UART_GETWRITETIMEOUT * - \ref UART_SETLOCALECHO * - \ref UART_GETLOCALECHO * - \ref UART_SETFLOWCONTROL * - \ref UART_GETFLOWCONTROL * - \ref UART_SETCOOKEDMODE * - \ref UART_GETCOOKEDMODE * - \ref UART_SETHDPXMODE * - \ref UART_GETHDPXMODE * - \ref UART_SETCLOCKMODE * - \ref UART_GETCLOCKMODE * - \ref UART_SETTXBUFSIZ * - \ref UART_GETTXBUFSIZ * - \ref UART_SETRXBUFSIZ * - \ref UART_GETRXBUFSIZ * - \ref UART_SETTXBUFLWMARK * - \ref UART_GETTXBUFLWMARK * - \ref UART_SETTXBUFHWMARK * - \ref UART_GETTXBUFHWMARK * - \ref UART_SETRXBUFLWMARK * - \ref UART_GETRXBUFLWMARK * - \ref UART_SETRXBUFHWMARK * - \ref UART_GETRXBUFHWMARK * * \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. * * \note Not all control functions may be supported on all platforms. * In any case applications should check the returned result. * * \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. */ int UsartIOCtl(NUTDEVICE * dev, int req, void *conf) { int rc = 0; USARTDCB *dcb; RINGBUF *rbf; uint32_t *lvp = (uint32_t *) conf; uint32_t lv = *lvp; uint8_t bv = (uint8_t) lv; dcb = dev->dev_dcb; switch (req) { case UART_SETSPEED: rc = (*dcb->dcb_set_speed) (lv); break; case UART_GETSPEED: *lvp = (*dcb->dcb_get_speed) (); break; case UART_SETDATABITS: rc = (*dcb->dcb_set_data_bits) (bv); break; case UART_GETDATABITS: *lvp = (*dcb->dcb_get_data_bits) (); break; case UART_SETPARITY: rc = (*dcb->dcb_set_parity) (bv); break; case UART_GETPARITY: *lvp = (*dcb->dcb_get_parity) (); break; case UART_SETSTOPBITS: rc = (*dcb->dcb_set_stop_bits) (bv); break; case UART_GETSTOPBITS: *lvp = (*dcb->dcb_get_stop_bits) (); break; case UART_SETSTATUS: /* * We are not changing the buffer size. Thus, we can safely ignore the * result of UsartResetBuffer(). This way we found a work around for * the AVRGCC 4.1.1 bug, which appeared here previously. */ if (lv & UART_RXBUFFEREMPTY) { rbf = &dcb->dcb_rx_rbf; UsartResetBuffer(rbf, rbf->rbf_siz, rbf->rbf_lwm, rbf->rbf_hwm); (*dcb->dcb_rx_start) (); } if (lv & UART_TXBUFFEREMPTY) { rbf = &dcb->dcb_tx_rbf; UsartResetBuffer(rbf, rbf->rbf_siz, rbf->rbf_lwm, rbf->rbf_hwm); } rc = (*dcb->dcb_set_status) (lv & ~(UART_RXBUFFEREMPTY | UART_TXBUFFEREMPTY)); break; case UART_GETSTATUS: *lvp = (*dcb->dcb_get_status) (); /* * Determine buffer status. */ if (dcb->dcb_rx_rbf.rbf_cnt == 0) { *lvp |= UART_RXBUFFEREMPTY; } if (dcb->dcb_tx_rbf.rbf_cnt == 0) { *lvp |= UART_TXBUFFEREMPTY; } 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: lv = dcb->dcb_modeflags; if (bv) { lv |= USART_MF_LOCALECHO; } else { lv &= ~USART_MF_LOCALECHO; } rc = (dcb->dcb_set_flow_control) (lv); if (rc == 0) { dcb->dcb_modeflags = lv; } break; case UART_GETLOCALECHO: if (dcb->dcb_modeflags & USART_MF_LOCALECHO) { *lvp = 1; } else { *lvp = 0; } break; case UART_SETFLOWCONTROL: lv = dcb->dcb_modeflags; rc = (dcb->dcb_set_flow_control) (lv); if (rc == 0) { dcb->dcb_modeflags = lv; } break; case UART_GETFLOWCONTROL: *lvp = (*dcb->dcb_get_flow_control) (); break; case UART_SETBLOCKREAD: lv = dcb->dcb_modeflags; if (bv) { lv |= USART_MF_BLOCKREAD; } else { lv &= ~USART_MF_BLOCKREAD; } rc = (dcb->dcb_set_flow_control) (lv); if (rc == 0) { dcb->dcb_modeflags = lv; } break; case UART_GETBLOCKREAD: *lvp = (*dcb->dcb_get_flow_control) (); break; case UART_SETBLOCKWRITE: lv = dcb->dcb_modeflags; if (bv) { lv |= USART_MF_BLOCKWRITE; } else { lv &= ~USART_MF_BLOCKWRITE; } rc = (dcb->dcb_set_flow_control) (lv); if (rc == 0) { dcb->dcb_modeflags = lv; } break; case UART_GETBLOCKWRITE: *lvp = (*dcb->dcb_get_flow_control) (); break; case UART_SETCOOKEDMODE: if (bv) { dcb->dcb_modeflags |= USART_MF_COOKEDMODE; } else { dcb->dcb_modeflags &= ~USART_MF_COOKEDMODE; } break; case UART_GETCOOKEDMODE: if (dcb->dcb_modeflags & USART_MF_COOKEDMODE) { *lvp = 1; } else { *lvp = 0; } break; case UART_SETHDPXMODE: lv = dcb->dcb_modeflags; if (bv) { lv |= USART_MF_HALFDUPLEX; } else { lv &= ~USART_MF_HALFDUPLEX; } rc = (dcb->dcb_set_flow_control) (lv); if (rc == 0) { dcb->dcb_modeflags = lv; } break; case UART_GETHDPXMODE: if (dcb->dcb_modeflags & USART_MF_HALFDUPLEX) { *lvp = 1; } else { *lvp = 0; } break; case UART_SETOWIMODE: lv = dcb->dcb_modeflags; if (bv) { lv |= USART_MF_OWIHALFDUPLEX; } else { lv &= ~USART_MF_OWIHALFDUPLEX; } rc = (dcb->dcb_set_flow_control) (lv); if (rc == 0) { dcb->dcb_modeflags = lv; } break; case UART_GETOWIMODE: if (dcb->dcb_modeflags & USART_MF_OWIHALFDUPLEX) { *lvp = 1; } else { *lvp = 0; } break; case UART_SETCLOCKMODE: rc = (*dcb->dcb_set_clock_mode) (lv); break; case UART_GETCLOCKMODE: *lvp = (*dcb->dcb_get_clock_mode) (); break; case UART_SETTXBUFSIZ: rbf = &dcb->dcb_tx_rbf; rc = UsartResetBuffer(rbf, (size_t) lv, rbf->rbf_lwm, rbf->rbf_hwm); if (rc == 0) { (*dcb->dcb_rx_start) (); } break; case UART_GETTXBUFSIZ: *lvp = dcb->dcb_tx_rbf.rbf_siz; break; case UART_SETRXBUFSIZ: rbf = &dcb->dcb_rx_rbf; rc = UsartResetBuffer(rbf, (size_t) lv, rbf->rbf_lwm, rbf->rbf_hwm); break; case UART_GETRXBUFSIZ: *lvp = dcb->dcb_rx_rbf.rbf_siz; break; case UART_SETTXBUFLWMARK: NutEnterCritical(); dcb->dcb_tx_rbf.rbf_lwm = (size_t) lv; NutExitCritical(); break; case UART_GETTXBUFLWMARK: *lvp = dcb->dcb_tx_rbf.rbf_lwm; break; case UART_SETTXBUFHWMARK: NutEnterCritical(); dcb->dcb_tx_rbf.rbf_hwm = (size_t) lv; NutExitCritical(); break; case UART_GETTXBUFHWMARK: *lvp = dcb->dcb_tx_rbf.rbf_hwm; break; case UART_SETRXBUFLWMARK: NutEnterCritical(); dcb->dcb_rx_rbf.rbf_lwm = (size_t) lv; NutExitCritical(); break; case UART_GETRXBUFLWMARK: *lvp = dcb->dcb_rx_rbf.rbf_lwm; break; case UART_SETRXBUFHWMARK: NutEnterCritical(); dcb->dcb_rx_rbf.rbf_hwm = (size_t) lv; NutExitCritical(); break; case UART_GETRXBUFHWMARK: *lvp = dcb->dcb_rx_rbf.rbf_hwm; break; default: rc = -1; break; } return rc; } /*! * \brief Retrieves the number of characters in input buffer. * \internal * * This function allows to query the number of bytes in the input buffer * by using standard C function for querying the size of an open file. * * This function is called by the low level _filelength() routine of * the C runtime library, using the _NUTDEVICE::dev_size entry. * * \note This is a Nut/OS specific extension and will most probably not * work on other systems. * * \param fp Pointer to a \ref _NUTFILE structure, obtained by a * previous call to UsartOpen(). * * \return The number of bytes currently stored in input buffer. */ long UsartSize (NUTFILE *fp) { long avail; NUTDEVICE *dev; USARTDCB *dcb; RINGBUF *rbf; NUTASSERT(fp != NULL && fp != NUTFILE_EOF); dev = fp->nf_dev; dcb = dev->dev_dcb; rbf = &dcb->dcb_rx_rbf; /* Atomic access to the ring buffer counter. */ NutEnterCritical(); avail = rbf->rbf_cnt; NutExitCritical(); return avail; } #ifndef CRT_DISABLE_SELECT_POLL /*! * \brief Callback function called by select routine to check, if read * or write would block * \internal * * This function is called by select_scan routine of the C runtime library * as part of the select() implementation. If select if currently waiting * on a waitqueue, this function will be called to check the current state * as soon as the select waitqueues got signalled by the driver * * \param fp Pointer to a \ref _NUTFILE structure, obtained by a * previous call to UsartOpen(). * \param flags Flags representing what we are waiting for (read / write / exception) * \param wq Waitqueue, which should be added to the drivers waitqueue list. The * thread that called select is waiting on this waitqueue * \param cmd Internal command, that is passed to NutSelectManageWq * * \return Flags representing the current filedescriptor state */ int UsartSelect (NUTFILE *fp, int flags, HANDLE *wq, select_cmd_t cmd) { NUTDEVICE *dev; USARTDCB *dcb; RINGBUF *rx_rbf; RINGBUF *tx_rbf; int rflags = 0; dev = fp->nf_dev; dcb = dev->dev_dcb; rx_rbf = &dcb->dcb_rx_rbf; tx_rbf = &dcb->dcb_tx_rbf; /* Manage the wait queue lists for the select call */ NutSelectManageWq(&rx_rbf->wq_list, wq, flags & WQ_FLAG_READ, cmd); NutSelectManageWq(&tx_rbf->wq_list, wq, flags & WQ_FLAG_WRITE, cmd); /* receive / transmit interrupt can change the buffer counts, so these checks will be done atomic. */ NutEnterCritical(); if (rx_rbf->rbf_cnt > 0) { rflags |= WQ_FLAG_READ; } if (tx_rbf->rbf_cnt < tx_rbf->rbf_siz) { rflags |= WQ_FLAG_WRITE; } NutExitCritical(); rflags &= flags; return rflags; } #endif /*@}*/