Remove CVS logs. Add ASSERT.
[bertos.git] / drv / ser.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2003, 2004, 2006 Develer S.r.l. (http://www.develer.com/)
30  * Copyright 2000 Bernardo Innocenti <bernie@codewiz.org>
31  *
32  * -->
33  *
34  * \brief Buffered serial I/O driver
35  *
36  * The serial rx interrupt buffers incoming data in a software FIFO
37  * to decouple the higher level protocols from the line speed.
38  * Outgoing data is buffered as well for better performance.
39  * This driver is not optimized for best performance, but it
40  * has proved to be fast enough to handle transfer rates up to
41  * 38400bps on a 16MHz 80196.
42  *
43  * MODULE CONFIGURATION
44  *
45  *  \li \c CONFIG_SER_HWHANDSHAKE - set to 1 to enable RTS/CTS handshake.
46  *         Support is incomplete/untested.
47  *  \li \c CONFIG_SER_TXTIMEOUT - Enable software serial transmission timeouts
48  *
49  *
50  * \version $Id$
51  * \author Bernardo Innocenti <bernie@develer.com>
52  */
53
54 #include "ser.h"
55 #include "wdt.h"
56 #include "ser_p.h"
57 #include <mware/formatwr.h>
58 #include <cfg/debug.h>
59 #include <appconfig.h>
60
61 /*
62  * Sanity check for config parameters required by this module.
63  */
64 #if !defined(CONFIG_KERNEL) || ((CONFIG_KERNEL != 0) && CONFIG_KERNEL != 1)
65         #error CONFIG_KERNEL must be set to either 0 or 1 in config.h
66 #endif
67 #if !defined(CONFIG_SER_RXTIMEOUT)
68         #error CONFIG_SER_TXTIMEOUT missing in config.h
69 #endif
70 #if !defined(CONFIG_SER_RXTIMEOUT)
71         #error CONFIG_SER_RXTIMEOUT missing in config.h
72 #endif
73 #if !defined(CONFIG_SER_GETS) || ((CONFIG_SER_GETS != 0) && CONFIG_SER_GETS != 1)
74         #error CONFIG_SER_GETS must be set to either 0 or 1 in config.h
75 #endif
76 #if !defined(CONFIG_SER_DEFBAUDRATE)
77         #error CONFIG_SER_DEFBAUDRATE missing in config.h
78 #endif
79 #if !defined(CONFIG_PRINTF)
80         #error CONFIG_PRINTF missing in config.h
81 #endif
82
83 #if CONFIG_KERNEL
84         #include <kern/proc.h>
85 #endif
86
87 #if CONFIG_SER_TXTIMEOUT != -1 || CONFIG_SER_RXTIMEOUT != -1
88         #include <drv/timer.h>
89 #endif
90
91
92 /* Serial configuration parameters */
93 #define SER_CTSDELAY        70  /**< CTS line retry interval (ms) */
94 #define SER_TXPOLLDELAY      2  /**< Transmit buffer full retry interval (ms) */
95 #define SER_RXPOLLDELAY      2  /**< Receive buffer empty retry interval (ms) */
96
97
98 struct Serial ser_handles[SER_CNT];
99
100
101 /**
102  * Inserisce il carattere c nel buffer di trasmissione.
103  * Questa funzione mette il processo chiamante in attesa
104  * quando il buffer e' pieno.
105  *
106  * \return EOF in caso di errore o timeout, altrimenti
107  *         il carattere inviato.
108  */
109 int ser_putchar(int c, struct Serial *port)
110 {
111         //ASSERT_VALID_FIFO(&port->txfifo);
112         if (fifo_isfull_locked(&port->txfifo))
113         {
114 #if CONFIG_SER_TXTIMEOUT != -1
115                 ticks_t start_time = timer_clock();
116 #endif
117
118                 /* Attende finche' il buffer e' pieno... */
119                 do
120                 {
121                         wdt_reset();
122 #if CONFIG_KERNEL && CONFIG_KERN_SCHED
123                         /* Give up timeslice to other processes. */
124                         proc_switch();
125 #endif
126 #if CONFIG_SER_TXTIMEOUT != -1
127                         if (timer_clock() - start_time >= port->txtimeout)
128                         {
129                                 ATOMIC(port->status |= SERRF_TXTIMEOUT);
130                                 return EOF;
131                         }
132 #endif /* CONFIG_SER_TXTIMEOUT */
133                 }
134                 while (fifo_isfull_locked(&port->txfifo));
135         }
136
137         fifo_push_locked(&port->txfifo, (unsigned char)c);
138
139         /* (re)trigger tx interrupt */
140         port->hw->table->txStart(port->hw);
141
142         /* Avoid returning signed extended char */
143         return (int)((unsigned char)c);
144 }
145
146
147 /**
148  * Preleva un carattere dal buffer di ricezione.
149  * Questa funzione mette il processo chiamante in attesa
150  * quando il buffer e' vuoto. L'attesa ha un timeout
151  * di ser_rxtimeout millisecondi.
152  *
153  * \return EOF in caso di errore o timeout, altrimenti
154  *         il carattere ricevuto.
155  */
156 int ser_getchar(struct Serial *port)
157 {
158         if (fifo_isempty_locked(&port->rxfifo))
159         {
160 #if CONFIG_SER_RXTIMEOUT != -1
161                 ticks_t start_time = timer_clock();
162 #endif
163                 /* Wait while buffer is empty */
164                 do
165                 {
166                         wdt_reset();
167 #if CONFIG_KERNEL && CONFIG_KERN_SCHED
168                         /* Give up timeslice to other processes. */
169                         proc_switch();
170 #endif
171 #if CONFIG_SER_RXTIMEOUT != -1
172                         if (timer_clock() - start_time >= port->rxtimeout)
173                         {
174                                 ATOMIC(port->status |= SERRF_RXTIMEOUT);
175                                 return EOF;
176                         }
177 #endif /* CONFIG_SER_RXTIMEOUT */
178                 }
179                 while (fifo_isempty_locked(&port->rxfifo) && (ser_getstatus(port) & SERRF_RX) == 0);
180         }
181
182         /*
183          * Get a byte from the FIFO (avoiding sign-extension),
184          * re-enable RTS, then return result.
185          */
186         if (ser_getstatus(port) & SERRF_RX)
187                 return EOF;
188         return (int)(unsigned char)fifo_pop_locked(&port->rxfifo);
189 }
190
191
192 /**
193  * Preleva un carattere dal buffer di ricezione.
194  * Se il buffer e' vuoto, ser_getchar_nowait() ritorna
195  * immediatamente EOF.
196  */
197 int ser_getchar_nowait(struct Serial *port)
198 {
199         if (fifo_isempty_locked(&port->rxfifo))
200                 return EOF;
201
202         /* NOTE: the double cast prevents unwanted sign extension */
203         return (int)(unsigned char)fifo_pop_locked(&port->rxfifo);
204 }
205
206
207 #if CONFIG_SER_GETS
208 /**
209  * Read a line long at most as size and put it
210  * in buf.
211  * \return number of chars read or EOF in case
212  *         of error.
213  */
214 int ser_gets(struct Serial *port, char *buf, int size)
215 {
216         return ser_gets_echo(port, buf, size, false);
217 }
218
219
220 /**
221  * Read a line long at most as size and put it
222  * in buf, with optional echo.
223  *
224  * \return number of chars read, or EOF in case
225  *         of error.
226  */
227 int ser_gets_echo(struct Serial *port, char *buf, int size, bool echo)
228 {
229         int i = 0;
230         int c;
231
232         for (;;)
233         {
234                 if ((c = ser_getchar(port)) == EOF)
235                 {
236                         buf[i] = '\0';
237                         return -1;
238                 }
239
240                 /* FIXME */
241                 if (c == '\r' || c == '\n' || i >= size-1)
242                 {
243                         buf[i] = '\0';
244                         if (echo)
245                                 ser_print(port, "\r\n");
246                         break;
247                 }
248                 buf[i++] = c;
249                 if (echo)
250                         ser_putchar(c, port);
251         }
252
253         return i;
254 }
255 #endif /* !CONFIG_SER_GETS */
256
257
258 /**
259  * Read at most \a size bytes from \a port and put them in \a buf
260  *
261  * \return number of bytes actually read, or EOF in
262  *         case of error.
263  */
264 int ser_read(struct Serial *port, void *buf, size_t size)
265 {
266         size_t i = 0;
267         char *_buf = (char *)buf;
268         int c;
269
270         while (i < size)
271         {
272                 if ((c = ser_getchar(port)) == EOF)
273                         return EOF;
274                 _buf[i++] = c;
275         }
276
277         return i;
278 }
279
280
281 /**
282  * Write a string to serial.
283  * \return 0 if OK, EOF in case of error.
284  */
285 int ser_print(struct Serial *port, const char *s)
286 {
287         while (*s)
288         {
289                 if (ser_putchar(*s++, port) == EOF)
290                         return EOF;
291         }
292         return 0;
293 }
294
295
296 /**
297  * \brief Write a buffer to serial.
298  *
299  * \return 0 if OK, EOF in case of error.
300  *
301  * \todo Optimize with fifo_pushblock()
302  */
303 int ser_write(struct Serial *port, const void *_buf, size_t len)
304 {
305         const char *buf = (const char *)_buf;
306
307         while (len--)
308         {
309                 if (ser_putchar(*buf++, port) == EOF)
310                         return EOF;
311         }
312         return 0;
313 }
314
315
316 #if CONFIG_PRINTF
317 /**
318  * Formatted write
319  */
320 int ser_printf(struct Serial *port, const char *format, ...)
321 {
322         va_list ap;
323         int len;
324
325         va_start(ap, format);
326         len = _formatted_write(format, (void (*)(char, void *))ser_putchar, port, ap);
327         va_end(ap);
328
329         return len;
330 }
331 #endif /* CONFIG_PRINTF */
332
333
334 #if CONFIG_SER_RXTIMEOUT != -1 || CONFIG_SER_TXTIMEOUT != -1
335 void ser_settimeouts(struct Serial *port, mtime_t rxtimeout, mtime_t txtimeout)
336 {
337         port->rxtimeout = ms_to_ticks(rxtimeout);
338         port->txtimeout = ms_to_ticks(txtimeout);
339 }
340 #endif /* CONFIG_SER_RXTIMEOUT || CONFIG_SER_TXTIMEOUT */
341
342 #if CONFIG_SER_RXTIMEOUT != -1
343 /**
344  * Discard input to resynchronize with remote end.
345  *
346  * Discard incoming data until the port stops receiving
347  * characters for at least \a delay milliseconds.
348  *
349  * \note Serial errors are reset before and after executing the purge.
350  */
351 void ser_resync(struct Serial *port, mtime_t delay)
352 {
353         mtime_t old_rxtimeout = ticks_to_ms(port->rxtimeout);
354
355         ser_settimeouts(port, delay, ticks_to_ms(port->txtimeout));
356         do
357         {
358                 ser_setstatus(port, 0);
359                 ser_getchar(port);
360         }
361         while (!(ser_getstatus(port) & SERRF_RXTIMEOUT));
362
363         /* Restore port to an usable status */
364         ser_setstatus(port, 0);
365         ser_settimeouts(port, old_rxtimeout, ticks_to_ms(port->txtimeout));
366 }
367 #endif /* CONFIG_SER_RXTIMEOUT */
368
369
370 void ser_setbaudrate(struct Serial *port, unsigned long rate)
371 {
372         port->hw->table->setBaudrate(port->hw, rate);
373 }
374
375
376 void ser_setparity(struct Serial *port, int parity)
377 {
378         port->hw->table->setParity(port->hw, parity);
379 }
380
381 void ser_clearstatus(struct Serial *port)
382 {
383        ser_setstatus(port, 0);
384 }
385
386 /**
387  * Flush both the RX and TX buffers.
388  */
389 void ser_purge(struct Serial *port)
390 {
391         fifo_flush_locked(&port->rxfifo);
392         fifo_flush_locked(&port->txfifo);
393 }
394
395
396 /**
397  * Wait until all pending output is completely
398  * transmitted to the other end.
399  *
400  * \note The current implementation only checks the
401  *       software transmission queue. Any hardware
402  *       FIFOs are ignored.
403  */
404 void ser_drain(struct Serial *ser)
405 {
406         /*
407          * Wait until the FIFO becomes empty, and then until the byte currently in
408          * the hardware register gets shifted out.
409          */
410         while (!fifo_isempty(&ser->txfifo)
411                || ser->hw->table->txSending(ser->hw))
412         {
413                 #if CONFIG_KERNEL && CONFIG_KERN_SCHED
414                         /* Give up timeslice to other processes. */
415                         proc_switch();
416                 #endif
417                         wdt_reset();
418         }
419 }
420
421
422 /**
423  * Initialize a serial port.
424  *
425  * \param unit  Serial unit to open. Possible values are architecture dependant.
426  */
427 struct Serial *ser_open(unsigned int unit)
428 {
429         struct Serial *port;
430
431         ASSERT(unit < countof(ser_handles));
432         port = &ser_handles[unit];
433
434         ASSERT(!port->is_open);
435         DB(port->is_open = true;)
436
437         port->unit = unit;
438
439         port->hw = ser_hw_getdesc(unit);
440
441         /* Initialize circular buffers */
442         ASSERT(port->hw->txbuffer);
443         ASSERT(port->hw->rxbuffer);
444         fifo_init(&port->txfifo, port->hw->txbuffer, port->hw->txbuffer_size);
445         fifo_init(&port->rxfifo, port->hw->rxbuffer, port->hw->rxbuffer_size);
446
447         port->hw->table->init(port->hw, port);
448
449         /* Set default values */
450 #if CONFIG_SER_RXTIMEOUT != -1 || CONFIG_SER_TXTIMEOUT != -1
451         ser_settimeouts(port, CONFIG_SER_RXTIMEOUT, CONFIG_SER_TXTIMEOUT);
452 #endif
453 #if CONFIG_SER_DEFBAUDRATE
454         ser_setbaudrate(port, CONFIG_SER_DEFBAUDRATE);
455 #endif
456
457         /* Clear error flags */
458         ser_setstatus(port, 0);
459
460         return port;
461 }
462
463
464 /**
465  * Clean up serial port, disabling the associated hardware.
466  */
467 void ser_close(struct Serial *port)
468 {
469         ASSERT(port->is_open);
470         DB(port->is_open = false;)
471
472         // Wait until we finish sending everything
473         ser_drain(port);
474
475         port->hw->table->cleanup(port->hw);
476         DB(port->hw = NULL;)
477
478         /*
479          * We purge the FIFO buffer only after the low-level cleanup, so that
480          * we are sure that there are no more interrupts.
481          */
482         ser_purge(port);
483 }