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