source: rtems/cpukit/libcsupport/src/termios.c @ 1c6926c1

Last change on this file since 1c6926c1 was 1c6926c1, checked in by Kevin Kirspel <kevin-kirspel@…>, on Mar 21, 2017 at 7:39:48 PM

termios: Synchronize with latest FreeBSD headers

Adding modified FreeBSD headers to synchronize RTEMS termios with
FreeBSD. Modify termios to support dedicated input and output baud for
termios structure. Updated BSPs to use dedicated input and output baud
in termios structure. Updated tools to use dedicated input and output
baud in termios structure. Updated termios testsuites to use dedicated
input and output baud in termios structure.

Close #2897.

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