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.8  2007/03/17 14:33:21  haraldkipp
00036  * Workaround for AVRGCC 4.1.1 bug, which failed to compile UsartIOCtl().
00037  *
00038  * Revision 1.7  2006/10/05 17:20:54  haraldkipp
00039  * Added a comment to warn the user about ioctl() functions, that may not be
00040  * supported.
00041  *
00042  * Revision 1.6  2006/08/23 09:20:47  freckle
00043  * fix bug #1541139
00044  *
00045  * Revision 1.5  2004/10/14 16:43:06  drsung
00046  * Fixed compiler warning "comparison between signed and unsigned"
00047  *
00048  * Revision 1.4  2004/05/24 20:17:15  drsung
00049  * Added function UsartSize to return number of chars in input buffer.
00050  *
00051  * Revision 1.3  2004/05/20 09:05:07  drsung
00052  * Memory was allocated twice for NUTFILE in UsartOpen.
00053  *
00054  * Revision 1.2  2004/03/18 13:59:14  haraldkipp
00055  * Comment updated
00056  *
00057  * Revision 1.1  2003/12/15 19:25:33  haraldkipp
00058  * New USART driver added
00059  *
00060  */
00061 
00062 #include <compiler.h>
00063 #include <stdlib.h>
00064 #include <string.h>
00065 
00066 #include <sys/atom.h>
00067 #include <sys/heap.h>
00068 #include <sys/event.h>
00069 #include <sys/timer.h>
00070 
00071 #include <dev/irqreg.h>
00072 #include <dev/usart.h>
00073 
00074 #include <fcntl.h>
00075 
00076 /*
00077  * Not nice because stdio already defined them. But in order to save memory,
00078  * we do the whole buffering and including stdio here would be more weird.
00079  */
00080 #ifndef _IOFBF
00081 #define _IOFBF  0x00
00082 #define _IOLBF  0x01
00083 #define _IONBF  0x02
00084 #endif
00085 
00090 
00104 int UsartInit(NUTDEVICE * dev)
00105 {
00106     int rc;
00107     USARTDCB *dcb = dev->dev_dcb;
00108 
00109     /* Initialize the low level hardware driver. */
00110     if ((rc = (*dcb->dcb_init) ()) == 0) {
00111         /* Ignore errors on initial configuration. */
00112         (*dcb->dcb_set_speed) (USART_INITSPEED);
00113     }
00114     return rc;
00115 }
00116 
00127 static int UsartResetBuffer(RINGBUF * rbf, size_t size, size_t lowm, size_t hiwm)
00128 {
00129     u_char *xbp = rbf->rbf_start;
00130     size_t xsz = rbf->rbf_siz;
00131 
00132     /* Disable further buffer usage. */
00133     NutEnterCritical();
00134     rbf->rbf_siz = 0;
00135     NutExitCritical();
00136 
00137     /* Resize the buffer, if required. */
00138     if (xsz != size) {
00139         if (xsz && xbp) {
00140             free(xbp);
00141         }
00142         if (size && (xbp = malloc(size)) == 0) {
00143             return -1;
00144         }
00145     }
00146 
00147     /* Update ring buffer status. */
00148     if (size) {
00149         rbf->rbf_start = xbp;
00150         rbf->rbf_head = xbp;
00151         rbf->rbf_tail = xbp;
00152         rbf->rbf_last = xbp + size;
00153         rbf->rbf_lwm = lowm;
00154         rbf->rbf_hwm = hiwm;
00155         rbf->rbf_cnt = 0;
00156 
00157         /* Re-enable buffer usage. */
00158         NutEnterCritical();
00159         rbf->rbf_siz = size;
00160         NutExitCritical();
00161     }
00162     return 0;
00163 }
00164 
00191 int UsartRead(NUTFILE * fp, void *buffer, int size)
00192 {
00193     size_t rc;
00194     size_t avail;
00195     size_t taken = 0;
00196     u_char ch;
00197     u_char *cp = buffer;
00198     NUTDEVICE *dev = fp->nf_dev;
00199     USARTDCB *dcb = dev->dev_dcb;
00200     RINGBUF *rbf = &dcb->dcb_rx_rbf;
00201 
00202     /*
00203      * No buffer allocated, this device is read only.
00204      */
00205     if (rbf->rbf_siz == 0) {
00206         return -1;
00207     }
00208 
00209     /*
00210      * Call without data pointer discards receive buffer.
00211      */
00212     if (buffer == 0) {
00213         UsartResetBuffer(rbf, rbf->rbf_siz, rbf->rbf_lwm, rbf->rbf_hwm);
00214         (*dcb->dcb_rx_start) ();
00215         return 0;
00216     }
00217 
00218     /*
00219      * Wait until at least one character is buffered or until a read
00220      * timeout occured.
00221      */
00222     for (;;) {
00223         /* Atomic access to the ring buffer counter. */
00224         NutEnterCritical();
00225         avail = rbf->rbf_cnt;
00226         NutExitCritical();
00227         if (avail) {
00228             break;
00229         }
00230         /* 
00231          * This will enable RTS hardware handshake or re-enable the 
00232          * remote transmitter by sending a XON character.
00233          */
00234         (*dcb->dcb_rx_start) ();
00235         if (NutEventWait(&rbf->rbf_que, dcb->dcb_rtimeout)) {
00236             return 0;
00237         }
00238     }
00239 
00240     /*
00241      * Get cooked characters from receive buffer.
00242      */
00243     if (dcb->dcb_modeflags & USART_MF_COOKEDMODE) {
00244         for (rc = 0; rc < (size_t) size;) {
00245             if (taken >= avail) {
00246                 break;
00247             }
00248             ch = *rbf->rbf_tail++;
00249             if (rbf->rbf_tail == rbf->rbf_last) {
00250                 rbf->rbf_tail = rbf->rbf_start;
00251             }
00252             taken++;
00253             if (ch == '\r' || ch == '\n') {
00254                 if (dcb->dcb_last_eol == 0 || dcb->dcb_last_eol == ch) {
00255                     dcb->dcb_last_eol = ch;
00256                     *cp++ = '\n';
00257                     rc++;
00258                 }
00259             } else {
00260                 dcb->dcb_last_eol = 0;
00261                 *cp++ = ch;
00262                 rc++;
00263             }
00264         }
00265     }
00266 
00267     /*
00268      * Get raw characters from receive buffer.
00269      */
00270     else {
00271         if ((rc = size) > avail)
00272             rc = avail;
00273         for (taken = 0; taken < rc; taken++) {
00274             *cp++ = *rbf->rbf_tail++;
00275             if (rbf->rbf_tail == rbf->rbf_last) {
00276                 rbf->rbf_tail = rbf->rbf_start;
00277             }
00278         }
00279     }
00280 
00281     if (taken) {
00282         NutEnterCritical();
00283         rbf->rbf_cnt -= taken;
00284         NutExitCritical();
00285         if (rbf->rbf_cnt < rbf->rbf_lwm) {
00286             (*dcb->dcb_rx_start) ();
00287         }
00288     }
00289     return (int) rc;
00290 }
00291 
00310 static size_t UsartFlushOutput(USARTDCB *dcb, size_t added, size_t left)
00311 {
00312     size_t rc;
00313     RINGBUF *rbf = &dcb->dcb_tx_rbf;
00314 
00315     /* 
00316      * Add the new characters to the buffer count.
00317      */
00318     NutEnterCritical();
00319     rbf->rbf_cnt += added;
00320     rc = rbf->rbf_cnt;
00321     NutExitCritical();
00322 
00323     while (rc > left) {
00324         /* Start transmitter and wait for the next event. */
00325         (*dcb->dcb_tx_start) ();
00326         if (NutEventWait(&rbf->rbf_que, dcb->dcb_wtimeout)) {
00327             break;
00328         }
00329         /* Refresh the count. */
00330         NutEnterCritical();
00331         rc = rbf->rbf_cnt;
00332         NutExitCritical();
00333     };
00334     return rc;
00335 }
00336 
00349 static int UsartPut(NUTDEVICE * dev, CONST void *buffer, int len, int pflg)
00350 {
00351     int rc;
00352     CONST u_char *cp;
00353     u_char lbmode;
00354     ureg_t cooked;
00355     u_char ch;
00356     size_t cnt;
00357     size_t added;
00358     USARTDCB *dcb = dev->dev_dcb;
00359     RINGBUF *rbf = &dcb->dcb_tx_rbf;
00360 
00361     /*
00362      * No output ring buffer allocated, this device is read only.
00363      */
00364     if (rbf->rbf_siz == 0) {
00365         return -1;
00366     }
00367 
00368     /*
00369      * Call without data pointer flushes the buffer. In this case a return
00370      * value not equal zero indicates write timeout.
00371      */
00372     if (buffer == 0) {
00373         return UsartFlushOutput(dcb, 0, 0);
00374     }
00375 
00376     if (dcb->dcb_modeflags & USART_MF_LINEBUFFER)
00377         lbmode = 1;
00378     else
00379         lbmode = 0;
00380 
00381     if (dcb->dcb_modeflags & USART_MF_COOKEDMODE)
00382         cooked = 1;
00383     else
00384         cooked = 0;
00385 
00386     /*
00387      * Get the number of buffered bytes. The transmit interrupt will modify
00388      * this value, so make the query atomic.
00389      */
00390     NutEnterCritical();
00391     cnt = rbf->rbf_cnt;
00392     NutExitCritical();
00393 
00394     /*
00395      * Move bytes to the transmit buffer.
00396      */
00397     cp = buffer;
00398     added = 0;
00399     for (rc = 0; rc < len;) {
00400         /*
00401          * If we reached the high watermark, then kick the hardware driver 
00402          * to start transmission and wait until the low watermark is reached.
00403          */
00404         if (cnt + added >= rbf->rbf_hwm) {
00405             cnt = UsartFlushOutput(dcb, added, rbf->rbf_lwm);
00406             added = 0;
00407             /* If still above the mark, then a timeout occured. */
00408             if(cnt > rbf->rbf_lwm) {
00409                 break;
00410             }
00411         }
00412 
00413         /*
00414          * Get the next data byte. If the pflg parameter is set, then data
00415          * is located in program space.
00416          */
00417         ch = pflg ? PRG_RDB(cp) : *cp;
00418 
00419         /*
00420          * In cooked mode we prepend a carriage return to any linefeed
00421          * character.
00422          */
00423         if (cooked == 1 && ch == '\n') {
00424             cooked = 2;
00425             ch = '\r';
00426             if (lbmode == 1)
00427                 lbmode = 2;
00428         } else {
00429             if (cooked == 2)
00430                 cooked = 1;
00431             cp++;
00432             rc++;
00433         }
00434         *rbf->rbf_head++ = ch;
00435         if (rbf->rbf_head == rbf->rbf_last) {
00436             rbf->rbf_head = rbf->rbf_start;
00437         }
00438         added++;
00439     }
00440 
00441     if (added) {
00442         NutEnterCritical();
00443         rbf->rbf_cnt += added;
00444         NutExitCritical();
00445         (*dcb->dcb_tx_start) ();
00446     }
00447 
00448     return rc;
00449 }
00450 
00470 int UsartWrite(NUTFILE * fp, CONST void *buffer, int len)
00471 {
00472     return UsartPut(fp->nf_dev, buffer, len, 0);
00473 }
00474 
00496 int UsartWrite_P(NUTFILE * fp, PGM_P buffer, int len)
00497 {
00498     return UsartPut(fp->nf_dev, (CONST char *) buffer, len, 1);
00499 }
00500 
00514 int UsartClose(NUTFILE * fp)
00515 {
00516     NUTDEVICE *dev = fp->nf_dev;
00517     USARTDCB *dcb = dev->dev_dcb;
00518 
00519     if (fp == 0 || fp == NUTFILE_EOF)
00520         return -1;
00521 
00522     free(fp);
00523     UsartResetBuffer(&dcb->dcb_tx_rbf, 0, 0, 0);
00524     UsartResetBuffer(&dcb->dcb_rx_rbf, 0, 0, 0);
00525 
00526     return 0;
00527 }
00528 
00547 NUTFILE *UsartOpen(NUTDEVICE * dev, CONST char *name, int mode, int acc)
00548 {
00549     USARTDCB *dcb = dev->dev_dcb;
00550     NUTFILE *fp;
00551 
00552     /*
00553      * Create the tranmit buffer unless this is used for read only.
00554      */
00555     if ((mode & 0x0003) != _O_RDONLY) {
00556         if (UsartResetBuffer(&dcb->dcb_tx_rbf, USART_TXBUFSIZ, USART_TXLOWMARK, USART_TXHIWMARK)) {
00557             return NUTFILE_EOF;
00558         }
00559     }
00560 
00561     /*
00562      * Create the receive buffer unless this is used for write only.
00563      */
00564     if ((mode & 0x0003) != _O_WRONLY) {
00565         if (UsartResetBuffer(&dcb->dcb_rx_rbf, USART_RXBUFSIZ, USART_RXLOWMARK, USART_RXHIWMARK)) {
00566             free(dcb->dcb_tx_rbf.rbf_start);
00567             return NUTFILE_EOF;
00568         }
00569     }
00570 
00571     /*
00572      * Allocate memory for the file structure.
00573      */
00574     if ((fp = malloc(sizeof(NUTFILE))) == 0) {
00575         free(dcb->dcb_tx_rbf.rbf_start);
00576         free(dcb->dcb_rx_rbf.rbf_start);
00577         return NUTFILE_EOF;
00578     }
00579 
00580     /* Set proper device modes. */
00581     if ((mode & 0xC000) == _O_BINARY) {
00582         dcb->dcb_modeflags &= ~USART_MF_COOKEDMODE;
00583     } else {
00584         dcb->dcb_modeflags |= USART_MF_COOKEDMODE;
00585     }
00586 
00587     /* 
00588      * For now we do the initialization here. Later we may implement
00589      * a file creation routine to get a linked list of all opened
00590      * files in the system.
00591      */
00592     fp->nf_next = 0;
00593     fp->nf_dev = dev;
00594     fp->nf_fcb = 0;
00595 
00596     if ((mode & 0x0003) != _O_WRONLY) {
00597         (*dcb->dcb_rx_start) ();
00598     }
00599 
00600     return fp;
00601 }
00602 
00662 int UsartIOCtl(NUTDEVICE * dev, int req, void *conf)
00663 {
00664     int rc = 0;
00665     USARTDCB *dcb;
00666     RINGBUF *rbf;
00667     u_long *lvp = (u_long *) conf;
00668     u_long lv = *lvp;
00669     u_char bv = (u_char) lv;
00670 
00671     dcb = dev->dev_dcb;
00672 
00673     switch (req) {
00674     case UART_SETSPEED:
00675         rc = (*dcb->dcb_set_speed) (lv);
00676         break;
00677     case UART_GETSPEED:
00678         *lvp = (*dcb->dcb_get_speed) ();
00679         break;
00680 
00681     case UART_SETDATABITS:
00682         rc = (*dcb->dcb_set_data_bits) (bv);
00683         break;
00684     case UART_GETDATABITS:
00685         *lvp = (*dcb->dcb_get_data_bits) ();
00686         break;
00687 
00688     case UART_SETPARITY:
00689         rc = (*dcb->dcb_set_parity) (bv);
00690         break;
00691     case UART_GETPARITY:
00692         *lvp = (*dcb->dcb_get_parity) ();
00693         break;
00694 
00695     case UART_SETSTOPBITS:
00696         rc = (*dcb->dcb_set_stop_bits) (bv);
00697         break;
00698     case UART_GETSTOPBITS:
00699         *lvp = (*dcb->dcb_get_stop_bits) ();
00700         break;
00701 
00702     case UART_SETSTATUS:
00703         /*
00704          * We are not changing the buffer size. Thus, we can safely ignore the
00705          * result of UsartResetBuffer(). This way we found a work around for
00706          * the AVRGCC 4.1.1 bug, which appeared here previously.
00707          */
00708         if (lv & UART_RXBUFFEREMPTY) {
00709             rbf = &dcb->dcb_rx_rbf;
00710             UsartResetBuffer(rbf, rbf->rbf_siz, rbf->rbf_lwm, rbf->rbf_hwm);
00711             (*dcb->dcb_rx_start) ();
00712         }
00713         if (lv & UART_TXBUFFEREMPTY) {
00714             rbf = &dcb->dcb_tx_rbf;
00715             UsartResetBuffer(rbf, rbf->rbf_siz, rbf->rbf_lwm, rbf->rbf_hwm);
00716         }
00717         rc = (*dcb->dcb_set_status) (lv & ~(UART_RXBUFFEREMPTY | UART_TXBUFFEREMPTY));
00718         break;
00719     case UART_GETSTATUS:
00720         *lvp = (*dcb->dcb_get_status) ();
00721         /*
00722          * Determine buffer status.
00723          */
00724         if (dcb->dcb_rx_rbf.rbf_cnt == 0) {
00725             *lvp |= UART_RXBUFFEREMPTY;
00726         }
00727         if (dcb->dcb_tx_rbf.rbf_cnt == 0) {
00728             *lvp |= UART_TXBUFFEREMPTY;
00729         }
00730         break;
00731 
00732     case UART_SETREADTIMEOUT:
00733         dcb->dcb_rtimeout = lv;
00734         break;
00735     case UART_GETREADTIMEOUT:
00736         *lvp = dcb->dcb_rtimeout;
00737         break;
00738 
00739     case UART_SETWRITETIMEOUT:
00740         dcb->dcb_wtimeout = lv;
00741         break;
00742     case UART_GETWRITETIMEOUT:
00743         *lvp = dcb->dcb_wtimeout;
00744         break;
00745 
00746     case UART_SETLOCALECHO:
00747         if (bv)
00748             dcb->dcb_modeflags |= USART_MF_LOCALECHO;
00749         else
00750             dcb->dcb_modeflags &= ~USART_MF_LOCALECHO;
00751         break;
00752     case UART_GETLOCALECHO:
00753         if (dcb->dcb_modeflags & USART_MF_LOCALECHO)
00754             *lvp = 1;
00755         else
00756             *lvp = 0;
00757         break;
00758 
00759     case UART_SETFLOWCONTROL:
00760         rc = (*dcb->dcb_set_flow_control) (lv);
00761         break;
00762     case UART_GETFLOWCONTROL:
00763         *lvp = (*dcb->dcb_get_flow_control) ();
00764         break;
00765 
00766     case UART_SETCOOKEDMODE:
00767         if (bv)
00768             dcb->dcb_modeflags |= USART_MF_COOKEDMODE;
00769         else
00770             dcb->dcb_modeflags &= ~USART_MF_COOKEDMODE;
00771         break;
00772     case UART_GETCOOKEDMODE:
00773         if (dcb->dcb_modeflags & USART_MF_COOKEDMODE)
00774             *lvp = 1;
00775         else
00776             *lvp = 0;
00777         break;
00778 
00779     case UART_SETCLOCKMODE:
00780         rc = (*dcb->dcb_set_clock_mode) (lv);
00781         break;
00782     case UART_GETCLOCKMODE:
00783         *lvp = (*dcb->dcb_get_clock_mode) ();
00784         break;
00785 
00786     case UART_SETTXBUFSIZ:
00787         rbf = &dcb->dcb_tx_rbf;
00788         rc = UsartResetBuffer(rbf, (size_t) lv, rbf->rbf_lwm, rbf->rbf_hwm);
00789         if (rc == 0) {
00790             (*dcb->dcb_rx_start) ();
00791         }
00792         break;
00793     case UART_GETTXBUFSIZ:
00794         *lvp = dcb->dcb_tx_rbf.rbf_siz;
00795         break;
00796 
00797     case UART_SETRXBUFSIZ:
00798         rbf = &dcb->dcb_rx_rbf;
00799         rc = UsartResetBuffer(rbf, (size_t) lv, rbf->rbf_lwm, rbf->rbf_hwm);
00800         break;
00801     case UART_GETRXBUFSIZ:
00802         *lvp = dcb->dcb_rx_rbf.rbf_siz;
00803         break;
00804 
00805     case UART_SETTXBUFLWMARK:
00806         NutEnterCritical();
00807         dcb->dcb_tx_rbf.rbf_lwm = (size_t) lv;
00808         NutExitCritical();
00809         break;
00810     case UART_GETTXBUFLWMARK:
00811         *lvp = dcb->dcb_tx_rbf.rbf_lwm;
00812         break;
00813 
00814     case UART_SETTXBUFHWMARK:
00815         NutEnterCritical();
00816         dcb->dcb_tx_rbf.rbf_hwm = (size_t) lv;
00817         NutExitCritical();
00818         break;
00819     case UART_GETTXBUFHWMARK:
00820         *lvp = dcb->dcb_tx_rbf.rbf_hwm;
00821         break;
00822 
00823     case UART_SETRXBUFLWMARK:
00824         NutEnterCritical();
00825         dcb->dcb_rx_rbf.rbf_lwm = (size_t) lv;
00826         NutExitCritical();
00827         break;
00828     case UART_GETRXBUFLWMARK:
00829         *lvp = dcb->dcb_rx_rbf.rbf_lwm;
00830         break;
00831 
00832     case UART_SETRXBUFHWMARK:
00833         NutEnterCritical();
00834         dcb->dcb_rx_rbf.rbf_hwm = (size_t) lv;
00835         NutExitCritical();
00836         break;
00837     case UART_GETRXBUFHWMARK:
00838         *lvp = dcb->dcb_rx_rbf.rbf_hwm;
00839         break;
00840 
00841     default:
00842         rc = -1;
00843         break;
00844     }
00845     return rc;
00846 }
00847 
00859 long UsartSize (NUTFILE *fp)
00860 {
00861     long avail;
00862     NUTDEVICE *dev = fp->nf_dev;
00863     USARTDCB *dcb = dev->dev_dcb;
00864     RINGBUF *rbf = &dcb->dcb_rx_rbf;
00865 
00866     /* Atomic access to the ring buffer counter. */
00867     NutEnterCritical();
00868     avail = rbf->rbf_cnt;
00869     NutExitCritical();
00870 
00871     return avail;
00872 }
00873 
00874 

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