source: rtems/c/src/lib/libc/termios.c @ 97faafa5

4.104.114.84.95
Last change on this file since 97faafa5 was a75c783, checked in by Joel Sherrill <joel.sherrill@…>, on 10/21/97 at 17:03:18

Converted from using a message queue for the raw input queue to using a
ring buffer in conjunction with a counting semaphore.

  • Property mode set to 100644
File size: 17.7 KB
Line 
1/*
2 * TERMIOS serial line support
3 *
4 *  Author:
5 *    W. Eric Norum
6 *    Saskatchewan Accelerator Laboratory
7 *    University of Saskatchewan
8 *    Saskatoon, Saskatchewan, CANADA
9 *    eric@skatter.usask.ca
10 *
11 *  The license and distribution terms for this file may be
12 *  found in the file LICENSE in this distribution or at
13 *  http://www.OARcorp.com/rtems/license.html.
14 *
15 *  $Id$
16 */
17
18#include <rtems.h>
19#include <rtems/libio.h>
20#include <ctype.h>
21#include <errno.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <termios.h>
25#include <unistd.h>
26#include <ringbuf.h>
27
28/*
29 * The size of the cooked buffer
30 */
31#define CBUFSIZE  256
32
33/*
34 * Variables associated with each termios instance.
35 * One structure for each hardware I/O device.
36 */
37struct rtems_termios_tty {
38  /*
39   * Linked-list of active TERMIOS devices
40   */
41  struct rtems_termios_tty  *forw;
42  struct rtems_termios_tty  *back;
43
44  /*
45   * How many times has this device been opened
46   */
47  int   refcount;
48
49  /*
50   * This device
51   */
52  rtems_device_major_number major;
53  rtems_device_major_number minor;
54
55  /*
56   * Mutual-exclusion semaphores
57   */
58  rtems_id  isem;
59  rtems_id  osem;
60
61  /*
62   * The canonical (cooked) character buffer
63   */
64  char    cbuf[CBUFSIZE];
65  int   ccount;
66  int   cindex;
67
68  /*
69   * Keep track of cursor (printhead) position
70   */
71  int   column;
72  int   read_start_column;
73
74  /*
75   * The ioctl settings
76   */
77  struct termios  termios;
78  rtems_interval  vtimeTicks;
79
80  /*
81   * Raw character buffer
82   */
83  rtems_id      rawInputSem;
84  Ring_buffer_t rawInputBuffer;
85
86  rtems_unsigned32  rawMessageOptions;
87  rtems_interval  rawMessageTimeout;
88  rtems_interval  rawMessageFirstTimeout;
89
90  /*
91   * Callbacks to device-specific routines
92   */
93  int   (*lastClose)(int major, int minor, void *arg);
94  int   (*read)(int minor, char *buf );
95  int   (*write)(int minor, char *buf, int len);
96};
97static struct rtems_termios_tty *ttyHead, *ttyTail;
98static rtems_id ttyMutex;
99
100void
101rtems_termios_initialize (void)
102{
103  rtems_status_code sc;
104
105  /*
106   * Create the mutex semaphore for the tty list
107   */
108  if (!ttyMutex) {
109    sc = rtems_semaphore_create (
110      rtems_build_name ('T', 'R', 'm', 'i'),
111      1,
112      RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
113      RTEMS_NO_PRIORITY,
114      &ttyMutex);
115    if (sc != RTEMS_SUCCESSFUL)
116      rtems_fatal_error_occurred (sc);
117  }
118}
119 
120/*
121 * Open a termios device
122 */
123rtems_status_code
124rtems_termios_open (
125  rtems_device_major_number  major,
126  rtems_device_minor_number  minor,
127  void                      *arg,
128  int                      (*deviceFirstOpen)(int major, int minor, void *arg),
129  int                      (*deviceLastClose)(int major, int minor, void *arg),
130  int                      (*deviceRead)(int minor, char *buf/*, int len*/),
131  int                      (*deviceWrite)(int minor, char *buf, int len)
132  )
133{
134  rtems_status_code sc;
135  rtems_libio_open_close_args_t *args = arg;
136  struct rtems_termios_tty *tty;
137
138  /*
139   * See if the device has already been opened
140   */
141  sc = rtems_semaphore_obtain (ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
142  if (sc != RTEMS_SUCCESSFUL)
143    return sc;
144  for (tty = ttyHead ; tty != NULL ; tty = tty->forw) {
145    if ((tty->major == major) && (tty->minor == minor))
146      break;
147  }
148  if (tty == NULL) {
149    static char c = 'a';
150
151    /*
152     * Create a new device
153     */
154    tty = malloc (sizeof (struct rtems_termios_tty));
155    if (tty == NULL) {
156      rtems_semaphore_release (ttyMutex);
157      return RTEMS_NO_MEMORY;
158    }
159    tty->forw = ttyHead;
160    ttyHead = tty;
161    tty->back = NULL;
162    if (ttyTail == NULL)
163      ttyTail = tty;
164
165    /*
166     * Set up mutex semaphores
167     */
168    sc = rtems_semaphore_create (
169      rtems_build_name ('T', 'R', 'i', c),
170      1,
171      RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
172      RTEMS_NO_PRIORITY,
173      &tty->isem);
174    if (sc != RTEMS_SUCCESSFUL)
175      rtems_fatal_error_occurred (sc);
176    sc = rtems_semaphore_create (
177      rtems_build_name ('T', 'R', 'o', c),
178      1,
179      RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
180      RTEMS_NO_PRIORITY,
181      &tty->osem);
182    if (sc != RTEMS_SUCCESSFUL)
183      rtems_fatal_error_occurred (sc);
184
185    /*
186     * Set callbacks
187     */
188    tty->write = deviceWrite;
189    tty->lastClose = deviceLastClose;
190    if ((tty->read = deviceRead) == NULL) {
191      sc = rtems_semaphore_create (
192        rtems_build_name ('T', 'R', 'r', c),
193        0,
194        RTEMS_COUNTING_SEMAPHORE | RTEMS_FIFO | RTEMS_LOCAL,
195        RTEMS_NO_PRIORITY,
196        &tty->rawInputSem);
197      if (sc != RTEMS_SUCCESSFUL)
198        rtems_fatal_error_occurred (sc);
199    }
200
201    /*
202     * Initialize variables
203     */
204    tty->column = 0;
205    tty->cindex = tty->ccount = 0;
206
207    /*
208     * Set default parameters
209     */
210    tty->termios.c_iflag = BRKINT | ICRNL | IXON | IMAXBEL;
211    tty->termios.c_oflag = OPOST | ONLCR | XTABS;
212    tty->termios.c_cflag = B9600 | CS8 | CREAD;
213    tty->termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOK | ECHOE | ECHOCTL;
214    tty->termios.c_cc[VINTR] = '\003';
215    tty->termios.c_cc[VQUIT] = '\034';
216    tty->termios.c_cc[VERASE] = '\177';
217    tty->termios.c_cc[VKILL] = '\025';
218    tty->termios.c_cc[VEOF] = '\004';
219    tty->termios.c_cc[VEOL] = '\000';
220    tty->termios.c_cc[VEOL2] = '\000';
221    tty->termios.c_cc[VSTART] = '\021';
222    tty->termios.c_cc[VSTOP] = '\023';
223    tty->termios.c_cc[VSUSP] = '\032';
224    tty->termios.c_cc[VREPRINT] = '\022';
225    tty->termios.c_cc[VDISCARD] = '\017';
226    tty->termios.c_cc[VWERASE] = '\027';
227    tty->termios.c_cc[VLNEXT] = '\026';
228
229    /*
230     * Device-specific open
231     */
232    if (deviceFirstOpen)
233      (*deviceFirstOpen) (major, minor, arg);
234
235    /*
236     * Bump name characer
237     */
238    if (c++ == 'z')
239      c = 'a';
240  }
241  tty->refcount++;
242  args->iop->data1 = tty;
243  rtems_semaphore_release (ttyMutex);
244  return RTEMS_SUCCESSFUL;
245}
246
247rtems_status_code
248rtems_termios_close (void *arg)
249{
250  rtems_libio_open_close_args_t *args = arg;
251  struct rtems_termios_tty *tty = args->iop->data1;
252  rtems_status_code sc;
253
254  sc = rtems_semaphore_obtain (ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
255  if (sc != RTEMS_SUCCESSFUL)
256    rtems_fatal_error_occurred (sc);
257  if (--tty->refcount == 0) {
258    if (tty->lastClose)
259       (*tty->lastClose) (tty->major, tty->minor, arg);
260    if (tty->forw == NULL)
261      ttyTail = tty->back;
262    else
263      tty->forw->back = tty->back;
264    if (tty->back == NULL)
265      ttyHead = tty->forw;
266    else
267      tty->back->forw = tty->forw;
268    rtems_semaphore_delete (tty->isem);
269    rtems_semaphore_delete (tty->osem);
270    if (tty->read == NULL)
271      rtems_semaphore_delete (tty->rawInputSem);
272    free (tty);
273  }
274  rtems_semaphore_release (ttyMutex);
275  return RTEMS_SUCCESSFUL;
276}
277
278rtems_status_code
279rtems_termios_ioctl (void *arg)
280{
281  rtems_libio_ioctl_args_t *args = arg;
282  struct rtems_termios_tty *tty = args->iop->data1;
283  rtems_status_code sc;
284
285  sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
286  if (sc != RTEMS_SUCCESSFUL)
287    return sc;
288  switch (args->command) {
289  default:
290    sc = RTEMS_INVALID_NUMBER;
291    break;
292
293  case RTEMS_IO_GET_ATTRIBUTES:
294    *(struct termios *)args->buffer = tty->termios;
295    break;
296
297  case RTEMS_IO_SET_ATTRIBUTES:
298    tty->termios = *(struct termios *)args->buffer;
299    if (tty->termios.c_lflag & ICANON) {
300      tty->rawMessageOptions = RTEMS_WAIT;
301      tty->rawMessageTimeout = RTEMS_NO_TIMEOUT;
302      tty->rawMessageFirstTimeout = RTEMS_NO_TIMEOUT;
303    }
304    else {
305      rtems_interval ticksPerSecond;
306      rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
307      tty->vtimeTicks = tty->termios.c_cc[VTIME] * ticksPerSecond / 10;
308      if (tty->termios.c_cc[VTIME]) {
309        tty->rawMessageOptions = RTEMS_WAIT;
310        tty->rawMessageTimeout = tty->vtimeTicks;
311        if (tty->termios.c_cc[VMIN])
312          tty->rawMessageFirstTimeout = RTEMS_NO_TIMEOUT;
313        else
314          tty->rawMessageFirstTimeout = tty->vtimeTicks;
315      }
316      else {
317        if (tty->termios.c_cc[VMIN]) {
318          tty->rawMessageOptions = RTEMS_WAIT;
319          tty->rawMessageTimeout = RTEMS_NO_TIMEOUT;
320          tty->rawMessageFirstTimeout = RTEMS_NO_TIMEOUT;
321        }
322        else {
323          tty->rawMessageOptions = RTEMS_NO_WAIT;
324        }
325      }
326    }
327    break;
328  }
329  rtems_semaphore_release (tty->osem);
330  return sc;
331}
332
333/*
334 * Handle output processing
335 */
336static void
337oproc (unsigned char c, struct rtems_termios_tty *tty)
338{
339  int i;
340
341  if (tty->termios.c_oflag & OPOST) {
342    switch (c) {
343    case '\n':
344      if (tty->termios.c_oflag & ONLRET)
345        tty->column = 0;
346      if (tty->termios.c_oflag & ONLCR) {
347        (*tty->write)(tty->minor, "\r", 1);
348        tty->column = 0;
349      }
350      break;
351
352    case '\r':
353      if ((tty->termios.c_oflag & ONOCR) && (tty->column == 0))
354        return;
355      if (tty->termios.c_oflag & OCRNL) {
356        c = '\n';
357        if (tty->termios.c_oflag & ONLRET)
358          tty->column = 0;
359        break;
360      }
361      tty->column = 0;
362      break;
363
364    case '\t':
365      i = 8 - (tty->column & 7);
366      if ((tty->termios.c_oflag & TABDLY) == XTABS) {
367        tty->column += i;
368        (*tty->write)(tty->minor,  "        ",  i);
369        return;
370      }
371      tty->column += i;
372      break;
373
374    case '\b':
375      if (tty->column > 0)
376        tty->column--;
377      break;
378
379    default:
380      if (tty->termios.c_oflag & OLCUC)
381        c = toupper(c);
382      if (!iscntrl(c))
383        tty->column++;
384      break;
385    }
386  }
387  (*tty->write)(tty->minor, &c, 1);
388}
389
390rtems_status_code
391rtems_termios_write (void *arg)
392{
393  rtems_libio_rw_args_t *args = arg;
394  struct rtems_termios_tty *tty = args->iop->data1;
395  rtems_status_code sc;
396
397  sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
398  if (sc != RTEMS_SUCCESSFUL)
399    return sc;
400  if (tty->termios.c_oflag & OPOST) {
401    unsigned32 count = args->count;
402    unsigned8 *buffer = args->buffer;
403    while (count--)
404      oproc (*buffer++, tty);
405    args->bytes_moved = args->count;
406  }
407  else {
408    if ((*tty->write)(tty->minor, args->buffer, args->count) < 0)
409      sc = RTEMS_UNSATISFIED;
410    else
411      args->bytes_moved = args->count;
412  }
413  rtems_semaphore_release (tty->osem);
414  return sc;
415}
416
417/*
418 * Echo a typed character
419 */
420static void
421echo (unsigned char c, struct rtems_termios_tty *tty)
422{
423  if ((tty->termios.c_lflag & ECHOCTL) && iscntrl(c) && (c != '\t') && (c != '\n')) {
424    char echobuf[2];
425
426    echobuf[0] = '^';
427    echobuf[1] = c ^ 0x40;
428    (*tty->write)(tty->minor, echobuf, 2);
429    tty->column += 2;
430  }
431  else {
432    oproc (c, tty);
433  }
434}
435
436/*
437 * Erase a character or line
438 * FIXME: Needs support for WERASE and ECHOPRT.
439 * FIXME: Some of the tests should check for IEXTEN, too.
440 */
441static void
442erase (struct rtems_termios_tty *tty, int lineFlag)
443{
444  if (tty->ccount == 0)
445    return;
446  if (lineFlag) {
447    if (!(tty->termios.c_lflag & ECHO)) {
448      tty->ccount = 0;
449      return;
450    }
451    if (!(tty->termios.c_lflag & ECHOE)) {
452      tty->ccount = 0;
453      echo (tty->termios.c_cc[VKILL], tty);
454      if (tty->termios.c_lflag & ECHOK)
455        echo ('\n', tty);
456      return;
457    }
458  }
459  while (tty->ccount) {
460    unsigned char c = tty->cbuf[--tty->ccount];
461
462    if (tty->termios.c_lflag & ECHO) {
463      if (!lineFlag && !(tty->termios.c_lflag & ECHOE)) {
464        echo (tty->termios.c_cc[VERASE], tty);
465      }
466      else if (c == '\t') {
467        int col = tty->read_start_column;
468        int i = 0;
469
470        /*
471         * Find the character before the tab
472         */
473        while (i != tty->ccount) {
474          c = tty->cbuf[i++];
475          if (c == '\t') {
476            col = (col | 7) + 1;
477          }
478          else if (iscntrl (c)) {
479            if (tty->termios.c_lflag & ECHOCTL)
480              col += 2;
481          }
482          else {
483            col++;
484          }
485        }
486
487        /*
488         * Back up over the tab
489         */
490        while (tty->column > col) {
491          (*tty->write)(tty->minor, "\b", 1);
492          tty->column--;
493        }
494      }
495      else {
496        if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) {
497          (*tty->write)(tty->minor, "\b \b", 3);
498          if (tty->column)
499            tty->column--;
500        }
501        if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) {
502          (*tty->write)(tty->minor, "\b \b", 3);
503          if (tty->column)
504            tty->column--;
505        }
506      }
507    }
508    if (!lineFlag)
509      break;
510  }
511}
512
513/*
514 * Process a single input character
515 */
516static int
517iproc (unsigned char c, struct rtems_termios_tty *tty)
518{
519  if (tty->termios.c_iflag & ISTRIP)
520    c &= 0x7f;
521  if (tty->termios.c_iflag & IUCLC)
522    c = tolower (c);
523  if (c == '\r') {
524    if (tty->termios.c_iflag & IGNCR)
525      return 0;
526    if (tty->termios.c_iflag & ICRNL)
527      c = '\n';
528  }
529  else if ((c == '\n') && (tty->termios.c_iflag & INLCR)) {
530    c = '\r';
531  }
532  if ((c != '\0') && (tty->termios.c_lflag & ICANON)) {
533    if (c == tty->termios.c_cc[VERASE]) {
534      erase (tty, 0);
535      return 0;
536    }
537    else if (c == tty->termios.c_cc[VKILL]) {
538      erase (tty, 1);
539      return 0;
540    }
541    else if (c == tty->termios.c_cc[VEOF]) {
542      return 1;
543    }
544    else if (c == '\n') {
545      if (tty->termios.c_lflag & (ECHO | ECHONL))
546        echo (c, tty);
547      tty->cbuf[tty->ccount++] = c;
548      return 1;
549    }
550    else if ((c == tty->termios.c_cc[VEOL])
551          || (c == tty->termios.c_cc[VEOL2])) {
552      if (tty->termios.c_lflag & ECHO)
553        echo (c, tty);
554      tty->cbuf[tty->ccount++] = c;
555      return 1;
556    }
557  }
558
559  /*
560   * FIXME: Should do IMAXBEL handling somehow
561   */
562  if (tty->ccount < (CBUFSIZE-1)) {
563    if (tty->termios.c_lflag & ECHO)
564      echo (c, tty);
565    tty->cbuf[tty->ccount++] = c;
566  }
567  return 0;
568}
569
570/*
571 * Process input character, with semaphore.
572 */
573static int
574siproc (unsigned char c, struct rtems_termios_tty *tty)
575{
576  int i;
577
578  /*
579   * Obtain output semaphore if character will be echoed
580   */
581  if (tty->termios.c_lflag & (ECHO|ECHOE|ECHOK|ECHONL|ECHOPRT|ECHOCTL|ECHOKE)) {
582    rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
583    i = iproc (c, tty);
584    rtems_semaphore_release (tty->osem);
585  }
586  else {
587    i = iproc (c, tty);
588  }
589  return i;
590}
591
592/*
593 * Fill the input buffer by polling the device
594 */
595static rtems_status_code
596fillBufferPoll (struct rtems_termios_tty *tty)
597{
598  unsigned char c;
599  int n;
600
601  if (tty->termios.c_lflag & ICANON) {
602    for (;;) {
603      n = (*tty->read)(tty->minor, &c);
604      if (n < 0) {
605        return RTEMS_UNSATISFIED;
606      }
607      else if (n == 0) {
608        rtems_task_wake_after (1);
609      }
610      else {
611        if  (siproc (c, tty))
612          break;
613      }
614    }
615  }
616  else {
617    rtems_interval then, now;
618    if (!tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
619      rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &then);
620    for (;;) {
621      n = (*tty->read)(tty->minor, &c);
622      if (n < 0) {
623        return RTEMS_UNSATISFIED;
624      }
625      else if (n == 0) {
626        if (tty->termios.c_cc[VMIN]) {
627          if (tty->termios.c_cc[VTIME] && tty->ccount) {
628            rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
629            if ((now - then) > tty->vtimeTicks) {
630              break;
631            }
632          }
633        }
634        else {
635          if (!tty->termios.c_cc[VTIME])
636            break;
637          rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
638          if ((now - then) > tty->vtimeTicks) {
639            break;
640          }
641        }
642        rtems_task_wake_after (1);
643      }
644      else {
645        siproc (c, tty);
646        if (tty->ccount >= tty->termios.c_cc[VMIN])
647          break;
648        if (tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
649          rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &then);
650      }
651    }
652  }
653  return RTEMS_SUCCESSFUL;
654}
655
656/*
657 * Fill the input buffer from the raw input queue
658 */
659static rtems_status_code
660fillBufferQueue (struct rtems_termios_tty *tty)
661{
662  rtems_interval timeout = tty->rawMessageFirstTimeout;
663  rtems_status_code sc;
664  rtems_unsigned8   c;
665
666  for (;;) {
667
668    /*
669     * Read characters from raw queue
670     */
671    sc = rtems_semaphore_obtain (tty->rawInputSem,
672              tty->rawMessageOptions,
673              timeout);
674    if (sc != RTEMS_SUCCESSFUL)
675      break;
676    Ring_buffer_Remove_character( &tty->rawInputBuffer, c );
677
678   /*
679    * Process characters read from raw queue
680    */
681    if (tty->termios.c_lflag & ICANON) {
682      if  (siproc (c, tty))
683        return RTEMS_SUCCESSFUL;
684    }
685    else {
686      siproc (c, tty);
687      if (tty->ccount >= tty->termios.c_cc[VMIN])
688        return RTEMS_SUCCESSFUL;
689    }
690
691    timeout = tty->rawMessageTimeout;
692  }
693  return RTEMS_SUCCESSFUL;
694}
695
696rtems_status_code
697rtems_termios_read (void *arg)
698{
699  rtems_libio_rw_args_t *args = arg;
700  struct rtems_termios_tty *tty = args->iop->data1;
701  unsigned32 count = args->count;
702  unsigned8 *buffer = args->buffer;
703  rtems_status_code sc;
704
705  sc = rtems_semaphore_obtain (tty->isem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
706  if (sc != RTEMS_SUCCESSFUL)
707    return sc;
708  if (tty->cindex == tty->ccount) {
709    tty->cindex = tty->ccount = 0;
710    tty->read_start_column = tty->column;
711    if (tty->read)
712      sc = fillBufferPoll (tty);
713    else
714      sc = fillBufferQueue (tty);
715    if (sc != RTEMS_SUCCESSFUL)
716      tty->cindex = tty->ccount = 0;
717  }
718  while (count && (tty->cindex < tty->ccount)) {
719    *buffer++ = tty->cbuf[tty->cindex++];
720    count--;
721  }
722  args->bytes_moved = args->count - count;
723  rtems_semaphore_release (tty->isem);
724  return sc;
725}
726
727/*
728 * Place characters on raw queue.
729 * NOTE: This routine runs in the context of the device interrupt handler.
730 */
731void
732rtems_termios_enqueue_raw_characters (void *ttyp, char *buf, int len)
733{
734  struct rtems_termios_tty *tty = ttyp;
735
736  while (len) {
737    if (Ring_buffer_Is_full(&tty->rawInputBuffer))
738      break;
739    Ring_buffer_Add_character(&tty->rawInputBuffer, *buf);
740    if (rtems_semaphore_release(tty->rawInputSem) != RTEMS_SUCCESSFUL)
741      break;
742    len -= 1;
743    buf += 1;
744  }
745}
746
Note: See TracBrowser for help on using the repository browser.