source: rtems/cpukit/libcsupport/src/termios.c @ 55e0be36

5
Last change on this file since 55e0be36 was 55e0be36, checked in by Sebastian Huber <sebastian.huber@…>, on 09/16/16 at 10:58:06

termios: Use IMFS nodes for new Termios devices

This makes the new Termios devices independent of device major/minor
numbers. It enables BSP independent Termios device drivers which may
reside in the cpukit domain. These drivers require an IMFS and do not
work with the device file system. However, the device file system
should go away in the future.

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