source: rtems/c/src/lib/libc/termios.c @ 5dae90a

4.104.114.84.95
Last change on this file since 5dae90a was 5adf355a, checked in by Joel Sherrill <joel.sherrill@…>, on 05/27/99 at 16:11:52

Split initialization and reserve resources from termios to reduce
size of mininum application.

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