source: rtems/c/src/lib/libc/termios.c @ 7f62220

Last change on this file since 7f62220 was 7307d943, checked in by Joel Sherrill <joel.sherrill@…>, on 10/16/01 at 20:52:57

2001-10-11 Mike Siers <mikes@…>

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