source: rtems/cpukit/libcsupport/src/termios.c @ 153b2669

5
Last change on this file since 153b2669 was 153b2669, checked in by Sebastian Huber <sebastian.huber@…>, on 05/07/20 at 06:45:29

termios: Replace rtems_termios_isig_status_code

Merge the rtems_termios_isig_status_code and
rtems_termios_iproc_status_code enums into a single
rtems_termios_iproc_status_code which is now a part of the API.

Simplify rtems_termios_posix_isig_handler() to avoid unreachable code.

Close #3800.

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