source: rtems/cpukit/libcsupport/src/termios.c @ 58cde54

4.11
Last change on this file since 58cde54 was 58cde54, checked in by Sebastian Huber <sebastian.huber@…>, on Jun 10, 2013 at 12:18:57 PM

termios: Move wake up writer task action

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