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

4.115
Last change on this file since e22af78 was 5dff7425, checked in by Joel Sherrill <joel.sherrill@…>, on 11/20/14 at 23:17:18

libcsupport/src/termios.c: Explicitly ignore return value (Coverity ID 1255347)

Coverity spotted that we were ignoring a return value. But ignoring
it is intentional. Adding (void) clearly indicates it is being ignored
explicitly.

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