Serial enhancements: interruptible receive handler and 8 bit serial status for AVR...
[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  * All Rights Reserved.
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  *      \li \c CONFIG_SER_HWHANDSHAKE define this preprocessor symbol to enable
20  *      support for RTS/CTS handshake. Support is incomplete/untested
21  *      for 80196.
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.3  2004/06/02 21:35:24  aleph
32  * Serial enhancements: interruptible receive handler and 8 bit serial status for AVR; remove volatile attribute to FIFOBuffer, useless for new fifobuf routens
33  *
34  * Revision 1.2  2004/05/23 18:21:53  bernie
35  * Trim CVS logs and cleanup header info.
36  *
37  */
38
39 #include <mware/formatwr.h>
40 #include <drv/kdebug.h>
41 #include "ser.h"
42 #include "ser_p.h"
43 #include "hw.h"
44
45 #ifdef CONFIG_KERNEL
46         #include <kern/proc.h>
47 #endif
48 #if defined(CONFIG_SER_TXTIMEOUT) || defined(CONFIG_SER_RXTIMEOUT)
49         #include <drv/timer.h>
50 #endif
51
52
53 /* Serial configuration parameters */
54 #define SER_CTSDELAY        70  /*!< CTS line retry interval (ms) */
55 #define SER_TXPOLLDELAY      2  /*!< Transmit buffer full retry interval (ms) */
56 #define SER_RXPOLLDELAY      2  /*!< Receive buffer empty retry interval (ms) */
57
58
59 struct Serial ser_handles[SER_CNT];
60
61
62 /*!
63  * Inserisce il carattere c nel buffer di trasmissione.
64  * Questa funzione mette il processo chiamante in attesa
65  * quando il buffer e' pieno.
66  *
67  * \return EOF in caso di errore o timeout, altrimenti
68  *         il carattere inviato.
69  */
70 int ser_putchar(int c, struct Serial *port)
71 {
72         if (fifo_isfull_locked(&port->txfifo))
73         {
74 #ifdef CONFIG_SER_TXTIMEOUT
75                 time_t start_time = timer_gettick();
76 #endif
77
78                 /* Attende finche' il buffer e' pieno... */
79                 do
80                 {
81 #ifdef CONFIG_KERN_SCHED
82                         /* Give up timeslice to other processes. */
83                         proc_switch();
84 #endif
85 #ifdef CONFIG_SER_TXTIMEOUT
86                         if (timer_gettick() - start_time >= port->txtimeout)
87                         {
88                                 port->status |= SERRF_TXTIMEOUT;
89                                 return EOF;
90                         }
91 #endif /* CONFIG_SER_TXTIMEOUT */
92                 }
93                 while (fifo_isfull_locked(&port->txfifo));
94         }
95
96         fifo_push(&port->txfifo, (unsigned char)c);
97
98         /* (re)trigger tx interrupt */
99         port->hw->table->enabletxirq(port->hw);
100
101         /* Avoid returning signed estended char */
102         return (int)((unsigned char)c);
103 }
104
105
106 /*!
107  * Preleva un carattere dal buffer di ricezione.
108  * Questa funzione mette il processo chiamante in attesa
109  * quando il buffer e' vuoto. L'attesa ha un timeout
110  * di ser_rxtimeout millisecondi.
111  *
112  * \return EOF in caso di errore o timeout, altrimenti
113  *         il carattere ricevuto.
114  */
115 int ser_getchar(struct Serial *port)
116 {
117         int result;
118
119         if (fifo_isempty_locked(&port->rxfifo))
120         {
121 #ifdef CONFIG_SER_RXTIMEOUT
122                 time_t start_time = timer_gettick();
123 #endif
124                 /* Wait while buffer is empty */
125                 do
126                 {
127 #ifdef CONFIG_KERN_SCHED
128                         /* Give up timeslice to other processes. */
129                         proc_switch();
130 #endif
131 #ifdef CONFIG_SER_RXTIMEOUT
132                         if (timer_gettick() - start_time >= port->rxtimeout)
133                         {
134                                 port->status |= SERRF_RXTIMEOUT;
135                                 return EOF;
136                         }
137 #endif /* CONFIG_SER_RXTIMEOUT */
138                 }
139                 while (fifo_isempty_locked(&port->rxfifo));
140         }
141
142         /*
143          * Get a byte from the FIFO (avoiding sign-extension),
144          * re-enable RTS, then return result.
145          */
146         result = (int)(unsigned char)fifo_pop(&port->rxfifo);
147         return port->status ? EOF : result;
148 }
149
150
151 /*!
152  * Preleva un carattere dal buffer di ricezione.
153  * Se il buffer e' vuoto, ser_getchar_nowait() ritorna
154  * immediatamente EOF.
155  */
156 int ser_getchar_nowait(struct Serial *port)
157 {
158         if (fifo_isempty_locked(&port->rxfifo))
159                 return EOF;
160
161         /* NOTE: the double cast prevents unwanted sign extension */
162         return (int)(unsigned char)fifo_pop(&port->rxfifo);
163 }
164
165
166 /*!
167  * Read a line long at most as size and puts it
168  * in buf.
169  * \return number of chars read or EOF in case
170  *         of error.
171  */
172 int ser_gets(struct Serial *port, char *buf, int size)
173 {
174         return ser_gets_echo(port, buf, size, false);
175 }
176
177
178 /*!
179  * Read a line long at most as size and puts it
180  * in buf, with optional echo.
181  * \return number of chars read or EOF in case
182  *         of error.
183  */
184 int ser_gets_echo(struct Serial *port, char *buf, int size, bool echo)
185 {
186         int i = 0;
187         int c;
188
189         for (;;)
190         {
191                 if ((c = ser_getchar(port)) == EOF)
192                         return -1;
193                 /* FIXME */
194                 if (c == '\r' || c == '\n' || i >= size-1)
195                 {
196                         buf[i] = '\0';
197                         if (echo)
198                                 ser_print(port, "\r\n");
199                         break;
200                 }
201                 buf[i++] = c;
202                 if (echo)
203                         ser_putchar(c, port);
204         }
205
206         return i;
207 }
208
209
210 /*!
211  * Read at most size bytes and puts them
212  * in buf.
213  * \return number of bytes read or EOF in case
214  *         of error.
215  */
216 int ser_read(struct Serial *port, char *buf, size_t size)
217 {
218         size_t i = 0;
219         int c;
220
221         while (i < size)
222         {
223                 if ((c = ser_getchar(port)) == EOF)
224                         return EOF;
225                 buf[i++] = c;
226         }
227
228         return i;
229 }
230
231
232 /*!
233  * Write a string to serial.
234  * \return 0 if OK, EOF in case of error.
235  */
236 int ser_print(struct Serial *port, const char *s)
237 {
238         while (*s)
239         {
240                 if (ser_putchar(*s++, port) == EOF)
241                         return EOF;
242         }
243         return 0;
244 }
245
246
247 /*!
248  * \brief Write a buffer to serial.
249  *
250  * \return 0 if OK, EOF in case of error.
251  */
252 int ser_write(struct Serial *port, const void *buf, size_t len)
253 {
254         while (len--)
255         {
256                 if (ser_putchar(*((const char *)buf)++, port) == EOF)
257                         return EOF;
258         }
259         return 0;
260 }
261
262
263 /*!
264  * Formatted write
265  */
266 int ser_printf(struct Serial *port, const char *format, ...)
267 {
268         va_list ap;
269         int len;
270
271         ser_setstatus(port, 0);
272         va_start(ap, format);
273         len = _formatted_write(format, (void (*)(char, void *))ser_putchar, port, ap);
274         va_end(ap);
275
276         return len;
277 }
278
279 #if defined(CONFIG_SER_RXTIMEOUT) || defined(CONFIG_SER_TXTIMEOUT)
280 void ser_settimeouts(struct Serial *port, time_t rxtimeout, time_t txtimeout)
281 {
282         port->rxtimeout = rxtimeout;
283         port->txtimeout = txtimeout;
284 }
285 #endif /* defined(CONFIG_SER_RXTIMEOUT) || defined(CONFIG_SER_TXTIMEOUT) */
286
287
288 void ser_setbaudrate(struct Serial *port, unsigned long rate)
289 {
290         port->hw->table->setbaudrate(port->hw, rate);
291 }
292
293
294 void ser_setparity(struct Serial *port, int parity)
295 {
296         port->hw->table->setparity(port->hw, parity);
297 }
298
299
300 /*!
301  * Flush both the RX and TX buffers.
302  */
303 void ser_purge(struct Serial *ser)
304 {
305         fifo_flush(&ser->rxfifo);
306         fifo_flush(&ser->txfifo);
307 }
308
309
310 /*!
311  * Initialize serial
312  */
313 struct Serial *ser_open(unsigned int unit)
314 {
315         struct Serial *port;
316
317         ASSERT(unit < countof(ser_handles));
318
319         port = &ser_handles[unit];
320
321         ASSERT(!port->is_open);
322
323         port->unit = unit;
324         port->is_open = true;
325
326         /* Initialize circular buffer */
327         fifo_init(&port->rxfifo, port->rxbuffer, sizeof(port->rxbuffer));
328         fifo_init(&port->txfifo, port->txbuffer, sizeof(port->txbuffer));
329
330         port->hw = ser_hw_getdesc(unit);
331         port->hw->table->init(port->hw, port);
332
333         /* Set default values */
334 #if defined(CONFIG_SER_RXTIMEOUT) || defined(CONFIG_SER_TXTIMEOUT)
335         ser_settimeouts(port, CONFIG_SER_RXTIMEOUT, CONFIG_SER_TXTIMEOUT);
336 #endif
337         ser_setbaudrate(port, CONFIG_SER_DEFBAUDRATE);
338
339         return port;
340 }
341
342
343 /*!
344  * Clean up serial port, disabling the associated hardware.
345  */
346 void ser_close(struct Serial *port)
347 {
348         ASSERT(port->is_open);
349
350         port->is_open = false;
351         port->hw->table->cleanup(port->hw);
352         port->hw = NULL;
353 }