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