Implement ser_setstatus and ser_getstatus as functions to avoid race conditions.
[bertos.git] / drv / ser.c
1 /**
2  * \file
3  * <!--
4  * Copyright 2003, 2004, 2006 Develer S.r.l. (http://www.develer.com/)
5  * Copyright 2000 Bernardo Innocenti <bernie@codewiz.org>
6  * This file is part of DevLib - See README.devlib for information.
7  * -->
8  *
9  * \brief Buffered serial I/O driver
10  *
11  * The serial rx interrupt buffers incoming data in a software FIFO
12  * to decouple the higher level protocols from the line speed.
13  * Outgoing data is buffered as well for better performance.
14  * This driver is not optimized for best performance, but it
15  * has proved to be fast enough to handle transfer rates up to
16  * 38400bps on a 16MHz 80196.
17  *
18  * MODULE CONFIGURATION
19  *
20  *  \li \c CONFIG_SER_HWHANDSHAKE - set to 1 to enable RTS/CTS handshake.
21  *         Support is incomplete/untested.
22  *  \li \c CONFIG_SER_TXTIMEOUT - Enable software serial transmission timeouts
23  *
24  *
25  * \version $Id$
26  * \author Bernardo Innocenti <bernie@develer.com>
27  */
28
29 /*#*
30  *#* $Log$
31  *#* Revision 1.32  2006/11/17 17:03:58  batt
32  *#* Implement ser_setstatus and ser_getstatus as functions to avoid race conditions.
33  *#*
34  *#* Revision 1.31  2006/07/21 10:58:00  batt
35  *#* Use timer_clock() instead of obsolete timer_ticks().
36  *#*
37  *#* Revision 1.30  2006/07/19 12:56:26  bernie
38  *#* Convert to new Doxygen style.
39  *#*
40  *#* Revision 1.29  2006/05/18 00:39:30  bernie
41  *#* ser_open(): Document a bit more.
42  *#*
43  *#* Revision 1.28  2006/02/17 22:23:06  bernie
44  *#* Update POSIX serial emulator.
45  *#*
46  *#* Revision 1.27  2005/11/27 23:33:40  bernie
47  *#* Use appconfig.h instead of cfg/config.h.
48  *#*
49  *#* Revision 1.26  2005/11/04 16:20:02  bernie
50  *#* Fix reference to README.devlib in header.
51  *#*
52  *#* Revision 1.25  2005/04/11 19:10:27  bernie
53  *#* Include top-level headers from cfg/ subdir.
54  *#*
55  *#* Revision 1.24  2005/01/21 20:13:15  aleph
56  *#* Fix drain at ser_close()
57  *#*
58  *#* Revision 1.23  2005/01/14 00:47:07  aleph
59  *#* ser_drain(): Wait for hw transmission complete.
60  *#*
61  *#* Revision 1.22  2004/12/08 08:56:14  bernie
62  *#* Rename time_t to mtime_t.
63  *#*
64  *#* Revision 1.21  2004/11/16 18:10:13  bernie
65  *#* Add sanity checks for missing configuration parameters.
66  *#*
67  *#* Revision 1.20  2004/10/19 11:48:00  bernie
68  *#* Remove unused variable.
69  *#*
70  *#* Revision 1.19  2004/10/19 08:14:13  bernie
71  *#* Fix a few longstanding bugs wrt status handling (made by rasky on scfirm).
72  *#*
73  *#* Revision 1.18  2004/09/20 03:31:15  bernie
74  *#* Sanitize for C++.
75  *#*
76  *#* Revision 1.17  2004/09/14 21:06:07  bernie
77  *#* Use debug.h instead of kdebug.h; Spelling fixes.
78  *#*
79  *#* Revision 1.16  2004/09/06 21:40:50  bernie
80  *#* Move buffer handling in chip-specific driver.
81  *#*
82  *#* Revision 1.15  2004/08/25 14:12:08  rasky
83  *#* Aggiornato il comment block dei log RCS
84  *#*
85  *#* Revision 1.14  2004/08/24 16:22:57  bernie
86  *#* Thinkos; Doxygen fixes
87  *#*
88  *#* Revision 1.13  2004/08/24 16:20:48  bernie
89  *#* ser_read(): Make buffer argument void *#* for consistency with ANSI C and ser_write()
90  *#*
91  *#* Revision 1.12  2004/08/24 13:49:39  bernie
92  *#* Fix thinko.
93  *#*
94  *#* Revision 1.11  2004/08/15 05:32:22  bernie
95  *#* ser_resync(): New function.
96  *#*
97  *#* Revision 1.10  2004/08/10 06:29:50  bernie
98  *#* Rename timer_gettick() to timer_ticks().
99  *#*
100  *#* Revision 1.9  2004/08/08 06:06:20  bernie
101  *#* Use new-style CONFIG_ idiom; Fix module-wide documentation.
102  *#*
103  *#* Revision 1.8  2004/07/29 22:57:09  bernie
104  *#* ser_drain(): New function; Make Serial::is_open a debug-only feature; Switch to new-style CONFIG_* macros.
105  *#*
106  *#* Revision 1.7  2004/07/18 21:49:03  bernie
107  *#* Make CONFIG_SER_DEFBAUDRATE optional.
108  *#*
109  *#* Revision 1.6  2004/06/07 15:56:28  aleph
110  *#* Remove cast-as-lvalue extension abuse
111  *#*
112  *#* Revision 1.5  2004/06/06 16:41:44  bernie
113  *#* ser_putchar(): Use fifo_push_locked() to fix potential race on 8bit processors.
114  *#*
115  *#* Revision 1.4  2004/06/03 11:27:09  bernie
116  *#* Add dual-license information.
117  *#*
118  *#* Revision 1.3  2004/06/02 21:35:24  aleph
119  *#* Serial enhancements: interruptible receive handler and 8 bit serial status for AVR; remove volatile attribute to FIFOBuffer, useless for new fifobuf routens
120  *#*
121  *#* Revision 1.2  2004/05/23 18:21:53  bernie
122  *#* Trim CVS logs and cleanup header info.
123  *#*
124  *#*/
125
126 #include "ser.h"
127 #include "ser_p.h"
128 #include <mware/formatwr.h>
129 #include <cfg/debug.h>
130 #include <appconfig.h>
131
132 /*
133  * Sanity check for config parameters required by this module.
134  */
135 #if !defined(CONFIG_KERNEL) || ((CONFIG_KERNEL != 0) && CONFIG_KERNEL != 1)
136         #error CONFIG_KERNEL must be set to either 0 or 1 in config.h
137 #endif
138 #if !defined(CONFIG_SER_RXTIMEOUT)
139         #error CONFIG_SER_TXTIMEOUT missing in config.h
140 #endif
141 #if !defined(CONFIG_SER_RXTIMEOUT)
142         #error CONFIG_SER_RXTIMEOUT missing in config.h
143 #endif
144 #if !defined(CONFIG_SER_GETS) || ((CONFIG_SER_GETS != 0) && CONFIG_SER_GETS != 1)
145         #error CONFIG_SER_GETS must be set to either 0 or 1 in config.h
146 #endif
147 #if !defined(CONFIG_SER_DEFBAUDRATE)
148         #error CONFIG_SER_DEFBAUDRATE missing in config.h
149 #endif
150 #if !defined(CONFIG_PRINTF)
151         #error CONFIG_PRINTF missing in config.h
152 #endif
153
154 #if CONFIG_KERNEL
155         #include <kern/proc.h>
156 #endif
157
158 #if CONFIG_SER_TXTIMEOUT != -1 || CONFIG_SER_RXTIMEOUT != -1
159         #include <drv/timer.h>
160 #endif
161
162
163 /* Serial configuration parameters */
164 #define SER_CTSDELAY        70  /**< CTS line retry interval (ms) */
165 #define SER_TXPOLLDELAY      2  /**< Transmit buffer full retry interval (ms) */
166 #define SER_RXPOLLDELAY      2  /**< Receive buffer empty retry interval (ms) */
167
168
169 struct Serial ser_handles[SER_CNT];
170
171
172 /**
173  * Inserisce il carattere c nel buffer di trasmissione.
174  * Questa funzione mette il processo chiamante in attesa
175  * quando il buffer e' pieno.
176  *
177  * \return EOF in caso di errore o timeout, altrimenti
178  *         il carattere inviato.
179  */
180 int ser_putchar(int c, struct Serial *port)
181 {
182         //ASSERT_VALID_FIFO(&port->txfifo);
183         if (fifo_isfull_locked(&port->txfifo))
184         {
185 #if CONFIG_SER_TXTIMEOUT != -1
186                 ticks_t start_time = timer_clock();
187 #endif
188
189                 /* Attende finche' il buffer e' pieno... */
190                 do
191                 {
192 #if CONFIG_KERNEL && CONFIG_KERN_SCHED
193                         /* Give up timeslice to other processes. */
194                         proc_switch();
195 #endif
196 #if CONFIG_SER_TXTIMEOUT != -1
197                         if (timer_clock() - start_time >= port->txtimeout)
198                         {
199                                 port->status |= SERRF_TXTIMEOUT;
200                                 return EOF;
201                         }
202 #endif /* CONFIG_SER_TXTIMEOUT */
203                 }
204                 while (fifo_isfull_locked(&port->txfifo));
205         }
206
207         fifo_push_locked(&port->txfifo, (unsigned char)c);
208
209         /* (re)trigger tx interrupt */
210         port->hw->table->txStart(port->hw);
211
212         /* Avoid returning signed extended char */
213         return (int)((unsigned char)c);
214 }
215
216
217 /**
218  * Preleva un carattere dal buffer di ricezione.
219  * Questa funzione mette il processo chiamante in attesa
220  * quando il buffer e' vuoto. L'attesa ha un timeout
221  * di ser_rxtimeout millisecondi.
222  *
223  * \return EOF in caso di errore o timeout, altrimenti
224  *         il carattere ricevuto.
225  */
226 int ser_getchar(struct Serial *port)
227 {
228         if (fifo_isempty_locked(&port->rxfifo))
229         {
230 #if CONFIG_SER_RXTIMEOUT != -1
231                 ticks_t start_time = timer_clock();
232 #endif
233                 /* Wait while buffer is empty */
234                 do
235                 {
236 #if CONFIG_KERNEL && CONFIG_KERN_SCHED
237                         /* Give up timeslice to other processes. */
238                         proc_switch();
239 #endif
240 #if CONFIG_SER_RXTIMEOUT != -1
241                         if (timer_clock() - start_time >= port->rxtimeout)
242                         {
243                                 port->status |= SERRF_RXTIMEOUT;
244                                 return EOF;
245                         }
246 #endif /* CONFIG_SER_RXTIMEOUT */
247                 }
248                 while (fifo_isempty_locked(&port->rxfifo) && (port->status & SERRF_RX) == 0);
249         }
250
251         /*
252          * Get a byte from the FIFO (avoiding sign-extension),
253          * re-enable RTS, then return result.
254          */
255         if (port->status & SERRF_RX)
256                 return EOF;
257         return (int)(unsigned char)fifo_pop_locked(&port->rxfifo);
258 }
259
260
261 /**
262  * Preleva un carattere dal buffer di ricezione.
263  * Se il buffer e' vuoto, ser_getchar_nowait() ritorna
264  * immediatamente EOF.
265  */
266 int ser_getchar_nowait(struct Serial *port)
267 {
268         if (fifo_isempty_locked(&port->rxfifo))
269                 return EOF;
270
271         /* NOTE: the double cast prevents unwanted sign extension */
272         return (int)(unsigned char)fifo_pop_locked(&port->rxfifo);
273 }
274
275
276 #if CONFIG_SER_GETS
277 /**
278  * Read a line long at most as size and put it
279  * in buf.
280  * \return number of chars read or EOF in case
281  *         of error.
282  */
283 int ser_gets(struct Serial *port, char *buf, int size)
284 {
285         return ser_gets_echo(port, buf, size, false);
286 }
287
288
289 /**
290  * Read a line long at most as size and put it
291  * in buf, with optional echo.
292  *
293  * \return number of chars read, or EOF in case
294  *         of error.
295  */
296 int ser_gets_echo(struct Serial *port, char *buf, int size, bool echo)
297 {
298         int i = 0;
299         int c;
300
301         for (;;)
302         {
303                 if ((c = ser_getchar(port)) == EOF)
304                 {
305                         buf[i] = '\0';
306                         return -1;
307                 }
308
309                 /* FIXME */
310                 if (c == '\r' || c == '\n' || i >= size-1)
311                 {
312                         buf[i] = '\0';
313                         if (echo)
314                                 ser_print(port, "\r\n");
315                         break;
316                 }
317                 buf[i++] = c;
318                 if (echo)
319                         ser_putchar(c, port);
320         }
321
322         return i;
323 }
324 #endif /* !CONFIG_SER_GETS */
325
326
327 /**
328  * Read at most \a size bytes from \a port and put them in \a buf
329  *
330  * \return number of bytes actually read, or EOF in
331  *         case of error.
332  */
333 int ser_read(struct Serial *port, void *buf, size_t size)
334 {
335         size_t i = 0;
336         char *_buf = (char *)buf;
337         int c;
338
339         while (i < size)
340         {
341                 if ((c = ser_getchar(port)) == EOF)
342                         return EOF;
343                 _buf[i++] = c;
344         }
345
346         return i;
347 }
348
349
350 /**
351  * Write a string to serial.
352  * \return 0 if OK, EOF in case of error.
353  */
354 int ser_print(struct Serial *port, const char *s)
355 {
356         while (*s)
357         {
358                 if (ser_putchar(*s++, port) == EOF)
359                         return EOF;
360         }
361         return 0;
362 }
363
364
365 /**
366  * \brief Write a buffer to serial.
367  *
368  * \return 0 if OK, EOF in case of error.
369  *
370  * \todo Optimize with fifo_pushblock()
371  */
372 int ser_write(struct Serial *port, const void *_buf, size_t len)
373 {
374         const char *buf = (const char *)_buf;
375
376         while (len--)
377         {
378                 if (ser_putchar(*buf++, port) == EOF)
379                         return EOF;
380         }
381         return 0;
382 }
383
384
385 #if CONFIG_PRINTF
386 /**
387  * Formatted write
388  */
389 int ser_printf(struct Serial *port, const char *format, ...)
390 {
391         va_list ap;
392         int len;
393
394         va_start(ap, format);
395         len = _formatted_write(format, (void (*)(char, void *))ser_putchar, port, ap);
396         va_end(ap);
397
398         return len;
399 }
400 #endif /* CONFIG_PRINTF */
401
402
403 #if CONFIG_SER_RXTIMEOUT != -1 || CONFIG_SER_TXTIMEOUT != -1
404 void ser_settimeouts(struct Serial *port, mtime_t rxtimeout, mtime_t txtimeout)
405 {
406         port->rxtimeout = ms_to_ticks(rxtimeout);
407         port->txtimeout = ms_to_ticks(txtimeout);
408 }
409 #endif /* CONFIG_SER_RXTIMEOUT || CONFIG_SER_TXTIMEOUT */
410
411 #if CONFIG_SER_RXTIMEOUT != -1
412 /**
413  * Discard input to resynchronize with remote end.
414  *
415  * Discard incoming data until the port stops receiving
416  * characters for at least \a delay milliseconds.
417  *
418  * \note Serial errors are reset before and after executing the purge.
419  */
420 void ser_resync(struct Serial *port, mtime_t delay)
421 {
422         mtime_t old_rxtimeout = ticks_to_ms(port->rxtimeout);
423
424         ser_settimeouts(port, delay, ticks_to_ms(port->txtimeout));
425         do
426         {
427                 ser_setstatus(port, 0);
428                 ser_getchar(port);
429         }
430         while (!(ser_getstatus(port) & SERRF_RXTIMEOUT));
431
432         /* Restore port to an usable status */
433         ser_setstatus(port, 0);
434         ser_settimeouts(port, old_rxtimeout, ticks_to_ms(port->txtimeout));
435 }
436 #endif /* CONFIG_SER_RXTIMEOUT */
437
438
439 void ser_setbaudrate(struct Serial *port, unsigned long rate)
440 {
441         port->hw->table->setBaudrate(port->hw, rate);
442 }
443
444
445 void ser_setparity(struct Serial *port, int parity)
446 {
447         port->hw->table->setParity(port->hw, parity);
448 }
449
450
451 /**
452  * Flush both the RX and TX buffers.
453  */
454 void ser_purge(struct Serial *port)
455 {
456         fifo_flush_locked(&port->rxfifo);
457         fifo_flush_locked(&port->txfifo);
458 }
459
460 /**
461  * Get status of port \c port.
462  */
463 serstatus_t ser_getstatus(struct Serial *port)
464 {
465         serstatus_t status;
466         ATOMIC(status = port->status);
467
468         return status;
469 }
470
471
472 /**
473  * Set new \c port status.
474  */
475 void ser_setstatus(struct Serial *port, serstatus_t status)
476 {
477         ATOMIC(port->status = status);
478 }
479
480
481 /**
482  * Wait until all pending output is completely
483  * transmitted to the other end.
484  *
485  * \note The current implementation only checks the
486  *       software transmission queue. Any hardware
487  *       FIFOs are ignored.
488  */
489 void ser_drain(struct Serial *ser)
490 {
491         /*
492          * Wait until the FIFO becomes empty, and then until the byte currently in
493          * the hardware register gets shifted out.
494          */
495         while (!fifo_isempty(&ser->txfifo)
496                || ser->hw->table->txSending(ser->hw))
497         {
498                 #if CONFIG_KERNEL && CONFIG_KERN_SCHED
499                         /* Give up timeslice to other processes. */
500                         proc_switch();
501                 #endif
502         }
503 }
504
505
506 /**
507  * Initialize a serial port.
508  *
509  * \param unit  Serial unit to open. Possible values are architecture dependant.
510  */
511 struct Serial *ser_open(unsigned int unit)
512 {
513         struct Serial *port;
514
515         ASSERT(unit < countof(ser_handles));
516         port = &ser_handles[unit];
517
518         ASSERT(!port->is_open);
519         DB(port->is_open = true;)
520
521         port->unit = unit;
522
523         port->hw = ser_hw_getdesc(unit);
524
525         /* Initialize circular buffers */
526         ASSERT(port->hw->txbuffer);
527         ASSERT(port->hw->rxbuffer);
528         fifo_init(&port->txfifo, port->hw->txbuffer, port->hw->txbuffer_size);
529         fifo_init(&port->rxfifo, port->hw->rxbuffer, port->hw->rxbuffer_size);
530
531         port->hw->table->init(port->hw, port);
532
533         /* Set default values */
534 #if CONFIG_SER_RXTIMEOUT != -1 || CONFIG_SER_TXTIMEOUT != -1
535         ser_settimeouts(port, CONFIG_SER_RXTIMEOUT, CONFIG_SER_TXTIMEOUT);
536 #endif
537 #if CONFIG_SER_DEFBAUDRATE
538         ser_setbaudrate(port, CONFIG_SER_DEFBAUDRATE);
539 #endif
540
541         /* Clear error flags */
542         ser_setstatus(port, 0);
543
544         return port;
545 }
546
547
548 /**
549  * Clean up serial port, disabling the associated hardware.
550  */
551 void ser_close(struct Serial *port)
552 {
553         ASSERT(port->is_open);
554         DB(port->is_open = false;)
555
556         // Wait until we finish sending everything
557         ser_drain(port);
558
559         port->hw->table->cleanup(port->hw);
560         DB(port->hw = NULL;)
561
562         /*
563          * We purge the FIFO buffer only after the low-level cleanup, so that
564          * we are sure that there are no more interrupts.
565          */
566         ser_purge(port);
567 }