source: rtems/c/src/exec/libcsupport/src/termios.c @ b3fd1641

4.104.114.84.95
Last change on this file since b3fd1641 was b3fd1641, checked in by Joel Sherrill <joel.sherrill@…>, on 08/21/98 at 15:32:19

Fix from Eric Norum <eric@…>:

"Thomas Doerfler" <td@…> wrote:

While implementing/testing the console/termios support for
PPC403 in RTEMS-4.0.0-beta3, I am stuck at a certain location in
termios.c:

During "rtems_termios_initialize", the main control data structure
"*tty" is allocated using malloc(). (Note, that malloc does not
clear the allocated memory and my BSP does not clear memory during
startup). Furtheron, a lot of fields of that structure are
initialized, but the field "rawOutBufState" is not, and therefore
keeps an arbitrary contents.

When "osend()" is called the first time(with the serial device
driver working in interrupt mode), termios gets stuck and will not
call the device drivers output function.

My questions now are:

  • anybody already experienced this bug?
  • is it a bug at all or did I do anything fundamentally wrong?
  • is there already a common bugfix for that?

I don't like poking around in other people code, as long as I am
not absolutely sure, what I do...

Yes, there's a bug there.
I thought that Joel had patched this already, but here's a patch to
fix this. This patch also addresses a concern that many others have
raised regarding enabling and disabling of transmitter interrupts.

First, here's the example I've been using of a simple UART-style
interrupt-driven driver:
===============================================================
void
device_write_routine (int minor, char *buf, int count)
{

UART->control_register &= ~UART_TRANSMITTER_READY;
UART->output_register = *buf;
UART->control_register |= UART_TRANSMIT_INTERRUPT_ENABLE;

}

void
device_transmit_interrupt_routine (int vector)
{

UART->control_register &= ~UART_TRANSMIT_INTERRUPT_ENABLE;
rtems_termios_dequeue_characters (device_ttyp, 1);

}
==============================================================

Several people have expressed their concern about the disable/enable
of transmitter interrupts for every character. On some machines
this disable/enable is an expensive operation. With the attached
patch applied you can write the two routines as:
==============================================================
void
device_write_routine (int minor, char *buf, int count)
{

code_to_clear_transmitter_ready_status ();
if (device_ttyp->rawOutBufState == rob_idle)

code_to_enable_transmitter_interrupts ();

code_to_send_one_character_to_transmitter (*buf);

}

void
device_transmit_interrupt_routine (int vector)
{

rtems_termios_dequeue_characters (device_ttyp, 1);
if (device_ttyp->rawOutBufState == rob_idle)

code_to_disable_transmitter_interrupts ();

}
===============================================================

  • Property mode set to 100644
