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