ser.c

Go to the documentation of this file.
00001 
00054 #include "ser.h"
00055 #include "wdt.h"
00056 #include "ser_p.h"
00057 #include <mware/formatwr.h>
00058 #include <cfg/debug.h>
00059 #include <appconfig.h>
00060 
00061 #include <string.h> /* memset */
00062 
00063 /*
00064  * Sanity check for config parameters required by this module.
00065  */
00066 #if !defined(CONFIG_KERNEL) || ((CONFIG_KERNEL != 0) && CONFIG_KERNEL != 1)
00067     #error CONFIG_KERNEL must be set to either 0 or 1 in config.h
00068 #endif
00069 #if !defined(CONFIG_SER_RXTIMEOUT)
00070     #error CONFIG_SER_TXTIMEOUT missing in config.h
00071 #endif
00072 #if !defined(CONFIG_SER_RXTIMEOUT)
00073     #error CONFIG_SER_RXTIMEOUT missing in config.h
00074 #endif
00075 #if !defined(CONFIG_SER_DEFBAUDRATE)
00076     #error CONFIG_SER_DEFBAUDRATE missing in config.h
00077 #endif
00078 
00079 #if CONFIG_KERNEL
00080     #include <kern/proc.h>
00081 #endif
00082 
00083 #if CONFIG_SER_TXTIMEOUT != -1 || CONFIG_SER_RXTIMEOUT != -1
00084     #include <drv/timer.h>
00085 #endif
00086 
00087 
00088 struct Serial ser_handles[SER_CNT];
00089 
00098 static int ser_putchar(int c, struct Serial *port)
00099 {
00100     if (fifo_isfull_locked(&port->txfifo))
00101     {
00102 #if CONFIG_SER_TXTIMEOUT != -1
00103         /* If timeout == 0 we don't want to wait */
00104         if (port->txtimeout == 0)
00105             return EOF;
00106 
00107         ticks_t start_time = timer_clock();
00108 #endif
00109 
00110         /* Wait while buffer is full... */
00111         do
00112         {
00113             wdt_reset();
00114 #if CONFIG_KERNEL && CONFIG_KERN_SCHED
00115             /* Give up timeslice to other processes. */
00116             proc_switch();
00117 #endif
00118 #if CONFIG_SER_TXTIMEOUT != -1
00119             if (timer_clock() - start_time >= port->txtimeout)
00120             {
00121                 ATOMIC(port->status |= SERRF_TXTIMEOUT);
00122                 return EOF;
00123             }
00124 #endif /* CONFIG_SER_TXTIMEOUT */
00125         }
00126         while (fifo_isfull_locked(&port->txfifo));
00127     }
00128 
00129     fifo_push_locked(&port->txfifo, (unsigned char)c);
00130 
00131     /* (re)trigger tx interrupt */
00132     port->hw->table->txStart(port->hw);
00133 
00134     /* Avoid returning signed extended char */
00135     return (int)((unsigned char)c);
00136 }
00137 
00138 
00147 static int ser_getchar(struct Serial *port)
00148 {
00149     if (fifo_isempty_locked(&port->rxfifo))
00150     {
00151 #if CONFIG_SER_RXTIMEOUT != -1
00152         /* If timeout == 0 we don't want to wait for chars */
00153         if (port->rxtimeout == 0)
00154             return EOF;
00155 
00156         ticks_t start_time = timer_clock();
00157 #endif
00158         /* Wait while buffer is empty */
00159         do
00160         {
00161             wdt_reset();
00162 #if CONFIG_KERNEL && CONFIG_KERN_SCHED
00163             /* Give up timeslice to other processes. */
00164             proc_switch();
00165 #endif
00166 #if CONFIG_SER_RXTIMEOUT != -1
00167             if (timer_clock() - start_time >= port->rxtimeout)
00168             {
00169                 ATOMIC(port->status |= SERRF_RXTIMEOUT);
00170                 return EOF;
00171             }
00172 #endif /* CONFIG_SER_RXTIMEOUT */
00173         }
00174         while (fifo_isempty_locked(&port->rxfifo) && (ser_getstatus(port) & SERRF_RX) == 0);
00175     }
00176 
00177     /*
00178      * Get a byte from the FIFO (avoiding sign-extension),
00179      * re-enable RTS, then return result.
00180      */
00181     if (ser_getstatus(port) & SERRF_RX)
00182         return EOF;
00183     return (int)(unsigned char)fifo_pop_locked(&port->rxfifo);
00184 }
00185 
00192 int ser_getchar_nowait(struct KFileSerial *fd)
00193 {
00194     if (fifo_isempty_locked(&fd->ser->rxfifo))
00195         return EOF;
00196 
00197     /* NOTE: the double cast prevents unwanted sign extension */
00198     return (int)(unsigned char)fifo_pop_locked(&fd->ser->rxfifo);
00199 }
00200 
00201 
00202 
00208 static size_t ser_read(struct KFile *fd, void *_buf, size_t size)
00209 {
00210     KFileSerial *fds = KFILESERIAL(fd);
00211 
00212     size_t i = 0;
00213     char *buf = (char *)_buf;
00214     int c;
00215 
00216     while (i < size)
00217     {
00218         if ((c = ser_getchar(fds->ser)) == EOF)
00219             break;
00220         buf[i++] = c;
00221     }
00222 
00223     return i;
00224 }
00225 
00233 static size_t ser_write(struct KFile *fd, const void *_buf, size_t size)
00234 {
00235     KFileSerial *fds = KFILESERIAL(fd);
00236     const char *buf = (const char *)_buf;
00237     size_t i = 0;
00238 
00239     while (size--)
00240     {
00241         if (ser_putchar(*buf++, fds->ser) == EOF)
00242             break;
00243         i++;
00244     }
00245     return i;
00246 }
00247 
00248 
00249 #if CONFIG_SER_RXTIMEOUT != -1 || CONFIG_SER_TXTIMEOUT != -1
00250 void ser_settimeouts(struct KFileSerial *fd, mtime_t rxtimeout, mtime_t txtimeout)
00251 {
00252     fd->ser->rxtimeout = ms_to_ticks(rxtimeout);
00253     fd->ser->txtimeout = ms_to_ticks(txtimeout);
00254 }
00255 #endif /* CONFIG_SER_RXTIMEOUT || CONFIG_SER_TXTIMEOUT */
00256 
00257 #if CONFIG_SER_RXTIMEOUT != -1
00258 
00266 void ser_resync(struct KFileSerial *fd, mtime_t delay)
00267 {
00268     mtime_t old_rxtimeout = ticks_to_ms(fd->ser->rxtimeout);
00269 
00270     ser_settimeouts(fd, delay, ticks_to_ms(fd->ser->txtimeout));
00271     do
00272     {
00273         ser_setstatus(fd->ser, 0);
00274         ser_getchar(fd->ser);
00275     }
00276     while (!(ser_getstatus(fd->ser) & SERRF_RXTIMEOUT));
00277 
00278     /* Restore port to an usable status */
00279     ser_setstatus(fd->ser, 0);
00280     ser_settimeouts(fd, old_rxtimeout, ticks_to_ms(fd->ser->txtimeout));
00281 }
00282 #endif /* CONFIG_SER_RXTIMEOUT */
00283 
00284 
00285 void ser_setbaudrate(struct KFileSerial *fd, unsigned long rate)
00286 {
00287     fd->ser->hw->table->setBaudrate(fd->ser->hw, rate);
00288 }
00289 
00290 
00291 void ser_setparity(struct KFileSerial *fd, int parity)
00292 {
00293     fd->ser->hw->table->setParity(fd->ser->hw, parity);
00294 }
00295 
00296 static int ser_error(struct KFile *fd)
00297 {
00298     KFileSerial *fds = KFILESERIAL(fd);
00299     return ser_getstatus(fds->ser);
00300 }
00301 
00302 static void ser_clearerr(struct KFile *fd)
00303 {
00304     KFileSerial *fds = KFILESERIAL(fd);
00305     ser_setstatus(fds->ser, 0);
00306 }
00307 
00308 
00309 
00313 void ser_purge(struct KFileSerial *fd)
00314 {
00315     ser_purgeRx(fd);
00316     ser_purgeTx(fd);
00317 }
00318 
00322 void ser_purgeRx(struct KFileSerial *fd)
00323 {
00324     fifo_flush_locked(&fd->ser->rxfifo);
00325 }
00326 
00330 void ser_purgeTx(struct KFileSerial *fd)
00331 {
00332     fifo_flush_locked(&fd->ser->txfifo);
00333 }
00334 
00335 
00344 static int ser_flush(struct KFile *fd)
00345 {
00346     KFileSerial *fds = KFILESERIAL(fd);
00347 
00348     /*
00349      * Wait until the FIFO becomes empty, and then until the byte currently in
00350      * the hardware register gets shifted out.
00351      */
00352     while (!fifo_isempty(&fds->ser->txfifo)
00353            || fds->ser->hw->table->txSending(fds->ser->hw))
00354     {
00355         #if CONFIG_KERNEL && CONFIG_KERN_SCHED
00356             /* Give up timeslice to other processes. */
00357             proc_switch();
00358         #endif
00359             wdt_reset();
00360     }
00361     return 0;
00362 }
00363 
00364 
00371 static struct Serial *ser_open(struct KFileSerial *fd, unsigned int unit)
00372 {
00373     struct Serial *port;
00374 
00375     ASSERT(unit < countof(ser_handles));
00376     port = &ser_handles[unit];
00377 
00378     ASSERT(!port->is_open);
00379     DB(port->is_open = true);
00380 
00381     port->unit = unit;
00382 
00383     port->hw = ser_hw_getdesc(unit);
00384 
00385     /* Initialize circular buffers */
00386     ASSERT(port->hw->txbuffer);
00387     ASSERT(port->hw->rxbuffer);
00388     fifo_init(&port->txfifo, port->hw->txbuffer, port->hw->txbuffer_size);
00389     fifo_init(&port->rxfifo, port->hw->rxbuffer, port->hw->rxbuffer_size);
00390 
00391     port->hw->table->init(port->hw, port);
00392 
00393     fd->ser = port;
00394     /* Set default values */
00395 #if CONFIG_SER_RXTIMEOUT != -1 || CONFIG_SER_TXTIMEOUT != -1
00396     ser_settimeouts(fd, CONFIG_SER_RXTIMEOUT, CONFIG_SER_TXTIMEOUT);
00397 #endif
00398 #if CONFIG_SER_DEFBAUDRATE
00399     ser_setbaudrate(fd, CONFIG_SER_DEFBAUDRATE);
00400 #endif
00401 
00402     /* Clear error flags */
00403     ser_setstatus(port, 0);
00404 
00405     return port;
00406 }
00407 
00408 
00412 static int ser_close(struct KFile *fd)
00413 {
00414     KFileSerial *fds = KFILESERIAL(fd);
00415     Serial *port = fds->ser;
00416 
00417     ASSERT(port->is_open);
00418     DB(port->is_open = false);
00419 
00420     // Wait until we finish sending everything
00421     ser_flush(fd);
00422 
00423     port->hw->table->cleanup(port->hw);
00424     DB(port->hw = NULL);
00425 
00426     /*
00427      * We purge the FIFO buffer only after the low-level cleanup, so that
00428      * we are sure that there are no more interrupts.
00429      */
00430     ser_purge(fds);
00431     return 0;
00432 }
00433 
00437 static struct KFile *ser_reopen(struct KFile *fd)
00438 {
00439     KFileSerial *fds = KFILESERIAL(fd);
00440 
00441     ser_close(fd);
00442     ser_open(fds, fds->ser->unit);
00443     return (KFile *)fds;
00444 }
00445 
00449 void ser_init(struct KFileSerial *fds, unsigned int unit)
00450 {
00451     memset(fds, 0, sizeof(*fds));
00452 
00453     DB(fds->fd._type = KFT_SERIAL);
00454     fds->fd.reopen = ser_reopen;
00455     fds->fd.close = ser_close;
00456     fds->fd.read = ser_read;
00457     fds->fd.write = ser_write;
00458     fds->fd.flush = ser_flush;
00459     fds->fd.error = ser_error;
00460     fds->fd.clearerr = ser_clearerr;
00461     ser_open(fds, unit);
00462 }
00463 
00464 
00470 static size_t spimaster_read(struct KFile *fd, void *_buf, size_t size)
00471 {
00472     KFileSerial *fd_spi = KFILESERIAL(fd);
00473 
00474     ser_flush(&fd_spi->fd);
00475     ser_purgeRx(fd_spi);
00476 
00477     size_t total_rd = 0;
00478     uint8_t *buf = (uint8_t *)_buf;
00479     int c;
00480 
00481     while (size--)
00482     {
00483         /*
00484          * Send and receive chars 1 by 1, otherwise the rxfifo
00485          * will overrun.
00486          */
00487         ser_putchar(0, fd_spi->ser);
00488 
00489         if ((c = ser_getchar(fd_spi->ser)) == EOF)
00490             break;
00491 
00492         *buf++ = c;
00493         total_rd++;
00494     }
00495     return total_rd;
00496 }
00497 
00501 static size_t spimaster_write(struct KFile *fd, const void *buf, size_t size)
00502 {
00503     KFileSerial *fd_spi = KFILESERIAL(fd);
00504 
00505     ser_purgeRx(fd_spi);
00506 
00507     return ser_write(&fd_spi->fd, buf, size);
00508 }
00509 
00510 
00522 void spimaster_init(KFileSerial *fds, unsigned int unit)
00523 {
00524     ser_init(fds, unit);
00525     fds->fd.read = spimaster_read;
00526     fds->fd.write = spimaster_write;
00527 }
00528 
00529