| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078 |
- /*
- * 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 <cfg/crt.h>
- #include <compiler.h>
- #include <stdlib.h>
- #include <string.h>
- #include <memdebug.h>
- #include <sys/atom.h>
- #include <sys/heap.h>
- #include <sys/event.h>
- #include <sys/timer.h>
- #include <sys/nutdebug.h>
- #include <sys/select.h>
- #include <dev/irqreg.h>
- #include <dev/usart.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 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
- /*@}*/
|