source: vendor/emx/current/src/os2/termio.c

Last change on this file was 18, checked in by bird, 22 years ago

Initial revision

  • Property cvs2svn:cvs-rev set to 1.1
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 32.3 KB
Line 
1/* termio.c -- General terminal interface
2 Copyright (c) 1993-1998 by Eberhard Mattes
3
4This file is part of emx.
5
6emx is free software; you can redistribute it and/or modify it
7under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11emx is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with emx; see the file COPYING. If not, write to
18the Free Software Foundation, 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA.
20
21As special exception, emx.dll can be distributed without source code
22unless it has been changed. If you modify emx.dll, this exception
23no longer applies and you must remove this paragraph from all source
24files for emx.dll. */
25
26
27#define INCL_DOSPROCESS
28#define INCL_DOSEXCEPTIONS
29#define INCL_DOSDEVICES
30#define INCL_DOSDEVIOCTL
31#define INCL_DOSSEMAPHORES
32#define INCL_DOSDATETIME
33#define INCL_DOSERRORS
34#define INCL_KBD
35#include <os2emx.h>
36#include "emxdll.h"
37#include "files.h"
38#include "clib.h"
39#include <sys/errno.h>
40#include <sys/fcntl.h>
41#include <sys/termio.h> /* SysV */
42#include <sys/ioctl.h> /* _TCSANOW, _TCSADRAIN, and _TCSAFLUSH */
43#include <termios.h> /* POSIX.1 */
44#include <sys/signal.h>
45
46#define NULL ((void *)0)
47
48#define KBD_SLEEP 50
49
50
51/* Semaphores. */
52
53HEV kbd_sem_cont;
54HEV kbd_sem_stop;
55HEV kbd_sem_time;
56HMUX kbd_sem_mux;
57HMTX kbd_sem_access;
58HEV kbd_sem_new;
59
60char kbd_started;
61
62static char kbd_buf[1024];
63static unsigned kbd_ptr_in;
64static unsigned kbd_ptr_out;
65static char kbd_kill_flag;
66static char kbd_stop_flag;
67static char kbd_ext_flag;
68static KBDKEYINFO kbd_inp;
69static TID kbd_tid;
70
71static char kbd_idelete;
72static int kbd_cc_intr;
73static int kbd_cc_quit;
74
75
76static void kbd_thread (ULONG arg);
77
78
79/* Start the keyboard thread. */
80
81static void start_keyboard (void)
82{
83 ULONG rc;
84 SEMRECORD semrec[3];
85
86 create_event_sem (&kbd_sem_new, DC_SEM_SHARED);
87 create_event_sem (&kbd_sem_cont, 0);
88 create_event_sem (&kbd_sem_stop, 0);
89 create_event_sem (&kbd_sem_time, DC_SEM_SHARED);
90
91 semrec[0].hsemCur = (HSEM)kbd_sem_new;
92 semrec[0].ulUser = 0;
93 semrec[1].hsemCur = (HSEM)kbd_sem_time;
94 semrec[1].ulUser = 1;
95 semrec[2].hsemCur = (HSEM)signal_sem;
96 semrec[2].ulUser = 2;
97 create_muxwait_sem (&kbd_sem_mux, 3, semrec, DCMW_WAIT_ANY);
98 create_mutex_sem (&kbd_sem_access);
99
100 kbd_ptr_in = kbd_ptr_out = 0;
101
102 rc = DosCreateThread (&kbd_tid, kbd_thread, 0,
103 CREATE_READY | STACK_COMMITTED, 0x4000);
104 if (rc != 0) error (rc, "DosCreateThread");
105 kbd_started = TRUE;
106}
107
108
109/* Set default values for a termio structure.
110
111 Note that the default values for c_iflag, c_lflag and c_cc differ
112 from Unix. */
113
114void init_termio (ULONG handle)
115{
116 my_file *d;
117
118 if (!IS_VALID_FILE (handle)) return;
119 d = GET_FILE (handle);
120 d->c_iflag = BRKINT | ICRNL | IXON | IXANY;
121 d->c_oflag = 0;
122 d->c_cflag = B9600 | CS8 | CREAD | HUPCL;
123 d->c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | IDEFAULT;
124 d->c_cc[VINTR] = 0x03; /* Ctrl-C */
125 d->c_cc[VQUIT] = 0x1c; /* Ctrl-\ */
126 d->c_cc[VERASE] = 0x08; /* Ctrl-H */
127 d->c_cc[VKILL] = 0x15; /* Ctrl-U */
128 d->c_cc[VEOF] = 0x04; /* Ctrl-D */
129 d->c_cc[VEOL] = 0; /* Disabled */
130 d->c_cc[VMIN] = 6;
131 d->c_cc[VTIME] = 1;
132 d->c_cc[VSUSP] = 0; /* Disabled */
133 d->c_cc[VSTOP] = 0x13; /* Ctrl-S */
134 d->c_cc[VSTART] = 0x11; /* Ctrl-Q */
135 d->tio_escape = 0;
136 d->tio_buf_count = 0;
137}
138
139
140/* Return the number of characters in the keyboard buffer. */
141
142ULONG kbd_avail (void)
143{
144 ULONG result;
145
146 request_mutex (kbd_sem_access);
147 if (kbd_ptr_in >= kbd_ptr_out)
148 result = kbd_ptr_in - kbd_ptr_out;
149 else
150 result = sizeof (kbd_buf) - (kbd_ptr_out - kbd_ptr_in);
151 DosReleaseMutexSem (kbd_sem_access);
152 return result;
153}
154
155
156/* Put character C into the keyboard buffer. Return true if
157 successful. */
158
159int kbd_put (UCHAR c)
160{
161 int result;
162 unsigned i;
163
164 request_mutex (kbd_sem_access);
165 i = (kbd_ptr_in + 1) % sizeof (kbd_buf);
166 if (i == kbd_ptr_out)
167 {
168 /* Buffer full. Clear the buffer (like Unix) and return FALSE. */
169
170 kbd_ptr_in = kbd_ptr_out = 0;
171 result = FALSE;
172 }
173 else
174 {
175 kbd_buf[kbd_ptr_in] = c;
176 kbd_ptr_in = i;
177 DosPostEventSem (kbd_sem_new);
178 result = TRUE;
179 }
180 DosReleaseMutexSem (kbd_sem_access);
181 return result;
182}
183
184
185static UCHAR kbd_get (void)
186{
187 UCHAR c;
188
189 if (kbd_ext_flag)
190 {
191 kbd_ext_flag = FALSE;
192 return kbd_inp.chScan;
193 }
194
195 do
196 {
197 KbdCharIn (&kbd_inp, IO_NOWAIT, 0);
198 if (kbd_kill_flag)
199 DosExit (EXIT_THREAD, 0);
200 if (kbd_inp.fbStatus == 0)
201 {
202 if (kbd_stop_flag)
203 {
204 reset_event_sem (kbd_sem_cont);
205 DosPostEventSem (kbd_sem_stop);
206 DosWaitEventSem (kbd_sem_cont, SEM_INDEFINITE_WAIT);
207 }
208 else
209 DosSleep (KBD_SLEEP);
210 }
211 } while (!(kbd_inp.fbStatus & KBDTRF_FINAL_CHAR_IN));
212
213 c = kbd_inp.chChar;
214
215 if (kbd_idelete && kbd_inp.chScan == 14)
216 {
217 /* Swap BS and DEL. */
218
219 if (c == 0x08)
220 return 0x7f;
221 else if (c == 0x7f)
222 return 0x08;
223 }
224
225 /* Return extended codes for Alt-Space and Ctrl-Space. */
226
227 if (c == ' ' && kbd_inp.chScan == 57)
228 {
229 if (kbd_inp.fsState & KBDSTF_ALT)
230 {
231 kbd_inp.chScan = 57; /* Alt-Space */
232 kbd_ext_flag = TRUE;
233 return 0;
234 }
235 if (kbd_inp.fsState & KBDSTF_CONTROL)
236 {
237 kbd_inp.chScan = 2; /* Ctrl-Space */
238 kbd_ext_flag = TRUE;
239 return 0;
240 }
241 }
242
243 if (c == 0xe0)
244 {
245 if (!(kbd_inp.fbStatus & KBDTRF_EXTENDED_CODE))
246 return c;
247
248 if (kbd_inp.fsState & (KBDSTF_RIGHTSHIFT | KBDSTF_LEFTSHIFT))
249 {
250 if (kbd_inp.chScan == 82) /* Ins */
251 kbd_inp.chScan = 4; /* Shift-Ins */
252 else if (kbd_inp.chScan == 83) /* Del */
253 kbd_inp.chScan = 5; /* Shift-Del */
254 }
255 kbd_ext_flag = TRUE;
256 return 0;
257 }
258
259 if (c == 0)
260 kbd_ext_flag = TRUE;
261 return c;
262}
263
264
265/* Stuff COUNT characters at SRC into the keyboard buffer. The first
266 character of SRC will be read first, later. Return true if
267 successful. */
268
269static int kbd_stuff (const char *src, unsigned count)
270{
271 int result;
272 unsigned i;
273
274 request_mutex (kbd_sem_access);
275 if (count == 0)
276 result = TRUE;
277 else
278 {
279 i = kbd_ptr_out;
280 while (count != 0)
281 {
282 if (i == 0)
283 i = sizeof (kbd_buf) - 1;
284 else
285 --i;
286 if (i == kbd_ptr_in)
287 break;
288 kbd_ptr_out = i;
289 kbd_buf[i] = src[--count];
290 }
291 result = (count == 0);
292 }
293 DosReleaseMutexSem (kbd_sem_access);
294 return result;
295}
296
297
298/* Flush the keyboard buffer. */
299
300static void kbd_flush (my_file *d)
301{
302 if (kbd_started)
303 request_mutex (kbd_sem_access);
304 KbdFlushBuffer (0);
305 kbd_ptr_in = kbd_ptr_out = 0;
306 if (d != NULL)
307 d->tio_buf_count = 0;
308 if (kbd_started)
309 DosReleaseMutexSem (kbd_sem_access);
310}
311
312
313/* Switch the keyboard to binary mode. */
314
315static void kbd_bin_mode (void)
316{
317 static KBDINFO ki; /* Must not cross a 64K boundary */
318
319 ki.cb = sizeof (ki);
320 ki.fsMask = KEYBOARD_ECHO_OFF | KEYBOARD_BINARY_MODE;
321 ki.chTurnAround = (USHORT)'\r';
322 ki.fsInterim = 0;
323 ki.fsState = 0;
324 KbdSetStatus (&ki, 0);
325}
326
327
328void kbd_stop (void)
329{
330 static KBDINFO ki; /* Must not cross a 64K boundary */
331
332 if (kbd_started && !kbd_stop_flag)
333 {
334 reset_event_sem (kbd_sem_stop);
335 kbd_stop_flag = TRUE;
336 DosWaitEventSem (kbd_sem_stop, SEM_INDEFINITE_WAIT);
337
338 ki.cb = sizeof (ki);
339 ki.fsMask = KEYBOARD_ECHO_ON | KEYBOARD_ASCII_MODE;
340 ki.chTurnAround = (USHORT)'\r';
341 ki.fsInterim = 0;
342 ki.fsState = 0;
343 KbdSetStatus (&ki, 0);
344 }
345}
346
347
348void kbd_restart (void)
349{
350 if (kbd_started)
351 {
352 kbd_bin_mode ();
353 if (kbd_stop_flag)
354 {
355 kbd_stop_flag = FALSE;
356 DosPostEventSem (kbd_sem_cont);
357 }
358 }
359}
360
361
362/* Exception handler for kbd_thread(). On termination of the thread,
363 kbd_tid will be set to zero. */
364
365static ULONG kbd_exception (EXCEPTIONREPORTRECORD *report,
366 EXCEPTIONREGISTRATIONRECORD *registration,
367 CONTEXTRECORD *context,
368 void *dummy)
369{
370 if (report->fHandlerFlags & (EH_EXIT_UNWIND|EH_UNWINDING))
371 return XCPT_CONTINUE_SEARCH;
372 switch (report->ExceptionNum)
373 {
374 case XCPT_ASYNC_PROCESS_TERMINATE: /* This is the correct one */
375 case XCPT_PROCESS_TERMINATE: /* OS/2 bug */
376 kbd_tid = 0;
377 break;
378 }
379 return XCPT_CONTINUE_SEARCH;
380}
381
382
383static void kbd_thread (ULONG arg)
384{
385 UCHAR c;
386 EXCEPTIONREGISTRATIONRECORD registration;
387
388 registration.prev_structure = NULL;
389 registration.ExceptionHandler = kbd_exception;
390 DosSetExceptionHandler (&registration);
391
392 kbd_bin_mode ();
393 for (;;)
394 {
395 c = kbd_get ();
396 if (c == 0)
397 {
398 /* Handle scan codes: either store both characters or none. */
399
400 c = kbd_get ();
401 if (kbd_put (0))
402 kbd_put (c);
403 continue;
404 }
405
406 /* Raise signal if ISIG is set and the character matches VINTR
407 or VQUIT. */
408
409 if (kbd_cc_intr != 0 && c == kbd_cc_intr)
410 {
411 generate_signal (threads[1], SIGINT);
412 continue;
413 }
414 else if (kbd_cc_quit != 0 && c == kbd_cc_quit)
415 {
416 generate_signal (threads[1], SIGQUIT);
417 continue;
418 }
419
420 /* Put the character into the keyboard buffer. */
421
422 kbd_put (c);
423 }
424}
425
426
427void term_termio (void)
428{
429 kbd_kill_flag = TRUE;
430 if (kbd_tid != 0)
431 DosKillThread (kbd_tid);
432}
433
434
435#define CHAR_SIG 0x0100
436#define CHAR_NO 0x0101
437#define CHAR_TIME 0x0102
438
439
440/* Read a character from the keyboard.
441
442 If no character is available and WAITP is false, return CHAR_NO.
443 If WAITP is true, wait until a character is available or a signal
444 occurs. Return CHAR_SIG if interrupted by a signal. Return
445 CHAR_TIME if timed out. Otherwise, return the character. */
446
447static unsigned read_keyboard (int waitp)
448{
449 ULONG rc, user;
450 unsigned c;
451 thread_data *td;
452
453 td = get_thread ();
454 for (;;)
455 {
456 if (sig_flag)
457 return CHAR_SIG;
458 reset_event_sem (kbd_sem_new);
459 request_mutex (kbd_sem_access);
460 if (kbd_ptr_out == kbd_ptr_in)
461 c = CHAR_NO;
462 else
463 {
464 c = kbd_buf[kbd_ptr_out];
465 kbd_ptr_out = (kbd_ptr_out + 1) % sizeof (kbd_buf);
466 }
467 DosReleaseMutexSem (kbd_sem_access);
468 if (c != CHAR_NO || !waitp)
469 return c;
470 rc = DosWaitMuxWaitSem (kbd_sem_mux, SEM_INDEFINITE_WAIT, &user);
471 if (rc == ERROR_INTERRUPT)
472 return CHAR_SIG;
473 if (rc != 0)
474 error (rc, "DosWaitMuxWaitSem");
475 if (user == 1)
476 return CHAR_TIME;
477 }
478}
479
480
481/* Read a character from an asynchronous serial device.
482
483 If no character is available and WAITP is false, return CHAR_NO.
484 If WAITP is true, wait until a character is available or a signal
485 occurs. Return CHAR_SIG if interrupted by a signal. Return
486 CHAR_TIME if timed out. Otherwise, return the character. */
487
488static unsigned read_async (ULONG handle, int waitp)
489{
490 ULONG rc, nread;
491 UCHAR c;
492 thread_data *td;
493
494 td = get_thread ();
495 if (sig_flag)
496 return CHAR_SIG;
497 /* TODO: waitp */
498 /* TODO: time out */
499 rc = DosRead (handle, &c, 1, &nread);
500 if (rc == ERROR_INTERRUPT)
501 return CHAR_SIG;
502 if (rc != 0)
503 return CHAR_NO;
504 return c;
505}
506
507
508ULONG async_avail (ULONG handle)
509{
510 ULONG rc, data_length;
511 RXQUEUE data;
512
513 data_length = sizeof (data);
514 rc = DosDevIOCtl (handle, IOCTL_ASYNC, ASYNC_GETINQUECOUNT,
515 NULL, 0, NULL,
516 &data, data_length, &data_length);
517 if (rc != 0)
518 return 0;
519 return data.cch;
520}
521
522
523ULONG async_writable (ULONG handle)
524{
525 ULONG rc, data_length;
526 RXQUEUE data;
527
528 data_length = sizeof (data);
529 rc = DosDevIOCtl (handle, IOCTL_ASYNC, ASYNC_GETOUTQUECOUNT,
530 NULL, 0, NULL,
531 &data, data_length, &data_length);
532 if (rc != 0 || data.cch > data.cb)
533 return 0;
534 return data.cb - data.cch;
535}
536
537
538ULONG termio_avail (ULONG handle)
539{
540
541 if (handle_flags[handle] & HF_CON)
542 {
543 if (GET_FILE (handle)->c_lflag & IDEFAULT)
544 return 0;
545 else
546 return kbd_avail ();
547 }
548 else if (handle_flags[handle] & HF_ASYNC)
549 return async_avail (handle);
550 else
551 return 0;
552}
553
554
555static void tr_out (my_file *d, UCHAR c)
556{
557 ULONG cbwritten;
558
559 DosWrite (d->tio_output_handle, &c, 1, &cbwritten);
560}
561
562
563/* Get and preprocess a character for termio input.
564
565 If no character is available and WAITP is false, return CHAR_NO.
566 If WAITP is true, wait until a character is available or a signal
567 occurs. Return CHAR_SIG if interrupted by a signal. Return
568 CHAR_TIME if timed out. Otherwise, return the character. */
569
570static unsigned tr_get (my_file *d, int waitp)
571{
572 unsigned c;
573
574 do
575 {
576 if (d->flags & HF_ASYNC)
577 c = read_async (d->tio_input_handle, waitp);
578 else
579 c = read_keyboard (waitp);
580 if (c & ~0xff)
581 return c;
582 if (d->c_iflag & ISTRIP)
583 c &= 0x7f;
584 } while ((d->c_iflag & IGNCR) && c == '\r');
585
586 if ((d->c_iflag & INLCR) && c == '\n')
587 c = '\r';
588 else if ((d->c_iflag & ICRNL) && c == '\r')
589 c = '\n';
590
591 if ((d->c_iflag & IUCLC) && (c >= 'A' && c <= 'Z'))
592 c += 'a' - 'A';
593
594 return c;
595}
596
597
598static void tr_canon_echo (my_file *d, UCHAR c)
599{
600 if (d->c_lflag & ECHO)
601 {
602 if (c == '\n')
603 {
604 tr_out (d, '\r');
605 tr_out (d, '\n');
606 }
607 else
608 tr_out (d, c);
609 }
610}
611
612
613static void tr_back (my_file *d)
614{
615 ULONG cbwritten;
616
617 --d->tio_buf_count;
618 DosWrite (d->tio_output_handle, "\b \b", 3, &cbwritten);
619}
620
621
622/* Note that this function must not flush the buffer on overflow.
623 Code calling tr_canon_copy() depends on this assumption. */
624
625static void tr_put (my_file *d, UCHAR c)
626{
627 if (d->tio_buf_count < d->tio_buf_size)
628 d->tio_buf[d->tio_buf_count++] = c;
629}
630
631
632static void tr_canon_put (my_file *d, UCHAR c)
633{
634 d->tio_escape = FALSE;
635 tr_put (d, c);
636 tr_canon_echo (d, c);
637}
638
639
640static void tr_canon_esc_put (my_file *d, UCHAR c)
641{
642 tr_out (d, '\b');
643 tr_canon_put (d, c);
644}
645
646
647static int tr_canon_copy (my_file *d, char *dst, unsigned count, int *errnop)
648{
649 unsigned n;
650
651 n = (count < d->tio_buf_count ? count : d->tio_buf_count);
652 memcpy (dst, d->tio_buf + d->tio_buf_idx, n);
653 d->tio_buf_count -= n; d->tio_buf_idx += n;
654 *errnop = 0;
655 return n;
656}
657
658
659static int tr_canon_read (my_file *d, char *dst, unsigned count, int *errnop)
660{
661 unsigned c;
662
663 /* If there's (a remainder of) a line in the buffer, return up to
664 COUNT bytes from the buffer. */
665
666 if (d->tio_buf_count != 0)
667 return tr_canon_copy (d, dst, count, errnop);
668
669 /* Read another line. If the end of a line (or EOF) is read, call
670 tr_canon_copy() to copy (part of) the line to DST. */
671
672 d->tio_buf_idx = 0;
673 d->tio_escape = FALSE;
674 for (;;)
675 {
676 c = tr_get (d, TRUE);
677 if (c == CHAR_SIG)
678 {
679 /* Interrupted by a signal. */
680
681 termio_flush (d->tio_input_handle);
682 *errnop = EINTR;
683 return -1;
684 }
685 else if (c == 0)
686 {
687 /* We got the 0 character which precedes a scan code.
688 Ignore both characters. */
689
690 c = tr_get (d, TRUE);
691 if (c == CHAR_SIG)
692 {
693 /* Interrupted by a signal. */
694
695 kbd_flush (d);
696 *errnop = EINTR;
697 return -1;
698 }
699 }
700 else if (c == '\n' || c == d->c_cc[VEOL])
701 {
702 /* End of line. Return the buffered line, which contains at
703 least the end-of-line character. */
704 /* TODO: escape? */
705
706 tr_put (d, c);
707 tr_canon_echo (d, c);
708 return tr_canon_copy (d, dst, count, errnop);
709 }
710 else if (c == d->c_cc[VEOF])
711 {
712 /* End of file. Return the buffered line. */
713
714 if (d->tio_escape)
715 tr_canon_esc_put (d, c);
716 else if (d->tio_buf_count == 0)
717 {
718 *errnop = 0;
719 return 0;
720 }
721 else
722 return tr_canon_copy (d, dst, count, errnop);
723 }
724 else if (c == d->c_cc[VERASE])
725 {
726 /* Erase character. Delete one character. */
727
728 if (d->tio_escape)
729 tr_canon_esc_put (d, c);
730 else if (d->tio_buf_count != 0)
731 tr_back (d);
732 }
733 else if (c == d->c_cc[VKILL])
734 {
735 /* Kill character. Delete all characters typed on this line. */
736
737 if (d->tio_escape)
738 tr_canon_esc_put (d, c);
739 else
740 {
741 d->tio_buf_count = 0;
742 if (d->c_lflag & ECHOK)
743 {
744 tr_out (d, '\r');
745 tr_out (d, '\n');
746 }
747 }
748 }
749 else if (d->tio_escape)
750 {
751 d->tio_escape = FALSE;
752 tr_put (d, '\\');
753 tr_put (d, c);
754 tr_canon_echo (d, c);
755 }
756 else if (c == '\\')
757 {
758 d->tio_escape = TRUE;
759 tr_canon_echo (d, c);
760 }
761 else
762 tr_canon_put (d, c);
763 }
764}
765
766
767static int tr_raw_read (my_file *d, char *dst, unsigned count,
768 int *errnop)
769{
770 int result, timer_flag, vmin, vtime;
771 unsigned c;
772 ULONG rc;
773 HTIMER timer;
774
775 vmin = d->c_cc[VMIN];
776 vtime = d->c_cc[VTIME];
777
778 /* If O_NDELAY is set, rule out all cases where we have to wait. */
779
780 if ((d->flags & HF_NDELAY) && (vmin != 0 || vtime != 0))
781 {
782 ULONG avail;
783
784 avail = termio_avail (d->tio_input_handle);
785 if (avail < count && avail < (vmin != 0 ? vmin : 1))
786 {
787 *errnop = EAGAIN;
788 return -1;
789 }
790 }
791
792 result = 0;
793 timer_flag = FALSE;
794 if (d->flags & HF_CON)
795 reset_event_sem (kbd_sem_time);
796
797 /* Start the timer if VMIN == 0 and VTIME > 0. */
798
799 if (vmin == 0 && vtime != 0 && (d->flags & HF_CON))
800 {
801 rc = DosAsyncTimer ((1000 / 10) * vtime, (HSEM)kbd_sem_time, &timer);
802 if (rc != 0) error (rc, "DosAsyncTimer");
803 timer_flag = TRUE;
804 vmin = 1;
805 }
806
807 /* Read the character until the buffer is empty, enough characters
808 have been read, a signal occurs, or a time out occurs. */
809
810 *errnop = 0;
811 while (count != 0)
812 {
813 c = tr_get (d, result < vmin);
814 if (c == CHAR_NO || c == CHAR_TIME)
815 break;
816
817 if (c == CHAR_SIG)
818 {
819 /* Interrupted by a signal. Do not flush keyboard buffer.
820 Rather copy the keys back to the keyboard buffer. If
821 there isn't enough room, flush the keyboard buffer. */
822
823 if (result != 0 && !kbd_stuff (dst - result, result))
824 kbd_flush (d);
825
826 *errnop = EINTR; result = -1;
827 break;
828 }
829
830 /* TODO: Handle 0, scan code pairs. */
831
832 *dst++ = (char)c;
833 ++result; --count;
834 tr_canon_echo (d, c);
835
836 /* Restart timer for VMIN > 0 and VTIME > 0. */
837
838 if (count != 0 && d->c_cc[VMIN] != 0 && vtime != 0
839 && (d->flags & HF_CON))
840 {
841 if (timer_flag)
842 {
843 DosStopTimer (timer);
844 timer_flag = FALSE;
845 }
846 reset_event_sem (kbd_sem_time);
847 rc = DosAsyncTimer ((1000 / 10) * vtime, (HSEM)kbd_sem_time, &timer);
848 if (rc != 0) error (rc, "DosAsyncTimer");
849 timer_flag = TRUE;
850 }
851 }
852 if (timer_flag)
853 {
854 DosStopTimer (timer);
855 /* Reset the semaphore, in case ICANON is activated. */
856 reset_event_sem (kbd_sem_time);
857 }
858 return result;
859}
860
861
862/* Unix-like read() for the terminal. DST is the destination address,
863 COUNT is the number of bytes to read. Return the number of bytes
864 read or, on error, -1. Store the error number to *ERRNOP.
865
866 Currently this function is implemented for the keyboard only. */
867
868int termio_read (ULONG handle, char *dst, unsigned count, int *errnop)
869{
870 int result;
871 my_file *d;
872
873 sig_block_start ();
874
875 /* Update the file description's flag bits with the flag bits of the
876 handle. */
877
878 d = GET_FILE (handle);
879 d->flags = handle_flags[handle];
880 d->tio_input_handle = handle;
881 d->tio_output_handle = (d->flags & HF_CON ? 1 : handle);
882
883 if (count == 0)
884 {
885 *errnop = 0;
886 result = 0;
887 }
888 else if (d->c_lflag & ICANON)
889 result = tr_canon_read (d, dst, count, errnop);
890 else
891 result = tr_raw_read (d, dst, count, errnop);
892 sig_block_end ();
893 return result;
894}
895
896
897void termio_flush (ULONG handle)
898{
899 my_file *d;
900
901 if (IS_VALID_FILE (handle))
902 d = GET_FILE (handle);
903 else
904 d = NULL;
905 if (handle_flags[handle] & HF_CON)
906 kbd_flush (d);
907 else
908 {
909 /* TODO: ASYNC */
910 }
911}
912
913
914static void async_set (ULONG handle, ULONG new_cflag, ULONG new_iflag,
915 const UCHAR *new_cc)
916{
917 my_file *d;
918 ULONG rc, parm_length;
919 SETEXTBAUDRATE speed;
920 LINECONTROL lctl;
921 DCBINFO dcb;
922
923 d = GET_FILE (handle);
924 if (new_cflag != d->c_cflag)
925 {
926 lctl = d->x.async.lctl;
927 if ((new_cflag & CBAUD) != (d->c_cflag & CBAUD))
928 {
929 speed.ulRate = d->x.async.speed.ulCurrentRate;
930 switch (new_cflag & CBAUD)
931 {
932 case B0:
933 /* TODO: Hang up */
934 break;
935 case B50:
936 speed.ulRate = 50; break;
937 case B75:
938 speed.ulRate = 75; break;
939 case B110:
940 speed.ulRate = 110; break;
941 case B134:
942 speed.ulRate = 134; break;
943 case B150:
944 speed.ulRate = 150; break;
945 case B200:
946 speed.ulRate = 200; break;
947 case B300:
948 speed.ulRate = 300; break;
949 case B600:
950 speed.ulRate = 600; break;
951 case B1200:
952 speed.ulRate = 1200; break;
953 case B1800:
954 speed.ulRate = 1800; break;
955 case B2400:
956 speed.ulRate = 2400; break;
957 case B4800:
958 speed.ulRate = 4800; break;
959 case B9600:
960 speed.ulRate = 9600; break;
961 case B19200:
962 speed.ulRate = 19200; break;
963 case B38400:
964 speed.ulRate = 38400; break;
965 }
966 if (speed.ulRate != d->x.async.speed.ulCurrentRate)
967 {
968 /* Set the bit rate. */
969
970 parm_length = sizeof (speed);
971 rc = DosDevIOCtl (handle, IOCTL_ASYNC, ASYNC_SETEXTBAUDRATE,
972 &speed, parm_length, &parm_length,
973 NULL, 0, NULL);
974 d->x.async.speed.ulCurrentRate = speed.ulRate;
975 }
976 }
977
978 /* Character size */
979
980 switch (new_cflag & CSIZE)
981 {
982 case CS5:
983 lctl.bDataBits = 5; break;
984 case CS6:
985 lctl.bDataBits = 6; break;
986 case CS7:
987 lctl.bDataBits = 7; break;
988 case CS8:
989 lctl.bDataBits = 8; break;
990 }
991
992 /* Parity none/odd/even */
993
994 if ((new_cflag & (PARENB|PARODD)) != (d->c_cflag & (PARENB|PARODD)))
995 {
996 if (!(new_cflag & PARENB))
997 lctl.bParity = 0; /* No parity */
998 else if (new_cflag & PARODD)
999 lctl.bParity = 1; /* Odd parity */
1000 else
1001 lctl.bParity = 2; /* Even parity */
1002 }
1003
1004 /* Stop bits */
1005
1006 if ((new_cflag & CSTOPB) != (d->c_cflag & CSTOPB))
1007 {
1008 if (new_cflag & CSTOPB)
1009 lctl.bStopBits = 2; /* 2 stop bits */
1010 else
1011 lctl.bStopBits = 0; /* 1 stop bit */
1012 }
1013
1014 /* Stop bits vs. character size */
1015
1016 if (lctl.bDataBits == 5 && lctl.bStopBits == 2)
1017 lctl.bStopBits = 1;
1018 else if (lctl.bDataBits != 5 && lctl.bStopBits == 1)
1019 lctl.bStopBits = 2;
1020
1021 if (memcmp (&lctl, &d->x.async.lctl.bDataBits, sizeof (lctl)) != 0)
1022 {
1023 /* Set the line characteristics. */
1024
1025 parm_length = sizeof (lctl);
1026 rc = DosDevIOCtl (handle, IOCTL_ASYNC, ASYNC_SETLINECTRL,
1027 &lctl, parm_length, &parm_length,
1028 NULL, 0, NULL);
1029 d->x.async.lctl = lctl;
1030 }
1031 }
1032
1033 /* Note: There are no VSTOP and VSTART entries in `struct termio'! */
1034
1035 if (new_iflag != d->c_iflag
1036 || new_cc[VMIN] != d->c_cc[VMIN]
1037 || new_cc[VTIME] != d->c_cc[VTIME])
1038 {
1039 dcb = d->x.async.dcb;
1040 if ((new_iflag & IXON) != (d->c_iflag & IXON))
1041 {
1042 if (new_iflag & IXON)
1043 dcb.fbFlowReplace |= 1;
1044 else
1045 dcb.fbFlowReplace &= ~1;
1046 }
1047 if ((new_iflag & IXOFF) != (d->c_iflag & IXOFF))
1048 {
1049 if (new_iflag & IXOFF)
1050 dcb.fbFlowReplace |= 2;
1051 else
1052 dcb.fbFlowReplace &= ~2;
1053 }
1054 if (new_cc[VMIN] != d->c_cc[VMIN]
1055 || new_cc[VTIME] != d->c_cc[VTIME])
1056 {
1057 if (new_cc[VMIN] == 0)
1058 {
1059 if (new_cc[VTIME] == 0)
1060 dcb.fbTimeout = (dcb.fbTimeout & ~6) | 6;
1061 else
1062 {
1063 dcb.fbTimeout = (dcb.fbTimeout & ~6) | 4;
1064 dcb.usReadTimeout = new_cc[VTIME] * 10 - 1;
1065 }
1066 }
1067 else
1068 {
1069 dcb.fbTimeout = (dcb.fbTimeout & ~6) | 2;
1070 dcb.usReadTimeout = new_cc[VTIME] * 10 - 1;
1071 }
1072 }
1073 if (memcmp (&dcb, &d->x.async.dcb, sizeof (dcb)) != 0)
1074 {
1075 /* Set the DCB */
1076
1077 parm_length = sizeof (dcb);
1078 rc = DosDevIOCtl (handle, IOCTL_ASYNC, ASYNC_SETDCBINFO,
1079 &dcb, parm_length, &parm_length,
1080 NULL, 0, NULL);
1081 d->x.async.dcb = dcb;
1082 }
1083 }
1084}
1085
1086
1087static void termio_set_common (ULONG handle, my_file *d)
1088{
1089 if (handle_flags[handle] & HF_CON)
1090 {
1091 kbd_idelete = (d->c_iflag & IDELETE) != 0;
1092 if (d->c_lflag & ISIG)
1093 {
1094 kbd_cc_intr = d->c_cc[VINTR];
1095 kbd_cc_quit = d->c_cc[VQUIT];
1096 }
1097 else
1098 kbd_cc_intr = kbd_cc_quit = 0;
1099
1100 if (d->c_lflag & IDEFAULT)
1101 {
1102 if (kbd_started)
1103 kbd_stop ();
1104 }
1105 else if (kbd_started)
1106 kbd_restart ();
1107 else
1108 start_keyboard ();
1109 }
1110}
1111
1112
1113void termio_set (ULONG handle, const struct termio *tio)
1114{
1115 my_file *d;
1116
1117 d = GET_FILE (handle);
1118 if (!(tio->c_lflag & IDEFAULT) && d->tio_buf == NULL)
1119 {
1120 d->tio_buf_size = 1024;
1121 d->tio_buf = checked_private_alloc (d->tio_buf_size);
1122 }
1123
1124 if (handle_flags[handle] & HF_ASYNC)
1125 async_set (handle, tio->c_cflag, tio->c_iflag, tio->c_cc);
1126
1127 d->c_iflag = tio->c_iflag;
1128 d->c_oflag = tio->c_oflag;
1129 d->c_cflag = tio->c_cflag;
1130 d->c_lflag = tio->c_lflag;
1131 memcpy (d->c_cc, tio->c_cc, NCC);
1132 d->c_cc[VSUSP] = 0; /* Disabled */
1133 d->c_cc[VSTOP] = 0x13; /* Ctrl-S */
1134 d->c_cc[VSTART] = 0x11; /* Ctrl-Q */
1135
1136 termio_set_common (handle, d);
1137}
1138
1139
1140void termio_get (ULONG handle, struct termio *tio)
1141{
1142 const my_file *d;
1143
1144 d = GET_FILE (handle);
1145 tio->c_iflag = d->c_iflag;
1146 tio->c_oflag = d->c_oflag;
1147 tio->c_cflag = d->c_cflag;
1148 tio->c_lflag = d->c_lflag;
1149 tio->c_line = 0;
1150 memcpy (tio->c_cc, d->c_cc, NCC);
1151}
1152
1153
1154void termios_get (ULONG handle, struct termios *tio)
1155{
1156 const my_file *d;
1157
1158 d = GET_FILE (handle);
1159 tio->c_iflag = d->c_iflag;
1160 tio->c_oflag = d->c_oflag;
1161 tio->c_cflag = d->c_cflag;
1162 tio->c_lflag = d->c_lflag & ~IDEFAULT;
1163 memcpy (tio->c_cc, d->c_cc, NCCS);
1164}
1165
1166
1167void termios_set (ULONG handle, const struct termios *tio)
1168{
1169 my_file *d;
1170
1171 d = GET_FILE (handle);
1172 if (d->tio_buf == NULL)
1173 {
1174 d->tio_buf_size = 1024;
1175 d->tio_buf = checked_private_alloc (d->tio_buf_size);
1176 }
1177
1178 if (handle_flags[handle] & HF_ASYNC)
1179 async_set (handle, tio->c_cflag, tio->c_iflag, tio->c_cc);
1180
1181 d->c_iflag = tio->c_iflag;
1182 d->c_oflag = tio->c_oflag;
1183 d->c_cflag = tio->c_cflag;
1184 d->c_lflag = tio->c_lflag & ~IDEFAULT;
1185 memcpy (d->c_cc, tio->c_cc, NCCS);
1186
1187 termio_set_common (handle, d);
1188}
1189
1190
1191ULONG query_async (ULONG handle)
1192{
1193 ULONG rc;
1194 ULONG data_length;
1195 my_file *d;
1196
1197 d = GET_FILE (handle);
1198
1199 /* Retrieve the bit rate. */
1200
1201 memset (&d->x.async.speed, 0, sizeof (d->x.async.speed));
1202 data_length = sizeof (d->x.async.speed);
1203 rc = DosDevIOCtl (handle, IOCTL_ASYNC, ASYNC_GETEXTBAUDRATE,
1204 NULL, 0, NULL,
1205 &d->x.async.speed, data_length, &data_length);
1206 if (rc != 0) return rc;
1207
1208 /* Retrieve the line characteristics. */
1209
1210 memset (&d->x.async.lctl, 0, sizeof (d->x.async.lctl));
1211 data_length = sizeof (d->x.async.lctl);
1212 rc = DosDevIOCtl (handle, IOCTL_ASYNC, ASYNC_GETLINECTRL,
1213 NULL, 0, NULL,
1214 &d->x.async.lctl, data_length, &data_length);
1215 if (rc != 0) return rc;
1216
1217 /* Retrieve the DCB parameters. */
1218
1219 memset (&d->x.async.dcb, 0, sizeof (d->x.async.dcb));
1220 data_length = sizeof (d->x.async.dcb);
1221 rc = DosDevIOCtl (handle, IOCTL_ASYNC, ASYNC_GETDCBINFO,
1222 NULL, 0, NULL,
1223 &d->x.async.dcb, data_length, &data_length);
1224 return rc;
1225}
1226
1227
1228/* Translate values for termio. */
1229
1230ULONG translate_async (ULONG handle)
1231{
1232 ULONG cflag;
1233 my_file *d;
1234
1235 d = GET_FILE (handle);
1236 cflag = 0;
1237 switch (d->x.async.speed.ulCurrentRate)
1238 {
1239 case 50: cflag |= B50; break;
1240 case 75: cflag |= B75; break;
1241 case 110: cflag |= B110; break;
1242 case 134: cflag |= B134; break;
1243 case 150: cflag |= B150; break;
1244 case 200: cflag |= B200; break;
1245 case 300: cflag |= B300; break;
1246 case 600: cflag |= B600; break;
1247 case 1200: cflag |= B1200; break;
1248 case 1800: cflag |= B1800; break;
1249 case 2400: cflag |= B2400; break;
1250 case 4800: cflag |= B4800; break;
1251 case 9600: cflag |= B9600; break;
1252 case 19200: cflag |= B19200; break;
1253 case 38400: cflag |= B38400; break;
1254 default: cflag |= B1200; break;
1255 }
1256
1257 switch (d->x.async.lctl.bDataBits)
1258 {
1259 case 5: cflag |= CS5; break;
1260 case 6: cflag |= CS6; break;
1261 case 7: cflag |= CS7; break;
1262 case 8: cflag |= CS8; break;
1263 default: return ERROR_GEN_FAILURE;
1264 }
1265 switch (d->x.async.lctl.bParity)
1266 {
1267 case 1: cflag |= PARENB|PARODD; break; /* Odd parity */
1268 case 2: cflag |= PARENB; break; /* Even parity */
1269 case 3: break; /* Mark parity */
1270 case 4: break; /* Space parity */
1271 default: break;
1272 }
1273 switch (d->x.async.lctl.bStopBits)
1274 {
1275 case 0: break; /* 1 stop bit */
1276 case 1: cflag |= CSTOPB; /* 1.5 stop bits (CS5 only) */
1277 case 2: cflag |= CSTOPB; /* 2 stop bits (all but CS5) */
1278 default: break;
1279 }
1280 cflag |= CREAD; /* Enable receiver */
1281 cflag |= HUPCL; /* Hang up on last close */
1282
1283 /* Note: IGNPAR, INPCK, and PARMRK are not supported; parity
1284 checking is always enabled and characters with wrong parity are
1285 always read. */
1286
1287 /* TODO: CLOCAL, HUPCL? */
1288
1289 init_termio (handle);
1290 d = GET_FILE (handle);
1291 d->c_cflag = cflag;
1292 d->c_lflag = IDEFAULT;
1293 d->c_cc[VSTOP] = d->x.async.dcb.bXOFFChar;
1294 d->c_cc[VSTART] = d->x.async.dcb.bXONChar;
1295
1296 /* Automatic transmit flow control (XON/XOFF) */
1297
1298 if (d->x.async.dcb.fbFlowReplace & 1)
1299 d->c_iflag |= IXON;
1300 else
1301 d->c_iflag &= ~IXON;
1302
1303 /* Automatic receive flow control (XON/XOFF) */
1304
1305 if (d->x.async.dcb.fbFlowReplace & 2)
1306 d->c_iflag |= IXOFF;
1307 else
1308 d->c_iflag &= ~IXOFF;
1309
1310 /* Time out processing. Note that dcb.usReadTimeout contains the
1311 number of hundredths of a second, minus 1. That is, 0 means 0.01
1312 seconds. */
1313
1314 switch (d->x.async.dcb.fbTimeout & 6)
1315 {
1316 case 2: /* Normal */
1317 d->c_cc[VMIN] = 255; /* Approximation to infinity */
1318 d->c_cc[VTIME] = (d->x.async.dcb.usReadTimeout > 2550 - 1
1319 ? 255 : (d->x.async.dcb.usReadTimeout + 1 + 9) / 10);
1320 break;
1321
1322 case 4: /* Wait for something */
1323 d->c_cc[VMIN] = 0;
1324 d->c_cc[VTIME] = (d->x.async.dcb.usReadTimeout > 2550 - 1
1325 ? 255 : (d->x.async.dcb.usReadTimeout + 1 + 9) / 10);
1326 break;
1327
1328 case 6: /* No-Wait */
1329 default: /* Should not happen */
1330 d->c_cc[VMIN] = 0;
1331 d->c_cc[VTIME] = 0;
1332 break;
1333 }
1334 return 0;
1335}
Note: See TracBrowser for help on using the repository browser.