source: rtems/c/src/lib/libc/termios.c @ 0315b79b

Last change on this file since 0315b79b was 0315b79b, checked in by Joel Sherrill <joel.sherrill@…>, on 04/05/00 at 19:32:36

Added support for Cygwin.

  • Property mode set to 100644
File size: 28.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
27/*
28 *  FreeBSD does not support a full POSIX termios so we have to help it out
29 */
30
31#if defined(__FreeBSD__)
32#define XTABS   0
33#define ONLRET  0
34#define ONOCR   0
35#define TABDLY  0
36#define OLCUC   0
37#define ILCUC   0
38#define OCRNL   0
39#define IUCLC   0
40#endif
41
42/*
43 *  Cygwin does not define these
44 */
45
46#if defined(__CYGWIN__)
47#define ECHOPRT 0
48#endif
49
50/*
51 * The size of the cooked buffer
52 */
53#define CBUFSIZE        256
54
55/*
56 * The sizes of the raw message buffers.
57 * On most architectures it is quite a bit more
58 * efficient if these are powers of two.
59 */
60#define RAW_INPUT_BUFFER_SIZE   128
61#define RAW_OUTPUT_BUFFER_SIZE  64
62
63/*
64 * Variables associated with each termios instance.
65 * One structure for each hardware I/O device.
66 */
67struct rtems_termios_tty {
68        /*
69         * Linked-list of active TERMIOS devices
70         */
71        struct rtems_termios_tty        *forw;
72        struct rtems_termios_tty        *back;
73
74        /*
75         * How many times has this device been opened
76         */
77        int             refcount;
78
79        /*
80         * This device
81         */
82        rtems_device_major_number       major;
83        rtems_device_major_number       minor;
84
85        /*
86         * Mutual-exclusion semaphores
87         */
88        rtems_id        isem;
89        rtems_id        osem;
90
91        /*
92         * The canonical (cooked) character buffer
93         */
94        char            cbuf[CBUFSIZE];
95        int             ccount;
96        int             cindex;
97
98        /*
99         * Keep track of cursor (printhead) position
100         */
101        int             column;
102        int             read_start_column;
103
104        /*
105         * The ioctl settings
106         */
107        struct termios  termios;
108        rtems_interval  vtimeTicks;
109
110        /*
111         * Raw input character buffer
112         */
113        volatile char           rawInBuf[RAW_INPUT_BUFFER_SIZE];
114        volatile unsigned int   rawInBufHead;
115        volatile unsigned int   rawInBufTail;
116        rtems_id                rawInBufSemaphore;
117        rtems_unsigned32        rawInBufSemaphoreOptions;
118        rtems_interval          rawInBufSemaphoreTimeout;
119        rtems_interval          rawInBufSemaphoreFirstTimeout;
120        unsigned int            rawInBufDropped;        /* Statistics */
121
122        /*
123         * Raw output character buffer
124         */
125        volatile char           rawOutBuf[RAW_OUTPUT_BUFFER_SIZE];
126        volatile unsigned int   rawOutBufHead;
127        volatile unsigned int   rawOutBufTail;
128        rtems_id                rawOutBufSemaphore;
129        enum {rob_idle, rob_busy, rob_wait }    rawOutBufState;
130
131        /*
132         * Callbacks to device-specific routines
133         */
134        rtems_termios_callbacks device;
135        volatile unsigned int   flow_ctrl;
136        unsigned int            lowwater,highwater;
137};
138
139/* fields for "flow_ctrl" status */
140#define FL_IREQXOF 1        /* input queue requests stop of incoming data */
141#define FL_ISNTXOF 2        /* XOFF has been sent to other side of line   */
142#define FL_IRTSOFF 4        /* RTS has been turned off for other side..   */
143
144#define FL_ORCVXOF 0x10     /* XOFF has been received                     */
145#define FL_OSTOP   0x20     /* output has been stopped due to XOFF        */
146
147#define FL_MDRTS   0x100    /* input controlled with RTS/CTS handshake    */
148#define FL_MDXON   0x200    /* input controlled with XON/XOFF protocol    */
149#define FL_MDXOF   0x400    /* output controlled with XON/XOFF protocol   */
150
151extern struct rtems_termios_tty *rtems_termios_ttyHead;
152extern struct rtems_termios_tty *rtems_termios_ttyTail;
153extern rtems_id rtems_termios_ttyMutex;
154
155/*
156 * Open a termios device
157 */
158rtems_status_code
159rtems_termios_open (
160  rtems_device_major_number      major,
161  rtems_device_minor_number      minor,
162  void                          *arg,
163  const rtems_termios_callbacks *callbacks
164  )
165{
166        rtems_status_code sc;
167        rtems_libio_open_close_args_t *args = arg;
168        struct rtems_termios_tty *tty;
169
170        /*
171         * See if the device has already been opened
172         */
173        sc = rtems_semaphore_obtain (rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
174        if (sc != RTEMS_SUCCESSFUL)
175                return sc;
176        for (tty = rtems_termios_ttyHead ; tty != NULL ; tty = tty->forw) {
177                if ((tty->major == major) && (tty->minor == minor))
178                        break;
179        }
180        if (tty == NULL) {
181                static char c = 'a';
182
183                /*
184                 * Create a new device
185                 */
186                tty = calloc (1, sizeof (struct rtems_termios_tty));
187                if (tty == NULL) {
188                        rtems_semaphore_release (rtems_termios_ttyMutex);
189                        return RTEMS_NO_MEMORY;
190                }
191                tty->forw = rtems_termios_ttyHead;
192                rtems_termios_ttyHead = tty;
193                if (rtems_termios_ttyTail == NULL)
194                        rtems_termios_ttyTail = tty;
195
196                tty->minor = minor;
197                tty->major = major;
198
199                /*
200                 * Set up mutex semaphores
201                 */
202                sc = rtems_semaphore_create (
203                        rtems_build_name ('T', 'R', 'i', c),
204                        1,
205                        RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
206                        RTEMS_NO_PRIORITY,
207                        &tty->isem);
208                if (sc != RTEMS_SUCCESSFUL)
209                        rtems_fatal_error_occurred (sc);
210                sc = rtems_semaphore_create (
211                        rtems_build_name ('T', 'R', 'o', c),
212                        1,
213                        RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
214                        RTEMS_NO_PRIORITY,
215                        &tty->osem);
216                if (sc != RTEMS_SUCCESSFUL)
217                        rtems_fatal_error_occurred (sc);
218                sc = rtems_semaphore_create (
219                        rtems_build_name ('T', 'R', 'x', c),
220                        0,
221                        RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY,
222                        RTEMS_NO_PRIORITY,
223                        &tty->rawOutBufSemaphore);
224                if (sc != RTEMS_SUCCESSFUL)
225                        rtems_fatal_error_occurred (sc);
226                tty->rawOutBufState = rob_idle;
227
228                /*
229                 * Set callbacks
230                 */
231                tty->device = *callbacks;
232                if (!tty->device.pollRead) {
233                        sc = rtems_semaphore_create (
234                                rtems_build_name ('T', 'R', 'r', c),
235                                0,
236                                RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY,
237                                RTEMS_NO_PRIORITY,
238                                &tty->rawInBufSemaphore);
239                        if (sc != RTEMS_SUCCESSFUL)
240                                rtems_fatal_error_occurred (sc);
241                }
242
243                /*
244                 * Set default parameters
245                 */
246                tty->termios.c_iflag = BRKINT | ICRNL | IMAXBEL;
247                tty->termios.c_oflag = OPOST | ONLCR | XTABS;
248                tty->termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL;
249                tty->termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOK | ECHOE | ECHOCTL;
250
251                tty->termios.c_cc[VINTR] = '\003';
252                tty->termios.c_cc[VQUIT] = '\034';
253                tty->termios.c_cc[VERASE] = '\177';
254                tty->termios.c_cc[VKILL] = '\025';
255                tty->termios.c_cc[VEOF] = '\004';
256                tty->termios.c_cc[VEOL] = '\000';
257                tty->termios.c_cc[VEOL2] = '\000';
258                tty->termios.c_cc[VSTART] = '\021';
259                tty->termios.c_cc[VSTOP] = '\023';
260                tty->termios.c_cc[VSUSP] = '\032';
261                tty->termios.c_cc[VREPRINT] = '\022';
262                tty->termios.c_cc[VDISCARD] = '\017';
263                tty->termios.c_cc[VWERASE] = '\027';
264                tty->termios.c_cc[VLNEXT] = '\026';
265
266                /* start with no flow control, clear flow control flags */
267                tty->flow_ctrl = 0;
268                /*
269                 * set low/highwater mark for XON/XOFF support
270                 */
271                tty->lowwater  = RAW_INPUT_BUFFER_SIZE * 1/2;
272                tty->highwater = RAW_INPUT_BUFFER_SIZE * 3/4;
273                /*
274                 * Bump name characer
275                 */
276                if (c++ == 'z')
277                        c = 'a';
278
279        }
280        args->iop->data1 = tty;
281        if (!tty->refcount++ && tty->device.firstOpen)
282                (*tty->device.firstOpen)(major, minor, arg);
283        rtems_semaphore_release (rtems_termios_ttyMutex);
284        return RTEMS_SUCCESSFUL;
285}
286
287/*
288 * Drain output queue
289 */
290static void
291drainOutput (struct rtems_termios_tty *tty)
292{
293        rtems_interrupt_level level;
294        rtems_status_code sc;
295
296        if (tty->device.outputUsesInterrupts) {
297                rtems_interrupt_disable (level);
298                while (tty->rawOutBufTail != tty->rawOutBufHead) {
299                        tty->rawOutBufState = rob_wait;
300                        rtems_interrupt_enable (level);
301                        sc = rtems_semaphore_obtain (tty->rawOutBufSemaphore,
302                                                        RTEMS_WAIT,
303                                                        RTEMS_NO_TIMEOUT);
304                        if (sc != RTEMS_SUCCESSFUL)
305                                rtems_fatal_error_occurred (sc);
306                        rtems_interrupt_disable (level);
307                }
308                rtems_interrupt_enable (level);
309        }
310}
311
312rtems_status_code
313rtems_termios_close (void *arg)
314{
315        rtems_libio_open_close_args_t *args = arg;
316        struct rtems_termios_tty *tty = args->iop->data1;
317        rtems_status_code sc;
318
319        sc = rtems_semaphore_obtain (rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
320        if (sc != RTEMS_SUCCESSFUL)
321                rtems_fatal_error_occurred (sc);
322        if (--tty->refcount == 0) {
323                drainOutput (tty);
324                if (tty->device.lastClose)
325                         (*tty->device.lastClose)(tty->major, tty->minor, arg);
326                if (tty->forw == NULL)
327                        rtems_termios_ttyTail = tty->back;
328                else
329                        tty->forw->back = tty->back;
330                if (tty->back == NULL)
331                        rtems_termios_ttyHead = tty->forw;
332                else
333                        tty->back->forw = tty->forw;
334                rtems_semaphore_delete (tty->isem);
335                rtems_semaphore_delete (tty->osem);
336                rtems_semaphore_delete (tty->rawOutBufSemaphore);
337                if (!tty->device.pollRead)
338                        rtems_semaphore_delete (tty->rawInBufSemaphore);
339                free (tty);
340        }
341        rtems_semaphore_release (rtems_termios_ttyMutex);
342        return RTEMS_SUCCESSFUL;
343}
344
345static void
346termios_set_flowctrl(struct rtems_termios_tty *tty)
347{
348  rtems_interrupt_level level;
349  /*
350   * check for flow control options to be switched off
351   */
352
353  /* check for outgoing XON/XOFF flow control switched off */
354  if (( tty->flow_ctrl & FL_MDXON) &&
355      !(tty->termios.c_iflag & IXON)) {
356    /* clear related flags in flow_ctrl */
357    tty->flow_ctrl &= ~(FL_MDXON | FL_ORCVXOF);
358
359    /* has output been stopped due to received XOFF? */
360    if (tty->flow_ctrl & FL_OSTOP) {
361      /* disable interrupts    */
362      rtems_interrupt_disable(level);
363      tty->flow_ctrl &= ~FL_OSTOP;
364      /* check for chars in output buffer (or rob_state?) */
365      if (tty->rawOutBufState != rob_idle) {
366        /* if chars available, call write function... */
367        (*tty->device.write)(tty->minor,
368                             (char *)&tty->rawOutBuf[tty->rawOutBufTail], 1);
369      }
370      /* reenable interrupts */
371      rtems_interrupt_enable(level);
372    }
373  }
374  /* check for incoming XON/XOFF flow control switched off */
375  if (( tty->flow_ctrl & FL_MDXOF) &&
376      !(tty->termios.c_iflag & IXOFF)) {
377    /* clear related flags in flow_ctrl */
378    tty->flow_ctrl &= ~(FL_MDXOF);
379    /* FIXME: what happens, if we had sent XOFF but not yet XON? */
380    tty->flow_ctrl &= ~(FL_ISNTXOF);
381  }
382
383  /* check for incoming RTS/CTS flow control switched off */
384  if (( tty->flow_ctrl & FL_MDRTS) &&
385      !(tty->termios.c_cflag & CRTSCTS)) {
386    /* clear related flags in flow_ctrl */
387    tty->flow_ctrl &= ~(FL_MDRTS);
388   
389    /* restart remote Tx, if it was stopped */
390    if ((tty->flow_ctrl & FL_IRTSOFF) &&
391        (tty->device.startRemoteTx != NULL)) {
392      tty->device.startRemoteTx(tty->minor);
393    }
394    tty->flow_ctrl &= ~(FL_IRTSOFF);
395  }   
396 
397  /*
398   * check for flow control options to be switched on 
399   */
400  /* check for incoming RTS/CTS flow control switched on */
401  if (tty->termios.c_cflag & CRTSCTS) {
402    tty->flow_ctrl |= FL_MDRTS;
403  }
404  /* check for incoming XON/XOF flow control switched on */
405  if (tty->termios.c_iflag & IXOFF) {
406    tty->flow_ctrl |= FL_MDXOF;
407  }
408  /* check for outgoing XON/XOF flow control switched on */
409  if (tty->termios.c_iflag & IXON) {
410    tty->flow_ctrl |= FL_MDXON;
411  }
412}
413
414rtems_status_code
415rtems_termios_ioctl (void *arg)
416{
417        rtems_libio_ioctl_args_t *args = arg;
418        struct rtems_termios_tty *tty = args->iop->data1;
419        rtems_status_code sc;
420
421        args->ioctl_return = 0;
422        sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
423        if (sc != RTEMS_SUCCESSFUL) {
424                args->ioctl_return = sc;
425                return sc;
426        }
427        switch (args->command) {
428        default:
429                sc = RTEMS_INVALID_NUMBER;
430                break;
431
432        case RTEMS_IO_GET_ATTRIBUTES:
433                *(struct termios *)args->buffer = tty->termios;
434                break;
435
436        case RTEMS_IO_SET_ATTRIBUTES:
437                tty->termios = *(struct termios *)args->buffer;
438
439                /* check for and process change in flow control options */
440                termios_set_flowctrl(tty);
441
442                if (tty->termios.c_lflag & ICANON) {
443                        tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
444                        tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
445                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
446                }
447                else {
448                        rtems_interval ticksPerSecond;
449                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
450                        tty->vtimeTicks = tty->termios.c_cc[VTIME] * ticksPerSecond / 10;
451                        if (tty->termios.c_cc[VTIME]) {
452                                tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
453                                tty->rawInBufSemaphoreTimeout = tty->vtimeTicks;
454                                if (tty->termios.c_cc[VMIN])
455                                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
456                                else
457                                        tty->rawInBufSemaphoreFirstTimeout = tty->vtimeTicks;
458                        }
459                        else {
460                                if (tty->termios.c_cc[VMIN]) {
461                                        tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
462                                        tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
463                                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
464                                }
465                                else {
466                                        tty->rawInBufSemaphoreOptions = RTEMS_NO_WAIT;
467                                }
468                        }
469                }
470                if (tty->device.setAttributes)
471                        (*tty->device.setAttributes)(tty->minor, &tty->termios);
472                break;
473
474        case RTEMS_IO_TCDRAIN:
475                drainOutput (tty);
476                break;
477        }
478        rtems_semaphore_release (tty->osem);
479        args->ioctl_return = sc;
480        return sc;
481}
482
483/*
484 * Send characters to device-specific code
485 */
486static void
487osend (const char *buf, int len, struct rtems_termios_tty *tty)
488{
489        unsigned int newHead;
490        rtems_interrupt_level level;
491        rtems_status_code sc;
492
493        if (!tty->device.outputUsesInterrupts) {
494                (*tty->device.write)(tty->minor, buf, len);
495                return;
496        }
497        newHead = tty->rawOutBufHead;
498        while (len) {
499                /*
500                 * Performance improvement could be made here.
501                 * Copy multiple bytes to raw buffer:
502                 * if (len > 1) && (space to buffer end, or tail > 1)
503                 *      ncopy = MIN (len, space to buffer end or tail)
504                 *      memcpy (raw buffer, buf, ncopy)
505                 *      buf += ncopy
506                 *      len -= ncopy
507                 *
508                 * To minimize latency, the memcpy should be done
509                 * with interrupts enabled.
510                 */
511                newHead = (newHead + 1) % RAW_OUTPUT_BUFFER_SIZE;
512                rtems_interrupt_disable (level);
513                while (newHead == tty->rawOutBufTail) {
514                        tty->rawOutBufState = rob_wait;
515                        rtems_interrupt_enable (level);
516                        sc = rtems_semaphore_obtain (tty->rawOutBufSemaphore,
517                                                        RTEMS_WAIT,
518                                                        RTEMS_NO_TIMEOUT);
519                        if (sc != RTEMS_SUCCESSFUL)
520                                rtems_fatal_error_occurred (sc);
521                        rtems_interrupt_disable (level);
522                }
523                tty->rawOutBuf[tty->rawOutBufHead] = *buf++;
524                tty->rawOutBufHead = newHead;
525                if (tty->rawOutBufState == rob_idle) {
526                  /* check, whether XOFF has been received */
527                  if (!(tty->flow_ctrl & FL_ORCVXOF)) {
528                        (*tty->device.write)(tty->minor,
529                                (char *)&tty->rawOutBuf[tty->rawOutBufTail],1);
530                  }
531                  else {
532                    /* remember that output has been stopped due to flow ctrl*/
533                    tty->flow_ctrl |= FL_OSTOP;
534                  }
535                  tty->rawOutBufState = rob_busy;
536                }
537                rtems_interrupt_enable (level);
538                len--;
539        }
540}
541
542/*
543 * Handle output processing
544 */
545static void
546oproc (unsigned char c, struct rtems_termios_tty *tty)
547{
548        int     i;
549
550        if (tty->termios.c_oflag & OPOST) {
551                switch (c) {
552                case '\n':
553                        if (tty->termios.c_oflag & ONLRET)
554                                tty->column = 0;
555                        if (tty->termios.c_oflag & ONLCR) {
556                                osend ("\r", 1, tty);
557                                tty->column = 0;
558                        }
559                        break;
560
561                case '\r':
562                        if ((tty->termios.c_oflag & ONOCR) && (tty->column == 0))
563                                return;
564                        if (tty->termios.c_oflag & OCRNL) {
565                                c = '\n';
566                                if (tty->termios.c_oflag & ONLRET)
567                                        tty->column = 0;
568                                break;
569                        }
570                        tty->column = 0;
571                        break;
572
573                case '\t':
574                        i = 8 - (tty->column & 7);
575                        if ((tty->termios.c_oflag & TABDLY) == XTABS) {
576                                tty->column += i;
577                                osend ( "        ",  i, tty);
578                                return;
579                        }
580                        tty->column += i;
581                        break;
582
583                case '\b':
584                        if (tty->column > 0)
585                                tty->column--;
586                        break;
587
588                default:
589                        if (tty->termios.c_oflag & OLCUC)
590                                c = toupper(c);
591                        if (!iscntrl(c))
592                                tty->column++;
593                        break;
594                }
595        }
596        osend (&c, 1, tty);
597}
598
599rtems_status_code
600rtems_termios_write (void *arg)
601{
602        rtems_libio_rw_args_t *args = arg;
603        struct rtems_termios_tty *tty = args->iop->data1;
604        rtems_status_code sc;
605
606        sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
607        if (sc != RTEMS_SUCCESSFUL)
608                return sc;
609        if (tty->termios.c_oflag & OPOST) {
610                unsigned32 count = args->count;
611                unsigned8 *buffer = args->buffer;
612                while (count--)
613                        oproc (*buffer++, tty);
614                args->bytes_moved = args->count;
615        }
616        else {
617                osend (args->buffer, args->count, tty);
618                args->bytes_moved = args->count;
619        }
620        rtems_semaphore_release (tty->osem);
621        return sc;
622}
623
624/*
625 * Echo a typed character
626 */
627static void
628echo (unsigned char c, struct rtems_termios_tty *tty)
629{
630        if ((tty->termios.c_lflag & ECHOCTL) && iscntrl(c) && (c != '\t') && (c != '\n')) {
631                char echobuf[2];
632
633                echobuf[0] = '^';
634                echobuf[1] = c ^ 0x40;
635                osend (echobuf, 2, tty);
636                tty->column += 2;
637        }
638        else {
639                oproc (c, tty);
640        }
641}
642
643/*
644 * Erase a character or line
645 * FIXME: Needs support for WERASE and ECHOPRT.
646 * FIXME: Some of the tests should check for IEXTEN, too.
647 */
648static void
649erase (struct rtems_termios_tty *tty, int lineFlag)
650{
651        if (tty->ccount == 0)
652                return;
653        if (lineFlag) {
654                if (!(tty->termios.c_lflag & ECHO)) {
655                        tty->ccount = 0;
656                        return;
657                }
658                if (!(tty->termios.c_lflag & ECHOE)) {
659                        tty->ccount = 0;
660                        echo (tty->termios.c_cc[VKILL], tty);
661                        if (tty->termios.c_lflag & ECHOK)
662                                echo ('\n', tty);
663                        return;
664                }
665        }
666        while (tty->ccount) {
667                unsigned char c = tty->cbuf[--tty->ccount];
668
669                if (tty->termios.c_lflag & ECHO) {
670                        if (!lineFlag && !(tty->termios.c_lflag & ECHOE)) {
671                                echo (tty->termios.c_cc[VERASE], tty);
672                        }
673                        else if (c == '\t') {
674                                int col = tty->read_start_column;
675                                int i = 0;
676
677                                /*
678                                 * Find the character before the tab
679                                 */
680                                while (i != tty->ccount) {
681                                        c = tty->cbuf[i++];
682                                        if (c == '\t') {
683                                                col = (col | 7) + 1;
684                                        }
685                                        else if (iscntrl (c)) {
686                                                if (tty->termios.c_lflag & ECHOCTL)
687                                                        col += 2;
688                                        }
689                                        else {
690                                                col++;
691                                        }
692                                }
693
694                                /*
695                                 * Back up over the tab
696                                 */
697                                while (tty->column > col) {
698                                        osend ("\b", 1, tty);
699                                        tty->column--;
700                                }
701                        }
702                        else {
703                                if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) {
704                                        osend ("\b \b", 3, tty);
705                                        if (tty->column)
706                                                tty->column--;
707                                }
708                                if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) {
709                                        osend ("\b \b", 3, tty);
710                                        if (tty->column)
711                                                tty->column--;
712                                }
713                        }
714                }
715                if (!lineFlag)
716                        break;
717        }
718}
719
720/*
721 * Process a single input character
722 */
723static int
724iproc (unsigned char c, struct rtems_termios_tty *tty)
725{
726        if (tty->termios.c_iflag & ISTRIP)
727                c &= 0x7f;
728        if (tty->termios.c_iflag & IUCLC)
729                c = tolower (c);
730        if (c == '\r') {
731                if (tty->termios.c_iflag & IGNCR)
732                        return 0;
733                if (tty->termios.c_iflag & ICRNL)
734                        c = '\n';
735        }
736        else if ((c == '\n') && (tty->termios.c_iflag & INLCR)) {
737                c = '\r';
738        }
739        if ((c != '\0') && (tty->termios.c_lflag & ICANON)) {
740                if (c == tty->termios.c_cc[VERASE]) {
741                        erase (tty, 0);
742                        return 0;
743                }
744                else if (c == tty->termios.c_cc[VKILL]) {
745                        erase (tty, 1);
746                        return 0;
747                }
748                else if (c == tty->termios.c_cc[VEOF]) {
749                        return 1;
750                }
751                else if (c == '\n') {
752                        if (tty->termios.c_lflag & (ECHO | ECHONL))
753                                echo (c, tty);
754                        tty->cbuf[tty->ccount++] = c;
755                        return 1;
756                }
757                else if ((c == tty->termios.c_cc[VEOL])
758                      || (c == tty->termios.c_cc[VEOL2])) {
759                        if (tty->termios.c_lflag & ECHO)
760                                echo (c, tty);
761                        tty->cbuf[tty->ccount++] = c;
762                        return 1;
763                }
764        }
765
766        /*
767         * FIXME: Should do IMAXBEL handling somehow
768         */
769        if (tty->ccount < (CBUFSIZE-1)) {
770                if (tty->termios.c_lflag & ECHO)
771                        echo (c, tty);
772                tty->cbuf[tty->ccount++] = c;
773        }
774        return 0;
775}
776
777/*
778 * Process input character, with semaphore.
779 */
780static int
781siproc (unsigned char c, struct rtems_termios_tty *tty)
782{
783        int i;
784
785        /*
786         * Obtain output semaphore if character will be echoed
787         */
788        if (tty->termios.c_lflag & (ECHO|ECHOE|ECHOK|ECHONL|ECHOPRT|ECHOCTL|ECHOKE)) {
789                rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
790                i = iproc (c, tty);
791                rtems_semaphore_release (tty->osem);
792        }
793        else {
794                i = iproc (c, tty);
795        }
796        return i;
797}
798
799/*
800 * Fill the input buffer by polling the device
801 */
802static rtems_status_code
803fillBufferPoll (struct rtems_termios_tty *tty)
804{
805        int n;
806
807        if (tty->termios.c_lflag & ICANON) {
808                for (;;) {
809                        n = (*tty->device.pollRead)(tty->minor);
810                        if (n < 0) {
811                                rtems_task_wake_after (1);
812                        }
813                        else {
814                                if  (siproc (n, tty))
815                                        break;
816                        }
817                }
818        }
819        else {
820                rtems_interval then, now;
821                if (!tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
822                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &then);
823                for (;;) {
824                        n = (*tty->device.pollRead)(tty->minor);
825                        if (n < 0) {
826                                if (tty->termios.c_cc[VMIN]) {
827                                        if (tty->termios.c_cc[VTIME] && tty->ccount) {
828                                                rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
829                                                if ((now - then) > tty->vtimeTicks) {
830                                                        break;
831                                                }
832                                        }
833                                }
834                                else {
835                                        if (!tty->termios.c_cc[VTIME])
836                                                break;
837                                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
838                                        if ((now - then) > tty->vtimeTicks) {
839                                                break;
840                                        }
841                                }
842                                rtems_task_wake_after (1);
843                        }
844                        else {
845                                siproc (n, tty);
846                                if (tty->ccount >= tty->termios.c_cc[VMIN])
847                                        break;
848                                if (tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
849                                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &then);
850                        }
851                }
852        }
853        return RTEMS_SUCCESSFUL;
854}
855
856/*
857 * Fill the input buffer from the raw input queue
858 */
859static rtems_status_code
860fillBufferQueue (struct rtems_termios_tty *tty)
861{
862        rtems_interval timeout = tty->rawInBufSemaphoreFirstTimeout;
863        rtems_status_code sc;
864
865        for (;;) {
866                /*
867                 * Process characters read from raw queue
868                 */
869                while (tty->rawInBufHead != tty->rawInBufTail) {
870                        unsigned char c;
871                        unsigned int newHead;
872
873                        newHead = (tty->rawInBufHead + 1) % RAW_INPUT_BUFFER_SIZE;
874                        c = tty->rawInBuf[newHead];
875                        tty->rawInBufHead = newHead;
876                        if(((tty->rawInBufTail-newHead+RAW_INPUT_BUFFER_SIZE)
877                            % RAW_INPUT_BUFFER_SIZE)
878                           < tty->lowwater) {
879                          tty->flow_ctrl &= ~FL_IREQXOF;
880                          /* if tx stopped and XON should be sent... */
881                          if (((tty->flow_ctrl & (FL_MDXON | FL_ISNTXOF))
882                               ==                (FL_MDXON | FL_ISNTXOF))
883                              && ((tty->rawOutBufState == rob_idle)
884                                  || (tty->flow_ctrl & FL_OSTOP))) {
885                            /* XON should be sent now... */
886                            (*tty->device.write)(tty->minor,
887                                                 &(tty->termios.c_cc[VSTART]),
888                                                 1);
889                          }
890                          else if (tty->flow_ctrl & FL_MDRTS) {             
891                            tty->flow_ctrl &= ~FL_IRTSOFF;             
892                            /* activate RTS line */
893                            if (tty->device.startRemoteTx != NULL) {
894                              tty->device.startRemoteTx(tty->minor);
895                            }
896                          }
897                        }
898
899                        /* continue processing new character */
900                        if (tty->termios.c_lflag & ICANON) {
901                                if  (siproc (c, tty))
902                                        return RTEMS_SUCCESSFUL;
903                        }
904                        else {
905                                siproc (c, tty);
906                                if (tty->ccount >= tty->termios.c_cc[VMIN])
907                                        return RTEMS_SUCCESSFUL;
908                        }
909                        timeout = tty->rawInBufSemaphoreTimeout;
910                }
911
912                /*
913                 * Wait for characters
914                 */
915                sc = rtems_semaphore_obtain (tty->rawInBufSemaphore,
916                                                tty->rawInBufSemaphoreOptions,
917                                                timeout);
918                if (sc != RTEMS_SUCCESSFUL)
919                        break;
920        }
921        return RTEMS_SUCCESSFUL;
922}
923
924rtems_status_code
925rtems_termios_read (void *arg)
926{
927        rtems_libio_rw_args_t *args = arg;
928        struct rtems_termios_tty *tty = args->iop->data1;
929        unsigned32 count = args->count;
930        unsigned8 *buffer = args->buffer;
931        rtems_status_code sc;
932
933        sc = rtems_semaphore_obtain (tty->isem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
934        if (sc != RTEMS_SUCCESSFUL)
935                return sc;
936        if (tty->cindex == tty->ccount) {
937                tty->cindex = tty->ccount = 0;
938                tty->read_start_column = tty->column;
939                if (tty->device.pollRead)
940                        sc = fillBufferPoll (tty);
941                else
942                        sc = fillBufferQueue (tty);
943                if (sc != RTEMS_SUCCESSFUL)
944                        tty->cindex = tty->ccount = 0;
945        }
946        while (count && (tty->cindex < tty->ccount)) {
947                *buffer++ = tty->cbuf[tty->cindex++];
948                count--;
949        }
950        args->bytes_moved = args->count - count;
951        rtems_semaphore_release (tty->isem);
952        return sc;
953}
954
955/*
956 * Place characters on raw queue.
957 * NOTE: This routine runs in the context of the
958 *       device receive interrupt handler.
959 * Returns the number of characters dropped because of overflow.
960 */
961int
962rtems_termios_enqueue_raw_characters (void *ttyp, char *buf, int len)
963{
964        struct rtems_termios_tty *tty = ttyp;
965        unsigned int newTail;
966        char c;
967        int dropped = 0;
968        boolean flow_rcv = FALSE; /* TRUE, if flow control char received */
969        rtems_interrupt_level level;
970
971        while (len--) {
972          c = *buf++;
973          /* FIXME: implement IXANY: any character restarts output */
974          /* if incoming XON/XOFF controls outgoing stream: */
975          if (tty->flow_ctrl & FL_MDXON) {         
976            /* if received char is V_STOP and V_START (both are equal value) */
977            if (c == tty->termios.c_cc[VSTOP]) {
978              if (c == tty->termios.c_cc[VSTART]) {
979                /* received VSTOP and VSTART==VSTOP? */
980                /* then toggle "stop output" status  */
981                tty->flow_ctrl = tty->flow_ctrl ^ FL_ORCVXOF;
982              }
983              else {
984                /* VSTOP received (other code than VSTART) */
985                /* stop output                             */
986                tty->flow_ctrl |= FL_ORCVXOF;
987              }
988              flow_rcv = TRUE;
989            }
990            else if (c == tty->termios.c_cc[VSTART]) {
991              /* VSTART received */
992              /* restart output  */
993              tty->flow_ctrl &= ~FL_ORCVXOF;
994              flow_rcv = TRUE;
995            }
996          }
997          if (flow_rcv) {
998            /* restart output according to FL_ORCVXOF flag */
999            if ((tty->flow_ctrl & (FL_ORCVXOF | FL_OSTOP)) == FL_OSTOP) {
1000              /* disable interrupts    */
1001              rtems_interrupt_disable(level);
1002              tty->flow_ctrl &= ~FL_OSTOP;
1003              /* check for chars in output buffer (or rob_state?) */
1004              if (tty->rawOutBufState != rob_idle) {
1005              /* if chars available, call write function... */
1006                (*tty->device.write)(tty->minor,
1007                                     (char *)&tty->rawOutBuf[tty->rawOutBufTail], 1);
1008              }
1009              /* reenable interrupts */
1010              rtems_interrupt_enable(level);
1011            }
1012          }     
1013          else {
1014                newTail = (tty->rawInBufTail + 1) % RAW_INPUT_BUFFER_SIZE;
1015                /* if chars_in_buffer > highwater                */
1016                rtems_interrupt_disable(level);
1017                if ((((newTail - tty->rawInBufHead + RAW_INPUT_BUFFER_SIZE)
1018                      % RAW_INPUT_BUFFER_SIZE)
1019                     > tty->highwater) &&
1020                    !(tty->flow_ctrl & FL_IREQXOF)) {
1021                  /* incoming data stream should be stopped */
1022                  tty->flow_ctrl |= FL_IREQXOF;
1023                  if ((tty->flow_ctrl & (FL_MDXOF | FL_ISNTXOF))
1024                      ==                (FL_MDXOF             ) ){
1025                    if ((tty->flow_ctrl & FL_OSTOP) ||
1026                        (tty->rawOutBufState == rob_idle)) {
1027                      /* if tx is stopped due to XOFF or out of data */
1028                      /*    call write function here                 */
1029                      tty->flow_ctrl |= FL_ISNTXOF;
1030                      (*tty->device.write)(tty->minor,
1031                                           &(tty->termios.c_cc[VSTOP]),
1032                                           1);
1033                    }
1034                  }
1035                  else if ((tty->flow_ctrl & (FL_MDRTS | FL_IRTSOFF))
1036                           ==                (FL_MDRTS             ) ) {
1037                    tty->flow_ctrl |= FL_IRTSOFF;               
1038                    /* deactivate RTS line */
1039                    if (tty->device.stopRemoteTx != NULL) {
1040                      tty->device.stopRemoteTx(tty->minor);
1041                    }
1042                  }
1043                }
1044                /* reenable interrupts */
1045                rtems_interrupt_enable(level);
1046
1047                if (newTail == tty->rawInBufHead) {
1048                        dropped++;
1049                }
1050                else {
1051                        tty->rawInBuf[newTail] = c;
1052                        tty->rawInBufTail = newTail;
1053                }               
1054          }
1055        }
1056        tty->rawInBufDropped += dropped;
1057        rtems_semaphore_release (tty->rawInBufSemaphore);
1058        return dropped;
1059}
1060
1061/*
1062 * Characters have been transmitted
1063 * NOTE: This routine runs in the context of the
1064 *       device transmit interrupt handler.
1065 * The second argument is the number of characters transmitted so far.
1066 * This value will always be 1 for devices which generate an interrupt
1067 * for each transmitted character.
1068 * It returns number of characters left to transmit
1069 */
1070int
1071rtems_termios_dequeue_characters (void *ttyp, int len)
1072{
1073        struct rtems_termios_tty *tty = ttyp;
1074        unsigned int newTail;
1075        int nToSend;
1076
1077        /* check for XOF/XON to send */
1078        if ((tty->flow_ctrl & (FL_MDXOF | FL_IREQXOF | FL_ISNTXOF))
1079            == (FL_MDXOF | FL_IREQXOF)) {
1080          /* XOFF should be sent now... */
1081          (*tty->device.write)(tty->minor,
1082                               &(tty->termios.c_cc[VSTOP]), 1);
1083          tty->flow_ctrl |= FL_ISNTXOF;
1084          nToSend = 1;
1085        }
1086        else if ((tty->flow_ctrl & (FL_IREQXOF | FL_ISNTXOF))
1087                 == FL_ISNTXOF) {
1088          /* NOTE: send XON even, if no longer in XON/XOFF mode... */
1089          /* XON should be sent now... */
1090          (*tty->device.write)(tty->minor,
1091                               &(tty->termios.c_cc[VSTART]), 1);
1092          tty->flow_ctrl &= ~FL_ISNTXOF;
1093          nToSend = 1;
1094        }
1095        else {
1096          if (tty->rawOutBufState == rob_wait)
1097            rtems_semaphore_release (tty->rawOutBufSemaphore);
1098          if ( tty->rawOutBufHead == tty->rawOutBufTail )
1099            return 0;   
1100          newTail = (tty->rawOutBufTail + len) % RAW_OUTPUT_BUFFER_SIZE;
1101          if (newTail == tty->rawOutBufHead) {
1102            /*
1103             * Buffer empty
1104             */
1105            tty->rawOutBufState = rob_idle;
1106            nToSend = 0;
1107          }
1108          /* check, whether output should stop due to received XOFF */
1109          else if ((tty->flow_ctrl & (FL_MDXON | FL_ORCVXOF))
1110                   ==                (FL_MDXON | FL_ORCVXOF)) {
1111            /* Buffer not empty, but output stops due to XOFF */
1112            /* set flag, that output has been stopped */
1113            tty->flow_ctrl |= FL_OSTOP;
1114            nToSend = 0;
1115          }
1116          else {
1117            /*
1118             * Buffer not empty, start tranmitter
1119             */
1120            if (newTail > tty->rawOutBufHead)
1121              nToSend = RAW_OUTPUT_BUFFER_SIZE - newTail;
1122            else
1123              nToSend = tty->rawOutBufHead - newTail;
1124            /* when flow control XON or XOF, don't send blocks of data     */
1125            /* to allow fast reaction on incoming flow ctrl and low latency*/
1126            /* for outgoing flow control                                   */
1127            if (tty->flow_ctrl & (FL_MDXON | FL_MDXOF)) {
1128              nToSend = 1;
1129            }
1130            (*tty->device.write)(tty->minor, (char *)&tty->rawOutBuf[newTail], nToSend);
1131            tty->rawOutBufState = rob_busy;
1132          }
1133          tty->rawOutBufTail = newTail;
1134        }
1135        return nToSend;
1136}
1137
1138
Note: See TracBrowser for help on using the repository browser.