source: rtems/cpukit/libcsupport/src/termios.c @ 791469bd

5
Last change on this file since 791469bd was 791469bd, checked in by Sebastian Huber <sebastian.huber@…>, on 11/06/17 at 13:49:32

termios: Fix canonical mode

In canonical mode, input is made available line by line. We must stop
the canonical buffer filling upon reception of an end-of-line character.

Close #3218.

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