source: rtems/cpukit/libcsupport/src/termios.c @ 90f11edd

5
Last change on this file since 90f11edd was 90f11edd, checked in by Sebastian Huber <sebastian.huber@…>, on 01/29/20 at 08:32:05

termios: Fix input canonical mode

Canonical input processing was broken by
667501a314ba75f80f1c13c6b43dd35d0a00efd1 as shown by test case
termios09.

Update #3800.

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