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

5
Last change on this file since b2ed712 was b2ed712, checked in by Sebastian Huber <sebastian.huber@…>, on 08/25/17 at 08:58:58

Include missing <string.h>

Update #2133.

  • Property mode set to 100644
File size: 59.1 KB
Line 
1/**
2 * @file
3 * TERMIOS serial line support
4 */
5
6/*
7 *  Author:
8 *    W. Eric Norum
9 *    Saskatchewan Accelerator Laboratory
10 *    University of Saskatchewan
11 *    Saskatoon, Saskatchewan, CANADA
12 *    eric@skatter.usask.ca
13 *
14 *  The license and distribution terms for this file may be
15 *  found in the file LICENSE in this distribution or at
16 *  http://www.rtems.org/license/LICENSE.
17 */
18
19#if HAVE_CONFIG_H
20#include "config.h"
21#endif
22
23#include <rtems.h>
24#include <rtems/libio.h>
25#include <rtems/imfs.h>
26#include <rtems/score/assert.h>
27#include <ctype.h>
28#include <errno.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <termios.h>
33#include <unistd.h>
34#include <sys/fcntl.h>
35#include <sys/filio.h>
36#include <sys/ttycom.h>
37
38#include <rtems/termiostypes.h>
39
40/*
41 * The size of the cooked buffer
42 */
43#define CBUFSIZE  (rtems_termios_cbufsize)
44
45/*
46 * The sizes of the raw message buffers.
47 * On most architectures it is quite a bit more
48 * efficient if these are powers of two.
49 */
50#define RAW_INPUT_BUFFER_SIZE  (rtems_termios_raw_input_size)
51#define RAW_OUTPUT_BUFFER_SIZE  (rtems_termios_raw_output_size)
52
53/* fields for "flow_ctrl" status */
54#define FL_IREQXOF 1U        /* input queue requests stop of incoming data */
55#define FL_ISNTXOF 2U        /* XOFF has been sent to other side of line   */
56#define FL_IRTSOFF 4U        /* RTS has been turned off for other side..   */
57
58#define FL_ORCVXOF 0x10U     /* XOFF has been received                     */
59#define FL_OSTOP   0x20U     /* output has been stopped due to XOFF        */
60
61#define FL_MDRTS   0x100U    /* input controlled with RTS/CTS handshake    */
62#define FL_MDXON   0x200U    /* input controlled with XON/XOFF protocol    */
63#define FL_MDXOF   0x400U    /* output controlled with XON/XOFF protocol   */
64
65#define NODISC(n) \
66  { NULL,  NULL,  NULL,  NULL, \
67    NULL,  NULL,  NULL,  NULL }
68/*
69 * FIXME: change rtems_termios_linesw entries consistent
70 *        with rtems_termios_linesw entry usage...
71 */
72struct  rtems_termios_linesw rtems_termios_linesw[MAXLDISC] =
73{
74  NODISC(0),    /* 0- termios-built-in */
75  NODISC(1),    /* 1- defunct */
76  NODISC(2),    /* 2- NTTYDISC */
77  NODISC(3),    /* TABLDISC */
78  NODISC(4),    /* SLIPDISC */
79  NODISC(5),    /* PPPDISC */
80  NODISC(6),    /* loadable */
81  NODISC(7),    /* loadable */
82};
83
84int  rtems_termios_nlinesw =
85       sizeof (rtems_termios_linesw) / sizeof (rtems_termios_linesw[0]);
86
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 = ((iop->flags & LIBIO_FLAGS_NO_DELAY) == 0);
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          wait = false;
1575      } else {
1576        siproc (c, tty);
1577        if (tty->ccount >= tty->termios.c_cc[VMIN])
1578          wait = false;
1579      }
1580      timeout = tty->rawInBufSemaphoreTimeout;
1581
1582      rtems_termios_device_lock_acquire (ctx, &lock_context);
1583    }
1584
1585    rtems_termios_device_lock_release (ctx, &lock_context);
1586
1587    /*
1588     * Wait for characters
1589     */
1590    if (wait) {
1591      if (tty->ccount < CBUFSIZE - 1) {
1592        rtems_status_code sc;
1593
1594        sc = rtems_semaphore_obtain(
1595          tty->rawInBuf.Semaphore, tty->rawInBufSemaphoreOptions, timeout);
1596        if (sc != RTEMS_SUCCESSFUL)
1597          break;
1598      } else {
1599        break;
1600      }
1601    }
1602  }
1603}
1604
1605static uint32_t
1606rtems_termios_read_tty (struct rtems_termios_tty *tty, char *buffer,
1607  uint32_t initial_count)
1608{
1609  uint32_t count;
1610
1611  count = initial_count;
1612
1613  if (tty->cindex == tty->ccount) {
1614    tty->cindex = tty->ccount = 0;
1615    tty->read_start_column = tty->column;
1616    if (tty->handler.poll_read != NULL && tty->handler.mode == TERMIOS_POLLED)
1617      fillBufferPoll (tty);
1618    else
1619      fillBufferQueue (tty);
1620  }
1621  while (count && (tty->cindex < tty->ccount)) {
1622    *buffer++ = tty->cbuf[tty->cindex++];
1623    count--;
1624  }
1625  tty->tty_rcvwakeup = false;
1626  return initial_count - count;
1627}
1628
1629rtems_status_code
1630rtems_termios_read (void *arg)
1631{
1632  rtems_libio_rw_args_t *args = arg;
1633  struct rtems_termios_tty *tty = args->iop->data1;
1634  rtems_status_code sc;
1635
1636  sc = rtems_semaphore_obtain (tty->isem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
1637  if (sc != RTEMS_SUCCESSFUL)
1638    return sc;
1639
1640  if (rtems_termios_linesw[tty->t_line].l_read != NULL) {
1641    sc = rtems_termios_linesw[tty->t_line].l_read(tty,args);
1642    tty->tty_rcvwakeup = false;
1643    rtems_semaphore_release (tty->isem);
1644    return sc;
1645  }
1646
1647  args->bytes_moved = rtems_termios_read_tty (tty, args->buffer, args->count);
1648  rtems_semaphore_release (tty->isem);
1649  return sc;
1650}
1651
1652/*
1653 * signal receive interrupt to rx daemon
1654 * NOTE: This routine runs in the context of the
1655 *       device receive interrupt handler.
1656 */
1657void rtems_termios_rxirq_occured(struct rtems_termios_tty *tty)
1658{
1659  /*
1660   * send event to rx daemon task
1661   */
1662  rtems_event_send(tty->rxTaskId,TERMIOS_RX_PROC_EVENT);
1663}
1664
1665static bool
1666mustCallReceiveCallback (const rtems_termios_tty *tty, unsigned char c,
1667                         unsigned int newTail, unsigned int head)
1668{
1669  if ((tty->termios.c_lflag & ICANON) != 0) {
1670    return c == '\n' || c == tty->termios.c_cc[VEOF] ||
1671      c == tty->termios.c_cc[VEOL] || c == tty->termios.c_cc[VEOL2];
1672  } else {
1673    unsigned int rawContentSize = (newTail - head) % tty->rawInBuf.Size;
1674
1675    return rawContentSize >= tty->termios.c_cc[VMIN];
1676  }
1677}
1678
1679/*
1680 * Place characters on raw queue.
1681 * NOTE: This routine runs in the context of the
1682 *       device receive interrupt handler.
1683 * Returns the number of characters dropped because of overflow.
1684 */
1685int
1686rtems_termios_enqueue_raw_characters (void *ttyp, const char *buf, int len)
1687{
1688  struct rtems_termios_tty *tty = ttyp;
1689  char c;
1690  int dropped = 0;
1691  bool flow_rcv = false; /* true, if flow control char received */
1692  rtems_termios_device_context *ctx = tty->device_context;
1693  rtems_interrupt_lock_context lock_context;
1694
1695  if (rtems_termios_linesw[tty->t_line].l_rint != NULL) {
1696    while (len--) {
1697      c = *buf++;
1698      rtems_termios_linesw[tty->t_line].l_rint(c,tty);
1699    }
1700
1701    /*
1702     * check to see if rcv wakeup callback was set
1703     */
1704    if (tty->tty_rcv.sw_pfn != NULL && !tty->tty_rcvwakeup) {
1705      tty->tty_rcvwakeup = true;
1706      (*tty->tty_rcv.sw_pfn)(&tty->termios, tty->tty_rcv.sw_arg);
1707    }
1708    return 0;
1709  }
1710
1711  while (len--) {
1712    c = *buf++;
1713    /* FIXME: implement IXANY: any character restarts output */
1714    /* if incoming XON/XOFF controls outgoing stream: */
1715    if (tty->flow_ctrl & FL_MDXON) {
1716      /* if received char is V_STOP and V_START (both are equal value) */
1717      if (c == tty->termios.c_cc[VSTOP]) {
1718        if (c == tty->termios.c_cc[VSTART]) {
1719          /* received VSTOP and VSTART==VSTOP? */
1720          /* then toggle "stop output" status  */
1721          tty->flow_ctrl = tty->flow_ctrl ^ FL_ORCVXOF;
1722        }
1723        else {
1724          /* VSTOP received (other code than VSTART) */
1725          /* stop output                             */
1726          tty->flow_ctrl |= FL_ORCVXOF;
1727        }
1728        flow_rcv = true;
1729      }
1730      else if (c == tty->termios.c_cc[VSTART]) {
1731        /* VSTART received */
1732        /* restart output  */
1733        tty->flow_ctrl &= ~FL_ORCVXOF;
1734        flow_rcv = true;
1735      }
1736    }
1737    if (flow_rcv) {
1738      /* restart output according to FL_ORCVXOF flag */
1739      if ((tty->flow_ctrl & (FL_ORCVXOF | FL_OSTOP)) == FL_OSTOP) {
1740        /* disable interrupts    */
1741        rtems_termios_device_lock_acquire (ctx, &lock_context);
1742        tty->flow_ctrl &= ~FL_OSTOP;
1743        /* check for chars in output buffer (or rob_state?) */
1744        if (tty->rawOutBufState != rob_idle) {
1745          /* if chars available, call write function... */
1746          (*tty->handler.write)(
1747            ctx, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail], 1);
1748        }
1749        /* reenable interrupts */
1750        rtems_termios_device_lock_release (ctx, &lock_context);
1751      }
1752    } else {
1753      unsigned int head;
1754      unsigned int oldTail;
1755      unsigned int newTail;
1756      bool callReciveCallback;
1757
1758      if (c == '\r' && (tty->termios.c_iflag & IGNCR) != 0) {
1759        continue;
1760      }
1761
1762      c = iprocEarly (c, tty);
1763
1764      rtems_termios_device_lock_acquire (ctx, &lock_context);
1765
1766      head = tty->rawInBuf.Head;
1767      oldTail = tty->rawInBuf.Tail;
1768      newTail = (oldTail + 1) % tty->rawInBuf.Size;
1769
1770      /* if chars_in_buffer > highwater                */
1771      if ((tty->flow_ctrl & FL_IREQXOF) != 0 && (((newTail - head) %
1772          tty->rawInBuf.Size) > tty->highwater)) {
1773        /* incoming data stream should be stopped */
1774        tty->flow_ctrl |= FL_IREQXOF;
1775        if ((tty->flow_ctrl & (FL_MDXOF | FL_ISNTXOF))
1776            ==                (FL_MDXOF             ) ) {
1777          if ((tty->flow_ctrl & FL_OSTOP) ||
1778              (tty->rawOutBufState == rob_idle)) {
1779            /* if tx is stopped due to XOFF or out of data */
1780            /*    call write function here                 */
1781            tty->flow_ctrl |= FL_ISNTXOF;
1782            (*tty->handler.write)(ctx,
1783                (void *)&(tty->termios.c_cc[VSTOP]), 1);
1784          }
1785        } else if ((tty->flow_ctrl & (FL_MDRTS | FL_IRTSOFF)) == (FL_MDRTS) ) {
1786          tty->flow_ctrl |= FL_IRTSOFF;
1787          /* deactivate RTS line */
1788          if (tty->flow.stop_remote_tx != NULL) {
1789            tty->flow.stop_remote_tx(ctx);
1790          }
1791        }
1792      }
1793
1794      callReciveCallback = false;
1795
1796      if (newTail != head) {
1797        tty->rawInBuf.theBuf[newTail] = c;
1798        tty->rawInBuf.Tail = newTail;
1799
1800        /*
1801         * check to see if rcv wakeup callback was set
1802         */
1803        if (tty->tty_rcv.sw_pfn != NULL && !tty->tty_rcvwakeup) {
1804          if (mustCallReceiveCallback (tty, c, newTail, head)) {
1805            tty->tty_rcvwakeup = true;
1806            callReciveCallback = true;
1807          }
1808        }
1809      } else {
1810        ++dropped;
1811
1812        if (tty->tty_rcv.sw_pfn != NULL && !tty->tty_rcvwakeup) {
1813          tty->tty_rcvwakeup = true;
1814          callReciveCallback = true;
1815        }
1816      }
1817
1818      rtems_termios_device_lock_release (ctx, &lock_context);
1819
1820      if (callReciveCallback) {
1821        (*tty->tty_rcv.sw_pfn)(&tty->termios, tty->tty_rcv.sw_arg);
1822      }
1823    }
1824  }
1825
1826  tty->rawInBufDropped += dropped;
1827  rtems_semaphore_release (tty->rawInBuf.Semaphore);
1828  return dropped;
1829}
1830
1831/*
1832 * in task-driven mode, this function is called in Tx task context
1833 * in interrupt-driven mode, this function is called in TxIRQ context
1834 */
1835static int
1836rtems_termios_refill_transmitter (struct rtems_termios_tty *tty)
1837{
1838  bool wakeUpWriterTask = false;
1839  unsigned int newTail;
1840  int nToSend;
1841  rtems_termios_device_context *ctx = tty->device_context;
1842  rtems_interrupt_lock_context lock_context;
1843  int len;
1844
1845  rtems_termios_device_lock_acquire (ctx, &lock_context);
1846
1847  /* check for XOF/XON to send */
1848  if ((tty->flow_ctrl & (FL_MDXOF | FL_IREQXOF | FL_ISNTXOF))
1849      == (FL_MDXOF | FL_IREQXOF)) {
1850    /* XOFF should be sent now... */
1851    (*tty->handler.write)(ctx, (void *)&(tty->termios.c_cc[VSTOP]), 1);
1852
1853    tty->t_dqlen--;
1854    tty->flow_ctrl |= FL_ISNTXOF;
1855
1856    nToSend = 1;
1857
1858  } else if ((tty->flow_ctrl & (FL_IREQXOF | FL_ISNTXOF)) == FL_ISNTXOF) {
1859    /* NOTE: send XON even, if no longer in XON/XOFF mode... */
1860    /* XON should be sent now... */
1861    /*
1862     * FIXME: this .write call will generate another
1863     * dequeue callback. This will advance the "Tail" in the data
1864     * buffer, although the corresponding data is not yet out!
1865     * Therefore the dequeue "length" should be reduced by 1
1866     */
1867    (*tty->handler.write)(ctx, (void *)&(tty->termios.c_cc[VSTART]), 1);
1868
1869    tty->t_dqlen--;
1870    tty->flow_ctrl &= ~FL_ISNTXOF;
1871
1872    nToSend = 1;
1873  } else if ( tty->rawOutBuf.Head == tty->rawOutBuf.Tail ) {
1874    /*
1875     * buffer was empty
1876     */
1877    if (tty->rawOutBufState == rob_wait) {
1878      /*
1879       * this should never happen...
1880       */
1881      wakeUpWriterTask = true;
1882    }
1883
1884    (*tty->handler.write) (ctx, NULL, 0);
1885    nToSend = 0;
1886  } else {
1887    len = tty->t_dqlen;
1888    tty->t_dqlen = 0;
1889
1890    newTail = (tty->rawOutBuf.Tail + len) % tty->rawOutBuf.Size;
1891    tty->rawOutBuf.Tail = newTail;
1892    if (tty->rawOutBufState == rob_wait) {
1893      /*
1894       * wake up any pending writer task
1895       */
1896      wakeUpWriterTask = true;
1897    }
1898
1899    if (newTail == tty->rawOutBuf.Head) {
1900      /*
1901       * Buffer has become empty
1902       */
1903      tty->rawOutBufState = rob_idle;
1904      (*tty->handler.write) (ctx, NULL, 0);
1905      nToSend = 0;
1906
1907      /*
1908       * check to see if snd wakeup callback was set
1909       */
1910      if ( tty->tty_snd.sw_pfn != NULL) {
1911        (*tty->tty_snd.sw_pfn)(&tty->termios, tty->tty_snd.sw_arg);
1912      }
1913    } else {
1914      /*
1915       * Buffer not empty, check flow control, start transmitter
1916       */
1917      nToSend = startXmit (tty, newTail, true);
1918    }
1919  }
1920
1921  rtems_termios_device_lock_release (ctx, &lock_context);
1922
1923  if (wakeUpWriterTask) {
1924    rtems_semaphore_release (tty->rawOutBuf.Semaphore);
1925  }
1926
1927  return nToSend;
1928}
1929
1930/*
1931 * Characters have been transmitted
1932 * NOTE: This routine runs in the context of the
1933 *       device transmit interrupt handler.
1934 * The second argument is the number of characters transmitted so far.
1935 * This value will always be 1 for devices which generate an interrupt
1936 * for each transmitted character.
1937 * It returns number of characters left to transmit
1938 */
1939int
1940rtems_termios_dequeue_characters (void *ttyp, int len)
1941{
1942  struct rtems_termios_tty *tty = ttyp;
1943  rtems_status_code sc;
1944
1945  /*
1946   * sum up character count already sent
1947   */
1948  tty->t_dqlen += len;
1949
1950  if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
1951    /*
1952     * send wake up to transmitter task
1953     */
1954    sc = rtems_event_send(tty->txTaskId, TERMIOS_TX_START_EVENT);
1955    if (sc != RTEMS_SUCCESSFUL)
1956      rtems_fatal_error_occurred (sc);
1957    return 0; /* nothing to output in IRQ... */
1958  }
1959
1960  if (tty->t_line == PPPDISC ) {
1961    /*
1962     * call PPP line discipline start function
1963     */
1964    if (rtems_termios_linesw[tty->t_line].l_start != NULL) {
1965      rtems_termios_linesw[tty->t_line].l_start(tty);
1966    }
1967    return 0; /* nothing to output in IRQ... */
1968  }
1969
1970  return rtems_termios_refill_transmitter(tty);
1971}
1972
1973/*
1974 * this task actually processes any transmit events
1975 */
1976static rtems_task rtems_termios_txdaemon(rtems_task_argument argument)
1977{
1978  struct rtems_termios_tty *tty = (struct rtems_termios_tty *)argument;
1979  rtems_event_set the_event;
1980
1981  while (1) {
1982    /*
1983     * wait for rtems event
1984     */
1985    rtems_event_receive(
1986       (TERMIOS_TX_START_EVENT | TERMIOS_TX_TERMINATE_EVENT),
1987       RTEMS_EVENT_ANY | RTEMS_WAIT,
1988       RTEMS_NO_TIMEOUT,
1989       &the_event
1990    );
1991    if ((the_event & TERMIOS_TX_TERMINATE_EVENT) != 0) {
1992      tty->txTaskId = 0;
1993      rtems_task_delete(RTEMS_SELF);
1994    }
1995
1996    /*
1997     * call any line discipline start function
1998     */
1999    if (rtems_termios_linesw[tty->t_line].l_start != NULL) {
2000      rtems_termios_linesw[tty->t_line].l_start(tty);
2001
2002      if (tty->t_line == PPPDISC) {
2003        /*
2004         * Do not call rtems_termios_refill_transmitter() in this case similar
2005         * to rtems_termios_dequeue_characters().
2006         */
2007        continue;
2008      }
2009    }
2010
2011    /*
2012     * try to push further characters to device
2013     */
2014    rtems_termios_refill_transmitter(tty);
2015  }
2016}
2017
2018/*
2019 * this task actually processes any receive events
2020 */
2021static rtems_task rtems_termios_rxdaemon(rtems_task_argument argument)
2022{
2023  struct rtems_termios_tty *tty = (struct rtems_termios_tty *)argument;
2024  rtems_termios_device_context *ctx = tty->device_context;
2025  rtems_event_set the_event;
2026  int c;
2027  char c_buf;
2028
2029  while (1) {
2030    /*
2031     * wait for rtems event
2032     */
2033    rtems_event_receive(
2034      (TERMIOS_RX_PROC_EVENT | TERMIOS_RX_TERMINATE_EVENT),
2035      RTEMS_EVENT_ANY | RTEMS_WAIT,
2036      RTEMS_NO_TIMEOUT,
2037      &the_event
2038    );
2039    if ((the_event & TERMIOS_RX_TERMINATE_EVENT) != 0) {
2040      tty->rxTaskId = 0;
2041      rtems_task_delete(RTEMS_SELF);
2042    }
2043
2044    /*
2045     * do something
2046     */
2047    c = tty->handler.poll_read(ctx);
2048    if (c != EOF) {
2049      /*
2050       * poll_read did call enqueue on its own
2051       */
2052      c_buf = c;
2053      rtems_termios_enqueue_raw_characters ( tty,&c_buf,1);
2054    }
2055  }
2056}
2057
2058static int
2059rtems_termios_imfs_open (rtems_libio_t *iop,
2060  const char *path, int oflag, mode_t mode)
2061{
2062  rtems_termios_device_node *device_node;
2063  rtems_status_code sc;
2064  rtems_libio_open_close_args_t args;
2065  struct rtems_termios_tty *tty;
2066
2067  device_node = IMFS_generic_get_context_by_iop (iop);
2068
2069  memset (&args, 0, sizeof (args));
2070  args.iop = iop;
2071  args.flags = iop->flags;
2072  args.mode = mode;
2073
2074  sc = rtems_termios_obtain ();
2075  if (sc != RTEMS_SUCCESSFUL) {
2076    rtems_set_errno_and_return_minus_one (ENXIO);
2077  }
2078
2079  tty = rtems_termios_open_tty (device_node->major, device_node->minor, &args,
2080      device_node->tty, device_node, NULL);
2081  if (tty == NULL) {
2082    rtems_termios_release ();
2083    rtems_set_errno_and_return_minus_one (ENOMEM);
2084  }
2085
2086  rtems_termios_release ();
2087  return 0;
2088}
2089
2090static int
2091rtems_termios_imfs_close (rtems_libio_t *iop)
2092{
2093  rtems_status_code sc;
2094  rtems_libio_open_close_args_t args;
2095  struct rtems_termios_tty *tty;
2096
2097  memset (&args, 0, sizeof (args));
2098  args.iop = iop;
2099
2100  tty = iop->data1;
2101
2102  sc = rtems_termios_obtain ();
2103  _Assert (sc == RTEMS_SUCCESSFUL);
2104  (void) sc;
2105
2106  rtems_termios_close_tty (tty, &args);
2107  rtems_termios_release ();
2108  return 0;
2109}
2110
2111static ssize_t
2112rtems_termios_imfs_read (rtems_libio_t *iop, void *buffer, size_t count)
2113{
2114  struct rtems_termios_tty *tty;
2115  rtems_status_code sc;
2116  uint32_t bytes_moved;
2117
2118  tty = iop->data1;
2119
2120  sc = rtems_semaphore_obtain (tty->isem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
2121  _Assert (sc == RTEMS_SUCCESSFUL);
2122
2123  if (rtems_termios_linesw[tty->t_line].l_read != NULL) {
2124    rtems_libio_rw_args_t args;
2125
2126    memset (&args, 0, sizeof (args));
2127    args.iop = iop;
2128    args.buffer = buffer;
2129    args.count = count;
2130    args.flags = iop->flags;
2131
2132    sc = rtems_termios_linesw[tty->t_line].l_read (tty, &args);
2133    tty->tty_rcvwakeup = false;
2134    rtems_semaphore_release (tty->isem);
2135
2136    if (sc != RTEMS_SUCCESSFUL) {
2137      return rtems_status_code_to_errno (sc);
2138    }
2139
2140    return (ssize_t) args.bytes_moved;
2141  }
2142
2143  bytes_moved = rtems_termios_read_tty (tty, buffer, count);
2144  rtems_semaphore_release (tty->isem);
2145  return (ssize_t) bytes_moved;
2146}
2147
2148static ssize_t
2149rtems_termios_imfs_write (rtems_libio_t *iop, const void *buffer, size_t count)
2150{
2151  struct rtems_termios_tty *tty;
2152  rtems_status_code sc;
2153  uint32_t bytes_moved;
2154
2155  tty = iop->data1;
2156
2157  sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
2158  _Assert (sc == RTEMS_SUCCESSFUL);
2159
2160  if (rtems_termios_linesw[tty->t_line].l_write != NULL) {
2161    rtems_libio_rw_args_t args;
2162
2163    memset (&args, 0, sizeof (args));
2164    args.iop = iop;
2165    args.buffer = RTEMS_DECONST (void *, buffer);
2166    args.count = count;
2167    args.flags = iop->flags;
2168
2169    sc = rtems_termios_linesw[tty->t_line].l_write (tty, &args);
2170    rtems_semaphore_release (tty->osem);
2171
2172    if (sc != RTEMS_SUCCESSFUL) {
2173      return rtems_status_code_to_errno (sc);
2174    }
2175
2176    return (ssize_t) args.bytes_moved;
2177  }
2178
2179  bytes_moved = rtems_termios_write_tty (iop, tty, buffer, count);
2180  rtems_semaphore_release (tty->osem);
2181  return (ssize_t) bytes_moved;
2182}
2183
2184static int
2185rtems_termios_imfs_ioctl (rtems_libio_t *iop, ioctl_command_t request,
2186  void *buffer)
2187{
2188  rtems_status_code sc;
2189  rtems_libio_ioctl_args_t args;
2190
2191  memset (&args, 0, sizeof (args));
2192  args.iop = iop;
2193  args.command = request;
2194  args.buffer = buffer;
2195
2196  sc = rtems_termios_ioctl (&args);
2197  if ( sc == RTEMS_SUCCESSFUL ) {
2198    return args.ioctl_return;
2199  } else {
2200    return rtems_status_code_to_errno (sc);
2201  }
2202}
2203
2204static const rtems_filesystem_file_handlers_r rtems_termios_imfs_handler = {
2205  .open_h = rtems_termios_imfs_open,
2206  .close_h = rtems_termios_imfs_close,
2207  .read_h = rtems_termios_imfs_read,
2208  .write_h = rtems_termios_imfs_write,
2209  .ioctl_h = rtems_termios_imfs_ioctl,
2210  .lseek_h = rtems_filesystem_default_lseek,
2211  .fstat_h = IMFS_stat,
2212  .ftruncate_h = rtems_filesystem_default_ftruncate,
2213  .fsync_h = rtems_filesystem_default_fsync_or_fdatasync,
2214  .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
2215  .fcntl_h = rtems_filesystem_default_fcntl,
2216  .kqfilter_h = rtems_termios_kqfilter,
2217  .mmap_h = rtems_termios_mmap,
2218  .poll_h = rtems_termios_poll,
2219  .readv_h = rtems_filesystem_default_readv,
2220  .writev_h = rtems_filesystem_default_writev
2221};
2222
2223static IMFS_jnode_t *
2224rtems_termios_imfs_node_initialize (IMFS_jnode_t *node, void *arg)
2225{
2226  rtems_termios_device_node *device_node;
2227  dev_t dev;
2228
2229  node = IMFS_node_initialize_generic (node, arg);
2230  device_node = IMFS_generic_get_context_by_node (node);
2231  dev = IMFS_generic_get_device_identifier_by_node (node);
2232  device_node->major = rtems_filesystem_dev_major_t (dev);
2233  device_node->minor = rtems_filesystem_dev_minor_t (dev);
2234
2235  return node;
2236}
2237
2238static void
2239rtems_termios_imfs_node_destroy (IMFS_jnode_t *node)
2240{
2241  rtems_termios_device_node *device_node;
2242
2243  device_node = IMFS_generic_get_context_by_node (node);
2244  free (device_node);
2245  IMFS_node_destroy_default (node);
2246}
2247
2248static const IMFS_node_control rtems_termios_imfs_node_control =
2249  IMFS_GENERIC_INITIALIZER(
2250    &rtems_termios_imfs_handler,
2251    rtems_termios_imfs_node_initialize,
2252    rtems_termios_imfs_node_destroy
2253  );
Note: See TracBrowser for help on using the repository browser.