OpenCores
URL https://opencores.org/ocsvn/openrisc_2011-10-31/openrisc_2011-10-31/trunk

Subversion Repositories openrisc_2011-10-31

[/] [openrisc/] [trunk/] [rtos/] [rtems/] [c/] [src/] [lib/] [libc/] [termios.c] - Blame information for rev 509

Go to most recent revision | Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.