source: rtems/cpukit/libcsupport/src/termios.c @ 65483f8

4.115
Last change on this file since 65483f8 was 65483f8, checked in by Sebastian Huber <sebastian.huber@…>, on 06/10/13 at 12:22:28

termios: SMP support

  • Property mode set to 100644
File size: 41.9 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.com/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 struct rtems_termios_tty *rtems_termios_ttyHead;
80extern struct rtems_termios_tty *rtems_termios_ttyTail;
81extern rtems_id rtems_termios_ttyMutex;
82
83static size_t rtems_termios_cbufsize = 256;
84static size_t rtems_termios_raw_input_size = 128;
85static size_t rtems_termios_raw_output_size = 64;
86
87static rtems_task rtems_termios_rxdaemon(rtems_task_argument argument);
88static rtems_task rtems_termios_txdaemon(rtems_task_argument argument);
89/*
90 * some constants for I/O daemon task creation
91 */
92#define TERMIOS_TXTASK_PRIO 10
93#define TERMIOS_RXTASK_PRIO 9
94#define TERMIOS_TXTASK_STACKSIZE 1024
95#define TERMIOS_RXTASK_STACKSIZE 1024
96/*
97 * some events to be sent to the I/O tasks
98 */
99#define TERMIOS_TX_START_EVENT     RTEMS_EVENT_1
100#define TERMIOS_TX_TERMINATE_EVENT RTEMS_EVENT_0
101
102#define TERMIOS_RX_PROC_EVENT      RTEMS_EVENT_1
103#define TERMIOS_RX_TERMINATE_EVENT RTEMS_EVENT_0
104
105/*
106 * Open a termios device
107 */
108rtems_status_code
109rtems_termios_open (
110  rtems_device_major_number      major,
111  rtems_device_minor_number      minor,
112  void                          *arg,
113  const rtems_termios_callbacks *callbacks
114)
115{
116  rtems_status_code sc;
117  rtems_libio_open_close_args_t *args = arg;
118  struct rtems_termios_tty *tty;
119
120  /*
121   * See if the device has already been opened
122   */
123  sc = rtems_semaphore_obtain(
124    rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
125  if (sc != RTEMS_SUCCESSFUL)
126    return sc;
127
128  for (tty = rtems_termios_ttyHead ; tty != NULL ; tty = tty->forw) {
129    if ((tty->major == major) && (tty->minor == minor))
130      break;
131  }
132
133  if (tty == NULL) {
134    static char c = 'a';
135
136    /*
137     * Create a new device
138     */
139    tty = calloc (1, sizeof (struct rtems_termios_tty));
140    if (tty == NULL) {
141      rtems_semaphore_release (rtems_termios_ttyMutex);
142      return RTEMS_NO_MEMORY;
143    }
144    /*
145     * allocate raw input buffer
146     */
147    tty->rawInBuf.Size = RAW_INPUT_BUFFER_SIZE;
148    tty->rawInBuf.theBuf = malloc (tty->rawInBuf.Size);
149    if (tty->rawInBuf.theBuf == NULL) {
150            free(tty);
151      rtems_semaphore_release (rtems_termios_ttyMutex);
152      return RTEMS_NO_MEMORY;
153    }
154    /*
155     * allocate raw output buffer
156     */
157    tty->rawOutBuf.Size = RAW_OUTPUT_BUFFER_SIZE;
158    tty->rawOutBuf.theBuf = malloc (tty->rawOutBuf.Size);
159    if (tty->rawOutBuf.theBuf == NULL) {
160            free((void *)(tty->rawInBuf.theBuf));
161            free(tty);
162      rtems_semaphore_release (rtems_termios_ttyMutex);
163      return RTEMS_NO_MEMORY;
164    }
165    /*
166     * allocate cooked buffer
167     */
168    tty->cbuf  = malloc (CBUFSIZE);
169    if (tty->cbuf == NULL) {
170            free((void *)(tty->rawOutBuf.theBuf));
171            free((void *)(tty->rawInBuf.theBuf));
172            free(tty);
173      rtems_semaphore_release (rtems_termios_ttyMutex);
174      return RTEMS_NO_MEMORY;
175    }
176    /*
177     * Initialize wakeup callbacks
178     */
179    tty->tty_snd.sw_pfn = NULL;
180    tty->tty_snd.sw_arg = NULL;
181    tty->tty_rcv.sw_pfn = NULL;
182    tty->tty_rcv.sw_arg = NULL;
183    tty->tty_rcvwakeup  = 0;
184
185    /*
186     * link tty
187     */
188    tty->forw = rtems_termios_ttyHead;
189    tty->back = NULL;
190    if (rtems_termios_ttyHead != NULL)
191      rtems_termios_ttyHead->back = tty;
192    rtems_termios_ttyHead = tty;
193    if (rtems_termios_ttyTail == NULL)
194      rtems_termios_ttyTail = tty;
195
196    tty->minor = minor;
197    tty->major = major;
198
199    /*
200     * Set up mutex semaphores
201     */
202    sc = rtems_semaphore_create (
203      rtems_build_name ('T', 'R', 'i', c),
204      1,
205      RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
206      RTEMS_NO_PRIORITY,
207      &tty->isem);
208    if (sc != RTEMS_SUCCESSFUL)
209      rtems_fatal_error_occurred (sc);
210    sc = rtems_semaphore_create (
211      rtems_build_name ('T', 'R', 'o', c),
212      1,
213      RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
214      RTEMS_NO_PRIORITY,
215      &tty->osem);
216    if (sc != RTEMS_SUCCESSFUL)
217      rtems_fatal_error_occurred (sc);
218    sc = rtems_semaphore_create (
219      rtems_build_name ('T', 'R', 'x', c),
220      0,
221      RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_FIFO,
222      RTEMS_NO_PRIORITY,
223      &tty->rawOutBuf.Semaphore);
224    if (sc != RTEMS_SUCCESSFUL)
225      rtems_fatal_error_occurred (sc);
226    tty->rawOutBufState = rob_idle;
227
228    /*
229     * Set callbacks
230     */
231    tty->device = *callbacks;
232
233    rtems_interrupt_lock_initialize (&tty->interrupt_lock);
234
235    /*
236     * Create I/O tasks
237     */
238    if (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN) {
239      sc = rtems_task_create (
240                                   rtems_build_name ('T', 'x', 'T', c),
241           TERMIOS_TXTASK_PRIO,
242           TERMIOS_TXTASK_STACKSIZE,
243           RTEMS_NO_PREEMPT | RTEMS_NO_TIMESLICE |
244           RTEMS_NO_ASR,
245           RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
246           &tty->txTaskId);
247      if (sc != RTEMS_SUCCESSFUL)
248        rtems_fatal_error_occurred (sc);
249      sc = rtems_task_create (
250                                   rtems_build_name ('R', 'x', 'T', c),
251           TERMIOS_RXTASK_PRIO,
252           TERMIOS_RXTASK_STACKSIZE,
253           RTEMS_NO_PREEMPT | RTEMS_NO_TIMESLICE |
254           RTEMS_NO_ASR,
255           RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
256           &tty->rxTaskId);
257      if (sc != RTEMS_SUCCESSFUL)
258        rtems_fatal_error_occurred (sc);
259
260    }
261    if ((tty->device.pollRead == NULL) ||
262        (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN)){
263      sc = rtems_semaphore_create (
264        rtems_build_name ('T', 'R', 'r', c),
265        0,
266        RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_PRIORITY,
267        RTEMS_NO_PRIORITY,
268        &tty->rawInBuf.Semaphore);
269      if (sc != RTEMS_SUCCESSFUL)
270        rtems_fatal_error_occurred (sc);
271    }
272
273    /*
274     * Set default parameters
275     */
276    tty->termios.c_iflag = BRKINT | ICRNL | IXON | IMAXBEL;
277    tty->termios.c_oflag = OPOST | ONLCR | XTABS;
278    tty->termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL;
279    tty->termios.c_lflag =
280       ISIG | ICANON | IEXTEN | ECHO | ECHOK | ECHOE | ECHOCTL;
281
282    tty->termios.c_cc[VINTR] = '\003';
283    tty->termios.c_cc[VQUIT] = '\034';
284    tty->termios.c_cc[VERASE] = '\177';
285    tty->termios.c_cc[VKILL] = '\025';
286    tty->termios.c_cc[VEOF] = '\004';
287    tty->termios.c_cc[VEOL] = '\000';
288    tty->termios.c_cc[VEOL2] = '\000';
289    tty->termios.c_cc[VSTART] = '\021';
290    tty->termios.c_cc[VSTOP] = '\023';
291    tty->termios.c_cc[VSUSP] = '\032';
292    tty->termios.c_cc[VREPRINT] = '\022';
293    tty->termios.c_cc[VDISCARD] = '\017';
294    tty->termios.c_cc[VWERASE] = '\027';
295    tty->termios.c_cc[VLNEXT] = '\026';
296
297    /* start with no flow control, clear flow control flags */
298    tty->flow_ctrl = 0;
299    /*
300     * set low/highwater mark for XON/XOFF support
301     */
302    tty->lowwater  = tty->rawInBuf.Size * 1/2;
303    tty->highwater = tty->rawInBuf.Size * 3/4;
304    /*
305     * Bump name characer
306     */
307    if (c++ == 'z')
308      c = 'a';
309
310  }
311  args->iop->data1 = tty;
312  if (!tty->refcount++) {
313    if (tty->device.firstOpen)
314      (*tty->device.firstOpen)(major, minor, arg);
315
316    /*
317     * start I/O tasks, if needed
318     */
319    if (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN) {
320      sc = rtems_task_start(
321        tty->rxTaskId, rtems_termios_rxdaemon, (rtems_task_argument)tty);
322      if (sc != RTEMS_SUCCESSFUL)
323        rtems_fatal_error_occurred (sc);
324
325      sc = rtems_task_start(
326        tty->txTaskId, rtems_termios_txdaemon, (rtems_task_argument)tty);
327      if (sc != RTEMS_SUCCESSFUL)
328        rtems_fatal_error_occurred (sc);
329    }
330  }
331  rtems_semaphore_release (rtems_termios_ttyMutex);
332  return RTEMS_SUCCESSFUL;
333}
334
335/*
336 * Drain output queue
337 */
338static void
339drainOutput (struct rtems_termios_tty *tty)
340{
341  rtems_interrupt_level level;
342  rtems_status_code sc;
343
344  if (tty->device.outputUsesInterrupts != TERMIOS_POLLED) {
345    rtems_termios_interrupt_lock_acquire (tty, level);
346    while (tty->rawOutBuf.Tail != tty->rawOutBuf.Head) {
347      tty->rawOutBufState = rob_wait;
348      rtems_termios_interrupt_lock_release (tty, level);
349      sc = rtems_semaphore_obtain(
350        tty->rawOutBuf.Semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
351      if (sc != RTEMS_SUCCESSFUL)
352        rtems_fatal_error_occurred (sc);
353      rtems_termios_interrupt_lock_acquire (tty, level);
354    }
355    rtems_termios_interrupt_lock_release (tty, level);
356  }
357}
358
359static void
360flushOutput (struct rtems_termios_tty *tty)
361{
362  rtems_interrupt_level level;
363
364  rtems_termios_interrupt_lock_acquire (tty, level);
365  tty->rawOutBuf.Tail = 0;
366  tty->rawOutBuf.Head = 0;
367  tty->rawOutBufState = rob_idle;
368  rtems_termios_interrupt_lock_release (tty, level);
369}
370
371static void
372flushInput (struct rtems_termios_tty *tty)
373{
374  rtems_interrupt_level level;
375
376  rtems_termios_interrupt_lock_acquire (tty, level);
377  tty->rawInBuf.Tail = 0;
378  tty->rawInBuf.Head = 0;
379  rtems_termios_interrupt_lock_release (tty, level);
380}
381
382rtems_status_code
383rtems_termios_close (void *arg)
384{
385  rtems_libio_open_close_args_t *args = arg;
386  struct rtems_termios_tty *tty = args->iop->data1;
387  rtems_status_code sc;
388
389  sc = rtems_semaphore_obtain(
390    rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
391  if (sc != RTEMS_SUCCESSFUL)
392    rtems_fatal_error_occurred (sc);
393  if (--tty->refcount == 0) {
394    if (rtems_termios_linesw[tty->t_line].l_close != NULL) {
395      /*
396       * call discipline-specific close
397       */
398      sc = rtems_termios_linesw[tty->t_line].l_close(tty);
399    } else {
400      /*
401       * default: just flush output buffer
402       */
403      sc = rtems_semaphore_obtain(tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
404      if (sc != RTEMS_SUCCESSFUL) {
405        rtems_fatal_error_occurred (sc);
406      }
407      drainOutput (tty);
408      rtems_semaphore_release (tty->osem);
409    }
410
411    if (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN) {
412      /*
413       * send "terminate" to I/O tasks
414       */
415      sc = rtems_event_send( tty->rxTaskId, TERMIOS_RX_TERMINATE_EVENT );
416      if (sc != RTEMS_SUCCESSFUL)
417        rtems_fatal_error_occurred (sc);
418      sc = rtems_event_send( tty->txTaskId, TERMIOS_TX_TERMINATE_EVENT );
419      if (sc != RTEMS_SUCCESSFUL)
420        rtems_fatal_error_occurred (sc);
421    }
422    if (tty->device.lastClose)
423       (*tty->device.lastClose)(tty->major, tty->minor, arg);
424    if (tty->forw == NULL) {
425      rtems_termios_ttyTail = tty->back;
426      if ( rtems_termios_ttyTail != NULL ) {
427        rtems_termios_ttyTail->forw = NULL;
428      }
429    } else {
430      tty->forw->back = tty->back;
431    }
432
433    if (tty->back == NULL) {
434      rtems_termios_ttyHead = tty->forw;
435      if ( rtems_termios_ttyHead != NULL ) {
436        rtems_termios_ttyHead->back = NULL;
437      }
438    } else {
439      tty->back->forw = tty->forw;
440    }
441
442    rtems_semaphore_delete (tty->isem);
443    rtems_semaphore_delete (tty->osem);
444    rtems_semaphore_delete (tty->rawOutBuf.Semaphore);
445    if ((tty->device.pollRead == NULL) ||
446        (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN))
447      rtems_semaphore_delete (tty->rawInBuf.Semaphore);
448    free (tty->rawInBuf.theBuf);
449    free (tty->rawOutBuf.theBuf);
450    free (tty->cbuf);
451    free (tty);
452  }
453  rtems_semaphore_release (rtems_termios_ttyMutex);
454  return RTEMS_SUCCESSFUL;
455}
456
457rtems_status_code rtems_termios_bufsize (
458  size_t cbufsize,
459  size_t raw_input,
460  size_t raw_output
461)
462{
463  rtems_termios_cbufsize        = cbufsize;
464  rtems_termios_raw_input_size  = raw_input;
465  rtems_termios_raw_output_size = raw_output;
466  return RTEMS_SUCCESSFUL;
467}
468
469static void
470termios_set_flowctrl(struct rtems_termios_tty *tty)
471{
472  rtems_interrupt_level level;
473  /*
474   * check for flow control options to be switched off
475   */
476
477  /* check for outgoing XON/XOFF flow control switched off */
478  if (( tty->flow_ctrl & FL_MDXON) &&
479      !(tty->termios.c_iflag & IXON)) {
480    /* clear related flags in flow_ctrl */
481    tty->flow_ctrl &= ~(FL_MDXON | FL_ORCVXOF);
482
483    /* has output been stopped due to received XOFF? */
484    if (tty->flow_ctrl & FL_OSTOP) {
485      /* disable interrupts    */
486      rtems_termios_interrupt_lock_acquire (tty, level);
487      tty->flow_ctrl &= ~FL_OSTOP;
488      /* check for chars in output buffer (or rob_state?) */
489      if (tty->rawOutBufState != rob_idle) {
490        /* if chars available, call write function... */
491        (*tty->device.write)(
492          tty->minor, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1);
493      }
494      /* reenable interrupts */
495      rtems_termios_interrupt_lock_release (tty, level);
496    }
497  }
498  /* check for incoming XON/XOFF flow control switched off */
499  if (( tty->flow_ctrl & FL_MDXOF) && !(tty->termios.c_iflag & IXOFF)) {
500    /* clear related flags in flow_ctrl */
501    tty->flow_ctrl &= ~(FL_MDXOF);
502    /* FIXME: what happens, if we had sent XOFF but not yet XON? */
503    tty->flow_ctrl &= ~(FL_ISNTXOF);
504  }
505
506  /* check for incoming RTS/CTS flow control switched off */
507  if (( tty->flow_ctrl & FL_MDRTS) && !(tty->termios.c_cflag & CRTSCTS)) {
508    /* clear related flags in flow_ctrl */
509    tty->flow_ctrl &= ~(FL_MDRTS);
510
511    /* restart remote Tx, if it was stopped */
512    if ((tty->flow_ctrl & FL_IRTSOFF) && (tty->device.startRemoteTx != NULL)) {
513      tty->device.startRemoteTx(tty->minor);
514    }
515    tty->flow_ctrl &= ~(FL_IRTSOFF);
516  }
517
518  /*
519   * check for flow control options to be switched on
520   */
521  /* check for incoming RTS/CTS flow control switched on */
522  if (tty->termios.c_cflag & CRTSCTS) {
523    tty->flow_ctrl |= FL_MDRTS;
524  }
525  /* check for incoming XON/XOF flow control switched on */
526  if (tty->termios.c_iflag & IXOFF) {
527    tty->flow_ctrl |= FL_MDXOF;
528  }
529  /* check for outgoing XON/XOF flow control switched on */
530  if (tty->termios.c_iflag & IXON) {
531    tty->flow_ctrl |= FL_MDXON;
532  }
533}
534
535rtems_status_code
536rtems_termios_ioctl (void *arg)
537{
538  rtems_libio_ioctl_args_t *args = arg;
539  struct rtems_termios_tty *tty = args->iop->data1;
540  struct ttywakeup         *wakeup = (struct ttywakeup *)args->buffer;
541  rtems_status_code sc;
542
543  args->ioctl_return = 0;
544  sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
545  if (sc != RTEMS_SUCCESSFUL) {
546    return sc;
547  }
548  switch (args->command) {
549  default:
550    if (rtems_termios_linesw[tty->t_line].l_ioctl != NULL) {
551      sc = rtems_termios_linesw[tty->t_line].l_ioctl(tty,args);
552    }
553    else {
554      sc = RTEMS_INVALID_NUMBER;
555    }
556    break;
557
558  case RTEMS_IO_GET_ATTRIBUTES:
559    *(struct termios *)args->buffer = tty->termios;
560    break;
561
562  case RTEMS_IO_SET_ATTRIBUTES:
563    tty->termios = *(struct termios *)args->buffer;
564
565    /* check for and process change in flow control options */
566    termios_set_flowctrl(tty);
567
568    if (tty->termios.c_lflag & ICANON) {
569      tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
570      tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
571      tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
572    } else {
573      tty->vtimeTicks = tty->termios.c_cc[VTIME] *
574                    rtems_clock_get_ticks_per_second() / 10;
575      if (tty->termios.c_cc[VTIME]) {
576        tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
577        tty->rawInBufSemaphoreTimeout = tty->vtimeTicks;
578        if (tty->termios.c_cc[VMIN])
579          tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
580        else
581          tty->rawInBufSemaphoreFirstTimeout = tty->vtimeTicks;
582      } else {
583        if (tty->termios.c_cc[VMIN]) {
584          tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
585          tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
586          tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
587        } else {
588          tty->rawInBufSemaphoreOptions = RTEMS_NO_WAIT;
589        }
590      }
591    }
592    if (tty->device.setAttributes)
593      (*tty->device.setAttributes)(tty->minor, &tty->termios);
594    break;
595
596  case RTEMS_IO_TCDRAIN:
597    drainOutput (tty);
598    break;
599
600  case RTEMS_IO_TCFLUSH:
601    switch ((intptr_t) args->buffer) {
602      case TCIFLUSH:
603        flushInput (tty);
604        break;
605      case TCOFLUSH:
606        flushOutput (tty);
607        break;
608      case TCIOFLUSH:
609        flushOutput (tty);
610        flushInput (tty);
611        break;
612      default:
613        sc = RTEMS_INVALID_NAME;
614        break;
615    }
616    break;
617
618  case RTEMS_IO_SNDWAKEUP:
619    tty->tty_snd = *wakeup;
620    break;
621
622  case RTEMS_IO_RCVWAKEUP:
623    tty->tty_rcv = *wakeup;
624    break;
625
626    /*
627     * FIXME: add various ioctl code handlers
628     */
629
630#if 1 /* FIXME */
631  case TIOCSETD:
632    /*
633     * close old line discipline
634     */
635    if (rtems_termios_linesw[tty->t_line].l_close != NULL) {
636      sc = rtems_termios_linesw[tty->t_line].l_close(tty);
637    }
638    tty->t_line=*(int*)(args->buffer);
639    tty->t_sc = NULL; /* ensure that no more valid data */
640    /*
641     * open new line discipline
642     */
643    if (rtems_termios_linesw[tty->t_line].l_open != NULL) {
644      sc = rtems_termios_linesw[tty->t_line].l_open(tty);
645    }
646    break;
647  case TIOCGETD:
648    *(int*)(args->buffer)=tty->t_line;
649    break;
650#endif
651   case FIONREAD: {
652      int rawnc = tty->rawInBuf.Tail - tty->rawInBuf.Head;
653      if ( rawnc < 0 )
654        rawnc += tty->rawInBuf.Size;
655      /* Half guess that this is the right operation */
656      *(int *)args->buffer = tty->ccount - tty->cindex + rawnc;
657    }
658    break;
659  }
660
661  rtems_semaphore_release (tty->osem);
662  return sc;
663}
664
665/*
666 * Send characters to device-specific code
667 */
668void
669rtems_termios_puts (
670  const void *_buf, size_t len, struct rtems_termios_tty *tty)
671{
672  const char *buf = _buf;
673  unsigned int newHead;
674  rtems_interrupt_level level;
675  rtems_status_code sc;
676
677  if (tty->device.outputUsesInterrupts == TERMIOS_POLLED) {
678    (*tty->device.write)(tty->minor, buf, len);
679    return;
680  }
681  newHead = tty->rawOutBuf.Head;
682  while (len) {
683    /*
684     * Performance improvement could be made here.
685     * Copy multiple bytes to raw buffer:
686     * if (len > 1) && (space to buffer end, or tail > 1)
687     *  ncopy = MIN (len, space to buffer end or tail)
688     *  memcpy (raw buffer, buf, ncopy)
689     *  buf += ncopy
690     *  len -= ncopy
691     *
692     * To minimize latency, the memcpy should be done
693     * with interrupts enabled.
694     */
695    newHead = (newHead + 1) % tty->rawOutBuf.Size;
696    rtems_termios_interrupt_lock_acquire (tty, level);
697    while (newHead == tty->rawOutBuf.Tail) {
698      tty->rawOutBufState = rob_wait;
699      rtems_termios_interrupt_lock_release (tty, level);
700      sc = rtems_semaphore_obtain(
701        tty->rawOutBuf.Semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
702      if (sc != RTEMS_SUCCESSFUL)
703        rtems_fatal_error_occurred (sc);
704      rtems_termios_interrupt_lock_acquire (tty, level);
705    }
706    tty->rawOutBuf.theBuf[tty->rawOutBuf.Head] = *buf++;
707    tty->rawOutBuf.Head = newHead;
708    if (tty->rawOutBufState == rob_idle) {
709      /* check, whether XOFF has been received */
710      if (!(tty->flow_ctrl & FL_ORCVXOF)) {
711        (*tty->device.write)(
712          tty->minor, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1);
713      } else {
714        /* remember that output has been stopped due to flow ctrl*/
715        tty->flow_ctrl |= FL_OSTOP;
716      }
717      tty->rawOutBufState = rob_busy;
718    }
719    rtems_termios_interrupt_lock_release (tty, level);
720    len--;
721  }
722}
723
724/*
725 * Handle output processing
726 */
727static void
728oproc (unsigned char c, struct rtems_termios_tty *tty)
729{
730  int  i;
731
732  if (tty->termios.c_oflag & OPOST) {
733    switch (c) {
734    case '\n':
735      if (tty->termios.c_oflag & ONLRET)
736        tty->column = 0;
737      if (tty->termios.c_oflag & ONLCR) {
738        rtems_termios_puts ("\r", 1, tty);
739        tty->column = 0;
740      }
741      break;
742
743    case '\r':
744      if ((tty->termios.c_oflag & ONOCR) && (tty->column == 0))
745        return;
746      if (tty->termios.c_oflag & OCRNL) {
747        c = '\n';
748        if (tty->termios.c_oflag & ONLRET)
749          tty->column = 0;
750        break;
751      }
752      tty->column = 0;
753      break;
754
755    case '\t':
756      i = 8 - (tty->column & 7);
757      if ((tty->termios.c_oflag & TABDLY) == XTABS) {
758        tty->column += i;
759        rtems_termios_puts ( "        ",  i, tty);
760        return;
761      }
762      tty->column += i;
763      break;
764
765    case '\b':
766      if (tty->column > 0)
767        tty->column--;
768      break;
769
770    default:
771      if (tty->termios.c_oflag & OLCUC)
772        c = toupper(c);
773      if (!iscntrl(c))
774        tty->column++;
775      break;
776    }
777  }
778  rtems_termios_puts (&c, 1, tty);
779}
780
781rtems_status_code
782rtems_termios_write (void *arg)
783{
784  rtems_libio_rw_args_t *args = arg;
785  struct rtems_termios_tty *tty = args->iop->data1;
786  rtems_status_code sc;
787
788  sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
789  if (sc != RTEMS_SUCCESSFUL)
790    return sc;
791  if (rtems_termios_linesw[tty->t_line].l_write != NULL) {
792    sc = rtems_termios_linesw[tty->t_line].l_write(tty,args);
793    rtems_semaphore_release (tty->osem);
794    return sc;
795  }
796  if (tty->termios.c_oflag & OPOST) {
797    uint32_t   count = args->count;
798    char      *buffer = args->buffer;
799    while (count--)
800      oproc (*buffer++, tty);
801    args->bytes_moved = args->count;
802  } else {
803    rtems_termios_puts (args->buffer, args->count, tty);
804    args->bytes_moved = args->count;
805  }
806  rtems_semaphore_release (tty->osem);
807  return sc;
808}
809
810/*
811 * Echo a typed character
812 */
813static void
814echo (unsigned char c, struct rtems_termios_tty *tty)
815{
816  if ((tty->termios.c_lflag & ECHOCTL) &&
817       iscntrl(c) && (c != '\t') && (c != '\n')) {
818    char echobuf[2];
819
820    echobuf[0] = '^';
821    echobuf[1] = c ^ 0x40;
822    rtems_termios_puts (echobuf, 2, tty);
823    tty->column += 2;
824  } else {
825    oproc (c, tty);
826  }
827}
828
829/*
830 * Erase a character or line
831 * FIXME: Needs support for WERASE and ECHOPRT.
832 * FIXME: Some of the tests should check for IEXTEN, too.
833 */
834static void
835erase (struct rtems_termios_tty *tty, int lineFlag)
836{
837  if (tty->ccount == 0)
838    return;
839  if (lineFlag) {
840    if (!(tty->termios.c_lflag & ECHO)) {
841      tty->ccount = 0;
842      return;
843    }
844    if (!(tty->termios.c_lflag & ECHOE)) {
845      tty->ccount = 0;
846      echo (tty->termios.c_cc[VKILL], tty);
847      if (tty->termios.c_lflag & ECHOK)
848        echo ('\n', tty);
849      return;
850    }
851  }
852
853  while (tty->ccount) {
854    unsigned char c = tty->cbuf[--tty->ccount];
855
856    if (tty->termios.c_lflag & ECHO) {
857      if (!lineFlag && !(tty->termios.c_lflag & ECHOE)) {
858        echo (tty->termios.c_cc[VERASE], tty);
859      } else if (c == '\t') {
860        int col = tty->read_start_column;
861        int i = 0;
862
863        /*
864         * Find the character before the tab
865         */
866        while (i != tty->ccount) {
867          c = tty->cbuf[i++];
868          if (c == '\t') {
869            col = (col | 7) + 1;
870          } else if (iscntrl (c)) {
871            if (tty->termios.c_lflag & ECHOCTL)
872              col += 2;
873          } else {
874            col++;
875          }
876        }
877
878        /*
879         * Back up over the tab
880         */
881        while (tty->column > col) {
882          rtems_termios_puts ("\b", 1, tty);
883          tty->column--;
884        }
885      }
886      else {
887        if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) {
888          rtems_termios_puts ("\b \b", 3, tty);
889          if (tty->column)
890            tty->column--;
891        }
892        if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) {
893          rtems_termios_puts ("\b \b", 3, tty);
894          if (tty->column)
895            tty->column--;
896        }
897      }
898    }
899    if (!lineFlag)
900      break;
901  }
902}
903
904/*
905 * Process a single input character
906 */
907static int
908iproc (unsigned char c, struct rtems_termios_tty *tty)
909{
910  if (tty->termios.c_iflag & ISTRIP)
911    c &= 0x7f;
912
913  if (tty->termios.c_iflag & IUCLC)
914    c = tolower (c);
915
916  if (c == '\r') {
917    if (tty->termios.c_iflag & IGNCR)
918      return 0;
919    if (tty->termios.c_iflag & ICRNL)
920      c = '\n';
921  } else if ((c == '\n') && (tty->termios.c_iflag & INLCR)) {
922    c = '\r';
923  }
924
925  if ((c != '\0') && (tty->termios.c_lflag & ICANON)) {
926    if (c == tty->termios.c_cc[VERASE]) {
927      erase (tty, 0);
928      return 0;
929    }
930    else if (c == tty->termios.c_cc[VKILL]) {
931      erase (tty, 1);
932      return 0;
933    }
934    else if (c == tty->termios.c_cc[VEOF]) {
935      return 1;
936    } else if (c == '\n') {
937      if (tty->termios.c_lflag & (ECHO | ECHONL))
938        echo (c, tty);
939      tty->cbuf[tty->ccount++] = c;
940      return 1;
941    } else if ((c == tty->termios.c_cc[VEOL]) ||
942               (c == tty->termios.c_cc[VEOL2])) {
943      if (tty->termios.c_lflag & ECHO)
944        echo (c, tty);
945      tty->cbuf[tty->ccount++] = c;
946      return 1;
947    }
948  }
949
950  /*
951   * FIXME: Should do IMAXBEL handling somehow
952   */
953  if (tty->ccount < (CBUFSIZE-1)) {
954    if (tty->termios.c_lflag & ECHO)
955      echo (c, tty);
956    tty->cbuf[tty->ccount++] = c;
957  }
958  return 0;
959}
960
961/*
962 * Process input character, with semaphore.
963 */
964static int
965siproc (unsigned char c, struct rtems_termios_tty *tty)
966{
967  int i;
968
969  /*
970   * Obtain output semaphore if character will be echoed
971   */
972  if (tty->termios.c_lflag & (ECHO|ECHOE|ECHOK|ECHONL|ECHOPRT|ECHOCTL|ECHOKE)) {
973    rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
974    i = iproc (c, tty);
975    rtems_semaphore_release (tty->osem);
976  }
977  else {
978    i = iproc (c, tty);
979  }
980  return i;
981}
982
983/*
984 * Fill the input buffer by polling the device
985 */
986static rtems_status_code
987fillBufferPoll (struct rtems_termios_tty *tty)
988{
989  int n;
990
991  if (tty->termios.c_lflag & ICANON) {
992    for (;;) {
993      n = (*tty->device.pollRead)(tty->minor);
994      if (n < 0) {
995        rtems_task_wake_after (1);
996      } else {
997        if  (siproc (n, tty))
998          break;
999      }
1000    }
1001  } else {
1002    rtems_interval then, now;
1003
1004    then = rtems_clock_get_ticks_since_boot();
1005    for (;;) {
1006      n = (*tty->device.pollRead)(tty->minor);
1007      if (n < 0) {
1008        if (tty->termios.c_cc[VMIN]) {
1009          if (tty->termios.c_cc[VTIME] && tty->ccount) {
1010            now = rtems_clock_get_ticks_since_boot();
1011            if ((now - then) > tty->vtimeTicks) {
1012              break;
1013            }
1014          }
1015        } else {
1016          if (!tty->termios.c_cc[VTIME])
1017            break;
1018          now = rtems_clock_get_ticks_since_boot();
1019          if ((now - then) > tty->vtimeTicks) {
1020            break;
1021          }
1022        }
1023        rtems_task_wake_after (1);
1024      } else {
1025        siproc (n, tty);
1026        if (tty->ccount >= tty->termios.c_cc[VMIN])
1027          break;
1028        if (tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
1029          then = rtems_clock_get_ticks_since_boot();
1030      }
1031    }
1032  }
1033  return RTEMS_SUCCESSFUL;
1034}
1035
1036/*
1037 * Fill the input buffer from the raw input queue
1038 */
1039static rtems_status_code
1040fillBufferQueue (struct rtems_termios_tty *tty)
1041{
1042  rtems_interval timeout = tty->rawInBufSemaphoreFirstTimeout;
1043  rtems_status_code sc;
1044  int               wait = 1;
1045
1046  while ( wait ) {
1047    /*
1048     * Process characters read from raw queue
1049     */
1050    while ((tty->rawInBuf.Head != tty->rawInBuf.Tail) &&
1051                       (tty->ccount < (CBUFSIZE-1))) {
1052      unsigned char c;
1053      unsigned int newHead;
1054
1055      newHead = (tty->rawInBuf.Head + 1) % tty->rawInBuf.Size;
1056      c = tty->rawInBuf.theBuf[newHead];
1057      tty->rawInBuf.Head = newHead;
1058      if(((tty->rawInBuf.Tail-newHead+tty->rawInBuf.Size)
1059          % tty->rawInBuf.Size)
1060         < tty->lowwater) {
1061        tty->flow_ctrl &= ~FL_IREQXOF;
1062        /* if tx stopped and XON should be sent... */
1063        if (((tty->flow_ctrl & (FL_MDXON | FL_ISNTXOF))
1064             ==                (FL_MDXON | FL_ISNTXOF))
1065            && ((tty->rawOutBufState == rob_idle)
1066          || (tty->flow_ctrl & FL_OSTOP))) {
1067          /* XON should be sent now... */
1068          (*tty->device.write)(
1069            tty->minor, (void *)&(tty->termios.c_cc[VSTART]), 1);
1070        } else if (tty->flow_ctrl & FL_MDRTS) {
1071          tty->flow_ctrl &= ~FL_IRTSOFF;
1072          /* activate RTS line */
1073          if (tty->device.startRemoteTx != NULL) {
1074            tty->device.startRemoteTx(tty->minor);
1075          }
1076        }
1077      }
1078
1079      /* continue processing new character */
1080      if (tty->termios.c_lflag & ICANON) {
1081        if (siproc (c, tty))
1082          wait = 0;
1083      } else {
1084        siproc (c, tty);
1085        if (tty->ccount >= tty->termios.c_cc[VMIN])
1086          wait = 0;
1087      }
1088      timeout = tty->rawInBufSemaphoreTimeout;
1089    }
1090
1091    /*
1092     * Wait for characters
1093     */
1094    if ( wait ) {
1095      sc = rtems_semaphore_obtain(
1096        tty->rawInBuf.Semaphore, tty->rawInBufSemaphoreOptions, timeout);
1097      if (sc != RTEMS_SUCCESSFUL)
1098        break;
1099    }
1100  }
1101  return RTEMS_SUCCESSFUL;
1102}
1103
1104rtems_status_code
1105rtems_termios_read (void *arg)
1106{
1107  rtems_libio_rw_args_t *args = arg;
1108  struct rtems_termios_tty *tty = args->iop->data1;
1109  uint32_t   count = args->count;
1110  char      *buffer = args->buffer;
1111  rtems_status_code sc;
1112
1113  sc = rtems_semaphore_obtain (tty->isem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
1114  if (sc != RTEMS_SUCCESSFUL)
1115    return sc;
1116
1117  if (rtems_termios_linesw[tty->t_line].l_read != NULL) {
1118    sc = rtems_termios_linesw[tty->t_line].l_read(tty,args);
1119    tty->tty_rcvwakeup = 0;
1120    rtems_semaphore_release (tty->isem);
1121    return sc;
1122  }
1123
1124  if (tty->cindex == tty->ccount) {
1125    tty->cindex = tty->ccount = 0;
1126    tty->read_start_column = tty->column;
1127    if (tty->device.pollRead != NULL &&
1128        tty->device.outputUsesInterrupts == TERMIOS_POLLED)
1129      sc = fillBufferPoll (tty);
1130    else
1131      sc = fillBufferQueue (tty);
1132
1133    if (sc != RTEMS_SUCCESSFUL)
1134      tty->cindex = tty->ccount = 0;
1135  }
1136  while (count && (tty->cindex < tty->ccount)) {
1137    *buffer++ = tty->cbuf[tty->cindex++];
1138    count--;
1139  }
1140  args->bytes_moved = args->count - count;
1141  tty->tty_rcvwakeup = 0;
1142  rtems_semaphore_release (tty->isem);
1143  return sc;
1144}
1145
1146/*
1147 * signal receive interrupt to rx daemon
1148 * NOTE: This routine runs in the context of the
1149 *       device receive interrupt handler.
1150 */
1151void rtems_termios_rxirq_occured(struct rtems_termios_tty *tty)
1152{
1153  /*
1154   * send event to rx daemon task
1155   */
1156  rtems_event_send(tty->rxTaskId,TERMIOS_RX_PROC_EVENT);
1157}
1158
1159/*
1160 * Place characters on raw queue.
1161 * NOTE: This routine runs in the context of the
1162 *       device receive interrupt handler.
1163 * Returns the number of characters dropped because of overflow.
1164 */
1165int
1166rtems_termios_enqueue_raw_characters (void *ttyp, const char *buf, int len)
1167{
1168  struct rtems_termios_tty *tty = ttyp;
1169  unsigned int newTail;
1170  char c;
1171  int dropped = 0;
1172  bool flow_rcv = false; /* true, if flow control char received */
1173  rtems_interrupt_level level;
1174
1175  if (rtems_termios_linesw[tty->t_line].l_rint != NULL) {
1176    while (len--) {
1177      c = *buf++;
1178      rtems_termios_linesw[tty->t_line].l_rint(c,tty);
1179    }
1180
1181    /*
1182     * check to see if rcv wakeup callback was set
1183     */
1184    if (( !tty->tty_rcvwakeup ) && ( tty->tty_rcv.sw_pfn != NULL )) {
1185      (*tty->tty_rcv.sw_pfn)(&tty->termios, tty->tty_rcv.sw_arg);
1186      tty->tty_rcvwakeup = 1;
1187        }
1188    return 0;
1189  }
1190
1191  while (len--) {
1192    c = *buf++;
1193    /* FIXME: implement IXANY: any character restarts output */
1194    /* if incoming XON/XOFF controls outgoing stream: */
1195    if (tty->flow_ctrl & FL_MDXON) {
1196      /* if received char is V_STOP and V_START (both are equal value) */
1197      if (c == tty->termios.c_cc[VSTOP]) {
1198        if (c == tty->termios.c_cc[VSTART]) {
1199          /* received VSTOP and VSTART==VSTOP? */
1200          /* then toggle "stop output" status  */
1201          tty->flow_ctrl = tty->flow_ctrl ^ FL_ORCVXOF;
1202        }
1203        else {
1204          /* VSTOP received (other code than VSTART) */
1205          /* stop output                             */
1206          tty->flow_ctrl |= FL_ORCVXOF;
1207        }
1208        flow_rcv = true;
1209      }
1210      else if (c == tty->termios.c_cc[VSTART]) {
1211        /* VSTART received */
1212        /* restart output  */
1213        tty->flow_ctrl &= ~FL_ORCVXOF;
1214        flow_rcv = true;
1215      }
1216    }
1217    if (flow_rcv) {
1218      /* restart output according to FL_ORCVXOF flag */
1219      if ((tty->flow_ctrl & (FL_ORCVXOF | FL_OSTOP)) == FL_OSTOP) {
1220        /* disable interrupts    */
1221        rtems_termios_interrupt_lock_acquire (tty, level);
1222        tty->flow_ctrl &= ~FL_OSTOP;
1223        /* check for chars in output buffer (or rob_state?) */
1224        if (tty->rawOutBufState != rob_idle) {
1225          /* if chars available, call write function... */
1226          (*tty->device.write)(
1227            tty->minor, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail], 1);
1228        }
1229        /* reenable interrupts */
1230        rtems_termios_interrupt_lock_release (tty, level);
1231      }
1232    } else {
1233      newTail = (tty->rawInBuf.Tail + 1) % tty->rawInBuf.Size;
1234      /* if chars_in_buffer > highwater                */
1235      rtems_termios_interrupt_lock_acquire (tty, level);
1236      if ((((newTail - tty->rawInBuf.Head + tty->rawInBuf.Size)
1237            % tty->rawInBuf.Size) > tty->highwater) &&
1238          !(tty->flow_ctrl & FL_IREQXOF)) {
1239        /* incoming data stream should be stopped */
1240        tty->flow_ctrl |= FL_IREQXOF;
1241        if ((tty->flow_ctrl & (FL_MDXOF | FL_ISNTXOF))
1242            ==                (FL_MDXOF             ) ) {
1243          if ((tty->flow_ctrl & FL_OSTOP) ||
1244              (tty->rawOutBufState == rob_idle)) {
1245            /* if tx is stopped due to XOFF or out of data */
1246            /*    call write function here                 */
1247            tty->flow_ctrl |= FL_ISNTXOF;
1248            (*tty->device.write)(tty->minor,
1249                (void *)&(tty->termios.c_cc[VSTOP]), 1);
1250          }
1251        } else if ((tty->flow_ctrl & (FL_MDRTS | FL_IRTSOFF)) == (FL_MDRTS) ) {
1252          tty->flow_ctrl |= FL_IRTSOFF;
1253          /* deactivate RTS line */
1254          if (tty->device.stopRemoteTx != NULL) {
1255            tty->device.stopRemoteTx(tty->minor);
1256          }
1257        }
1258      }
1259
1260      /* reenable interrupts */
1261      rtems_termios_interrupt_lock_release (tty, level);
1262
1263      if (newTail == tty->rawInBuf.Head) {
1264        dropped++;
1265      } else {
1266        tty->rawInBuf.theBuf[newTail] = c;
1267        tty->rawInBuf.Tail = newTail;
1268
1269        /*
1270         * check to see if rcv wakeup callback was set
1271         */
1272        if (( !tty->tty_rcvwakeup ) && ( tty->tty_rcv.sw_pfn != NULL )) {
1273          (*tty->tty_rcv.sw_pfn)(&tty->termios, tty->tty_rcv.sw_arg);
1274          tty->tty_rcvwakeup = 1;
1275        }
1276      }
1277    }
1278  }
1279
1280  tty->rawInBufDropped += dropped;
1281  rtems_semaphore_release (tty->rawInBuf.Semaphore);
1282  return dropped;
1283}
1284
1285/*
1286 * in task-driven mode, this function is called in Tx task context
1287 * in interrupt-driven mode, this function is called in TxIRQ context
1288 */
1289static int
1290rtems_termios_refill_transmitter (struct rtems_termios_tty *tty)
1291{
1292  bool wakeUpWriterTask = false;
1293  unsigned int newTail;
1294  int nToSend;
1295  rtems_interrupt_level level;
1296  int len;
1297
1298  rtems_termios_interrupt_lock_acquire (tty, level);
1299
1300  /* check for XOF/XON to send */
1301  if ((tty->flow_ctrl & (FL_MDXOF | FL_IREQXOF | FL_ISNTXOF))
1302      == (FL_MDXOF | FL_IREQXOF)) {
1303    /* XOFF should be sent now... */
1304    (*tty->device.write)(tty->minor, (void *)&(tty->termios.c_cc[VSTOP]), 1);
1305
1306    tty->t_dqlen--;
1307    tty->flow_ctrl |= FL_ISNTXOF;
1308
1309    nToSend = 1;
1310
1311  } else if ((tty->flow_ctrl & (FL_IREQXOF | FL_ISNTXOF)) == FL_ISNTXOF) {
1312    /* NOTE: send XON even, if no longer in XON/XOFF mode... */
1313    /* XON should be sent now... */
1314    /*
1315     * FIXME: this .write call will generate another
1316     * dequeue callback. This will advance the "Tail" in the data
1317     * buffer, although the corresponding data is not yet out!
1318     * Therefore the dequeue "length" should be reduced by 1
1319     */
1320    (*tty->device.write)(tty->minor, (void *)&(tty->termios.c_cc[VSTART]), 1);
1321
1322    tty->t_dqlen--;
1323    tty->flow_ctrl &= ~FL_ISNTXOF;
1324
1325    nToSend = 1;
1326  } else if ( tty->rawOutBuf.Head == tty->rawOutBuf.Tail ) {
1327    /*
1328     * buffer was empty
1329     */
1330    if (tty->rawOutBufState == rob_wait) {
1331      /*
1332       * this should never happen...
1333       */
1334      wakeUpWriterTask = true;
1335    }
1336
1337    (*tty->device.write) (tty->minor, NULL, 0);
1338    nToSend = 0;
1339  } else {
1340    len = tty->t_dqlen;
1341    tty->t_dqlen = 0;
1342
1343    newTail = (tty->rawOutBuf.Tail + len) % tty->rawOutBuf.Size;
1344    tty->rawOutBuf.Tail = newTail;
1345    if (tty->rawOutBufState == rob_wait) {
1346      /*
1347       * wake up any pending writer task
1348       */
1349      wakeUpWriterTask = true;
1350    }
1351
1352    if (newTail == tty->rawOutBuf.Head) {
1353      /*
1354       * Buffer has become empty
1355       */
1356      tty->rawOutBufState = rob_idle;
1357      (*tty->device.write) (tty->minor, NULL, 0);
1358      nToSend = 0;
1359
1360      /*
1361       * check to see if snd wakeup callback was set
1362       */
1363      if ( tty->tty_snd.sw_pfn != NULL) {
1364        (*tty->tty_snd.sw_pfn)(&tty->termios, tty->tty_snd.sw_arg);
1365      }
1366    }
1367    /* check, whether output should stop due to received XOFF */
1368    else if ((tty->flow_ctrl & (FL_MDXON | FL_ORCVXOF))
1369       ==                (FL_MDXON | FL_ORCVXOF)) {
1370      /* Buffer not empty, but output stops due to XOFF */
1371      /* set flag, that output has been stopped */
1372      tty->flow_ctrl |= FL_OSTOP;
1373      tty->rawOutBufState = rob_busy; /*apm*/
1374      (*tty->device.write) (tty->minor, NULL, 0);
1375      nToSend = 0;
1376    } else {
1377      /*
1378       * Buffer not empty, start tranmitter
1379       */
1380      if (newTail > tty->rawOutBuf.Head)
1381        nToSend = tty->rawOutBuf.Size - newTail;
1382      else
1383        nToSend = tty->rawOutBuf.Head - newTail;
1384      /* when flow control XON or XOF, don't send blocks of data     */
1385      /* to allow fast reaction on incoming flow ctrl and low latency*/
1386      /* for outgoing flow control                                   */
1387      if (tty->flow_ctrl & (FL_MDXON | FL_MDXOF)) {
1388        nToSend = 1;
1389      }
1390      tty->rawOutBufState = rob_busy; /*apm*/
1391      (*tty->device.write)(
1392        tty->minor, &tty->rawOutBuf.theBuf[newTail], nToSend);
1393    }
1394    tty->rawOutBuf.Tail = newTail; /*apm*/
1395  }
1396
1397  rtems_termios_interrupt_lock_release (tty, level);
1398
1399  if (wakeUpWriterTask) {
1400    rtems_semaphore_release (tty->rawOutBuf.Semaphore);
1401  }
1402
1403  return nToSend;
1404}
1405
1406/*
1407 * Characters have been transmitted
1408 * NOTE: This routine runs in the context of the
1409 *       device transmit interrupt handler.
1410 * The second argument is the number of characters transmitted so far.
1411 * This value will always be 1 for devices which generate an interrupt
1412 * for each transmitted character.
1413 * It returns number of characters left to transmit
1414 */
1415int
1416rtems_termios_dequeue_characters (void *ttyp, int len)
1417{
1418  struct rtems_termios_tty *tty = ttyp;
1419  rtems_status_code sc;
1420
1421  /*
1422   * sum up character count already sent
1423   */
1424  tty->t_dqlen += len;
1425
1426  if (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN) {
1427    /*
1428     * send wake up to transmitter task
1429     */
1430    sc = rtems_event_send(tty->txTaskId, TERMIOS_TX_START_EVENT);
1431    if (sc != RTEMS_SUCCESSFUL)
1432      rtems_fatal_error_occurred (sc);
1433    return 0; /* nothing to output in IRQ... */
1434  }
1435
1436  if (tty->t_line == PPPDISC ) {
1437    /*
1438     * call any line discipline start function
1439     */
1440    if (rtems_termios_linesw[tty->t_line].l_start != NULL) {
1441      rtems_termios_linesw[tty->t_line].l_start(tty);
1442    }
1443    return 0; /* nothing to output in IRQ... */
1444  }
1445
1446  return rtems_termios_refill_transmitter(tty);
1447}
1448
1449/*
1450 * this task actually processes any transmit events
1451 */
1452static rtems_task rtems_termios_txdaemon(rtems_task_argument argument)
1453{
1454  struct rtems_termios_tty *tty = (struct rtems_termios_tty *)argument;
1455  rtems_event_set the_event;
1456
1457  while (1) {
1458    /*
1459     * wait for rtems event
1460     */
1461    rtems_event_receive(
1462       (TERMIOS_TX_START_EVENT | TERMIOS_TX_TERMINATE_EVENT),
1463       RTEMS_EVENT_ANY | RTEMS_WAIT,
1464       RTEMS_NO_TIMEOUT,
1465       &the_event
1466    );
1467    if ((the_event & TERMIOS_TX_TERMINATE_EVENT) != 0) {
1468      tty->txTaskId = 0;
1469      rtems_task_delete(RTEMS_SELF);
1470    }
1471
1472    /*
1473     * call any line discipline start function
1474     */
1475    if (rtems_termios_linesw[tty->t_line].l_start != NULL) {
1476      rtems_termios_linesw[tty->t_line].l_start(tty);
1477    }
1478
1479    /*
1480     * try to push further characters to device
1481     */
1482    rtems_termios_refill_transmitter(tty);
1483  }
1484}
1485
1486/*
1487 * this task actually processes any receive events
1488 */
1489static rtems_task rtems_termios_rxdaemon(rtems_task_argument argument)
1490{
1491  struct rtems_termios_tty *tty = (struct rtems_termios_tty *)argument;
1492  rtems_event_set the_event;
1493  int c;
1494  char c_buf;
1495
1496  while (1) {
1497    /*
1498     * wait for rtems event
1499     */
1500    rtems_event_receive(
1501      (TERMIOS_RX_PROC_EVENT | TERMIOS_RX_TERMINATE_EVENT),
1502      RTEMS_EVENT_ANY | RTEMS_WAIT,
1503      RTEMS_NO_TIMEOUT,
1504      &the_event
1505    );
1506    if ((the_event & TERMIOS_RX_TERMINATE_EVENT) != 0) {
1507      tty->rxTaskId = 0;
1508      rtems_task_delete(RTEMS_SELF);
1509    }
1510
1511    /*
1512     * do something
1513     */
1514    c = tty->device.pollRead(tty->minor);
1515    if (c != EOF) {
1516      /*
1517       * pollRead did call enqueue on its own
1518       */
1519      c_buf = c;
1520      rtems_termios_enqueue_raw_characters ( tty,&c_buf,1);
1521    }
1522  }
1523}
Note: See TracBrowser for help on using the repository browser.