source: rtems/cpukit/libcsupport/src/termios.c @ 5eb7da97

4.104.114.84.95
Last change on this file since 5eb7da97 was 5eb7da97, checked in by Joel Sherrill <joel.sherrill@…>, on 04/01/99 at 16:20:03

Disable IXON by default based on comment from Eric Norum
<e.norum@…> and concerns from Thomas Doerfler
<td@…> when he submitted the patch:

Since enabling XON/XOFF has such a major performance hit on `smart' output
devices I think it should be *off* by default. I think some thought should
be given to adding hooks for hardware that can support XON/XOFF without
software intervention, or for hardware like the 68360 SCC's that can use
large buffers, but still handle special characters immediately.

The patch you sent is a very good start, though. I just think that the
software flow control should be off -- to match the way the serial I/O
support has worked up until now.

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