File size: 21.2 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 = calloc (1, 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                if (ttyTail == NULL)
217                        ttyTail = tty;
218
219                tty->minor = minor;
220                tty->major = major;
221
222                /*
223                 * Set up mutex semaphores
224                 */
225                sc = rtems_semaphore_create (
226                        rtems_build_name ('T', 'R', 'i', c),
227                        1,
228                        RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
229                        RTEMS_NO_PRIORITY,
230                        &tty->isem);
231                if (sc != RTEMS_SUCCESSFUL)
232                        rtems_fatal_error_occurred (sc);
233                sc = rtems_semaphore_create (
234                        rtems_build_name ('T', 'R', 'o', c),
235                        1,
236                        RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
237                        RTEMS_NO_PRIORITY,
238                        &tty->osem);
239                if (sc != RTEMS_SUCCESSFUL)
240                        rtems_fatal_error_occurred (sc);
241                sc = rtems_semaphore_create (
242                        rtems_build_name ('T', 'R', 'x', c),
243                        0,
244                        RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY,
245                        RTEMS_NO_PRIORITY,
246                        &tty->rawOutBufSemaphore);
247                if (sc != RTEMS_SUCCESSFUL)
248                        rtems_fatal_error_occurred (sc);
249                tty->rawOutBufState = rob_idle;
250
251                /*
252                 * Set callbacks
253                 */
254                tty->device = *callbacks;
255                if (!tty->device.pollRead) {
256                        sc = rtems_semaphore_create (
257                                rtems_build_name ('T', 'R', 'r', c),
258                                0,
259                                RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY,
260                                RTEMS_NO_PRIORITY,
261                                &tty->rawInBufSemaphore);
262                        if (sc != RTEMS_SUCCESSFUL)
263                                rtems_fatal_error_occurred (sc);
264                }
265
266                /*
267                 * Set default parameters
268                 */
269                tty->termios.c_iflag = BRKINT | ICRNL | IXON | IMAXBEL;
270                tty->termios.c_oflag = OPOST | ONLCR | XTABS;
271                tty->termios.c_cflag = B9600 | CS8 | CREAD;
272                tty->termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOK | ECHOE | ECHOCTL;
273                tty->termios.c_cc[VINTR] = '\003';
274                tty->termios.c_cc[VQUIT] = '\034';
275                tty->termios.c_cc[VERASE] = '\177';
276                tty->termios.c_cc[VKILL] = '\025';
277                tty->termios.c_cc[VEOF] = '\004';
278                tty->termios.c_cc[VEOL] = '\000';
279                tty->termios.c_cc[VEOL2] = '\000';
280                tty->termios.c_cc[VSTART] = '\021';
281                tty->termios.c_cc[VSTOP] = '\023';
282                tty->termios.c_cc[VSUSP] = '\032';
283                tty->termios.c_cc[VREPRINT] = '\022';
284                tty->termios.c_cc[VDISCARD] = '\017';
285                tty->termios.c_cc[VWERASE] = '\027';
286                tty->termios.c_cc[VLNEXT] = '\026';
287
288                /*
289                 * Device-specific open
290                 */
291                if (tty->device.firstOpen)
292                        (*tty->device.firstOpen)(major, minor, arg);
293
294                /*
295                 * Bump name characer
296                 */
297                if (c++ == 'z')
298                        c = 'a';
299        }
300        tty->refcount++;
301        args->iop->data1 = tty;
302        rtems_semaphore_release (ttyMutex);
303        return RTEMS_SUCCESSFUL;
304}
305
306/*
307 * Drain output queue
308 */
309static void
310drainOutput (struct rtems_termios_tty *tty)
311{
312        rtems_interrupt_level level;
313        rtems_status_code sc;
314
315        if (tty->device.outputUsesInterrupts) {
316                rtems_interrupt_disable (level);
317                while (tty->rawOutBufTail != tty->rawOutBufHead) {
318                        tty->rawOutBufState = rob_wait;
319                        rtems_interrupt_enable (level);
320                        sc = rtems_semaphore_obtain (tty->rawOutBufSemaphore,
321                                                        RTEMS_WAIT,
322                                                        RTEMS_NO_TIMEOUT);
323                        if (sc != RTEMS_SUCCESSFUL)
324                                rtems_fatal_error_occurred (sc);
325                        rtems_interrupt_disable (level);
326                }
327                rtems_interrupt_enable (level);
328        }
329}
330
331rtems_status_code
332rtems_termios_close (void *arg)
333{
334        rtems_libio_open_close_args_t *args = arg;
335        struct rtems_termios_tty *tty = args->iop->data1;
336        rtems_status_code sc;
337
338        sc = rtems_semaphore_obtain (ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
339        if (sc != RTEMS_SUCCESSFUL)
340                rtems_fatal_error_occurred (sc);
341        if (--tty->refcount == 0) {
342                drainOutput (tty);
343                if (tty->device.lastClose)
344                         (*tty->device.lastClose)(tty->major, tty->minor, arg);
345                if (tty->forw == NULL)
346                        ttyTail = tty->back;
347                else
348                        tty->forw->back = tty->back;
349                if (tty->back == NULL)
350                        ttyHead = tty->forw;
351                else
352                        tty->back->forw = tty->forw;
353                rtems_semaphore_delete (tty->isem);
354                rtems_semaphore_delete (tty->osem);
355                rtems_semaphore_delete (tty->rawOutBufSemaphore);
356                if (!tty->device.pollRead)
357                        rtems_semaphore_delete (tty->rawInBufSemaphore);
358                free (tty);
359        }
360        rtems_semaphore_release (ttyMutex);
361        return RTEMS_SUCCESSFUL;
362}
363
364rtems_status_code
365rtems_termios_ioctl (void *arg)
366{
367        rtems_libio_ioctl_args_t *args = arg;
368        struct rtems_termios_tty *tty = args->iop->data1;
369        rtems_status_code sc;
370
371        args->ioctl_return = 0;
372        sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
373        if (sc != RTEMS_SUCCESSFUL) {
374                args->ioctl_return = sc;
375                return sc;
376        }
377        switch (args->command) {
378        default:
379                sc = RTEMS_INVALID_NUMBER;
380                break;
381
382        case RTEMS_IO_GET_ATTRIBUTES:
383                *(struct termios *)args->buffer = tty->termios;
384                break;
385
386        case RTEMS_IO_SET_ATTRIBUTES:
387                tty->termios = *(struct termios *)args->buffer;
388                if (tty->termios.c_lflag & ICANON) {
389                        tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
390                        tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
391                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
392                }
393                else {
394                        rtems_interval ticksPerSecond;
395                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
396                        tty->vtimeTicks = tty->termios.c_cc[VTIME] * ticksPerSecond / 10;
397                        if (tty->termios.c_cc[VTIME]) {
398                                tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
399                                tty->rawInBufSemaphoreTimeout = tty->vtimeTicks;
400                                if (tty->termios.c_cc[VMIN])
401                                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
402                                else
403                                        tty->rawInBufSemaphoreFirstTimeout = tty->vtimeTicks;
404                        }
405                        else {
406                                if (tty->termios.c_cc[VMIN]) {
407                                        tty->rawInBufSemaphoreOptions = RTEMS_WAIT;
408                                        tty->rawInBufSemaphoreTimeout = RTEMS_NO_TIMEOUT;
409                                        tty->rawInBufSemaphoreFirstTimeout = RTEMS_NO_TIMEOUT;
410                                }
411                                else {
412                                        tty->rawInBufSemaphoreOptions = RTEMS_NO_WAIT;
413                                }
414                        }
415                }
416                if (tty->device.setAttributes)
417                        (*tty->device.setAttributes)(tty->minor, &tty->termios);
418                break;
419
420        case RTEMS_IO_TCDRAIN:
421                drainOutput (tty);
422                break;
423        }
424        rtems_semaphore_release (tty->osem);
425        args->ioctl_return = sc;
426        return sc;
427}
428
429/*
430 * Send characters to device-specific code
431 */
432static void
433osend (const char *buf, int len, struct rtems_termios_tty *tty)
434{
435        unsigned int newHead;
436        rtems_interrupt_level level;
437        rtems_status_code sc;
438
439        if (!tty->device.outputUsesInterrupts) {
440                (*tty->device.write)(tty->minor, buf, len);
441                return;
442        }
443        newHead = tty->rawOutBufHead;
444        while (len) {
445                /*
446                 * Performance improvement could be made here.
447                 * Copy multiple bytes to raw buffer:
448                 * if (len > 1) && (space to buffer end, or tail > 1)
449                 *      ncopy = MIN (len, space to buffer end or tail)
450                 *      memcpy (raw buffer, buf, ncopy)
451                 *      buf += ncopy
452                 *      len -= ncopy
453                 *
454                 * To minimize latency, the memcpy should be done
455                 * with interrupts enabled.
456                 */
457                newHead = (newHead + 1) % RAW_OUTPUT_BUFFER_SIZE;
458                rtems_interrupt_disable (level);
459                while (newHead == tty->rawOutBufTail) {
460                        tty->rawOutBufState = rob_wait;
461                        rtems_interrupt_enable (level);
462                        sc = rtems_semaphore_obtain (tty->rawOutBufSemaphore,
463                                                        RTEMS_WAIT,
464                                                        RTEMS_NO_TIMEOUT);
465                        if (sc != RTEMS_SUCCESSFUL)
466                                rtems_fatal_error_occurred (sc);
467                        rtems_interrupt_disable (level);
468                }
469                tty->rawOutBuf[tty->rawOutBufHead] = *buf++;
470                tty->rawOutBufHead = newHead;
471                if (tty->rawOutBufState == rob_idle) {
472                        (*tty->device.write)(tty->minor,
473                                (char *)&tty->rawOutBuf[tty->rawOutBufTail], 1);
474                        tty->rawOutBufState = rob_busy;
475                }
476                rtems_interrupt_enable (level);
477                len--;
478        }
479}
480
481/*
482 * Handle output processing
483 */
484static void
485oproc (unsigned char c, struct rtems_termios_tty *tty)
486{
487        int     i;
488
489        if (tty->termios.c_oflag & OPOST) {
490                switch (c) {
491                case '\n':
492                        if (tty->termios.c_oflag & ONLRET)
493                                tty->column = 0;
494                        if (tty->termios.c_oflag & ONLCR) {
495                                osend ("\r", 1, tty);
496                                tty->column = 0;
497                        }
498                        break;
499
500                case '\r':
501                        if ((tty->termios.c_oflag & ONOCR) && (tty->column == 0))
502                                return;
503                        if (tty->termios.c_oflag & OCRNL) {
504                                c = '\n';
505                                if (tty->termios.c_oflag & ONLRET)
506                                        tty->column = 0;
507                                break;
508                        }
509                        tty->column = 0;
510                        break;
511
512                case '\t':
513                        i = 8 - (tty->column & 7);
514                        if ((tty->termios.c_oflag & TABDLY) == XTABS) {
515                                tty->column += i;
516                                osend ( "        ",  i, tty);
517                                return;
518                        }
519                        tty->column += i;
520                        break;
521
522                case '\b':
523                        if (tty->column > 0)
524                                tty->column--;
525                        break;
526
527                default:
528                        if (tty->termios.c_oflag & OLCUC)
529                                c = toupper(c);
530                        if (!iscntrl(c))
531                                tty->column++;
532                        break;
533                }
534        }
535        osend (&c, 1, tty);
536}
537
538rtems_status_code
539rtems_termios_write (void *arg)
540{
541        rtems_libio_rw_args_t *args = arg;
542        struct rtems_termios_tty *tty = args->iop->data1;
543        rtems_status_code sc;
544
545        sc = rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
546        if (sc != RTEMS_SUCCESSFUL)
547                return sc;
548        if (tty->termios.c_oflag & OPOST) {
549                unsigned32 count = args->count;
550                unsigned8 *buffer = args->buffer;
551                while (count--)
552                        oproc (*buffer++, tty);
553                args->bytes_moved = args->count;
554        }
555        else {
556                osend (args->buffer, args->count, tty);
557                args->bytes_moved = args->count;
558        }
559        rtems_semaphore_release (tty->osem);
560        return sc;
561}
562
563/*
564 * Echo a typed character
565 */
566static void
567echo (unsigned char c, struct rtems_termios_tty *tty)
568{
569        if ((tty->termios.c_lflag & ECHOCTL) && iscntrl(c) && (c != '\t') && (c != '\n')) {
570                char echobuf[2];
571
572                echobuf[0] = '^';
573                echobuf[1] = c ^ 0x40;
574                osend (echobuf, 2, tty);
575                tty->column += 2;
576        }
577        else {
578                oproc (c, tty);
579        }
580}
581
582/*
583 * Erase a character or line
584 * FIXME: Needs support for WERASE and ECHOPRT.
585 * FIXME: Some of the tests should check for IEXTEN, too.
586 */
587static void
588erase (struct rtems_termios_tty *tty, int lineFlag)
589{
590        if (tty->ccount == 0)
591                return;
592        if (lineFlag) {
593                if (!(tty->termios.c_lflag & ECHO)) {
594                        tty->ccount = 0;
595                        return;
596                }
597                if (!(tty->termios.c_lflag & ECHOE)) {
598                        tty->ccount = 0;
599                        echo (tty->termios.c_cc[VKILL], tty);
600                        if (tty->termios.c_lflag & ECHOK)
601                                echo ('\n', tty);
602                        return;
603                }
604        }
605        while (tty->ccount) {
606                unsigned char c = tty->cbuf[--tty->ccount];
607
608                if (tty->termios.c_lflag & ECHO) {
609                        if (!lineFlag && !(tty->termios.c_lflag & ECHOE)) {
610                                echo (tty->termios.c_cc[VERASE], tty);
611                        }
612                        else if (c == '\t') {
613                                int col = tty->read_start_column;
614                                int i = 0;
615
616                                /*
617                                 * Find the character before the tab
618                                 */
619                                while (i != tty->ccount) {
620                                        c = tty->cbuf[i++];
621                                        if (c == '\t') {
622                                                col = (col | 7) + 1;
623                                        }
624                                        else if (iscntrl (c)) {
625                                                if (tty->termios.c_lflag & ECHOCTL)
626                                                        col += 2;
627                                        }
628                                        else {
629                                                col++;
630                                        }
631                                }
632
633                                /*
634                                 * Back up over the tab
635                                 */
636                                while (tty->column > col) {
637                                        osend ("\b", 1, tty);
638                                        tty->column--;
639                                }
640                        }
641                        else {
642                                if (iscntrl (c) && (tty->termios.c_lflag & ECHOCTL)) {
643                                        osend ("\b \b", 3, tty);
644                                        if (tty->column)
645                                                tty->column--;
646                                }
647                                if (!iscntrl (c) || (tty->termios.c_lflag & ECHOCTL)) {
648                                        osend ("\b \b", 3, tty);
649                                        if (tty->column)
650                                                tty->column--;
651                                }
652                        }
653                }
654                if (!lineFlag)
655                        break;
656        }
657}
658
659/*
660 * Process a single input character
661 */
662static int
663iproc (unsigned char c, struct rtems_termios_tty *tty)
664{
665        if (tty->termios.c_iflag & ISTRIP)
666                c &= 0x7f;
667        if (tty->termios.c_iflag & IUCLC)
668                c = tolower (c);
669        if (c == '\r') {
670                if (tty->termios.c_iflag & IGNCR)
671                        return 0;
672                if (tty->termios.c_iflag & ICRNL)
673                        c = '\n';
674        }
675        else if ((c == '\n') && (tty->termios.c_iflag & INLCR)) {
676                c = '\r';
677        }
678        if ((c != '\0') && (tty->termios.c_lflag & ICANON)) {
679                if (c == tty->termios.c_cc[VERASE]) {
680                        erase (tty, 0);
681                        return 0;
682                }
683                else if (c == tty->termios.c_cc[VKILL]) {
684                        erase (tty, 1);
685                        return 0;
686                }
687                else if (c == tty->termios.c_cc[VEOF]) {
688                        return 1;
689                }
690                else if (c == '\n') {
691                        if (tty->termios.c_lflag & (ECHO | ECHONL))
692                                echo (c, tty);
693                        tty->cbuf[tty->ccount++] = c;
694                        return 1;
695                }
696                else if ((c == tty->termios.c_cc[VEOL])
697                      || (c == tty->termios.c_cc[VEOL2])) {
698                        if (tty->termios.c_lflag & ECHO)
699                                echo (c, tty);
700                        tty->cbuf[tty->ccount++] = c;
701                        return 1;
702                }
703        }
704
705        /*
706         * FIXME: Should do IMAXBEL handling somehow
707         */
708        if (tty->ccount < (CBUFSIZE-1)) {
709                if (tty->termios.c_lflag & ECHO)
710                        echo (c, tty);
711                tty->cbuf[tty->ccount++] = c;
712        }
713        return 0;
714}
715
716/*
717 * Process input character, with semaphore.
718 */
719static int
720siproc (unsigned char c, struct rtems_termios_tty *tty)
721{
722        int i;
723
724        /*
725         * Obtain output semaphore if character will be echoed
726         */
727        if (tty->termios.c_lflag & (ECHO|ECHOE|ECHOK|ECHONL|ECHOPRT|ECHOCTL|ECHOKE)) {
728                rtems_semaphore_obtain (tty->osem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
729                i = iproc (c, tty);
730                rtems_semaphore_release (tty->osem);
731        }
732        else {
733                i = iproc (c, tty);
734        }
735        return i;
736}
737
738/*
739 * Fill the input buffer by polling the device
740 */
741static rtems_status_code
742fillBufferPoll (struct rtems_termios_tty *tty)
743{
744        int n;
745
746        if (tty->termios.c_lflag & ICANON) {
747                for (;;) {
748                        n = (*tty->device.pollRead)(tty->minor);
749                        if (n < 0) {
750                                rtems_task_wake_after (1);
751                        }
752                        else {
753                                if  (siproc (n, tty))
754                                        break;
755                        }
756                }
757        }
758        else {
759                rtems_interval then, now;
760                if (!tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
761                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &then);
762                for (;;) {
763                        n = (*tty->device.pollRead)(tty->minor);
764                        if (n < 0) {
765                                if (tty->termios.c_cc[VMIN]) {
766                                        if (tty->termios.c_cc[VTIME] && tty->ccount) {
767                                                rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
768                                                if ((now - then) > tty->vtimeTicks) {
769                                                        break;
770                                                }
771                                        }
772                                }
773                                else {
774                                        if (!tty->termios.c_cc[VTIME])
775                                                break;
776                                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
777                                        if ((now - then) > tty->vtimeTicks) {
778                                                break;
779                                        }
780                                }
781                                rtems_task_wake_after (1);
782                        }
783                        else {
784                                siproc (n, tty);
785                                if (tty->ccount >= tty->termios.c_cc[VMIN])
786                                        break;
787                                if (tty->termios.c_cc[VMIN] && tty->termios.c_cc[VTIME])
788                                        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &then);
789                        }
790                }
791        }
792        return RTEMS_SUCCESSFUL;
793}
794
795/*
796 * Fill the input buffer from the raw input queue
797 */
798static rtems_status_code
799fillBufferQueue (struct rtems_termios_tty *tty)
800{
801        rtems_interval timeout = tty->rawInBufSemaphoreFirstTimeout;
802        rtems_status_code sc;
803
804        for (;;) {
805                /*
806                 * Process characters read from raw queue
807                 */
808                while (tty->rawInBufHead != tty->rawInBufTail) {
809                        unsigned char c;
810                        unsigned int newHead;
811
812                        newHead = (tty->rawInBufHead + 1) % RAW_INPUT_BUFFER_SIZE;
813                        c = tty->rawInBuf[newHead];
814                        tty->rawInBufHead = newHead;
815                        if (tty->termios.c_lflag & ICANON) {
816                                if  (siproc (c, tty))
817                                        return RTEMS_SUCCESSFUL;
818                        }
819                        else {
820                                siproc (c, tty);
821                                if (tty->ccount >= tty->termios.c_cc[VMIN])
822                                        return RTEMS_SUCCESSFUL;
823                        }
824                        timeout = tty->rawInBufSemaphoreTimeout;
825                }
826
827                /*
828                 * Wait for characters
829                 */
830                sc = rtems_semaphore_obtain (tty->rawInBufSemaphore,
831                                                tty->rawInBufSemaphoreOptions,
832                                                timeout);
833                if (sc != RTEMS_SUCCESSFUL)
834                        break;
835        }
836        return RTEMS_SUCCESSFUL;
837}
838
839rtems_status_code
840rtems_termios_read (void *arg)
841{
842        rtems_libio_rw_args_t *args = arg;
843        struct rtems_termios_tty *tty = args->iop->data1;
844        unsigned32 count = args->count;
845        unsigned8 *buffer = args->buffer;
846        rtems_status_code sc;
847
848        sc = rtems_semaphore_obtain (tty->isem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
849        if (sc != RTEMS_SUCCESSFUL)
850                return sc;
851        if (tty->cindex == tty->ccount) {
852                tty->cindex = tty->ccount = 0;
853                tty->read_start_column = tty->column;
854                if (tty->device.pollRead)
855                        sc = fillBufferPoll (tty);
856                else
857                        sc = fillBufferQueue (tty);
858                if (sc != RTEMS_SUCCESSFUL)
859                        tty->cindex = tty->ccount = 0;
860        }
861        while (count && (tty->cindex < tty->ccount)) {
862                *buffer++ = tty->cbuf[tty->cindex++];
863                count--;
864        }
865        args->bytes_moved = args->count - count;
866        rtems_semaphore_release (tty->isem);
867        return sc;
868}
869
870/*
871 * Place characters on raw queue.
872 * NOTE: This routine runs in the context of the
873 *       device receive interrupt handler.
874 * Returns the number of characters dropped because of overlow.
875 */
876int
877rtems_termios_enqueue_raw_characters (void *ttyp, char *buf, int len)
878{
879        struct rtems_termios_tty *tty = ttyp;
880        unsigned int newTail;
881
882        while (len) {
883                newTail = (tty->rawInBufTail + 1) % RAW_INPUT_BUFFER_SIZE;
884                if (newTail == tty->rawInBufHead) {
885                        tty->rawInBufDropped += len;
886                        break;
887                }
888                tty->rawInBuf[newTail] = *buf++;
889                len--;
890                tty->rawInBufTail = newTail;
891        }
892        rtems_semaphore_release (tty->rawInBufSemaphore);
893        return len;
894}
895
896/*
897 * Characters have been transmitted
898 * NOTE: This routine runs in the context of the
899 *       device transmit interrupt handler.
900 * The second argument is the number of characters transmitted so far.
901 * This value will always be 1 for devices which generate an interrupt
902 * for each transmitted character.
903 */
904void
905rtems_termios_dequeue_characters (void *ttyp, int len)
906{
907        struct rtems_termios_tty *tty = ttyp;
908        unsigned int newTail;
909        int nToSend;
910
911        if (tty->rawOutBufState == rob_wait)
912                rtems_semaphore_release (tty->rawOutBufSemaphore);
913        if ( tty->rawOutBufHead == tty->rawOutBufTail )
914                return;
915        newTail = (tty->rawOutBufTail + len) % RAW_OUTPUT_BUFFER_SIZE;
916        if (newTail == tty->rawOutBufHead) {
917                /*
918                 * Buffer empty
919                 */
920                tty->rawOutBufState = rob_idle;
921        }
922        else {
923                /*
924                 * Buffer not empty, start tranmitter
925                 */
926                if (newTail > tty->rawOutBufHead)
927                        nToSend = RAW_OUTPUT_BUFFER_SIZE - newTail;
928                else
929                        nToSend = tty->rawOutBufHead - newTail;
930                (*tty->device.write)(tty->minor, (char *)&tty->rawOutBuf[newTail], nToSend);
931                tty->rawOutBufState = rob_busy;
932        }
933        tty->rawOutBufTail = newTail;
934}
Note: See TracBrowser for help on using the repository browser.