source: rtems/cpukit/libcsupport/src/termios.c @ 7f0c1fd5

4.115
Last change on this file since 7f0c1fd5 was 7f0c1fd5, checked in by Ralf Corsepius <ralf.corsepius@…>, on 12/05/11 at 05:06:50

2011-12-05 Ralf Corsépius <ralf.corsepius@…>

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