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