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