source: rtems/cpukit/libcsupport/src/termios.c @ 2c12262

5
Last change on this file since 2c12262 was 2c12262, checked in by Sebastian Huber <sebastian.huber@…>, on 11/28/17 at 05:30:35

termios: Use self-contained objects

Update #2840.

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