source: rtems/c/src/lib/libc/termios.c @ bd7c547

4.104.114.84.95
Last change on this file since bd7c547 was bd7c547, checked in by Joel Sherrill <joel.sherrill@…>, on 07/28/98 at 16:49:36

Closed window thanks to patch from Eric Norum.

  • 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                        tty->rawOutBufState = rob_busy;
484                        (*tty->device.write)(tty->minor,
485                                (char *)&tty->rawOutBuf[tty->rawOutBufTail], 1);
486                }
487                rtems_interrupt_enable (level);
488                len--;
489        }
490}
491
492/*
493 * Handle output processing
494 */
495static void
496oproc (unsigned char c, struct rtems_termios_tty *tty)
497{
498        int     i;
499
500        if (tty->termios.c_oflag & OPOST) {
501                switch (c) {
502                case '\n':
503                        if (tty->termios.c_oflag & ONLRET)
504                                tty->column = 0;
505                        if (tty->termios.c_oflag & ONLCR) {
506                                osend ("\r", 1, tty);
507                                tty->column = 0;
508                        }
509                        break;
510
511                case '\r':
512                        if ((tty->termios.c_oflag & ONOCR) && (tty->column == 0))
513                                return;
514                        if (tty->termios.c_oflag & OCRNL) {
515                                c = '\n';
516                                if (tty->termios.c_oflag & ONLRET)
517                                        tty->column = 0;
518                                break;
519                        }
520                        tty->column = 0;
521                        break;
522
523                case '\t':
524                        i = 8 - (tty->column & 7);
525                        if ((tty->termios.c_oflag & TABDLY) == XTABS) {
526                                tty->column += i;
527                                osend ( "        ",  i, tty);
528                                return;
529                        }
530                        tty->column += i;
531                        break;
532
533                case '\b':
534                        if (tty->column > 0)
535                                tty->column--;
536                        break;
537
538                default:
539                        if (tty->termios.c_oflag & OLCUC)
540                                c = toupper(c);
541                        if (!iscntrl(c))
542                                tty->column++;
543                        break;
544                }
545        }
546        osend (&c, 1, tty);
547}
548
549rtems_status_code
550rtems_termios_write (void *arg)
551{
552        rtems_libio_rw_args_t *args = arg;
553        struct rtems_termios_tty *tty = args->iop->data1;
554        rtems_status_code sc;
555
556        sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
557        if (sc != RTEMS_SUCCESSFUL)
558                return sc;
559        if (tty->termios.c_oflag & OPOST) {
560                unsigned32 count = args->count;
561                unsigned8 *buffer = args->buffer;
562                while (count--)
563                        oproc (*buffer++, tty);
564                args->bytes_moved = args->count;
565        }
566        else {
567                osend (args->buffer, args->count, tty);
568                args->bytes_moved = args->count;
569        }
570        rtems_semaphore_release (tty->osem);
571        return sc;
572}
573
574/*
575 * Echo a typed character
576 */
577static void
578echo (unsigned char c, struct rtems_termios_tty *tty)
579{
580        if ((tty->termios.c_lflag & ECHOCTL) && iscntrl(c) && (c != '\t') && (c != '\n')) {
581                char echobuf[2];
582
583                echobuf[0] = '^';
584                echobuf[1] = c ^ 0x40;
585                osend (echobuf, 2, tty);
586                tty->column += 2;
587        }
588        else {
589                oproc (c, tty);
590        }
591}
592
593/*
594 * Erase a character or line
595 * FIXME: Needs support for WERASE and ECHOPRT.
596 * FIXME: Some of the tests should check for IEXTEN, too.
597 */
598static void
599erase (struct rtems_termios_tty *tty, int lineFlag)
600{
601        if (tty->ccount == 0)
602                return;
603        if (lineFlag) {
604                if (!(tty->termios.c_lflag & ECHO)) {
605                        tty->ccount = 0;
606                        return;
607                }
608                if (!(tty->termios.c_lflag & ECHOE)) {
609                        tty->ccount = 0;
610                        echo (tty->termios.c_cc[VKILL], tty);
611                        if (tty->termios.c_lflag & ECHOK)
612                                echo ('\n', tty);
613                        return;
614                }
615        }
616        while (tty->ccount) {
617                unsigned char c = tty->cbuf[--tty->ccount];
618
619                if (tty->termios.c_lflag & ECHO) {
620                        if (!lineFlag && !(tty->termios.c_lflag & ECHOE)) {
621                                echo (tty->termios.c_cc[VERASE], tty);
622                        }
623                        else if (c == '\t') {
624                                int col = tty->read_start_column;
625                                int i = 0;
626
627                                /*
628                                 * Find the character before the tab
629                                 */
630                                while (i != tty->ccount) {
631                                        c = tty->cbuf[i++];
632                                        if (c == '\t') {
633                                                col = (col | 7) + 1;
634                                        }
635                                        else if (iscntrl (c)) {
636                                                if (tty->termios.c_lflag & ECHOCTL)
637                                                        col += 2;
638                                        }
639                                        else {
640                                                col++;
641                                        }
642                                }
643
644                                /*
645                                 * Back up over the tab
646                                 */
647                                while (tty->column > col) {
648                                        osend ("\b", 1, tty);
649                                        tty->column--;
650                                }
651                        }
652                        else {
653                                if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) {
654                                        osend ("\b \b", 3, tty);
655                                        if (tty->column)
656                                                tty->column--;
657                                }
658                                if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) {
659                                        osend ("\b \b", 3, tty);
660                                        if (tty->column)
661                                                tty->column--;
662                                }
663                        }
664                }
665                if (!lineFlag)
666                        break;
667        }
668}
669
670/*
671 * Process a single input character
672 */
673static int
674iproc (unsigned char c, struct rtems_termios_tty *tty)
675{
676        if (tty->termios.c_iflag & ISTRIP)
677                c &= 0x7f;
678        if (tty->termios.c_iflag & IUCLC)
679                c = tolower (c);
680        if (c == '\r') {
681                if (tty->termios.c_iflag & IGNCR)
682                        return 0;
683                if (tty->termios.c_iflag & ICRNL)
684                        c = '\n';
685        }
686        else if ((c == '\n') && (tty->termios.c_iflag & INLCR)) {
687                c = '\r';
688        }
689        if ((c != '\0') && (tty->termios.c_lflag & ICANON)) {
690                if (c == tty->termios.c_cc[VERASE]) {
691                        erase (tty, 0);
692                        return 0;
693                }
694                else if (c == tty->termios.c_cc[VKILL]) {
695                        erase (tty, 1);
696                        return 0;
697                }
698                else if (c == tty->termios.c_cc[VEOF]) {
699                        return 1;
700                }
701                else if (c == '\n') {
702                        if (tty->termios.c_lflag & (ECHO | ECHONL))
703                                echo (c, tty);
704                        tty->cbuf[tty->ccount++] = c;
705                        return 1;
706                }
707                else if ((c == tty->termios.c_cc[VEOL])
708                      || (c == tty->termios.c_cc[VEOL2])) {
709                        if (tty->termios.c_lflag & ECHO)
710                                echo (c, tty);
711                        tty->cbuf[tty->ccount++] = c;
712                        return 1;
713                }
714        }
715
716        /*
717         * FIXME: Should do IMAXBEL handling somehow
718         */
719        if (tty->ccount < (CBUFSIZE-1)) {
720                if (tty->termios.c_lflag & ECHO)
721                        echo (c, tty);
722                tty->cbuf[tty->ccount++] = c;
723        }
724        return 0;
725}
726
727/*
728 * Process input character, with semaphore.
729 */
730static int
731siproc (unsigned char c, struct rtems_termios_tty *tty)
732{
733        int i;
734
735        /*
736         * Obtain output semaphore if character will be echoed
737         */
738        if (tty->termios.c_lflag & (ECHO|ECHOE|ECHOK|ECHONL|ECHOPRT|ECHOCTL|ECHOKE)) {
739                rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
740                i = iproc (c, tty);
741                rtems_semaphore_release (tty->osem);
742        }
743        else {
744                i = iproc (c, tty);
745        }
746        return i;
747}
748
749/*
750 * Fill the input buffer by polling the device
751 */
752static rtems_status_code
753fillBufferPoll (struct rtems_termios_tty *tty)
754{
755        int n;
756
757        if (tty->termios.c_lflag & ICANON) {
758                for (;;) {
759                        n = (*tty->device.pollRead)(tty->minor);
760                        if (n < 0) {
761                                rtems_task_wake_after (1);
762                        }
763                        else {
764                                if  (siproc (n, tty))
765                                        break;
766                        }
767                }
768        }
769        else {
770                rtems_interval then, now;
771                if (!tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
772                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &then);
773                for (;;) {
774                        n = (*tty->device.pollRead)(tty->minor);
775                        if (n < 0) {
776                                if (tty->termios.c_cc[VMIN]) {
777                                        if (tty->termios.c_cc[VTIME] && tty->ccount) {
778                                                rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
779                                                if ((now - then) > tty->vtimeTicks) {
780                                                        break;
781                                                }
782                                        }
783                                }
784                                else {
785                                        if (!tty->termios.c_cc[VTIME])
786                                                break;
787                                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
788                                        if ((now - then) > tty->vtimeTicks) {
789                                                break;
790                                        }
791                                }
792                                rtems_task_wake_after (1);
793                        }
794                        else {
795                                siproc (n, tty);
796                                if (tty->ccount >= tty->termios.c_cc[VMIN])
797                                        break;
798                                if (tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
799                                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &then);
800                        }
801                }
802        }
803        return RTEMS_SUCCESSFUL;
804}
805
806/*
807 * Fill the input buffer from the raw input queue
808 */
809static rtems_status_code
810fillBufferQueue (struct rtems_termios_tty *tty)
811{
812        rtems_interval timeout = tty->rawInBufSemaphoreFirstTimeout;
813        rtems_status_code sc;
814
815        for (;;) {
816                /*
817                 * Process characters read from raw queue
818                 */
819                while (tty->rawInBufHead != tty->rawInBufTail) {
820                        unsigned char c;
821                        unsigned int newHead;
822
823                        newHead = (tty->rawInBufHead + 1) % RAW_INPUT_BUFFER_SIZE;
824                        c = tty->rawInBuf[newHead];
825                        tty->rawInBufHead = newHead;
826                        if (tty->termios.c_lflag & ICANON) {
827                                if  (siproc (c, tty))
828                                        return RTEMS_SUCCESSFUL;
829                        }
830                        else {
831                                siproc (c, tty);
832                                if (tty->ccount >= tty->termios.c_cc[VMIN])
833                                        return RTEMS_SUCCESSFUL;
834                        }
835                        timeout = tty->rawInBufSemaphoreTimeout;
836                }
837
838                /*
839                 * Wait for characters
840                 */
841                sc = rtems_semaphore_obtain (tty->rawInBufSemaphore,
842                                                tty->rawInBufSemaphoreOptions,
843                                                timeout);
844                if (sc != RTEMS_SUCCESSFUL)
845                        break;
846        }
847        return RTEMS_SUCCESSFUL;
848}
849
850rtems_status_code
851rtems_termios_read (void *arg)
852{
853        rtems_libio_rw_args_t *args = arg;
854        struct rtems_termios_tty *tty = args->iop->data1;
855        unsigned32 count = args->count;
856        unsigned8 *buffer = args->buffer;
857        rtems_status_code sc;
858
859        sc = rtems_semaphore_obtain (tty->isem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
860        if (sc != RTEMS_SUCCESSFUL)
861                return sc;
862        if (tty->cindex == tty->ccount) {
863                tty->cindex = tty->ccount = 0;
864                tty->read_start_column = tty->column;
865                if (tty->device.pollRead)
866                        sc = fillBufferPoll (tty);
867                else
868                        sc = fillBufferQueue (tty);
869                if (sc != RTEMS_SUCCESSFUL)
870                        tty->cindex = tty->ccount = 0;
871        }
872        while (count && (tty->cindex < tty->ccount)) {
873                *buffer++ = tty->cbuf[tty->cindex++];
874                count--;
875        }
876        args->bytes_moved = args->count - count;
877        rtems_semaphore_release (tty->isem);
878        return sc;
879}
880
881/*
882 * Place characters on raw queue.
883 * NOTE: This routine runs in the context of the
884 *       device receive interrupt handler.
885 * Returns the number of characters dropped because of overlow.
886 */
887int
888rtems_termios_enqueue_raw_characters (void *ttyp, char *buf, int len)
889{
890        struct rtems_termios_tty *tty = ttyp;
891        unsigned int newTail;
892
893        while (len) {
894                newTail = (tty->rawInBufTail + 1) % RAW_INPUT_BUFFER_SIZE;
895                if (newTail == tty->rawInBufHead) {
896                        tty->rawInBufDropped += len;
897                        break;
898                }
899                tty->rawInBuf[newTail] = *buf++;
900                len--;
901                tty->rawInBufTail = newTail;
902        }
903        rtems_semaphore_release (tty->rawInBufSemaphore);
904        return len;
905}
906
907/*
908 * Characters have been transmitted
909 * NOTE: This routine runs in the context of the
910 *       device transmit interrupt handler.
911 * The second argument is the number of characters transmitted so far.
912 * This value will always be 1 for devices which generate an interrupt
913 * for each transmitted character.
914 */
915void
916rtems_termios_dequeue_characters (void *ttyp, int len)
917{
918        struct rtems_termios_tty *tty = ttyp;
919        unsigned int newTail;
920        int nToSend;
921
922        if (tty->rawOutBufState == rob_wait)
923                rtems_semaphore_release (tty->rawOutBufSemaphore);
924        if ( tty->rawOutBufHead == tty->rawOutBufTail )
925                return;
926        newTail = (tty->rawOutBufTail + len) % RAW_OUTPUT_BUFFER_SIZE;
927        if (newTail == tty->rawOutBufHead) {
928                /*
929                 * Buffer empty
930                 */
931                tty->rawOutBufState = rob_idle;
932        }
933        else {
934                /*
935                 * Buffer not empty, start tranmitter
936                 */
937                tty->rawOutBufState = rob_busy;
938                if (newTail > tty->rawOutBufHead)
939                        nToSend = RAW_OUTPUT_BUFFER_SIZE - newTail;
940                else
941                        nToSend = tty->rawOutBufHead - newTail;
942                (*tty->device.write)(tty->minor, (char *)&tty->rawOutBuf[newTail], nToSend);
943        }
944        tty->rawOutBufTail = newTail;
945}
Note: See TracBrowser for help on using the repository browser.