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