ser.c

Go to the documentation of this file.
00001 
00053 #include "ser.h"
00054 #include "wdt.h"
00055 #include "timer.h"
00056 #include "ser_p.h"
00057 
00058 #include "cfg/cfg_ser.h"
00059 #include "cfg/cfg_proc.h"
00060 #include <cfg/debug.h>
00061 
00062 #include <mware/formatwr.h>
00063 
00064 #include <cpu/power.h> /* cpu_relax() */
00065 
00066 #include <string.h> /* memset() */
00067 
00068 /*
00069  * Sanity check for config parameters required by this module.
00070  */
00071 #if !defined(CONFIG_KERN) || ((CONFIG_KERN != 0) && CONFIG_KERN != 1)
00072     #error CONFIG_KERN must be set to either 0 or 1 in cfg_kern.h
00073 #endif
00074 #if !defined(CONFIG_SER_RXTIMEOUT)
00075     #error CONFIG_SER_TXTIMEOUT missing in cfg_ser.h
00076 #endif
00077 #if !defined(CONFIG_SER_RXTIMEOUT)
00078     #error CONFIG_SER_RXTIMEOUT missing in cfg_ser.h
00079 #endif
00080 #if !defined(CONFIG_SER_DEFBAUDRATE)
00081     #error CONFIG_SER_DEFBAUDRATE missing in cfg_ser.h
00082 #endif
00083 
00084 
00085 struct Serial *ser_handles[SER_CNT];
00086 
00095 static int ser_putchar(int c, struct Serial *port)
00096 {
00097     if (fifo_isfull_locked(&port->txfifo))
00098     {
00099 #if CONFIG_SER_TXTIMEOUT != -1
00100         /* If timeout == 0 we don't want to wait */
00101         if (port->txtimeout == 0)
00102             return EOF;
00103 
00104         ticks_t start_time = timer_clock();
00105 #endif
00106 
00107         /* Wait while buffer is full... */
00108         do
00109         {
00110             cpu_relax();
00111 
00112 #if CONFIG_SER_TXTIMEOUT != -1
00113             if (timer_clock() - start_time >= port->txtimeout)
00114             {
00115                 ATOMIC(port->status |= SERRF_TXTIMEOUT);
00116                 return EOF;
00117             }
00118 #endif /* CONFIG_SER_TXTIMEOUT */
00119         }
00120         while (fifo_isfull_locked(&port->txfifo));
00121     }
00122 
00123     fifo_push_locked(&port->txfifo, (unsigned char)c);
00124 
00125     /* (re)trigger tx interrupt */
00126     port->hw->table->txStart(port->hw);
00127 
00128     /* Avoid returning signed extended char */
00129     return (int)((unsigned char)c);
00130 }
00131 
00132 
00141 static int ser_getchar(struct Serial *port)
00142 {
00143     if (fifo_isempty_locked(&port->rxfifo))
00144     {
00145 #if CONFIG_SER_RXTIMEOUT != -1
00146         /* If timeout == 0 we don't want to wait for chars */
00147         if (port->rxtimeout == 0)
00148             return EOF;
00149 
00150         ticks_t start_time = timer_clock();
00151 #endif
00152 
00153         /* Wait while buffer is empty */
00154         do
00155         {
00156             cpu_relax();
00157 
00158 #if CONFIG_SER_RXTIMEOUT != -1
00159             if (timer_clock() - start_time >= port->rxtimeout)
00160             {
00161                 ATOMIC(port->status |= SERRF_RXTIMEOUT);
00162                 return EOF;
00163             }
00164 #endif /* CONFIG_SER_RXTIMEOUT */
00165         }
00166         while (fifo_isempty_locked(&port->rxfifo) && (ser_getstatus(port) & SERRF_RX) == 0);
00167     }
00168 
00169     /*
00170      * Get a byte from the FIFO (avoiding sign-extension),
00171      * re-enable RTS, then return result.
00172      */
00173     if (ser_getstatus(port) & SERRF_RX)
00174         return EOF;
00175     return (int)(unsigned char)fifo_pop_locked(&port->rxfifo);
00176 }
00177 
00184 int ser_getchar_nowait(struct Serial *fd)
00185 {
00186     if (fifo_isempty_locked(&fd->rxfifo))
00187         return EOF;
00188 
00189     /* NOTE: the double cast prevents unwanted sign extension */
00190     return (int)(unsigned char)fifo_pop_locked(&fd->rxfifo);
00191 }
00192 
00193 
00194 
00200 static size_t ser_read(struct KFile *fd, void *_buf, size_t size)
00201 {
00202     Serial *fds = SERIAL_CAST(fd);
00203 
00204     size_t i = 0;
00205     char *buf = (char *)_buf;
00206     int c;
00207 
00208     while (i < size)
00209     {
00210         if ((c = ser_getchar(fds)) == EOF)
00211             break;
00212         buf[i++] = c;
00213     }
00214 
00215     return i;
00216 }
00217 
00225 static size_t ser_write(struct KFile *fd, const void *_buf, size_t size)
00226 {
00227     Serial *fds = SERIAL_CAST(fd);
00228     const char *buf = (const char *)_buf;
00229     size_t i = 0;
00230 
00231     while (size--)
00232     {
00233         if (ser_putchar(*buf++, fds) == EOF)
00234             break;
00235         i++;
00236     }
00237     return i;
00238 }
00239 
00240 
00241 #if CONFIG_SER_RXTIMEOUT != -1 || CONFIG_SER_TXTIMEOUT != -1
00242 void ser_settimeouts(struct Serial *fd, mtime_t rxtimeout, mtime_t txtimeout)
00243 {
00244     fd->rxtimeout = ms_to_ticks(rxtimeout);
00245     fd->txtimeout = ms_to_ticks(txtimeout);
00246 }
00247 #endif /* CONFIG_SER_RXTIMEOUT || CONFIG_SER_TXTIMEOUT */
00248 
00249 #if CONFIG_SER_RXTIMEOUT != -1
00250 
00258 void ser_resync(struct Serial *fd, mtime_t delay)
00259 {
00260     mtime_t old_rxtimeout = ticks_to_ms(fd->rxtimeout);
00261 
00262     ser_settimeouts(fd, delay, ticks_to_ms(fd->txtimeout));
00263     do
00264     {
00265         ser_setstatus(fd, 0);
00266         ser_getchar(fd);
00267     }
00268     while (!(ser_getstatus(fd) & SERRF_RXTIMEOUT));
00269 
00270     /* Restore port to an usable status */
00271     ser_setstatus(fd, 0);
00272     ser_settimeouts(fd, old_rxtimeout, ticks_to_ms(fd->txtimeout));
00273 }
00274 #endif /* CONFIG_SER_RXTIMEOUT */
00275 
00276 
00277 void ser_setbaudrate(struct Serial *fd, unsigned long rate)
00278 {
00279     fd->hw->table->setBaudrate(fd->hw, rate);
00280 }
00281 
00282 
00283 void ser_setparity(struct Serial *fd, int parity)
00284 {
00285     fd->hw->table->setParity(fd->hw, parity);
00286 }
00287 
00288 static int ser_error(struct KFile *fd)
00289 {
00290     Serial *fds = SERIAL_CAST(fd);
00291     return ser_getstatus(fds);
00292 }
00293 
00294 static void ser_clearerr(struct KFile *fd)
00295 {
00296     Serial *fds = SERIAL_CAST(fd);
00297     ser_setstatus(fds, 0);
00298 }
00299 
00300 
00301 
00305 void ser_purge(struct Serial *fd)
00306 {
00307     ser_purgeRx(fd);
00308     ser_purgeTx(fd);
00309 }
00310 
00314 void ser_purgeRx(struct Serial *fd)
00315 {
00316     fifo_flush_locked(&fd->rxfifo);
00317 }
00318 
00322 void ser_purgeTx(struct Serial *fd)
00323 {
00324     fifo_flush_locked(&fd->txfifo);
00325 }
00326 
00327 
00336 static int ser_flush(struct KFile *fd)
00337 {
00338     Serial *fds = SERIAL_CAST(fd);
00339 
00340     /*
00341      * Wait until the FIFO becomes empty, and then until the byte currently in
00342      * the hardware register gets shifted out.
00343      */
00344     while (!fifo_isempty(&fds->txfifo)
00345            || fds->hw->table->txSending(fds->hw))
00346         cpu_relax();
00347     return 0;
00348 }
00349 
00350 
00357 static struct Serial *ser_open(struct Serial *fd, unsigned int unit)
00358 {
00359     ASSERT(unit < countof(ser_handles));
00360 
00361     ser_handles[unit] = fd;
00362     ASSERT(!fd->is_open);
00363     DB(fd->is_open = true);
00364 
00365     fd->unit = unit;
00366 
00367     fd->hw = ser_hw_getdesc(unit);
00368 
00369     /* Initialize circular buffers */
00370     ASSERT(fd->hw->txbuffer);
00371     ASSERT(fd->hw->rxbuffer);
00372     fifo_init(&fd->txfifo, fd->hw->txbuffer, fd->hw->txbuffer_size);
00373     fifo_init(&fd->rxfifo, fd->hw->rxbuffer, fd->hw->rxbuffer_size);
00374 
00375     fd->hw->table->init(fd->hw, fd);
00376 
00377     /* Set default values */
00378 #if CONFIG_SER_RXTIMEOUT != -1 || CONFIG_SER_TXTIMEOUT != -1
00379     ser_settimeouts(fd, CONFIG_SER_RXTIMEOUT, CONFIG_SER_TXTIMEOUT);
00380 #endif
00381 #if CONFIG_SER_DEFBAUDRATE
00382     ser_setbaudrate(fd, CONFIG_SER_DEFBAUDRATE);
00383 #endif
00384 
00385     /* Clear error flags */
00386     ser_setstatus(fd, 0);
00387 
00388     return fd;
00389 }
00390 
00391 
00395 static int ser_close(struct KFile *fd)
00396 {
00397     Serial *fds = SERIAL_CAST(fd);
00398     Serial *port = fds;
00399 
00400     ASSERT(port->is_open);
00401     DB(port->is_open = false);
00402 
00403     // Wait until we finish sending everything
00404     ser_flush(fd);
00405 
00406     port->hw->table->cleanup(port->hw);
00407     DB(port->hw = NULL);
00408 
00409     /*
00410      * We purge the FIFO buffer only after the low-level cleanup, so that
00411      * we are sure that there are no more interrupts.
00412      */
00413     ser_purge(fds);
00414     return 0;
00415 }
00416 
00420 static struct KFile *ser_reopen(struct KFile *fd)
00421 {
00422     Serial *fds = SERIAL_CAST(fd);
00423 
00424     ser_close(fd);
00425     ser_open(fds, fds->unit);
00426     return (KFile *)fds;
00427 }
00428 
00432 void ser_init(struct Serial *fds, unsigned int unit)
00433 {
00434     memset(fds, 0, sizeof(*fds));
00435 
00436     DB(fds->fd._type = KFT_SERIAL);
00437     fds->fd.reopen = ser_reopen;
00438     fds->fd.close = ser_close;
00439     fds->fd.read = ser_read;
00440     fds->fd.write = ser_write;
00441     fds->fd.flush = ser_flush;
00442     fds->fd.error = ser_error;
00443     fds->fd.clearerr = ser_clearerr;
00444     ser_open(fds, unit);
00445 }
00446 
00447 
00453 static size_t spimaster_read(struct KFile *fd, void *_buf, size_t size)
00454 {
00455     Serial *fd_spi = SERIAL_CAST(fd);
00456 
00457     ser_flush(&fd_spi->fd);
00458     ser_purgeRx(fd_spi);
00459 
00460     size_t total_rd = 0;
00461     uint8_t *buf = (uint8_t *)_buf;
00462     int c;
00463 
00464     while (size--)
00465     {
00466         /*
00467          * Send and receive chars 1 by 1, otherwise the rxfifo
00468          * will overrun.
00469          */
00470         ser_putchar(0, fd_spi);
00471 
00472         if ((c = ser_getchar(fd_spi)) == EOF)
00473             break;
00474 
00475         *buf++ = c;
00476         total_rd++;
00477     }
00478     return total_rd;
00479 }
00480 
00484 static size_t spimaster_write(struct KFile *fd, const void *buf, size_t size)
00485 {
00486     Serial *fd_spi = SERIAL_CAST(fd);
00487 
00488     ser_purgeRx(fd_spi);
00489 
00490     return ser_write(&fd_spi->fd, buf, size);
00491 }
00492 
00493 
00505 void spimaster_init(Serial *fds, unsigned int unit)
00506 {
00507     ser_init(fds, unit);
00508     fds->fd.read = spimaster_read;
00509     fds->fd.write = spimaster_write;
00510 }
00511 
00512