source: rtems/c/src/exec/libcsupport/src/termios.c @ 2adcb4dd

4.104.114.84.95
Last change on this file since 2adcb4dd was 2adcb4dd, checked in by Joel Sherrill <joel.sherrill@…>, on 06/13/00 at 21:41:45

Temporary hack to compile.

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