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

4.115
Last change on this file since f6b1e063 was f6b1e063, checked in by Sebastian Huber <sebastian.huber@…>, on 06/27/14 at 11:08:28

termios: Make tty list static

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