usart.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2003 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  */
00032 
00033 /*
00034  * $Log: usart.c,v $
00035  * Revision 1.9  2008/08/11 06:59:42  haraldkipp
00036  * BSD types replaced by stdint types (feature request #1282721).
00037  *
00038  * Revision 1.8  2007/03/17 14:33:21  haraldkipp
00039  * Workaround for AVRGCC 4.1.1 bug, which failed to compile UsartIOCtl().
00040  *
00041  * Revision 1.7  2006/10/05 17:20:54  haraldkipp
00042  * Added a comment to warn the user about ioctl() functions, that may not be
00043  * supported.
00044  *
00045  * Revision 1.6  2006/08/23 09:20:47  freckle
00046  * fix bug #1541139
00047  *
00048  * Revision 1.5  2004/10/14 16:43:06  drsung
00049  * Fixed compiler warning "comparison between signed and unsigned"
00050  *
00051  * Revision 1.4  2004/05/24 20:17:15  drsung
00052  * Added function UsartSize to return number of chars in input buffer.
00053  *
00054  * Revision 1.3  2004/05/20 09:05:07  drsung
00055  * Memory was allocated twice for NUTFILE in UsartOpen.
00056  *
00057  * Revision 1.2  2004/03/18 13:59:14  haraldkipp
00058  * Comment updated
00059  *
00060  * Revision 1.1  2003/12/15 19:25:33  haraldkipp
00061  * New USART driver added
00062  *
00063  */
00064 
00065 #include <compiler.h>
00066 #include <stdlib.h>
00067 #include <string.h>
00068 
00069 #include <sys/atom.h>
00070 #include <sys/heap.h>
00071 #include <sys/event.h>
00072 #include <sys/timer.h>
00073 
00074 #include <dev/irqreg.h>
00075 #include <dev/usart.h>
00076 
00077 #include <fcntl.h>
00078 
00079 /*
00080  * Not nice because stdio already defined them. But in order to save memory,
00081  * we do the whole buffering and including stdio here would be more weird.
00082  */
00083 #ifndef _IOFBF
00084 #define _IOFBF  0x00
00085 #define _IOLBF  0x01
00086 #define _IONBF  0x02
00087 #endif
00088 
00093 
00107 int UsartInit(NUTDEVICE * dev)
00108 {
00109     int rc;
00110     USARTDCB *dcb = dev->dev_dcb;
00111 
00112     /* Initialize the low level hardware driver. */
00113     if ((rc = (*dcb->dcb_init) ()) == 0) {
00114         /* Ignore errors on initial configuration. */
00115         (*dcb->dcb_set_speed) (USART_INITSPEED);
00116     }
00117     return rc;
00118 }
00119 
00130 static int UsartResetBuffer(RINGBUF * rbf, size_t size, size_t lowm, size_t hiwm)
00131 {
00132     uint8_t *xbp = rbf->rbf_start;
00133     size_t xsz = rbf->rbf_siz;
00134 
00135     /* Disable further buffer usage. */
00136     NutEnterCritical();
00137     rbf->rbf_siz = 0;
00138     NutExitCritical();
00139 
00140     /* Resize the buffer, if required. */
00141     if (xsz != size) {
00142         if (xsz && xbp) {
00143             free(xbp);
00144         }
00145         if (size && (xbp = malloc(size)) == 0) {
00146             return -1;
00147         }
00148     }
00149 
00150     /* Update ring buffer status. */
00151     if (size) {
00152         rbf->rbf_start = xbp;
00153         rbf->rbf_head = xbp;
00154         rbf->rbf_tail = xbp;
00155         rbf->rbf_last = xbp + size;
00156         rbf->rbf_lwm = lowm;
00157         rbf->rbf_hwm = hiwm;
00158         rbf->rbf_cnt = 0;
00159 
00160         /* Re-enable buffer usage. */
00161         NutEnterCritical();
00162         rbf->rbf_siz = size;
00163         NutExitCritical();
00164     }
00165     return 0;
00166 }
00167 
00194 int UsartRead(NUTFILE * fp, void *buffer, int size)
00195 {
00196     size_t rc;
00197     size_t avail;
00198     size_t taken = 0;
00199     uint8_t ch;
00200     uint8_t *cp = buffer;
00201     NUTDEVICE *dev = fp->nf_dev;
00202     USARTDCB *dcb = dev->dev_dcb;
00203     RINGBUF *rbf = &dcb->dcb_rx_rbf;
00204 
00205     /*
00206      * No buffer allocated, this device is read only.
00207      */
00208     if (rbf->rbf_siz == 0) {
00209         return -1;
00210     }
00211 
00212     /*
00213      * Call without data pointer discards receive buffer.
00214      */
00215     if (buffer == 0) {
00216         UsartResetBuffer(rbf, rbf->rbf_siz, rbf->rbf_lwm, rbf->rbf_hwm);
00217         (*dcb->dcb_rx_start) ();
00218         return 0;
00219     }
00220 
00221     /*
00222      * Wait until at least one character is buffered or until a read
00223      * timeout occured.
00224      */
00225     for (;;) {
00226         /* Atomic access to the ring buffer counter. */
00227         NutEnterCritical();
00228         avail = rbf->rbf_cnt;
00229         NutExitCritical();
00230         if (avail) {
00231             break;
00232         }
00233         /* 
00234          * This will enable RTS hardware handshake or re-enable the 
00235          * remote transmitter by sending a XON character.
00236          */
00237         (*dcb->dcb_rx_start) ();
00238         if (NutEventWait(&rbf->rbf_que, dcb->dcb_rtimeout)) {
00239             return 0;
00240         }
00241     }
00242 
00243     /*
00244      * Get cooked characters from receive buffer.
00245      */
00246     if (dcb->dcb_modeflags & USART_MF_COOKEDMODE) {
00247         for (rc = 0; rc < (size_t) size;) {
00248             if (taken >= avail) {
00249                 break;
00250             }
00251             ch = *rbf->rbf_tail++;
00252             if (rbf->rbf_tail == rbf->rbf_last) {
00253                 rbf->rbf_tail = rbf->rbf_start;
00254             }
00255             taken++;
00256             if (ch == '\r' || ch == '\n') {
00257                 if (dcb->dcb_last_eol == 0 || dcb->dcb_last_eol == ch) {
00258                     dcb->dcb_last_eol = ch;
00259                     *cp++ = '\n';
00260                     rc++;
00261                 }
00262             } else {
00263                 dcb->dcb_last_eol = 0;
00264                 *cp++ = ch;
00265                 rc++;
00266             }
00267         }
00268     }
00269 
00270     /*
00271      * Get raw characters from receive buffer.
00272      */
00273     else {
00274         if ((rc = size) > avail)
00275             rc = avail;
00276         for (taken = 0; taken < rc; taken++) {
00277             *cp++ = *rbf->rbf_tail++;
00278             if (rbf->rbf_tail == rbf->rbf_last) {
00279                 rbf->rbf_tail = rbf->rbf_start;
00280             }
00281         }
00282     }
00283 
00284     if (taken) {
00285         NutEnterCritical();
00286         rbf->rbf_cnt -= taken;
00287         NutExitCritical();
00288         if (rbf->rbf_cnt < rbf->rbf_lwm) {
00289             (*dcb->dcb_rx_start) ();
00290         }
00291     }
00292     return (int) rc;
00293 }
00294 
00313 static size_t UsartFlushOutput(USARTDCB *dcb, size_t added, size_t left)
00314 {
00315     size_t rc;
00316     RINGBUF *rbf = &dcb->dcb_tx_rbf;
00317 
00318     /* 
00319      * Add the new characters to the buffer count.
00320      */
00321     NutEnterCritical();
00322     rbf->rbf_cnt += added;
00323     rc = rbf->rbf_cnt;
00324     NutExitCritical();
00325 
00326     while (rc > left) {
00327         /* Start transmitter and wait for the next event. */
00328         (*dcb->dcb_tx_start) ();
00329         if (NutEventWait(&rbf->rbf_que, dcb->dcb_wtimeout)) {
00330             break;
00331         }
00332         /* Refresh the count. */
00333         NutEnterCritical();
00334         rc = rbf->rbf_cnt;
00335         NutExitCritical();
00336     };
00337     return rc;
00338 }
00339 
00352 static int UsartPut(NUTDEVICE * dev, CONST void *buffer, int len, int pflg)
00353 {
00354     int rc;
00355     CONST uint8_t *cp;
00356     uint8_t lbmode;
00357     ureg_t cooked;
00358     uint8_t ch;
00359     size_t cnt;
00360     size_t added;
00361     USARTDCB *dcb = dev->dev_dcb;
00362     RINGBUF *rbf = &dcb->dcb_tx_rbf;
00363 
00364     /*
00365      * No output ring buffer allocated, this device is read only.
00366      */
00367     if (rbf->rbf_siz == 0) {
00368         return -1;
00369     }
00370 
00371     /*
00372      * Call without data pointer flushes the buffer. In this case a return
00373      * value not equal zero indicates write timeout.
00374      */
00375     if (buffer == 0) {
00376         return UsartFlushOutput(dcb, 0, 0);
00377     }
00378 
00379     if (dcb->dcb_modeflags & USART_MF_LINEBUFFER)
00380         lbmode = 1;
00381     else
00382         lbmode = 0;
00383 
00384     if (dcb->dcb_modeflags & USART_MF_COOKEDMODE)
00385         cooked = 1;
00386     else
00387         cooked = 0;
00388 
00389     /*
00390      * Get the number of buffered bytes. The transmit interrupt will modify
00391      * this value, so make the query atomic.
00392      */
00393     NutEnterCritical();
00394     cnt = rbf->rbf_cnt;
00395     NutExitCritical();
00396 
00397     /*
00398      * Move bytes to the transmit buffer.
00399      */
00400     cp = buffer;
00401     added = 0;
00402     for (rc = 0; rc < len;) {
00403         /*
00404          * If we reached the high watermark, then kick the hardware driver 
00405          * to start transmission and wait until the low watermark is reached.
00406          */
00407         if (cnt + added >= rbf->rbf_hwm) {
00408             cnt = UsartFlushOutput(dcb, added, rbf->rbf_lwm);
00409             added = 0;
00410             /* If still above the mark, then a timeout occured. */
00411             if(cnt > rbf->rbf_lwm) {
00412                 break;
00413             }
00414         }
00415 
00416         /*
00417          * Get the next data byte. If the pflg parameter is set, then data
00418          * is located in program space.
00419          */
00420         ch = pflg ? PRG_RDB(cp) : *cp;
00421 
00422         /*
00423          * In cooked mode we prepend a carriage return to any linefeed
00424          * character.
00425          */
00426         if (cooked == 1 && ch == '\n') {
00427             cooked = 2;
00428             ch = '\r';
00429             if (lbmode == 1)
00430                 lbmode = 2;
00431         } else {
00432             if (cooked == 2)
00433                 cooked = 1;
00434             cp++;
00435             rc++;
00436         }
00437         *rbf->rbf_head++ = ch;
00438         if (rbf->rbf_head == rbf->rbf_last) {
00439             rbf->rbf_head = rbf->rbf_start;
00440         }
00441         added++;
00442     }
00443 
00444     if (added) {
00445         NutEnterCritical();
00446         rbf->rbf_cnt += added;
00447         NutExitCritical();
00448         (*dcb->dcb_tx_start) ();
00449     }
00450 
00451     return rc;
00452 }
00453 
00473 int UsartWrite(NUTFILE * fp, CONST void *buffer, int len)
00474 {
00475     return UsartPut(fp->nf_dev, buffer, len, 0);
00476 }
00477 
00499 int UsartWrite_P(NUTFILE * fp, PGM_P buffer, int len)
00500 {
00501     return UsartPut(fp->nf_dev, (CONST char *) buffer, len, 1);
00502 }
00503 
00517 int UsartClose(NUTFILE * fp)
00518 {
00519     NUTDEVICE *dev = fp->nf_dev;
00520     USARTDCB *dcb = dev->dev_dcb;
00521 
00522     if (fp == 0 || fp == NUTFILE_EOF)
00523         return -1;
00524 
00525     free(fp);
00526     UsartResetBuffer(&dcb->dcb_tx_rbf, 0, 0, 0);
00527     UsartResetBuffer(&dcb->dcb_rx_rbf, 0, 0, 0);
00528 
00529     return 0;
00530 }
00531 
00550 NUTFILE *UsartOpen(NUTDEVICE * dev, CONST char *name, int mode, int acc)
00551 {
00552     USARTDCB *dcb = dev->dev_dcb;
00553     NUTFILE *fp;
00554 
00555     /*
00556      * Create the tranmit buffer unless this is used for read only.
00557      */
00558     if ((mode & 0x0003) != _O_RDONLY) {
00559         if (UsartResetBuffer(&dcb->dcb_tx_rbf, USART_TXBUFSIZ, USART_TXLOWMARK, USART_TXHIWMARK)) {
00560             return NUTFILE_EOF;
00561         }
00562     }
00563 
00564     /*
00565      * Create the receive buffer unless this is used for write only.
00566      */
00567     if ((mode & 0x0003) != _O_WRONLY) {
00568         if (UsartResetBuffer(&dcb->dcb_rx_rbf, USART_RXBUFSIZ, USART_RXLOWMARK, USART_RXHIWMARK)) {
00569             free(dcb->dcb_tx_rbf.rbf_start);
00570             return NUTFILE_EOF;
00571         }
00572     }
00573 
00574     /*
00575      * Allocate memory for the file structure.
00576      */
00577     if ((fp = malloc(sizeof(NUTFILE))) == 0) {
00578         free(dcb->dcb_tx_rbf.rbf_start);
00579         free(dcb->dcb_rx_rbf.rbf_start);
00580         return NUTFILE_EOF;
00581     }
00582 
00583     /* Set proper device modes. */
00584     if ((mode & 0xC000) == _O_BINARY) {
00585         dcb->dcb_modeflags &= ~USART_MF_COOKEDMODE;
00586     } else {
00587         dcb->dcb_modeflags |= USART_MF_COOKEDMODE;
00588     }
00589 
00590     /* 
00591      * For now we do the initialization here. Later we may implement
00592      * a file creation routine to get a linked list of all opened
00593      * files in the system.
00594      */
00595     fp->nf_next = 0;
00596     fp->nf_dev = dev;
00597     fp->nf_fcb = 0;
00598 
00599     if ((mode & 0x0003) != _O_WRONLY) {
00600         (*dcb->dcb_rx_start) ();
00601     }
00602 
00603     return fp;
00604 }
00605 
00665 int UsartIOCtl(NUTDEVICE * dev, int req, void *conf)
00666 {
00667     int rc = 0;
00668     USARTDCB *dcb;
00669     RINGBUF *rbf;
00670     uint32_t *lvp = (uint32_t *) conf;
00671     uint32_t lv = *lvp;
00672     uint8_t bv = (uint8_t) lv;
00673 
00674     dcb = dev->dev_dcb;
00675 
00676     switch (req) {
00677     case UART_SETSPEED:
00678         rc = (*dcb->dcb_set_speed) (lv);
00679         break;
00680     case UART_GETSPEED:
00681         *lvp = (*dcb->dcb_get_speed) ();
00682         break;
00683 
00684     case UART_SETDATABITS:
00685         rc = (*dcb->dcb_set_data_bits) (bv);
00686         break;
00687     case UART_GETDATABITS:
00688         *lvp = (*dcb->dcb_get_data_bits) ();
00689         break;
00690 
00691     case UART_SETPARITY:
00692         rc = (*dcb->dcb_set_parity) (bv);
00693         break;
00694     case UART_GETPARITY:
00695         *lvp = (*dcb->dcb_get_parity) ();
00696         break;
00697 
00698     case UART_SETSTOPBITS:
00699         rc = (*dcb->dcb_set_stop_bits) (bv);
00700         break;
00701     case UART_GETSTOPBITS:
00702         *lvp = (*dcb->dcb_get_stop_bits) ();
00703         break;
00704 
00705     case UART_SETSTATUS:
00706         /*
00707          * We are not changing the buffer size. Thus, we can safely ignore the
00708          * result of UsartResetBuffer(). This way we found a work around for
00709          * the AVRGCC 4.1.1 bug, which appeared here previously.
00710          */
00711         if (lv & UART_RXBUFFEREMPTY) {
00712             rbf = &dcb->dcb_rx_rbf;
00713             UsartResetBuffer(rbf, rbf->rbf_siz, rbf->rbf_lwm, rbf->rbf_hwm);
00714             (*dcb->dcb_rx_start) ();
00715         }
00716         if (lv & UART_TXBUFFEREMPTY) {
00717             rbf = &dcb->dcb_tx_rbf;
00718             UsartResetBuffer(rbf, rbf->rbf_siz, rbf->rbf_lwm, rbf->rbf_hwm);
00719         }
00720         rc = (*dcb->dcb_set_status) (lv & ~(UART_RXBUFFEREMPTY | UART_TXBUFFEREMPTY));
00721         break;
00722     case UART_GETSTATUS:
00723         *lvp = (*dcb->dcb_get_status) ();
00724         /*
00725          * Determine buffer status.
00726          */
00727         if (dcb->dcb_rx_rbf.rbf_cnt == 0) {
00728             *lvp |= UART_RXBUFFEREMPTY;
00729         }
00730         if (dcb->dcb_tx_rbf.rbf_cnt == 0) {
00731             *lvp |= UART_TXBUFFEREMPTY;
00732         }
00733         break;
00734 
00735     case UART_SETREADTIMEOUT:
00736         dcb->dcb_rtimeout = lv;
00737         break;
00738     case UART_GETREADTIMEOUT:
00739         *lvp = dcb->dcb_rtimeout;
00740         break;
00741 
00742     case UART_SETWRITETIMEOUT:
00743         dcb->dcb_wtimeout = lv;
00744         break;
00745     case UART_GETWRITETIMEOUT:
00746         *lvp = dcb->dcb_wtimeout;
00747         break;
00748 
00749     case UART_SETLOCALECHO:
00750         if (bv)
00751             dcb->dcb_modeflags |= USART_MF_LOCALECHO;
00752         else
00753             dcb->dcb_modeflags &= ~USART_MF_LOCALECHO;
00754         break;
00755     case UART_GETLOCALECHO:
00756         if (dcb->dcb_modeflags & USART_MF_LOCALECHO)
00757             *lvp = 1;
00758         else
00759             *lvp = 0;
00760         break;
00761 
00762     case UART_SETFLOWCONTROL:
00763         rc = (*dcb->dcb_set_flow_control) (lv);
00764         break;
00765     case UART_GETFLOWCONTROL:
00766         *lvp = (*dcb->dcb_get_flow_control) ();
00767         break;
00768 
00769     case UART_SETCOOKEDMODE:
00770         if (bv)
00771             dcb->dcb_modeflags |= USART_MF_COOKEDMODE;
00772         else
00773             dcb->dcb_modeflags &= ~USART_MF_COOKEDMODE;
00774         break;
00775     case UART_GETCOOKEDMODE:
00776         if (dcb->dcb_modeflags & USART_MF_COOKEDMODE)
00777             *lvp = 1;
00778         else
00779             *lvp = 0;
00780         break;
00781 
00782     case UART_SETCLOCKMODE:
00783         rc = (*dcb->dcb_set_clock_mode) (lv);
00784         break;
00785     case UART_GETCLOCKMODE:
00786         *lvp = (*dcb->dcb_get_clock_mode) ();
00787         break;
00788 
00789     case UART_SETTXBUFSIZ:
00790         rbf = &dcb->dcb_tx_rbf;
00791         rc = UsartResetBuffer(rbf, (size_t) lv, rbf->rbf_lwm, rbf->rbf_hwm);
00792         if (rc == 0) {
00793             (*dcb->dcb_rx_start) ();
00794         }
00795         break;
00796     case UART_GETTXBUFSIZ:
00797         *lvp = dcb->dcb_tx_rbf.rbf_siz;
00798         break;
00799 
00800     case UART_SETRXBUFSIZ:
00801         rbf = &dcb->dcb_rx_rbf;
00802         rc = UsartResetBuffer(rbf, (size_t) lv, rbf->rbf_lwm, rbf->rbf_hwm);
00803         break;
00804     case UART_GETRXBUFSIZ:
00805         *lvp = dcb->dcb_rx_rbf.rbf_siz;
00806         break;
00807 
00808     case UART_SETTXBUFLWMARK:
00809         NutEnterCritical();
00810         dcb->dcb_tx_rbf.rbf_lwm = (size_t) lv;
00811         NutExitCritical();
00812         break;
00813     case UART_GETTXBUFLWMARK:
00814         *lvp = dcb->dcb_tx_rbf.rbf_lwm;
00815         break;
00816 
00817     case UART_SETTXBUFHWMARK:
00818         NutEnterCritical();
00819         dcb->dcb_tx_rbf.rbf_hwm = (size_t) lv;
00820         NutExitCritical();
00821         break;
00822     case UART_GETTXBUFHWMARK:
00823         *lvp = dcb->dcb_tx_rbf.rbf_hwm;
00824         break;
00825 
00826     case UART_SETRXBUFLWMARK:
00827         NutEnterCritical();
00828         dcb->dcb_rx_rbf.rbf_lwm = (size_t) lv;
00829         NutExitCritical();
00830         break;
00831     case UART_GETRXBUFLWMARK:
00832         *lvp = dcb->dcb_rx_rbf.rbf_lwm;
00833         break;
00834 
00835     case UART_SETRXBUFHWMARK:
00836         NutEnterCritical();
00837         dcb->dcb_rx_rbf.rbf_hwm = (size_t) lv;
00838         NutExitCritical();
00839         break;
00840     case UART_GETRXBUFHWMARK:
00841         *lvp = dcb->dcb_rx_rbf.rbf_hwm;
00842         break;
00843 
00844     default:
00845         rc = -1;
00846         break;
00847     }
00848     return rc;
00849 }
00850 
00862 long UsartSize (NUTFILE *fp)
00863 {
00864     long avail;
00865     NUTDEVICE *dev = fp->nf_dev;
00866     USARTDCB *dcb = dev->dev_dcb;
00867     RINGBUF *rbf = &dcb->dcb_rx_rbf;
00868 
00869     /* Atomic access to the ring buffer counter. */
00870     NutEnterCritical();
00871     avail = rbf->rbf_cnt;
00872     NutExitCritical();
00873 
00874     return avail;
00875 }
00876 
00877 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/