source: rtems/c/src/lib/libc/termios.c @ 4555bc1e

4.104.114.84.95
Last change on this file since 4555bc1e was 4555bc1e, checked in by Joel Sherrill <joel.sherrill@…>, on 07/17/98 at 22:34:54

Initialized tty->refcount to 0. When (for whatever reason) malloc()
returned a buffer which was not zero-filled, the reference count
was not correct. When the application exitted, the "lastClose"
handler was not being called to flush the output. This problem
had manifested itself on a variety of platforms.

The function rtems_termios_dequeue_characters() incorrectly incremented
the buffer pointers when it was invoked and there were no characters
in the ring buffer. This problem had also manifested itself on a
variety of platforms. The symptom was a strange repeating of the
data in the transmitter buffer when the transmitter serial device
was supposed to go idle.

  • Property mode set to 100644
File size: 21.4 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
32#if defined(__FreeBSD__)
33#define XTABS   0
34#define ONLRET  0
35#define ONOCR   0
36#define TABDLY  0
37#define OLCUC   0
38#define ILCUC   0
39#define OCRNL   0
40#define IUCLC   0
41#endif
42
43/*
44 * The size of the cooked buffer
45 */
46#define CBUFSIZE        256
47
48/*
49 * The sizes of the raw message buffers.
50 * On most architectures it is quite a bit more
51 * efficient if these are powers of two.
52 */
53#define RAW_INPUT_BUFFER_SIZE   128
54#define RAW_OUTPUT_BUFFER_SIZE  64
55
56/*
57 * Variables associated with each termios instance.
58 * One structure for each hardware I/O device.
59 */
60struct rtems_termios_tty {
61        /*
62         * Linked-list of active TERMIOS devices
63         */
64        struct rtems_termios_tty        *forw;
65        struct rtems_termios_tty        *back;
66
67        /*
68         * How many times has this device been opened
69         */
70        int             refcount;
71
72        /*
73         * This device
74         */
75        rtems_device_major_number       major;
76        rtems_device_major_number       minor;
77
78        /*
79         * Mutual-exclusion semaphores
80         */
81        rtems_id        isem;
82        rtems_id        osem;
83
84        /*
85         * The canonical (cooked) character buffer
86         */
87        char            cbuf[CBUFSIZE];
88        int             ccount;
89        int             cindex;
90
91        /*
92         * Keep track of cursor (printhead) position
93         */
94        int             column;
95        int             read_start_column;
96
97        /*
98         * The ioctl settings
99         */
100        struct termios  termios;
101        rtems_interval  vtimeTicks;
102
103        /*
104         * Raw input character buffer
105         */
106        volatile char           rawInBuf[RAW_INPUT_BUFFER_SIZE];
107        volatile unsigned int   rawInBufHead;
108        volatile unsigned int   rawInBufTail;
109        rtems_id                rawInBufSemaphore;
110        rtems_unsigned32        rawInBufSemaphoreOptions;
111        rtems_interval          rawInBufSemaphoreTimeout;
112        rtems_interval          rawInBufSemaphoreFirstTimeout;
113        unsigned int            rawInBufDropped;        /* Statistics */
114
115        /*
116         * Raw output character buffer
117         */
118        volatile char           rawOutBuf[RAW_OUTPUT_BUFFER_SIZE];
119        volatile unsigned int   rawOutBufHead;
120        volatile unsigned int   rawOutBufTail;
121        rtems_id                rawOutBufSemaphore;
122        enum {rob_idle, rob_busy, rob_wait }    rawOutBufState;
123
124        /*
125         * Callbacks to device-specific routines
126         */
127        rtems_termios_callbacks device;
128};
129
130static struct rtems_termios_tty *ttyHead, *ttyTail;
131static rtems_id ttyMutex;
132
133/*
134 *  Reserve enough resources to open every physical device once.
135 */
136
137static int first_time;   /* assumed to be zeroed by BSS initialization */
138
139void
140rtems_termios_reserve_resources (
141  rtems_configuration_table *configuration,
142  rtems_unsigned32           number_of_devices
143  )
144{
145        rtems_api_configuration_table *rtems_config;
146
147        if (!configuration)
148                rtems_fatal_error_occurred (0xFFF0F001);
149        rtems_config = configuration->RTEMS_api_configuration;
150        if (!rtems_config)
151                rtems_fatal_error_occurred (0xFFF0F002);
152        if (!first_time)
153                rtems_config->maximum_semaphores += 1;
154        first_time = 1;
155        rtems_config->maximum_semaphores += (4 * number_of_devices);
156}
157
158void
159rtems_termios_initialize (void)
160{
161        rtems_status_code sc;
162
163        /*
164         * Create the mutex semaphore for the tty list
165         */
166        if (!ttyMutex) {
167                sc = rtems_semaphore_create (
168                        rtems_build_name ('T', 'R', 'm', 'i'),
169                        1,
170                        RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
171                        RTEMS_NO_PRIORITY,
172                        &ttyMutex);
173                if (sc != RTEMS_SUCCESSFUL)
174                        rtems_fatal_error_occurred (sc);
175        }
176}
177       
178/*
179 * Open a termios device
180 */
181rtems_status_code
182rtems_termios_open (
183  rtems_device_major_number      major,
184  rtems_device_minor_number      minor,
185  void                          *arg,
186  const rtems_termios_callbacks *callbacks
187  )
188{
189        rtems_status_code sc;
190        rtems_libio_open_close_args_t *args = arg;
191        struct rtems_termios_tty *tty;
192
193        /*
194         * See if the device has already been opened
195         */
196        sc = rtems_semaphore_obtain (ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
197        if (sc != RTEMS_SUCCESSFUL)
198                return sc;
199        for (tty = ttyHead ; tty != NULL ; tty = tty->forw) {
200                if ((tty->major == major) && (tty->minor == minor))
201                        break;
202        }
203        if (tty == NULL) {
204                static char c = 'a';
205
206                /*
207                 * Create a new device
208                 */
209                tty = malloc (sizeof (struct rtems_termios_tty));
210                if (tty == NULL) {
211                        rtems_semaphore_release (ttyMutex);
212                        return RTEMS_NO_MEMORY;
213                }
214                tty->forw = ttyHead;
215                ttyHead = tty;
216                tty->back = NULL;
217                if (ttyTail == NULL)
218                        ttyTail = tty;
219
220                tty->minor = minor;
221                tty->major = major;
222                tty->refcount = 0;
223
224                /*
225                 * Set up mutex semaphores
226                 */
227                sc = rtems_semaphore_create (
228                        rtems_build_name ('T', 'R', 'i', c),
229                        1,
230                        RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
231                        RTEMS_NO_PRIORITY,
232                        &tty->isem);
233                if (sc != RTEMS_SUCCESSFUL)
234                        rtems_fatal_error_occurred (sc);
235                sc = rtems_semaphore_create (
236                        rtems_build_name ('T', 'R', 'o', c),
237                        1,
238                        RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
239                        RTEMS_NO_PRIORITY,
240                        &tty->osem);
241                if (sc != RTEMS_SUCCESSFUL)
242                        rtems_fatal_error_occurred (sc);
243                sc = rtems_semaphore_create (
244                        rtems_build_name ('T', 'R', 'x', c),
245                        0,
246                        RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY,
247                        RTEMS_NO_PRIORITY,
248                        &tty->rawOutBufSemaphore);
249                if (sc != RTEMS_SUCCESSFUL)
250                        rtems_fatal_error_occurred (sc);
251                tty->rawOutBufHead = 0;
252                tty->rawOutBufTail = 0;
253
254                /*
255                 * Set callbacks
256                 */
257                tty->device = *callbacks;
258                if (!tty->device.pollRead) {
259                        sc = rtems_semaphore_create (
260                                rtems_build_name ('T', 'R', 'r', c),
261                                0,
262                                RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY,
263                                RTEMS_NO_PRIORITY,
264                                &tty->rawInBufSemaphore);
265                        if (sc != RTEMS_SUCCESSFUL)
266                                rtems_fatal_error_occurred (sc);
267                        tty->rawInBufHead = 0;
268                        tty->rawInBufTail = 0;
269                }
270
271                /*
272                 * Initialize variables
273                 */
274                tty->column = 0;
275                tty->cindex = tty->ccount = 0;
276
277                /*
278                 * Set default parameters
279                 */
280                tty->termios.c_iflag = BRKINT | ICRNL | IXON | IMAXBEL;
281                tty->termios.c_oflag = OPOST | ONLCR | XTABS;
282                tty->termios.c_cflag = B9600 | CS8 | CREAD;
283                tty->termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOK | ECHOE | ECHOCTL;
284                tty->termios.c_cc[VINTR] = '\003';
285                tty->termios.c_cc[VQUIT] = '\034';
286                tty->termios.c_cc[VERASE] = '\177';
287                tty->termios.c_cc[VKILL] = '\025';
288                tty->termios.c_cc[VEOF] = '\004';
289                tty->termios.c_cc[VEOL] = '\000';
290                tty->termios.c_cc[VEOL2] = '\000';
291                tty->termios.c_cc[VSTART] = '\021';
292                tty->termios.c_cc[VSTOP] = '\023';
293                tty->termios.c_cc[VSUSP] = '\032';
294                tty->termios.c_cc[VREPRINT] = '\022';
295                tty->termios.c_cc[VDISCARD] = '\017';
296                tty->termios.c_cc[VWERASE] = '\027';
297                tty->termios.c_cc[VLNEXT] = '\026';
298
299                /*
300                 * Device-specific open
301                 */
302                if (tty->device.firstOpen)
303                        (*tty->device.firstOpen)(major, minor, arg);
304
305                /*
306                 * Bump name characer
307                 */
308                if (c++ == 'z')
309                        c = 'a';
310        }
311        tty->refcount++;
312        args->iop->data1 = tty;
313        rtems_semaphore_release (ttyMutex);
314        return RTEMS_SUCCESSFUL;
315}
316
317/*
318 * Drain output queue
319 */
320static void
321drainOutput (struct rtems_termios_tty *tty)
322{
323        rtems_interrupt_level level;
324        rtems_status_code sc;
325
326        if (tty->device.outputUsesInterrupts) {
327                rtems_interrupt_disable (level);
328                while (tty->rawOutBufTail != tty->rawOutBufHead) {
329                        tty->rawOutBufState = rob_wait;
330                        rtems_interrupt_enable (level);
331                        sc = rtems_semaphore_obtain (tty->rawOutBufSemaphore,
332                                                        RTEMS_WAIT,
333                                                        RTEMS_NO_TIMEOUT);
334                        if (sc != RTEMS_SUCCESSFUL)
335                                rtems_fatal_error_occurred (sc);
336                        rtems_interrupt_disable (level);
337                }
338                rtems_interrupt_enable (level);
339        }
340}
341
342rtems_status_code
343rtems_termios_close (void *arg)
344{
345        rtems_libio_open_close_args_t *args = arg;
346        struct rtems_termios_tty *tty = args->iop->data1;
347        rtems_status_code sc;
348
349        sc = rtems_semaphore_obtain (ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
350        if (sc != RTEMS_SUCCESSFUL)
351                rtems_fatal_error_occurred (sc);
352        if (--tty->refcount == 0) {
353                drainOutput (tty);
354                if (tty->device.lastClose)
355                         (*tty->device.lastClose)(tty->major, tty->minor, arg);
356                if (tty->forw == NULL)
357                        ttyTail = tty->back;
358                else
359                        tty->forw->back = tty->back;
360                if (tty->back == NULL)
361                        ttyHead = tty->forw;
362                else
363                        tty->back->forw = tty->forw;
364                rtems_semaphore_delete (tty->isem);
365                rtems_semaphore_delete (tty->osem);
366                rtems_semaphore_delete (tty->rawOutBufSemaphore);
367                if (!tty->device.pollRead)
368                        rtems_semaphore_delete (tty->rawInBufSemaphore);
369                free (tty);
370        }
371        rtems_semaphore_release (ttyMutex);
372        return RTEMS_SUCCESSFUL;
373}
374
375rtems_status_code
376rtems_termios_ioctl (void *arg)
377{
378        rtems_libio_ioctl_args_t *args = arg;
379        struct rtems_termios_tty *tty = args->iop->data1;
380        rtems_status_code sc;
381
382        args->ioctl_return = 0;
383        sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
384        if (sc != RTEMS_SUCCESSFUL) {
385                args->ioctl_return = sc;
386                return sc;
387        }
388        switch (args->command) {
389        default:
390                sc = RTEMS_INVALID_NUMBER;
391                break;
392
393        case RTEMS_IO_GET_ATTRIBUTES:
394                *(struct termios *)args->buffer = tty->termios;
395                break;
396
397        case RTEMS_IO_SET_ATTRIBUTES:
398                tty->termios = *(struct termios *)args->buffer;
399                if (tty->termios.c_lflag & ICANON) {
400                        tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
401                        tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
402                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
403                }
404                else {
405                        rtems_interval ticksPerSecond;
406                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
407                        tty->vtimeTicks = tty->termios.c_cc[VTIME] * ticksPerSecond / 10;
408                        if (tty->termios.c_cc[VTIME]) {
409                                tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
410                                tty->rawInBufSemaphoreTimeout = tty->vtimeTicks;
411                                if (tty->termios.c_cc[VMIN])
412                                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
413                                else
414                                        tty->rawInBufSemaphoreFirstTimeout = tty->vtimeTicks;
415                        }
416                        else {
417                                if (tty->termios.c_cc[VMIN]) {
418                                        tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
419                                        tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
420                                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
421                                }
422                                else {
423                                        tty->rawInBufSemaphoreOptions = RTEMS_NO_WAIT;
424                                }
425                        }
426                }
427                if (tty->device.setAttributes)
428                        (*tty->device.setAttributes)(tty->minor, &tty->termios);
429                break;
430
431        case RTEMS_IO_TCDRAIN:
432                drainOutput (tty);
433                break;
434        }
435        rtems_semaphore_release (tty->osem);
436        args->ioctl_return = sc;
437        return sc;
438}
439
440/*
441 * Send characters to device-specific code
442 */
443static void
444osend (const char *buf, int len, struct rtems_termios_tty *tty)
445{
446        unsigned int newHead;
447        rtems_interrupt_level level;
448        rtems_status_code sc;
449
450        if (!tty->device.outputUsesInterrupts) {
451                (*tty->device.write)(tty->minor, buf, len);
452                return;
453        }
454        newHead = tty->rawOutBufHead;
455        while (len) {
456                /*
457                 * Performance improvement could be made here.
458                 * Copy multiple bytes to raw buffer:
459                 * if (len > 1) && (space to buffer end, or tail > 1)
460                 *      ncopy = MIN (len, space to buffer end or tail)
461                 *      memcpy (raw buffer, buf, ncopy)
462                 *      buf += ncopy
463                 *      len -= ncopy
464                 *
465                 * To minimize latency, the memcpy should be done
466                 * with interrupts enabled.
467                 */
468                newHead = (newHead + 1) % RAW_OUTPUT_BUFFER_SIZE;
469                rtems_interrupt_disable (level);
470                while (newHead == tty->rawOutBufTail) {
471                        tty->rawOutBufState = rob_wait;
472                        rtems_interrupt_enable (level);
473                        sc = rtems_semaphore_obtain (tty->rawOutBufSemaphore,
474                                                        RTEMS_WAIT,
475                                                        RTEMS_NO_TIMEOUT);
476                        if (sc != RTEMS_SUCCESSFUL)
477                                rtems_fatal_error_occurred (sc);
478                        rtems_interrupt_disable (level);
479                }
480                tty->rawOutBuf[tty->rawOutBufHead] = *buf++;
481                tty->rawOutBufHead = newHead;
482                if (tty->rawOutBufState == rob_idle) {
483                        rtems_interrupt_enable (level);
484                        tty->rawOutBufState = rob_busy;
485                        (*tty->device.write)(tty->minor, (char *)&tty->rawOutBuf[tty->rawOutBufTail], 1);
486                }
487                else {
488                        rtems_interrupt_enable (level);
489                }
490                len--;
491        }
492}
493
494/*
495 * Handle output processing
496 */
497static void
498oproc (unsigned char c, struct rtems_termios_tty *tty)
499{
500        int     i;
501
502        if (tty->termios.c_oflag & OPOST) {
503                switch (c) {
504                case '\n':
505                        if (tty->termios.c_oflag & ONLRET)
506                                tty->column = 0;
507                        if (tty->termios.c_oflag & ONLCR) {
508                                osend ("\r", 1, tty);
509                                tty->column = 0;
510                        }
511                        break;
512
513                case '\r':
514                        if ((tty->termios.c_oflag & ONOCR) && (tty->column == 0))
515                                return;
516                        if (tty->termios.c_oflag & OCRNL) {
517                                c = '\n';
518                                if (tty->termios.c_oflag & ONLRET)
519                                        tty->column = 0;
520                                break;
521                        }
522                        tty->column = 0;
523                        break;
524
525                case '\t':
526                        i = 8 - (tty->column & 7);
527                        if ((tty->termios.c_oflag & TABDLY) == XTABS) {
528                                tty->column += i;
529                                osend ( "        ",  i, tty);
530                                return;
531                        }
532                        tty->column += i;
533                        break;
534
535                case '\b':
536                        if (tty->column > 0)
537                                tty->column--;
538                        break;
539
540                default:
541                        if (tty->termios.c_oflag & OLCUC)
542                                c = toupper(c);
543                        if (!iscntrl(c))
544                                tty->column++;
545                        break;
546                }
547        }
548        osend (&c, 1, tty);
549}
550
551rtems_status_code
552rtems_termios_write (void *arg)
553{
554        rtems_libio_rw_args_t *args = arg;
555        struct rtems_termios_tty *tty = args->iop->data1;
556        rtems_status_code sc;
557
558        sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
559        if (sc != RTEMS_SUCCESSFUL)
560                return sc;
561        if (tty->termios.c_oflag & OPOST) {
562                unsigned32 count = args->count;
563                unsigned8 *buffer = args->buffer;
564                while (count--)
565                        oproc (*buffer++, tty);
566                args->bytes_moved = args->count;
567        }
568        else {
569                osend (args->buffer, args->count, tty);
570                args->bytes_moved = args->count;
571        }
572        rtems_semaphore_release (tty->osem);
573        return sc;
574}
575
576/*
577 * Echo a typed character
578 */
579static void
580echo (unsigned char c, struct rtems_termios_tty *tty)
581{
582        if ((tty->termios.c_lflag & ECHOCTL) && iscntrl(c) && (c != '\t') && (c != '\n')) {
583                char echobuf[2];
584
585                echobuf[0] = '^';
586                echobuf[1] = c ^ 0x40;
587                osend (echobuf, 2, tty);
588                tty->column += 2;
589        }
590        else {
591                oproc (c, tty);
592        }
593}
594
595/*
596 * Erase a character or line
597 * FIXME: Needs support for WERASE and ECHOPRT.
598 * FIXME: Some of the tests should check for IEXTEN, too.
599 */
600static void
601erase (struct rtems_termios_tty *tty, int lineFlag)
602{
603        if (tty->ccount == 0)
604                return;
605        if (lineFlag) {
606                if (!(tty->termios.c_lflag & ECHO)) {
607                        tty->ccount = 0;
608                        return;
609                }
610                if (!(tty->termios.c_lflag & ECHOE)) {
611                        tty->ccount = 0;
612                        echo (tty->termios.c_cc[VKILL], tty);
613                        if (tty->termios.c_lflag & ECHOK)
614                                echo ('\n', tty);
615                        return;
616                }
617        }
618        while (tty->ccount) {
619                unsigned char c = tty->cbuf[--tty->ccount];
620
621                if (tty->termios.c_lflag & ECHO) {
622                        if (!lineFlag && !(tty->termios.c_lflag & ECHOE)) {
623                                echo (tty->termios.c_cc[VERASE], tty);
624                        }
625                        else if (c == '\t') {
626                                int col = tty->read_start_column;
627                                int i = 0;
628
629                                /*
630                                 * Find the character before the tab
631                                 */
632                                while (i != tty->ccount) {
633                                        c = tty->cbuf[i++];
634                                        if (c == '\t') {
635                                                col = (col | 7) + 1;
636                                        }
637                                        else if (iscntrl (c)) {
638                                                if (tty->termios.c_lflag & ECHOCTL)
639                                                        col += 2;
640                                        }
641                                        else {
642                                                col++;
643                                        }
644                                }
645
646                                /*
647                                 * Back up over the tab
648                                 */
649                                while (tty->column > col) {
650                                        osend ("\b", 1, tty);
651                                        tty->column--;
652                                }
653                        }
654                        else {
655                                if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) {
656                                        osend ("\b \b", 3, tty);
657                                        if (tty->column)
658                                                tty->column--;
659                                }
660                                if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) {
661                                        osend ("\b \b", 3, tty);
662                                        if (tty->column)
663                                                tty->column--;
664                                }
665                        }
666                }
667                if (!lineFlag)
668                        break;
669        }
670}
671
672/*
673 * Process a single input character
674 */
675static int
676iproc (unsigned char c, struct rtems_termios_tty *tty)
677{
678        if (tty->termios.c_iflag & ISTRIP)
679                c &= 0x7f;
680        if (tty->termios.c_iflag & IUCLC)
681                c = tolower (c);
682        if (c == '\r') {
683                if (tty->termios.c_iflag & IGNCR)
684                        return 0;
685                if (tty->termios.c_iflag & ICRNL)
686                        c = '\n';
687        }
688        else if ((c == '\n') && (tty->termios.c_iflag & INLCR)) {
689                c = '\r';
690        }
691        if ((c != '\0') && (tty->termios.c_lflag & ICANON)) {
692                if (c == tty->termios.c_cc[VERASE]) {
693                        erase (tty, 0);
694                        return 0;
695                }
696                else if (c == tty->termios.c_cc[VKILL]) {
697                        erase (tty, 1);
698                        return 0;
699                }
700                else if (c == tty->termios.c_cc[VEOF]) {
701                        return 1;
702                }
703                else if (c == '\n') {
704                        if (tty->termios.c_lflag & (ECHO | ECHONL))
705                                echo (c, tty);
706                        tty->cbuf[tty->ccount++] = c;
707                        return 1;
708                }
709                else if ((c == tty->termios.c_cc[VEOL])
710                      || (c == tty->termios.c_cc[VEOL2])) {
711                        if (tty->termios.c_lflag & ECHO)
712                                echo (c, tty);
713                        tty->cbuf[tty->ccount++] = c;
714                        return 1;
715                }
716        }
717
718        /*
719         * FIXME: Should do IMAXBEL handling somehow
720         */
721        if (tty->ccount < (CBUFSIZE-1)) {
722                if (tty->termios.c_lflag & ECHO)
723                        echo (c, tty);
724                tty->cbuf[tty->ccount++] = c;
725        }
726        return 0;
727}
728
729/*
730 * Process input character, with semaphore.
731 */
732static int
733siproc (unsigned char c, struct rtems_termios_tty *tty)
734{
735        int i;
736
737        /*
738         * Obtain output semaphore if character will be echoed
739         */
740        if (tty->termios.c_lflag & (ECHO|ECHOE|ECHOK|ECHONL|ECHOPRT|ECHOCTL|ECHOKE)) {
741                rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
742                i = iproc (c, tty);
743                rtems_semaphore_release (tty->osem);
744        }
745        else {
746                i = iproc (c, tty);
747        }
748        return i;
749}
750
751/*
752 * Fill the input buffer by polling the device
753 */
754static rtems_status_code
755fillBufferPoll (struct rtems_termios_tty *tty)
756{
757        int n;
758
759        if (tty->termios.c_lflag & ICANON) {
760                for (;;) {
761                        n = (*tty->device.pollRead)(tty->minor);
762                        if (n < 0) {
763                                rtems_task_wake_after (1);
764                        }
765                        else {
766                                if  (siproc (n, tty))
767                                        break;
768                        }
769                }
770        }
771        else {
772                rtems_interval then, now;
773                if (!tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
774                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &then);
775                for (;;) {
776                        n = (*tty->device.pollRead)(tty->minor);
777                        if (n < 0) {
778                                if (tty->termios.c_cc[VMIN]) {
779                                        if (tty->termios.c_cc[VTIME] && tty->ccount) {
780                                                rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
781                                                if ((now - then) > tty->vtimeTicks) {
782                                                        break;
783                                                }
784                                        }
785                                }
786                                else {
787                                        if (!tty->termios.c_cc[VTIME])
788                                                break;
789                                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
790                                        if ((now - then) > tty->vtimeTicks) {
791                                                break;
792                                        }
793                                }
794                                rtems_task_wake_after (1);
795                        }
796                        else {
797                                siproc (n, tty);
798                                if (tty->ccount >= tty->termios.c_cc[VMIN])
799                                        break;
800                                if (tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
801                                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &then);
802                        }
803                }
804        }
805        return RTEMS_SUCCESSFUL;
806}
807
808/*
809 * Fill the input buffer from the raw input queue
810 */
811static rtems_status_code
812fillBufferQueue (struct rtems_termios_tty *tty)
813{
814        rtems_interval timeout = tty->rawInBufSemaphoreFirstTimeout;
815        rtems_status_code sc;
816
817        for (;;) {
818                /*
819                 * Process characters read from raw queue
820                 */
821                while (tty->rawInBufHead != tty->rawInBufTail) {
822                        unsigned char c;
823                        unsigned int newHead;
824
825                        newHead = (tty->rawInBufHead + 1) % RAW_INPUT_BUFFER_SIZE;
826                        c = tty->rawInBuf[newHead];
827                        tty->rawInBufHead = newHead;
828                        if (tty->termios.c_lflag & ICANON) {
829                                if  (siproc (c, tty))
830                                        return RTEMS_SUCCESSFUL;
831                        }
832                        else {
833                                siproc (c, tty);
834                                if (tty->ccount >= tty->termios.c_cc[VMIN])
835                                        return RTEMS_SUCCESSFUL;
836                        }
837                        timeout = tty->rawInBufSemaphoreTimeout;
838                }
839
840                /*
841                 * Wait for characters
842                 */
843                sc = rtems_semaphore_obtain (tty->rawInBufSemaphore,
844                                                tty->rawInBufSemaphoreOptions,
845                                                timeout);
846                if (sc != RTEMS_SUCCESSFUL)
847                        break;
848        }
849        return RTEMS_SUCCESSFUL;
850}
851
852rtems_status_code
853rtems_termios_read (void *arg)
854{
855        rtems_libio_rw_args_t *args = arg;
856        struct rtems_termios_tty *tty = args->iop->data1;
857        unsigned32 count = args->count;
858        unsigned8 *buffer = args->buffer;
859        rtems_status_code sc;
860
861        sc = rtems_semaphore_obtain (tty->isem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
862        if (sc != RTEMS_SUCCESSFUL)
863                return sc;
864        if (tty->cindex == tty->ccount) {
865                tty->cindex = tty->ccount = 0;
866                tty->read_start_column = tty->column;
867                if (tty->device.pollRead)
868                        sc = fillBufferPoll (tty);
869                else
870                        sc = fillBufferQueue (tty);
871                if (sc != RTEMS_SUCCESSFUL)
872                        tty->cindex = tty->ccount = 0;
873        }
874        while (count && (tty->cindex < tty->ccount)) {
875                *buffer++ = tty->cbuf[tty->cindex++];
876                count--;
877        }
878        args->bytes_moved = args->count - count;
879        rtems_semaphore_release (tty->isem);
880        return sc;
881}
882
883/*
884 * Place characters on raw queue.
885 * NOTE: This routine runs in the context of the
886 *       device receive interrupt handler.
887 * Returns the number of characters dropped because of overlow.
888 */
889int
890rtems_termios_enqueue_raw_characters (void *ttyp, char *buf, int len)
891{
892        struct rtems_termios_tty *tty = ttyp;
893        unsigned int newTail;
894
895        while (len) {
896                newTail = (tty->rawInBufTail + 1) % RAW_INPUT_BUFFER_SIZE;
897                if (newTail == tty->rawInBufHead) {
898                        tty->rawInBufDropped += len;
899                        break;
900                }
901                tty->rawInBuf[newTail] = *buf++;
902                len--;
903                tty->rawInBufTail = newTail;
904        }
905        rtems_semaphore_release (tty->rawInBufSemaphore);
906        return len;
907}
908
909/*
910 * Characters have been transmitted
911 * NOTE: This routine runs in the context of the
912 *       device transmit interrupt handler.
913 * The second argument is the number of characters transmitted so far.
914 * This value will always be 1 for devices which generate an interrupt
915 * for each transmitted character.
916 */
917void
918rtems_termios_dequeue_characters (void *ttyp, int len)
919{
920        struct rtems_termios_tty *tty = ttyp;
921        unsigned int newTail;
922        int nToSend;
923
924        if (tty->rawOutBufState == rob_wait)
925                rtems_semaphore_release (tty->rawOutBufSemaphore);
926        if ( tty->rawOutBufHead == tty->rawOutBufTail )
927                return;
928        newTail = (tty->rawOutBufTail + len) % RAW_OUTPUT_BUFFER_SIZE;
929        if (newTail == tty->rawOutBufHead) {
930                /*
931                 * Buffer empty
932                 */
933                tty->rawOutBufState = rob_idle;
934        }
935        else {
936                /*
937                 * Buffer not empty, start tranmitter
938                 */
939                tty->rawOutBufState = rob_busy;
940                if (newTail > tty->rawOutBufHead)
941                        nToSend = RAW_OUTPUT_BUFFER_SIZE - newTail;
942                else
943                        nToSend = tty->rawOutBufHead - newTail;
944                (*tty->device.write)(tty->minor, (char *)&tty->rawOutBuf[newTail], nToSend);
945        }
946        tty->rawOutBufTail = newTail;
947}
Note: See TracBrowser for help on using the repository browser.