source: rtems/cpukit/libcsupport/src/termios.c @ a830cb8

4.115
Last change on this file since a830cb8 was a830cb8, checked in by Sebastian Huber <sebastian.huber@…>, on Oct 7, 2014 at 2:27:51 PM

termios: Separate flow control from normal handler

  • Property mode set to 100644
File size: 50.5 KB
Line 
1/*
2 * TERMIOS serial line support
3 *
4 *  Author:
5 *    W. Eric Norum
6 *    Saskatchewan Accelerator Laboratory
7 *    University of Saskatchewan
8 *    Saskatoon, Saskatchewan, CANADA
9 *    eric@skatter.usask.ca
10 *
11 *  The license and distribution terms for this file may be
12 *  found in the file LICENSE in this distribution or at
13 *  http://www.rtems.org/license/LICENSE.
14 */
15
16#if HAVE_CONFIG_H
17#include "config.h"
18#endif
19
20#include <rtems.h>
21#include <rtems/libio.h>
22#include <ctype.h>
23#include <errno.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <termios.h>
27#include <unistd.h>
28#include <sys/ttycom.h>
29
30#include <rtems/termiostypes.h>
31
32/*
33 * The size of the cooked buffer
34 */
35#define CBUFSIZE  (rtems_termios_cbufsize)
36
37/*
38 * The sizes of the raw message buffers.
39 * On most architectures it is quite a bit more
40 * efficient if these are powers of two.
41 */
42#define RAW_INPUT_BUFFER_SIZE  (rtems_termios_raw_input_size)
43#define RAW_OUTPUT_BUFFER_SIZE  (rtems_termios_raw_output_size)
44
45/* fields for "flow_ctrl" status */
46#define FL_IREQXOF 1U        /* input queue requests stop of incoming data */
47#define FL_ISNTXOF 2U        /* XOFF has been sent to other side of line   */
48#define FL_IRTSOFF 4U        /* RTS has been turned off for other side..   */
49
50#define FL_ORCVXOF 0x10U     /* XOFF has been received                     */
51#define FL_OSTOP   0x20U     /* output has been stopped due to XOFF        */
52
53#define FL_MDRTS   0x100U    /* input controlled with RTS/CTS handshake    */
54#define FL_MDXON   0x200U    /* input controlled with XON/XOFF protocol    */
55#define FL_MDXOF   0x400U    /* output controlled with XON/XOFF protocol   */
56
57#define NODISC(n) \
58  { NULL,  NULL,  NULL,  NULL, \
59    NULL,  NULL,  NULL,  NULL }
60/*
61 * FIXME: change rtems_termios_linesw entries consistent
62 *        with rtems_termios_linesw entry usage...
63 */
64struct  rtems_termios_linesw rtems_termios_linesw[MAXLDISC] =
65{
66  NODISC(0),    /* 0- termios-built-in */
67  NODISC(1),    /* 1- defunct */
68  NODISC(2),    /* 2- NTTYDISC */
69  NODISC(3),    /* TABLDISC */
70  NODISC(4),    /* SLIPDISC */
71  NODISC(5),    /* PPPDISC */
72  NODISC(6),    /* loadable */
73  NODISC(7),    /* loadable */
74};
75
76int  rtems_termios_nlinesw =
77       sizeof (rtems_termios_linesw) / sizeof (rtems_termios_linesw[0]);
78
79extern rtems_id rtems_termios_ttyMutex;
80
81static size_t rtems_termios_cbufsize = 256;
82static size_t rtems_termios_raw_input_size = 128;
83static size_t rtems_termios_raw_output_size = 64;
84
85static struct rtems_termios_tty *rtems_termios_ttyHead;
86static struct rtems_termios_tty *rtems_termios_ttyTail;
87
88static RTEMS_CHAIN_DEFINE_EMPTY(rtems_termios_devices);
89
90static rtems_task rtems_termios_rxdaemon(rtems_task_argument argument);
91static rtems_task rtems_termios_txdaemon(rtems_task_argument argument);
92/*
93 * some constants for I/O daemon task creation
94 */
95#define TERMIOS_TXTASK_PRIO 10
96#define TERMIOS_RXTASK_PRIO 9
97#define TERMIOS_TXTASK_STACKSIZE 1024
98#define TERMIOS_RXTASK_STACKSIZE 1024
99/*
100 * some events to be sent to the I/O tasks
101 */
102#define TERMIOS_TX_START_EVENT     RTEMS_EVENT_1
103#define TERMIOS_TX_TERMINATE_EVENT RTEMS_EVENT_0
104
105#define TERMIOS_RX_PROC_EVENT      RTEMS_EVENT_1
106#define TERMIOS_RX_TERMINATE_EVENT RTEMS_EVENT_0
107
108static rtems_termios_device_node *
109rtems_termios_find_device_node(
110  rtems_device_major_number major,
111  rtems_device_minor_number minor
112)
113{
114  rtems_chain_node *tail = rtems_chain_tail(&rtems_termios_devices);
115  rtems_chain_node *current = rtems_chain_first(&rtems_termios_devices);
116
117  while (current != tail) {
118    rtems_termios_device_node *device_node =
119      (rtems_termios_device_node *) current;
120
121    if (device_node->major == major && device_node->minor == minor) {
122      return device_node;
123    }
124
125    current = rtems_chain_next(current);
126  }
127
128  return NULL;
129}
130
131rtems_status_code rtems_termios_device_install(
132  const char                         *device_file,
133  rtems_device_major_number           major,
134  rtems_device_minor_number           minor,
135  const rtems_termios_device_handler *handler,
136  const rtems_termios_device_flow    *flow,
137  void                               *context
138)
139{
140  rtems_status_code          sc;
141  rtems_termios_device_node *new_device_node;
142  rtems_termios_device_node *existing_device_node;
143
144  new_device_node = malloc(sizeof(*new_device_node));
145  if (new_device_node == NULL) {
146    return RTEMS_NO_MEMORY;
147  }
148
149  new_device_node->major = major;
150  new_device_node->minor = minor;
151  new_device_node->handler = handler;
152  new_device_node->flow = flow;
153  new_device_node->context = context;
154  new_device_node->tty = NULL;
155
156  sc = rtems_semaphore_obtain(
157    rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
158  if (sc != RTEMS_SUCCESSFUL) {
159    free(new_device_node);
160    return RTEMS_INCORRECT_STATE;
161  }
162
163  existing_device_node = rtems_termios_find_device_node (major, minor);
164  if (existing_device_node != NULL) {
165    free(new_device_node);
166    rtems_semaphore_release (rtems_termios_ttyMutex);
167    return RTEMS_RESOURCE_IN_USE;
168  }
169
170  if (device_file != NULL) {
171    sc = rtems_io_register_name (device_file, major, minor);
172    if (sc != RTEMS_SUCCESSFUL) {
173      free(new_device_node);
174      rtems_semaphore_release (rtems_termios_ttyMutex);
175      return RTEMS_UNSATISFIED;
176    }
177  }
178
179  rtems_chain_append_unprotected(
180    &rtems_termios_devices, &new_device_node->node);
181
182  rtems_semaphore_release (rtems_termios_ttyMutex);
183
184  return RTEMS_SUCCESSFUL;
185}
186
187rtems_status_code rtems_termios_device_remove(
188  const char                *device_file,
189  rtems_device_major_number  major,
190  rtems_device_minor_number  minor
191)
192{
193  rtems_status_code          sc;
194  rtems_termios_device_node *device_node;
195
196  sc = rtems_semaphore_obtain(
197    rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
198  if (sc != RTEMS_SUCCESSFUL) {
199    return RTEMS_INCORRECT_STATE;
200  }
201
202  device_node = rtems_termios_find_device_node (major, minor);
203  if (device_node == NULL) {
204    rtems_semaphore_release (rtems_termios_ttyMutex);
205    return RTEMS_INVALID_ID;
206  }
207
208  if (device_node->tty != NULL) {
209    rtems_semaphore_release (rtems_termios_ttyMutex);
210    return RTEMS_RESOURCE_IN_USE;
211  }
212
213  if (device_file != NULL) {
214    int rv = unlink (device_file);
215
216    if (rv != 0) {
217      rtems_semaphore_release (rtems_termios_ttyMutex);
218      return RTEMS_UNSATISFIED;
219    }
220  }
221
222  rtems_chain_extract_unprotected (&device_node->node);
223  free (device_node);
224
225  rtems_semaphore_release (rtems_termios_ttyMutex);
226
227  return RTEMS_SUCCESSFUL;
228}
229
230static bool
231rtems_termios_callback_firstOpen(
232  rtems_termios_tty             *tty,
233  rtems_libio_open_close_args_t *args
234)
235{
236  (*tty->device.firstOpen) (tty->major, tty->minor, args);
237
238  return true;
239}
240
241static void
242rtems_termios_callback_lastClose(
243  rtems_termios_tty             *tty,
244  rtems_libio_open_close_args_t *args
245)
246{
247  (*tty->device.lastClose) (tty->major, tty->minor, args);
248}
249
250static int
251rtems_termios_callback_pollRead (struct rtems_termios_tty *tty)
252{
253  return (*tty->device.pollRead) (tty->minor);
254}
255
256static void
257rtems_termios_callback_write(
258  rtems_termios_tty *tty,
259  const char        *buf,
260  size_t             len
261)
262{
263  (*tty->device.write) (tty->minor, buf, len);
264}
265
266static bool
267rtems_termios_callback_setAttributes(
268  rtems_termios_tty    *tty,
269  const struct termios *term
270)
271{
272  (*tty->device.setAttributes) (tty->minor, term);
273
274  return true;
275}
276
277static void
278rtems_termios_callback_stopRemoteTx (rtems_termios_tty *tty)
279{
280  (*tty->device.stopRemoteTx) (tty->minor);
281}
282
283static void
284rtems_termios_callback_startRemoteTx (rtems_termios_tty *tty)
285{
286  (*tty->device.startRemoteTx) (tty->minor);
287}
288
289/*
290 * Drain output queue
291 */
292static void
293drainOutput (struct rtems_termios_tty *tty)
294{
295  rtems_interrupt_lock_context lock_context;
296  rtems_status_code sc;
297
298  if (tty->handler.mode != TERMIOS_POLLED) {
299    rtems_termios_interrupt_lock_acquire (tty, &lock_context);
300    while (tty->rawOutBuf.Tail != tty->rawOutBuf.Head) {
301      tty->rawOutBufState = rob_wait;
302      rtems_termios_interrupt_lock_release (tty, &lock_context);
303      sc = rtems_semaphore_obtain(
304        tty->rawOutBuf.Semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
305      if (sc != RTEMS_SUCCESSFUL)
306        rtems_fatal_error_occurred (sc);
307      rtems_termios_interrupt_lock_acquire (tty, &lock_context);
308    }
309    rtems_termios_interrupt_lock_release (tty, &lock_context);
310  }
311}
312
313static void
314rtems_termios_destroy_tty (rtems_termios_tty *tty, void *arg, bool last_close)
315{
316  rtems_status_code sc;
317
318  if (rtems_termios_linesw[tty->t_line].l_close != NULL) {
319    /*
320     * call discipline-specific close
321     */
322    sc = rtems_termios_linesw[tty->t_line].l_close(tty);
323  } else if (last_close) {
324    /*
325     * default: just flush output buffer
326     */
327    sc = rtems_semaphore_obtain(tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
328    if (sc != RTEMS_SUCCESSFUL) {
329      rtems_fatal_error_occurred (sc);
330    }
331    drainOutput (tty);
332    rtems_semaphore_release (tty->osem);
333  }
334
335  if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
336    /*
337     * send "terminate" to I/O tasks
338     */
339    sc = rtems_event_send( tty->rxTaskId, TERMIOS_RX_TERMINATE_EVENT );
340    if (sc != RTEMS_SUCCESSFUL)
341      rtems_fatal_error_occurred (sc);
342    sc = rtems_event_send( tty->txTaskId, TERMIOS_TX_TERMINATE_EVENT );
343    if (sc != RTEMS_SUCCESSFUL)
344      rtems_fatal_error_occurred (sc);
345  }
346  if (last_close && tty->handler.last_close)
347     (*tty->handler.last_close)(tty, arg);
348
349  if (tty->device_node != NULL)
350    tty->device_node->tty = NULL;
351
352  rtems_semaphore_delete (tty->isem);
353  rtems_semaphore_delete (tty->osem);
354  rtems_semaphore_delete (tty->rawOutBuf.Semaphore);
355  if ((tty->handler.poll_read == NULL) ||
356      (tty->handler.mode == TERMIOS_TASK_DRIVEN))
357    rtems_semaphore_delete (tty->rawInBuf.Semaphore);
358  rtems_interrupt_lock_destroy (&tty->interrupt_lock);
359  free (tty->rawInBuf.theBuf);
360  free (tty->rawOutBuf.theBuf);
361  free (tty->cbuf);
362  free (tty);
363}
364
365static rtems_termios_tty *
366rtems_termios_open_tty(
367  rtems_device_major_number      major,
368  rtems_device_minor_number      minor,
369  rtems_libio_open_close_args_t *args,
370  rtems_termios_tty             *tty,
371  rtems_termios_device_node     *device_node,
372  const rtems_termios_callbacks *callbacks
373)
374{
375  rtems_status_code sc;
376
377  if (tty == NULL) {
378    static char c = 'a';
379
380    /*
381     * Create a new device
382     */
383    tty = calloc (1, sizeof (struct rtems_termios_tty));
384    if (tty == NULL) {
385      return NULL;
386    }
387    /*
388     * allocate raw input buffer
389     */
390    tty->rawInBuf.Size = RAW_INPUT_BUFFER_SIZE;
391    tty->rawInBuf.theBuf = malloc (tty->rawInBuf.Size);
392    if (tty->rawInBuf.theBuf == NULL) {
393            free(tty);
394      return NULL;
395    }
396    /*
397     * allocate raw output buffer
398     */
399    tty->rawOutBuf.Size = RAW_OUTPUT_BUFFER_SIZE;
400    tty->rawOutBuf.theBuf = malloc (tty->rawOutBuf.Size);
401    if (tty->rawOutBuf.theBuf == NULL) {
402            free((void *)(tty->rawInBuf.theBuf));
403            free(tty);
404      return NULL;
405    }
406    /*
407     * allocate cooked buffer
408     */
409    tty->cbuf  = malloc (CBUFSIZE);
410    if (tty->cbuf == NULL) {
411            free((void *)(tty->rawOutBuf.theBuf));
412            free((void *)(tty->rawInBuf.theBuf));
413            free(tty);
414      return NULL;
415    }
416    /*
417     * Initialize wakeup callbacks
418     */
419    tty->tty_snd.sw_pfn = NULL;
420    tty->tty_snd.sw_arg = NULL;
421    tty->tty_rcv.sw_pfn = NULL;
422    tty->tty_rcv.sw_arg = NULL;
423    tty->tty_rcvwakeup  = 0;
424
425    tty->minor = minor;
426    tty->major = major;
427
428    /*
429     * Set up mutex semaphores
430     */
431    sc = rtems_semaphore_create (
432      rtems_build_name ('T', 'R', 'i', c),
433      1,
434      RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
435      RTEMS_NO_PRIORITY,
436      &tty->isem);
437    if (sc != RTEMS_SUCCESSFUL)
438      rtems_fatal_error_occurred (sc);
439    sc = rtems_semaphore_create (
440      rtems_build_name ('T', 'R', 'o', c),
441      1,
442      RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
443      RTEMS_NO_PRIORITY,
444      &tty->osem);
445    if (sc != RTEMS_SUCCESSFUL)
446      rtems_fatal_error_occurred (sc);
447    sc = rtems_semaphore_create (
448      rtems_build_name ('T', 'R', 'x', c),
449      0,
450      RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_FIFO,
451      RTEMS_NO_PRIORITY,
452      &tty->rawOutBuf.Semaphore);
453    if (sc != RTEMS_SUCCESSFUL)
454      rtems_fatal_error_occurred (sc);
455    tty->rawOutBufState = rob_idle;
456
457    /*
458     * Set callbacks
459     */
460    if (device_node != NULL) {
461      device_node->tty = tty;
462      tty->handler = *device_node->handler;
463
464      if (device_node->flow != NULL) {
465        tty->flow = *device_node->flow;
466      }
467
468      tty->device_node = device_node;
469      tty->device_context = device_node->context;
470      memset(&tty->device, 0, sizeof(tty->device));
471    } else {
472      tty->handler.first_open = callbacks->firstOpen != NULL ?
473        rtems_termios_callback_firstOpen : NULL;
474      tty->handler.last_close = callbacks->lastClose != NULL ?
475        rtems_termios_callback_lastClose : NULL;
476      tty->handler.poll_read = callbacks->pollRead != NULL ?
477        rtems_termios_callback_pollRead : NULL;
478      tty->handler.write = callbacks->write != NULL ?
479        rtems_termios_callback_write : NULL;
480      tty->handler.set_attributes = callbacks->setAttributes != NULL ?
481        rtems_termios_callback_setAttributes : NULL;
482      tty->flow.stop_remote_tx = callbacks->stopRemoteTx != NULL ?
483        rtems_termios_callback_stopRemoteTx : NULL;
484      tty->flow.start_remote_tx = callbacks->startRemoteTx != NULL ?
485        rtems_termios_callback_startRemoteTx : NULL;
486      tty->handler.mode = callbacks->outputUsesInterrupts;
487      tty->device_context = NULL;
488      tty->device_node = NULL;
489      tty->device = *callbacks;
490    }
491
492    rtems_interrupt_lock_initialize (&tty->interrupt_lock, "Termios");
493
494    /*
495     * Create I/O tasks
496     */
497    if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
498      sc = rtems_task_create (
499                                   rtems_build_name ('T', 'x', 'T', c),
500           TERMIOS_TXTASK_PRIO,
501           TERMIOS_TXTASK_STACKSIZE,
502           RTEMS_NO_PREEMPT | RTEMS_NO_TIMESLICE |
503           RTEMS_NO_ASR,
504           RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
505           &tty->txTaskId);
506      if (sc != RTEMS_SUCCESSFUL)
507        rtems_fatal_error_occurred (sc);
508      sc = rtems_task_create (
509                                   rtems_build_name ('R', 'x', 'T', c),
510           TERMIOS_RXTASK_PRIO,
511           TERMIOS_RXTASK_STACKSIZE,
512           RTEMS_NO_PREEMPT | RTEMS_NO_TIMESLICE |
513           RTEMS_NO_ASR,
514           RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
515           &tty->rxTaskId);
516      if (sc != RTEMS_SUCCESSFUL)
517        rtems_fatal_error_occurred (sc);
518
519    }
520    if ((tty->handler.poll_read == NULL) ||
521        (tty->handler.mode == TERMIOS_TASK_DRIVEN)){
522      sc = rtems_semaphore_create (
523        rtems_build_name ('T', 'R', 'r', c),
524        0,
525        RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_PRIORITY,
526        RTEMS_NO_PRIORITY,
527        &tty->rawInBuf.Semaphore);
528      if (sc != RTEMS_SUCCESSFUL)
529        rtems_fatal_error_occurred (sc);
530    }
531
532    /*
533     * Set default parameters
534     */
535    tty->termios.c_iflag = BRKINT | ICRNL | IXON | IMAXBEL;
536    tty->termios.c_oflag = OPOST | ONLCR | XTABS;
537    tty->termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL;
538    tty->termios.c_lflag =
539       ISIG | ICANON | IEXTEN | ECHO | ECHOK | ECHOE | ECHOCTL;
540
541    tty->termios.c_cc[VINTR] = '\003';
542    tty->termios.c_cc[VQUIT] = '\034';
543    tty->termios.c_cc[VERASE] = '\177';
544    tty->termios.c_cc[VKILL] = '\025';
545    tty->termios.c_cc[VEOF] = '\004';
546    tty->termios.c_cc[VEOL] = '\000';
547    tty->termios.c_cc[VEOL2] = '\000';
548    tty->termios.c_cc[VSTART] = '\021';
549    tty->termios.c_cc[VSTOP] = '\023';
550    tty->termios.c_cc[VSUSP] = '\032';
551    tty->termios.c_cc[VREPRINT] = '\022';
552    tty->termios.c_cc[VDISCARD] = '\017';
553    tty->termios.c_cc[VWERASE] = '\027';
554    tty->termios.c_cc[VLNEXT] = '\026';
555
556    /* start with no flow control, clear flow control flags */
557    tty->flow_ctrl = 0;
558    /*
559     * set low/highwater mark for XON/XOFF support
560     */
561    tty->lowwater  = tty->rawInBuf.Size * 1/2;
562    tty->highwater = tty->rawInBuf.Size * 3/4;
563    /*
564     * Bump name characer
565     */
566    if (c++ == 'z')
567      c = 'a';
568
569  }
570  args->iop->data1 = tty;
571  if (!tty->refcount++) {
572    if (tty->handler.first_open &&
573        !(*tty->handler.first_open)(tty, args)) {
574      rtems_termios_destroy_tty(tty, args, false);
575      return NULL;
576    }
577
578    /*
579     * start I/O tasks, if needed
580     */
581    if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
582      sc = rtems_task_start(
583        tty->rxTaskId, rtems_termios_rxdaemon, (rtems_task_argument)tty);
584      if (sc != RTEMS_SUCCESSFUL)
585        rtems_fatal_error_occurred (sc);
586
587      sc = rtems_task_start(
588        tty->txTaskId, rtems_termios_txdaemon, (rtems_task_argument)tty);
589      if (sc != RTEMS_SUCCESSFUL)
590        rtems_fatal_error_occurred (sc);
591    }
592  }
593
594  return tty;
595}
596
597rtems_status_code
598rtems_termios_device_open(
599  rtems_device_major_number  major,
600  rtems_device_minor_number  minor,
601  void                      *arg
602)
603{
604  rtems_status_code          sc;
605  rtems_termios_device_node *device_node;
606  struct rtems_termios_tty  *tty;
607
608  sc = rtems_semaphore_obtain(
609    rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
610  if (sc != RTEMS_SUCCESSFUL)
611    return sc;
612
613  device_node = rtems_termios_find_device_node (major, minor);
614  if (device_node == NULL) {
615    rtems_semaphore_release (rtems_termios_ttyMutex);
616    return RTEMS_INVALID_ID;
617  }
618
619  tty = rtems_termios_open_tty(
620    major, minor, arg, device_node->tty, device_node, NULL);
621  if (tty == NULL) {
622    rtems_semaphore_release (rtems_termios_ttyMutex);
623    return RTEMS_NO_MEMORY;
624  }
625
626  rtems_semaphore_release (rtems_termios_ttyMutex);
627
628  return RTEMS_SUCCESSFUL;
629}
630
631/*
632 * Open a termios device
633 */
634rtems_status_code
635rtems_termios_open (
636  rtems_device_major_number      major,
637  rtems_device_minor_number      minor,
638  void                          *arg,
639  const rtems_termios_callbacks *callbacks
640)
641{
642  rtems_status_code sc;
643  struct rtems_termios_tty *tty;
644
645  /*
646   * See if the device has already been opened
647   */
648  sc = rtems_semaphore_obtain(
649    rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
650  if (sc != RTEMS_SUCCESSFUL)
651    return sc;
652
653  for (tty = rtems_termios_ttyHead ; tty != NULL ; tty = tty->forw) {
654    if ((tty->major == major) && (tty->minor == minor))
655      break;
656  }
657
658  tty = rtems_termios_open_tty(
659    major, minor, arg, tty, NULL, callbacks);
660  if (tty == NULL) {
661    rtems_semaphore_release (rtems_termios_ttyMutex);
662    return RTEMS_NO_MEMORY;
663  }
664
665  if (tty->refcount == 1) {
666    /*
667     * link tty
668     */
669    tty->forw = rtems_termios_ttyHead;
670    tty->back = NULL;
671    if (rtems_termios_ttyHead != NULL)
672      rtems_termios_ttyHead->back = tty;
673    rtems_termios_ttyHead = tty;
674    if (rtems_termios_ttyTail == NULL)
675      rtems_termios_ttyTail = tty;
676  }
677
678  rtems_semaphore_release (rtems_termios_ttyMutex);
679
680  return RTEMS_SUCCESSFUL;
681}
682
683static void
684flushOutput (struct rtems_termios_tty *tty)
685{
686  rtems_interrupt_lock_context lock_context;
687
688  rtems_termios_interrupt_lock_acquire (tty, &lock_context);
689  tty->rawOutBuf.Tail = 0;
690  tty->rawOutBuf.Head = 0;
691  tty->rawOutBufState = rob_idle;
692  rtems_termios_interrupt_lock_release (tty, &lock_context);
693}
694
695static void
696flushInput (struct rtems_termios_tty *tty)
697{
698  rtems_interrupt_lock_context lock_context;
699
700  rtems_termios_interrupt_lock_acquire (tty, &lock_context);
701  tty->rawInBuf.Tail = 0;
702  tty->rawInBuf.Head = 0;
703  rtems_termios_interrupt_lock_release (tty, &lock_context);
704}
705
706static void
707rtems_termios_close_tty (rtems_termios_tty *tty, void *arg)
708{
709  if (--tty->refcount == 0) {
710    rtems_termios_destroy_tty (tty, arg, true);
711  }
712}
713
714rtems_status_code
715rtems_termios_close (void *arg)
716{
717  rtems_status_code sc;
718  rtems_libio_open_close_args_t *args = arg;
719  struct rtems_termios_tty *tty = args->iop->data1;
720
721  sc = rtems_semaphore_obtain(
722    rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
723  if (sc != RTEMS_SUCCESSFUL)
724    rtems_fatal_error_occurred (sc);
725
726  if (tty->refcount == 1) {
727    if (tty->forw == NULL) {
728      rtems_termios_ttyTail = tty->back;
729      if ( rtems_termios_ttyTail != NULL ) {
730        rtems_termios_ttyTail->forw = NULL;
731      }
732    } else {
733      tty->forw->back = tty->back;
734    }
735
736    if (tty->back == NULL) {
737      rtems_termios_ttyHead = tty->forw;
738      if ( rtems_termios_ttyHead != NULL ) {
739        rtems_termios_ttyHead->back = NULL;
740      }
741    } else {
742      tty->back->forw = tty->forw;
743    }
744  }
745
746  rtems_termios_close_tty (tty, arg);
747
748  rtems_semaphore_release (rtems_termios_ttyMutex);
749
750  return RTEMS_SUCCESSFUL;
751}
752
753rtems_status_code
754rtems_termios_device_close (void *arg)
755{
756  rtems_status_code sc;
757  rtems_libio_open_close_args_t *args = arg;
758  struct rtems_termios_tty *tty = args->iop->data1;
759
760  sc = rtems_semaphore_obtain(
761    rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
762  if (sc != RTEMS_SUCCESSFUL)
763    rtems_fatal_error_occurred (sc);
764
765  rtems_termios_close_tty (tty, arg);
766
767  rtems_semaphore_release (rtems_termios_ttyMutex);
768
769  return RTEMS_SUCCESSFUL;
770}
771
772rtems_status_code rtems_termios_bufsize (
773  size_t cbufsize,
774  size_t raw_input,
775  size_t raw_output
776)
777{
778  rtems_termios_cbufsize        = cbufsize;
779  rtems_termios_raw_input_size  = raw_input;
780  rtems_termios_raw_output_size = raw_output;
781  return RTEMS_SUCCESSFUL;
782}
783
784static void
785termios_set_flowctrl(struct rtems_termios_tty *tty)
786{
787  rtems_interrupt_lock_context lock_context;
788  /*
789   * check for flow control options to be switched off
790   */
791
792  /* check for outgoing XON/XOFF flow control switched off */
793  if (( tty->flow_ctrl & FL_MDXON) &&
794      !(tty->termios.c_iflag & IXON)) {
795    /* clear related flags in flow_ctrl */
796    tty->flow_ctrl &= ~(FL_MDXON | FL_ORCVXOF);
797
798    /* has output been stopped due to received XOFF? */
799    if (tty->flow_ctrl & FL_OSTOP) {
800      /* disable interrupts    */
801      rtems_termios_interrupt_lock_acquire (tty, &lock_context);
802      tty->flow_ctrl &= ~FL_OSTOP;
803      /* check for chars in output buffer (or rob_state?) */
804      if (tty->rawOutBufState != rob_idle) {
805        /* if chars available, call write function... */
806        (*tty->handler.write)(
807          tty, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1);
808      }
809      /* reenable interrupts */
810      rtems_termios_interrupt_lock_release (tty, &lock_context);
811    }
812  }
813  /* check for incoming XON/XOFF flow control switched off */
814  if (( tty->flow_ctrl & FL_MDXOF) && !(tty->termios.c_iflag & IXOFF)) {
815    /* clear related flags in flow_ctrl */
816    tty->flow_ctrl &= ~(FL_MDXOF);
817    /* FIXME: what happens, if we had sent XOFF but not yet XON? */
818    tty->flow_ctrl &= ~(FL_ISNTXOF);
819  }
820
821  /* check for incoming RTS/CTS flow control switched off */
822  if (( tty->flow_ctrl & FL_MDRTS) && !(tty->termios.c_cflag & CRTSCTS)) {
823    /* clear related flags in flow_ctrl */
824    tty->flow_ctrl &= ~(FL_MDRTS);
825
826    /* restart remote Tx, if it was stopped */
827    if ((tty->flow_ctrl & FL_IRTSOFF) &&
828        (tty->flow.start_remote_tx != NULL)) {
829      tty->flow.start_remote_tx(tty);
830    }
831    tty->flow_ctrl &= ~(FL_IRTSOFF);
832  }
833
834  /*
835   * check for flow control options to be switched on
836   */
837  /* check for incoming RTS/CTS flow control switched on */
838  if (tty->termios.c_cflag & CRTSCTS) {
839    tty->flow_ctrl |= FL_MDRTS;
840  }
841  /* check for incoming XON/XOF flow control switched on */
842  if (tty->termios.c_iflag & IXOFF) {
843    tty->flow_ctrl |= FL_MDXOF;
844  }
845  /* check for outgoing XON/XOF flow control switched on */
846  if (tty->termios.c_iflag & IXON) {
847    tty->flow_ctrl |= FL_MDXON;
848  }
849}
850
851rtems_status_code
852rtems_termios_ioctl (void *arg)
853{
854  rtems_libio_ioctl_args_t *args = arg;
855  struct rtems_termios_tty *tty = args->iop->data1;
856  struct ttywakeup         *wakeup = (struct ttywakeup *)args->buffer;
857  rtems_status_code sc;
858
859  args->ioctl_return = 0;
860  sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
861  if (sc != RTEMS_SUCCESSFUL) {
862    return sc;
863  }
864  switch (args->command) {
865  default:
866    if (rtems_termios_linesw[tty->t_line].l_ioctl != NULL) {
867      sc = rtems_termios_linesw[tty->t_line].l_ioctl(tty,args);
868    }
869    else {
870      sc = RTEMS_INVALID_NUMBER;
871    }
872    break;
873
874  case RTEMS_IO_GET_ATTRIBUTES:
875    *(struct termios *)args->buffer = tty->termios;
876    break;
877
878  case RTEMS_IO_SET_ATTRIBUTES:
879    tty->termios = *(struct termios *)args->buffer;
880
881    /* check for and process change in flow control options */
882    termios_set_flowctrl(tty);
883
884    if (tty->termios.c_lflag & ICANON) {
885      tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
886      tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
887      tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
888    } else {
889      tty->vtimeTicks = tty->termios.c_cc[VTIME] * 
890                    rtems_clock_get_ticks_per_second() / 10;
891      if (tty->termios.c_cc[VTIME]) {
892        tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
893        tty->rawInBufSemaphoreTimeout = tty->vtimeTicks;
894        if (tty->termios.c_cc[VMIN])
895          tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
896        else
897          tty->rawInBufSemaphoreFirstTimeout = tty->vtimeTicks;
898      } else {
899        if (tty->termios.c_cc[VMIN]) {
900          tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
901          tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
902          tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
903        } else {
904          tty->rawInBufSemaphoreOptions = RTEMS_NO_WAIT;
905        }
906      }
907    }
908    if (tty->handler.set_attributes) {
909      sc = (*tty->handler.set_attributes)(tty, &tty->termios) ?
910        RTEMS_SUCCESSFUL : RTEMS_IO_ERROR;
911    }
912    break;
913
914  case RTEMS_IO_TCDRAIN:
915    drainOutput (tty);
916    break;
917
918  case RTEMS_IO_TCFLUSH:
919    switch ((intptr_t) args->buffer) {
920      case TCIFLUSH:
921        flushInput (tty);
922        break;
923      case TCOFLUSH:
924        flushOutput (tty);
925        break;
926      case TCIOFLUSH:
927        flushOutput (tty);
928        flushInput (tty);
929        break;
930      default:
931        sc = RTEMS_INVALID_NAME;
932        break;
933    }
934    break;
935
936  case RTEMS_IO_SNDWAKEUP:
937    tty->tty_snd = *wakeup;
938    break;
939
940  case RTEMS_IO_RCVWAKEUP:
941    tty->tty_rcv = *wakeup;
942    break;
943
944    /*
945     * FIXME: add various ioctl code handlers
946     */
947
948#if 1 /* FIXME */
949  case TIOCSETD:
950    /*
951     * close old line discipline
952     */
953    if (rtems_termios_linesw[tty->t_line].l_close != NULL) {
954      sc = rtems_termios_linesw[tty->t_line].l_close(tty);
955    }
956    tty->t_line=*(int*)(args->buffer);
957    tty->t_sc = NULL; /* ensure that no more valid data */
958    /*
959     * open new line discipline
960     */
961    if (rtems_termios_linesw[tty->t_line].l_open != NULL) {
962      sc = rtems_termios_linesw[tty->t_line].l_open(tty);
963    }
964    break;
965  case TIOCGETD:
966    *(int*)(args->buffer)=tty->t_line;
967    break;
968#endif
969   case FIONREAD: {
970      int rawnc = tty->rawInBuf.Tail - tty->rawInBuf.Head;
971      if ( rawnc < 0 )
972        rawnc += tty->rawInBuf.Size;
973      /* Half guess that this is the right operation */
974      *(int *)args->buffer = tty->ccount - tty->cindex + rawnc;
975    }
976    break;
977  }
978
979  rtems_semaphore_release (tty->osem);
980  return sc;
981}
982
983/*
984 * Send as many chars at once as possible to device-specific code.
985 * If transmitting==true then assume transmission is already running and
986 * an explicit write(0) is needed if output has to stop for flow control.
987 */
988static unsigned int
989startXmit (
990  struct rtems_termios_tty *tty,
991  unsigned int newTail,
992  bool transmitting
993)
994{
995  unsigned int nToSend;
996
997  tty->rawOutBufState = rob_busy;
998
999  /* if XOFF was received, do not (re)start output */
1000  if (tty->flow_ctrl & FL_ORCVXOF) {
1001    /* set flag, that output has been stopped */
1002    tty->flow_ctrl |= FL_OSTOP;
1003    nToSend = 0;
1004    /* stop transmitter */
1005    if (transmitting) {
1006      (*tty->handler.write) (tty, NULL, 0);
1007    }
1008  } else {
1009    /* when flow control XON or XOF, don't send blocks of data     */
1010    /* to allow fast reaction on incoming flow ctrl and low latency*/
1011    /* for outgoing flow control                                   */
1012    if (tty->flow_ctrl & (FL_MDXON | FL_MDXOF))
1013      nToSend = 1;
1014    else if (newTail > tty->rawOutBuf.Head)
1015      nToSend = tty->rawOutBuf.Size - newTail;
1016    else
1017      nToSend = tty->rawOutBuf.Head - newTail;
1018
1019    (*tty->handler.write)(
1020        tty, &tty->rawOutBuf.theBuf[newTail], nToSend);
1021  }
1022
1023  return nToSend;
1024}
1025
1026/*
1027 * Send characters to device-specific code
1028 */
1029void
1030rtems_termios_puts (
1031  const void *_buf, size_t len, struct rtems_termios_tty *tty)
1032{
1033  const char *buf = _buf;
1034  unsigned int newHead;
1035  rtems_interrupt_lock_context lock_context;
1036  rtems_status_code sc;
1037
1038  if (tty->handler.mode == TERMIOS_POLLED) {
1039    (*tty->handler.write)(tty, buf, len);
1040    return;
1041  }
1042
1043  while (len) {
1044    size_t nToCopy;
1045    size_t nAvail;
1046
1047    /* Check space for at least one char */
1048    newHead = tty->rawOutBuf.Head + 1;
1049    if (newHead >= tty->rawOutBuf.Size)
1050      newHead -= tty->rawOutBuf.Size;
1051
1052    rtems_termios_interrupt_lock_acquire (tty, &lock_context);
1053    while (newHead == tty->rawOutBuf.Tail) {
1054      tty->rawOutBufState = rob_wait;
1055      rtems_termios_interrupt_lock_release (tty, &lock_context);
1056      sc = rtems_semaphore_obtain(
1057        tty->rawOutBuf.Semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
1058      if (sc != RTEMS_SUCCESSFUL)
1059        rtems_fatal_error_occurred (sc);
1060      rtems_termios_interrupt_lock_acquire (tty, &lock_context);
1061    }
1062
1063    /* Determine free space up to current tail or end of ring buffer */
1064    nToCopy = len;
1065    if (tty->rawOutBuf.Tail > tty->rawOutBuf.Head) {
1066      /* Available space is contiguous from Head to Tail */
1067      nAvail = tty->rawOutBuf.Tail - tty->rawOutBuf.Head - 1;
1068    } else {
1069      /* Available space wraps at buffer end. To keep it simple, utilize
1070         only the free space from Head to end during this iteration */
1071      nAvail = tty->rawOutBuf.Size - tty->rawOutBuf.Head;
1072      /* Head may not touch Tail after wraparound */
1073      if (tty->rawOutBuf.Tail == 0)
1074        nAvail--;
1075    }
1076    if (nToCopy > nAvail)
1077      nToCopy = nAvail;
1078
1079    /* To minimize latency, the memcpy could be done
1080     * with interrupts enabled or with limit on nToCopy (TBD)
1081     */
1082    memcpy(&tty->rawOutBuf.theBuf[tty->rawOutBuf.Head], buf, nToCopy);
1083
1084    newHead = tty->rawOutBuf.Head + nToCopy;
1085    if (newHead >= tty->rawOutBuf.Size)
1086      newHead -= tty->rawOutBuf.Size;
1087    tty->rawOutBuf.Head = newHead;
1088
1089    if (tty->rawOutBufState == rob_idle) {
1090      startXmit (tty, tty->rawOutBuf.Tail, false);
1091    }
1092
1093    rtems_termios_interrupt_lock_release (tty, &lock_context);
1094
1095    buf += nToCopy;
1096    len -= nToCopy;
1097  }
1098}
1099
1100/*
1101 * Handle output processing
1102 */
1103static void
1104oproc (unsigned char c, struct rtems_termios_tty *tty)
1105{
1106  int  i;
1107
1108  if (tty->termios.c_oflag & OPOST) {
1109    switch (c) {
1110    case '\n':
1111      if (tty->termios.c_oflag & ONLRET)
1112        tty->column = 0;
1113      if (tty->termios.c_oflag & ONLCR) {
1114        rtems_termios_puts ("\r", 1, tty);
1115        tty->column = 0;
1116      }
1117      break;
1118
1119    case '\r':
1120      if ((tty->termios.c_oflag & ONOCR) && (tty->column == 0))
1121        return;
1122      if (tty->termios.c_oflag & OCRNL) {
1123        c = '\n';
1124        if (tty->termios.c_oflag & ONLRET)
1125          tty->column = 0;
1126        break;
1127      }
1128      tty->column = 0;
1129      break;
1130
1131    case '\t':
1132      i = 8 - (tty->column & 7);
1133      if ((tty->termios.c_oflag & TABDLY) == XTABS) {
1134        tty->column += i;
1135        rtems_termios_puts ( "        ",  i, tty);
1136        return;
1137      }
1138      tty->column += i;
1139      break;
1140
1141    case '\b':
1142      if (tty->column > 0)
1143        tty->column--;
1144      break;
1145
1146    default:
1147      if (tty->termios.c_oflag & OLCUC)
1148        c = toupper(c);
1149      if (!iscntrl(c))
1150        tty->column++;
1151      break;
1152    }
1153  }
1154  rtems_termios_puts (&c, 1, tty);
1155}
1156
1157rtems_status_code
1158rtems_termios_write (void *arg)
1159{
1160  rtems_libio_rw_args_t *args = arg;
1161  struct rtems_termios_tty *tty = args->iop->data1;
1162  rtems_status_code sc;
1163
1164  sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
1165  if (sc != RTEMS_SUCCESSFUL)
1166    return sc;
1167  if (rtems_termios_linesw[tty->t_line].l_write != NULL) {
1168    sc = rtems_termios_linesw[tty->t_line].l_write(tty,args);
1169    rtems_semaphore_release (tty->osem);
1170    return sc;
1171  }
1172  if (tty->termios.c_oflag & OPOST) {
1173    uint32_t   count = args->count;
1174    char      *buffer = args->buffer;
1175    while (count--)
1176      oproc (*buffer++, tty);
1177    args->bytes_moved = args->count;
1178  } else {
1179    rtems_termios_puts (args->buffer, args->count, tty);
1180    args->bytes_moved = args->count;
1181  }
1182  rtems_semaphore_release (tty->osem);
1183  return sc;
1184}
1185
1186/*
1187 * Echo a typed character
1188 */
1189static void
1190echo (unsigned char c, struct rtems_termios_tty *tty)
1191{
1192  if ((tty->termios.c_lflag & ECHOCTL) &&
1193       iscntrl(c) && (c != '\t') && (c != '\n')) {
1194    char echobuf[2];
1195
1196    echobuf[0] = '^';
1197    echobuf[1] = c ^ 0x40;
1198    rtems_termios_puts (echobuf, 2, tty);
1199    tty->column += 2;
1200  } else {
1201    oproc (c, tty);
1202  }
1203}
1204
1205/*
1206 * Erase a character or line
1207 * FIXME: Needs support for WERASE and ECHOPRT.
1208 * FIXME: Some of the tests should check for IEXTEN, too.
1209 */
1210static void
1211erase (struct rtems_termios_tty *tty, int lineFlag)
1212{
1213  if (tty->ccount == 0)
1214    return;
1215  if (lineFlag) {
1216    if (!(tty->termios.c_lflag & ECHO)) {
1217      tty->ccount = 0;
1218      return;
1219    }
1220    if (!(tty->termios.c_lflag & ECHOE)) {
1221      tty->ccount = 0;
1222      echo (tty->termios.c_cc[VKILL], tty);
1223      if (tty->termios.c_lflag & ECHOK)
1224        echo ('\n', tty);
1225      return;
1226    }
1227  }
1228
1229  while (tty->ccount) {
1230    unsigned char c = tty->cbuf[--tty->ccount];
1231
1232    if (tty->termios.c_lflag & ECHO) {
1233      if (!lineFlag && !(tty->termios.c_lflag & ECHOE)) {
1234        echo (tty->termios.c_cc[VERASE], tty);
1235      } else if (c == '\t') {
1236        int col = tty->read_start_column;
1237        int i = 0;
1238
1239        /*
1240         * Find the character before the tab
1241         */
1242        while (i != tty->ccount) {
1243          c = tty->cbuf[i++];
1244          if (c == '\t') {
1245            col = (col | 7) + 1;
1246          } else if (iscntrl (c)) {
1247            if (tty->termios.c_lflag & ECHOCTL)
1248              col += 2;
1249          } else {
1250            col++;
1251          }
1252        }
1253
1254        /*
1255         * Back up over the tab
1256         */
1257        while (tty->column > col) {
1258          rtems_termios_puts ("\b", 1, tty);
1259          tty->column--;
1260        }
1261      }
1262      else {
1263        if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) {
1264          rtems_termios_puts ("\b \b", 3, tty);
1265          if (tty->column)
1266            tty->column--;
1267        }
1268        if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) {
1269          rtems_termios_puts ("\b \b", 3, tty);
1270          if (tty->column)
1271            tty->column--;
1272        }
1273      }
1274    }
1275    if (!lineFlag)
1276      break;
1277  }
1278}
1279
1280/*
1281 * Process a single input character
1282 */
1283static int
1284iproc (unsigned char c, struct rtems_termios_tty *tty)
1285{
1286  if (tty->termios.c_iflag & ISTRIP)
1287    c &= 0x7f;
1288
1289  if (tty->termios.c_iflag & IUCLC)
1290    c = tolower (c);
1291
1292  if (c == '\r') {
1293    if (tty->termios.c_iflag & IGNCR)
1294      return 0;
1295    if (tty->termios.c_iflag & ICRNL)
1296      c = '\n';
1297  } else if ((c == '\n') && (tty->termios.c_iflag & INLCR)) {
1298    c = '\r';
1299  }
1300
1301  if ((c != '\0') && (tty->termios.c_lflag & ICANON)) {
1302    if (c == tty->termios.c_cc[VERASE]) {
1303      erase (tty, 0);
1304      return 0;
1305    }
1306    else if (c == tty->termios.c_cc[VKILL]) {
1307      erase (tty, 1);
1308      return 0;
1309    }
1310    else if (c == tty->termios.c_cc[VEOF]) {
1311      return 1;
1312    } else if (c == '\n') {
1313      if (tty->termios.c_lflag & (ECHO | ECHONL))
1314        echo (c, tty);
1315      tty->cbuf[tty->ccount++] = c;
1316      return 1;
1317    } else if ((c == tty->termios.c_cc[VEOL]) ||
1318               (c == tty->termios.c_cc[VEOL2])) {
1319      if (tty->termios.c_lflag & ECHO)
1320        echo (c, tty);
1321      tty->cbuf[tty->ccount++] = c;
1322      return 1;
1323    }
1324  }
1325
1326  /*
1327   * FIXME: Should do IMAXBEL handling somehow
1328   */
1329  if (tty->ccount < (CBUFSIZE-1)) {
1330    if (tty->termios.c_lflag & ECHO)
1331      echo (c, tty);
1332    tty->cbuf[tty->ccount++] = c;
1333  }
1334  return 0;
1335}
1336
1337/*
1338 * Process input character, with semaphore.
1339 */
1340static int
1341siproc (unsigned char c, struct rtems_termios_tty *tty)
1342{
1343  int i;
1344
1345  /*
1346   * Obtain output semaphore if character will be echoed
1347   */
1348  if (tty->termios.c_lflag & (ECHO|ECHOE|ECHOK|ECHONL|ECHOPRT|ECHOCTL|ECHOKE)) {
1349    rtems_status_code sc;
1350    sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
1351    if (sc != RTEMS_SUCCESSFUL)
1352      rtems_fatal_error_occurred (sc);
1353    i = iproc (c, tty);
1354    sc = rtems_semaphore_release (tty->osem);
1355    if (sc != RTEMS_SUCCESSFUL)
1356      rtems_fatal_error_occurred (sc);
1357  }
1358  else {
1359    i = iproc (c, tty);
1360  }
1361  return i;
1362}
1363
1364/*
1365 * Fill the input buffer by polling the device
1366 */
1367static rtems_status_code
1368fillBufferPoll (struct rtems_termios_tty *tty)
1369{
1370  int n;
1371
1372  if (tty->termios.c_lflag & ICANON) {
1373    for (;;) {
1374      n = (*tty->handler.poll_read)(tty);
1375      if (n < 0) {
1376        rtems_task_wake_after (1);
1377      } else {
1378        if  (siproc (n, tty))
1379          break;
1380      }
1381    }
1382  } else {
1383    rtems_interval then, now;
1384
1385    then = rtems_clock_get_ticks_since_boot();
1386    for (;;) {
1387      n = (*tty->handler.poll_read)(tty);
1388      if (n < 0) {
1389        if (tty->termios.c_cc[VMIN]) {
1390          if (tty->termios.c_cc[VTIME] && tty->ccount) {
1391            now = rtems_clock_get_ticks_since_boot();
1392            if ((now - then) > tty->vtimeTicks) {
1393              break;
1394            }
1395          }
1396        } else {
1397          if (!tty->termios.c_cc[VTIME])
1398            break;
1399          now = rtems_clock_get_ticks_since_boot();
1400          if ((now - then) > tty->vtimeTicks) {
1401            break;
1402          }
1403        }
1404        rtems_task_wake_after (1);
1405      } else {
1406        siproc (n, tty);
1407        if (tty->ccount >= tty->termios.c_cc[VMIN])
1408          break;
1409        if (tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
1410          then = rtems_clock_get_ticks_since_boot();
1411      }
1412    }
1413  }
1414  return RTEMS_SUCCESSFUL;
1415}
1416
1417/*
1418 * Fill the input buffer from the raw input queue
1419 */
1420static rtems_status_code
1421fillBufferQueue (struct rtems_termios_tty *tty)
1422{
1423  rtems_interval timeout = tty->rawInBufSemaphoreFirstTimeout;
1424  rtems_status_code sc;
1425  int               wait = 1;
1426
1427  while ( wait ) {
1428    /*
1429     * Process characters read from raw queue
1430     */
1431    while ((tty->rawInBuf.Head != tty->rawInBuf.Tail) &&
1432                       (tty->ccount < (CBUFSIZE-1))) {
1433      unsigned char c;
1434      unsigned int newHead;
1435
1436      newHead = (tty->rawInBuf.Head + 1) % tty->rawInBuf.Size;
1437      c = tty->rawInBuf.theBuf[newHead];
1438      tty->rawInBuf.Head = newHead;
1439      if(((tty->rawInBuf.Tail-newHead+tty->rawInBuf.Size)
1440          % tty->rawInBuf.Size)
1441         < tty->lowwater) {
1442        tty->flow_ctrl &= ~FL_IREQXOF;
1443        /* if tx stopped and XON should be sent... */
1444        if (((tty->flow_ctrl & (FL_MDXON | FL_ISNTXOF))
1445             ==                (FL_MDXON | FL_ISNTXOF))
1446            && ((tty->rawOutBufState == rob_idle)
1447          || (tty->flow_ctrl & FL_OSTOP))) {
1448          /* XON should be sent now... */
1449          (*tty->handler.write)(
1450            tty, (void *)&(tty->termios.c_cc[VSTART]), 1);
1451        } else if (tty->flow_ctrl & FL_MDRTS) {
1452          tty->flow_ctrl &= ~FL_IRTSOFF;
1453          /* activate RTS line */
1454          if (tty->flow.start_remote_tx != NULL) {
1455            tty->flow.start_remote_tx(tty);
1456          }
1457        }
1458      }
1459
1460      /* continue processing new character */
1461      if (tty->termios.c_lflag & ICANON) {
1462        if (siproc (c, tty))
1463          wait = 0;
1464      } else {
1465        siproc (c, tty);
1466        if (tty->ccount >= tty->termios.c_cc[VMIN])
1467          wait = 0;
1468      }
1469      timeout = tty->rawInBufSemaphoreTimeout;
1470    }
1471
1472    /*
1473     * Wait for characters
1474     */
1475    if ( wait ) {
1476      sc = rtems_semaphore_obtain(
1477        tty->rawInBuf.Semaphore, tty->rawInBufSemaphoreOptions, timeout);
1478      if (sc != RTEMS_SUCCESSFUL)
1479        break;
1480    }
1481  }
1482  return RTEMS_SUCCESSFUL;
1483}
1484
1485rtems_status_code
1486rtems_termios_read (void *arg)
1487{
1488  rtems_libio_rw_args_t *args = arg;
1489  struct rtems_termios_tty *tty = args->iop->data1;
1490  uint32_t   count = args->count;
1491  char      *buffer = args->buffer;
1492  rtems_status_code sc;
1493
1494  sc = rtems_semaphore_obtain (tty->isem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
1495  if (sc != RTEMS_SUCCESSFUL)
1496    return sc;
1497
1498  if (rtems_termios_linesw[tty->t_line].l_read != NULL) {
1499    sc = rtems_termios_linesw[tty->t_line].l_read(tty,args);
1500    tty->tty_rcvwakeup = 0;
1501    rtems_semaphore_release (tty->isem);
1502    return sc;
1503  }
1504
1505  if (tty->cindex == tty->ccount) {
1506    tty->cindex = tty->ccount = 0;
1507    tty->read_start_column = tty->column;
1508    if (tty->handler.poll_read != NULL && tty->handler.mode == TERMIOS_POLLED)
1509      sc = fillBufferPoll (tty);
1510    else
1511      sc = fillBufferQueue (tty);
1512
1513    if (sc != RTEMS_SUCCESSFUL)
1514      tty->cindex = tty->ccount = 0;
1515  }
1516  while (count && (tty->cindex < tty->ccount)) {
1517    *buffer++ = tty->cbuf[tty->cindex++];
1518    count--;
1519  }
1520  args->bytes_moved = args->count - count;
1521  tty->tty_rcvwakeup = 0;
1522  rtems_semaphore_release (tty->isem);
1523  return sc;
1524}
1525
1526/*
1527 * signal receive interrupt to rx daemon
1528 * NOTE: This routine runs in the context of the
1529 *       device receive interrupt handler.
1530 */
1531void rtems_termios_rxirq_occured(struct rtems_termios_tty *tty)
1532{
1533  /*
1534   * send event to rx daemon task
1535   */
1536  rtems_event_send(tty->rxTaskId,TERMIOS_RX_PROC_EVENT);
1537}
1538
1539/*
1540 * Place characters on raw queue.
1541 * NOTE: This routine runs in the context of the
1542 *       device receive interrupt handler.
1543 * Returns the number of characters dropped because of overflow.
1544 */
1545int
1546rtems_termios_enqueue_raw_characters (void *ttyp, const char *buf, int len)
1547{
1548  struct rtems_termios_tty *tty = ttyp;
1549  unsigned int newTail;
1550  char c;
1551  int dropped = 0;
1552  bool flow_rcv = false; /* true, if flow control char received */
1553  rtems_interrupt_lock_context lock_context;
1554
1555  if (rtems_termios_linesw[tty->t_line].l_rint != NULL) {
1556    while (len--) {
1557      c = *buf++;
1558      rtems_termios_linesw[tty->t_line].l_rint(c,tty);
1559    }
1560
1561    /*
1562     * check to see if rcv wakeup callback was set
1563     */
1564    if (( !tty->tty_rcvwakeup ) && ( tty->tty_rcv.sw_pfn != NULL )) {
1565      (*tty->tty_rcv.sw_pfn)(&tty->termios, tty->tty_rcv.sw_arg);
1566      tty->tty_rcvwakeup = 1;
1567        }
1568    return 0;
1569  }
1570
1571  while (len--) {
1572    c = *buf++;
1573    /* FIXME: implement IXANY: any character restarts output */
1574    /* if incoming XON/XOFF controls outgoing stream: */
1575    if (tty->flow_ctrl & FL_MDXON) {
1576      /* if received char is V_STOP and V_START (both are equal value) */
1577      if (c == tty->termios.c_cc[VSTOP]) {
1578        if (c == tty->termios.c_cc[VSTART]) {
1579          /* received VSTOP and VSTART==VSTOP? */
1580          /* then toggle "stop output" status  */
1581          tty->flow_ctrl = tty->flow_ctrl ^ FL_ORCVXOF;
1582        }
1583        else {
1584          /* VSTOP received (other code than VSTART) */
1585          /* stop output                             */
1586          tty->flow_ctrl |= FL_ORCVXOF;
1587        }
1588        flow_rcv = true;
1589      }
1590      else if (c == tty->termios.c_cc[VSTART]) {
1591        /* VSTART received */
1592        /* restart output  */
1593        tty->flow_ctrl &= ~FL_ORCVXOF;
1594        flow_rcv = true;
1595      }
1596    }
1597    if (flow_rcv) {
1598      /* restart output according to FL_ORCVXOF flag */
1599      if ((tty->flow_ctrl & (FL_ORCVXOF | FL_OSTOP)) == FL_OSTOP) {
1600        /* disable interrupts    */
1601        rtems_termios_interrupt_lock_acquire (tty, &lock_context);
1602        tty->flow_ctrl &= ~FL_OSTOP;
1603        /* check for chars in output buffer (or rob_state?) */
1604        if (tty->rawOutBufState != rob_idle) {
1605          /* if chars available, call write function... */
1606          (*tty->handler.write)(
1607            tty, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail], 1);
1608        }
1609        /* reenable interrupts */
1610        rtems_termios_interrupt_lock_release (tty, &lock_context);
1611      }
1612    } else {
1613      newTail = (tty->rawInBuf.Tail + 1) % tty->rawInBuf.Size;
1614      /* if chars_in_buffer > highwater                */
1615      rtems_termios_interrupt_lock_acquire (tty, &lock_context);
1616      if ((((newTail - tty->rawInBuf.Head + tty->rawInBuf.Size)
1617            % tty->rawInBuf.Size) > tty->highwater) &&
1618          !(tty->flow_ctrl & FL_IREQXOF)) {
1619        /* incoming data stream should be stopped */
1620        tty->flow_ctrl |= FL_IREQXOF;
1621        if ((tty->flow_ctrl & (FL_MDXOF | FL_ISNTXOF))
1622            ==                (FL_MDXOF             ) ) {
1623          if ((tty->flow_ctrl & FL_OSTOP) ||
1624              (tty->rawOutBufState == rob_idle)) {
1625            /* if tx is stopped due to XOFF or out of data */
1626            /*    call write function here                 */
1627            tty->flow_ctrl |= FL_ISNTXOF;
1628            (*tty->handler.write)(tty,
1629                (void *)&(tty->termios.c_cc[VSTOP]), 1);
1630          }
1631        } else if ((tty->flow_ctrl & (FL_MDRTS | FL_IRTSOFF)) == (FL_MDRTS) ) {
1632          tty->flow_ctrl |= FL_IRTSOFF;
1633          /* deactivate RTS line */
1634          if (tty->flow.stop_remote_tx != NULL) {
1635            tty->flow.stop_remote_tx(tty);
1636          }
1637        }
1638      }
1639
1640      /* reenable interrupts */
1641      rtems_termios_interrupt_lock_release (tty, &lock_context);
1642
1643      if (newTail == tty->rawInBuf.Head) {
1644        dropped++;
1645      } else {
1646        tty->rawInBuf.theBuf[newTail] = c;
1647        tty->rawInBuf.Tail = newTail;
1648
1649        /*
1650         * check to see if rcv wakeup callback was set
1651         */
1652        if (( !tty->tty_rcvwakeup ) && ( tty->tty_rcv.sw_pfn != NULL )) {
1653          (*tty->tty_rcv.sw_pfn)(&tty->termios, tty->tty_rcv.sw_arg);
1654          tty->tty_rcvwakeup = 1;
1655        }
1656      }
1657    }
1658  }
1659
1660  tty->rawInBufDropped += dropped;
1661  rtems_semaphore_release (tty->rawInBuf.Semaphore);
1662  return dropped;
1663}
1664
1665/*
1666 * in task-driven mode, this function is called in Tx task context
1667 * in interrupt-driven mode, this function is called in TxIRQ context
1668 */
1669static int
1670rtems_termios_refill_transmitter (struct rtems_termios_tty *tty)
1671{
1672  bool wakeUpWriterTask = false;
1673  unsigned int newTail;
1674  int nToSend;
1675  rtems_interrupt_lock_context lock_context;
1676  int len;
1677
1678  rtems_termios_interrupt_lock_acquire (tty, &lock_context);
1679
1680  /* check for XOF/XON to send */
1681  if ((tty->flow_ctrl & (FL_MDXOF | FL_IREQXOF | FL_ISNTXOF))
1682      == (FL_MDXOF | FL_IREQXOF)) {
1683    /* XOFF should be sent now... */
1684    (*tty->handler.write)(tty, (void *)&(tty->termios.c_cc[VSTOP]), 1);
1685
1686    tty->t_dqlen--;
1687    tty->flow_ctrl |= FL_ISNTXOF;
1688
1689    nToSend = 1;
1690
1691  } else if ((tty->flow_ctrl & (FL_IREQXOF | FL_ISNTXOF)) == FL_ISNTXOF) {
1692    /* NOTE: send XON even, if no longer in XON/XOFF mode... */
1693    /* XON should be sent now... */
1694    /*
1695     * FIXME: this .write call will generate another
1696     * dequeue callback. This will advance the "Tail" in the data
1697     * buffer, although the corresponding data is not yet out!
1698     * Therefore the dequeue "length" should be reduced by 1
1699     */
1700    (*tty->handler.write)(tty, (void *)&(tty->termios.c_cc[VSTART]), 1);
1701
1702    tty->t_dqlen--;
1703    tty->flow_ctrl &= ~FL_ISNTXOF;
1704
1705    nToSend = 1;
1706  } else if ( tty->rawOutBuf.Head == tty->rawOutBuf.Tail ) {
1707    /*
1708     * buffer was empty
1709     */
1710    if (tty->rawOutBufState == rob_wait) {
1711      /*
1712       * this should never happen...
1713       */
1714      wakeUpWriterTask = true;
1715    }
1716
1717    (*tty->handler.write) (tty, NULL, 0);
1718    nToSend = 0;
1719  } else {
1720    len = tty->t_dqlen;
1721    tty->t_dqlen = 0;
1722
1723    newTail = (tty->rawOutBuf.Tail + len) % tty->rawOutBuf.Size;
1724    tty->rawOutBuf.Tail = newTail;
1725    if (tty->rawOutBufState == rob_wait) {
1726      /*
1727       * wake up any pending writer task
1728       */
1729      wakeUpWriterTask = true;
1730    }
1731
1732    if (newTail == tty->rawOutBuf.Head) {
1733      /*
1734       * Buffer has become empty
1735       */
1736      tty->rawOutBufState = rob_idle;
1737      (*tty->handler.write) (tty, NULL, 0);
1738      nToSend = 0;
1739
1740      /*
1741       * check to see if snd wakeup callback was set
1742       */
1743      if ( tty->tty_snd.sw_pfn != NULL) {
1744        (*tty->tty_snd.sw_pfn)(&tty->termios, tty->tty_snd.sw_arg);
1745      }
1746    } else {
1747      /*
1748       * Buffer not empty, check flow control, start transmitter
1749       */
1750      nToSend = startXmit (tty, newTail, true);
1751    }
1752  }
1753
1754  rtems_termios_interrupt_lock_release (tty, &lock_context);
1755
1756  if (wakeUpWriterTask) {
1757    rtems_semaphore_release (tty->rawOutBuf.Semaphore);
1758  }
1759
1760  return nToSend;
1761}
1762
1763/*
1764 * Characters have been transmitted
1765 * NOTE: This routine runs in the context of the
1766 *       device transmit interrupt handler.
1767 * The second argument is the number of characters transmitted so far.
1768 * This value will always be 1 for devices which generate an interrupt
1769 * for each transmitted character.
1770 * It returns number of characters left to transmit
1771 */
1772int
1773rtems_termios_dequeue_characters (void *ttyp, int len)
1774{
1775  struct rtems_termios_tty *tty = ttyp;
1776  rtems_status_code sc;
1777
1778  /*
1779   * sum up character count already sent
1780   */
1781  tty->t_dqlen += len;
1782
1783  if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
1784    /*
1785     * send wake up to transmitter task
1786     */
1787    sc = rtems_event_send(tty->txTaskId, TERMIOS_TX_START_EVENT);
1788    if (sc != RTEMS_SUCCESSFUL)
1789      rtems_fatal_error_occurred (sc);
1790    return 0; /* nothing to output in IRQ... */
1791  }
1792
1793  if (tty->t_line == PPPDISC ) {
1794    /*
1795     * call PPP line discipline start function
1796     */
1797    if (rtems_termios_linesw[tty->t_line].l_start != NULL) {
1798      rtems_termios_linesw[tty->t_line].l_start(tty);
1799    }
1800    return 0; /* nothing to output in IRQ... */
1801  }
1802
1803  return rtems_termios_refill_transmitter(tty);
1804}
1805
1806/*
1807 * this task actually processes any transmit events
1808 */
1809static rtems_task rtems_termios_txdaemon(rtems_task_argument argument)
1810{
1811  struct rtems_termios_tty *tty = (struct rtems_termios_tty *)argument;
1812  rtems_event_set the_event;
1813
1814  while (1) {
1815    /*
1816     * wait for rtems event
1817     */
1818    rtems_event_receive(
1819       (TERMIOS_TX_START_EVENT | TERMIOS_TX_TERMINATE_EVENT),
1820       RTEMS_EVENT_ANY | RTEMS_WAIT,
1821       RTEMS_NO_TIMEOUT,
1822       &the_event
1823    );
1824    if ((the_event & TERMIOS_TX_TERMINATE_EVENT) != 0) {
1825      tty->txTaskId = 0;
1826      rtems_task_delete(RTEMS_SELF);
1827    }
1828
1829    /*
1830     * call any line discipline start function
1831     */
1832    if (rtems_termios_linesw[tty->t_line].l_start != NULL) {
1833      rtems_termios_linesw[tty->t_line].l_start(tty);
1834
1835      if (tty->t_line == PPPDISC) {
1836        /*
1837         * Do not call rtems_termios_refill_transmitter() in this case similar
1838         * to rtems_termios_dequeue_characters().
1839         */
1840        continue;
1841      }
1842    }
1843
1844    /*
1845     * try to push further characters to device
1846     */
1847    rtems_termios_refill_transmitter(tty);
1848  }
1849}
1850
1851/*
1852 * this task actually processes any receive events
1853 */
1854static rtems_task rtems_termios_rxdaemon(rtems_task_argument argument)
1855{
1856  struct rtems_termios_tty *tty = (struct rtems_termios_tty *)argument;
1857  rtems_event_set the_event;
1858  int c;
1859  char c_buf;
1860
1861  while (1) {
1862    /*
1863     * wait for rtems event
1864     */
1865    rtems_event_receive(
1866      (TERMIOS_RX_PROC_EVENT | TERMIOS_RX_TERMINATE_EVENT),
1867      RTEMS_EVENT_ANY | RTEMS_WAIT,
1868      RTEMS_NO_TIMEOUT,
1869      &the_event
1870    );
1871    if ((the_event & TERMIOS_RX_TERMINATE_EVENT) != 0) {
1872      tty->rxTaskId = 0;
1873      rtems_task_delete(RTEMS_SELF);
1874    }
1875
1876    /*
1877     * do something
1878     */
1879    c = tty->handler.poll_read(tty);
1880    if (c != EOF) {
1881      /*
1882       * poll_read did call enqueue on its own
1883       */
1884      c_buf = c;
1885      rtems_termios_enqueue_raw_characters ( tty,&c_buf,1);
1886    }
1887  }
1888}
Note: See TracBrowser for help on using the repository browser.