3 #include <minix/drivers.h>
4 #include <minix/driver.h>
6 #include <sys/ttycom.h>
7 #include <sys/ttydefaults.h>
13 #include <sys/select.h>
15 /* Address of a tty structure. */
16 #define tty_addr(line) (&tty_table[line])
18 /* Macros for magic tty structure pointers. */
19 #define FIRST_TTY tty_addr(0)
20 #define END_TTY tty_addr(sizeof(tty_table) / sizeof(tty_table[0]))
22 /* A device exists if at least its 'devread' function is defined. */
23 #define tty_active(tp) ((tp)->tty_devread != NULL)
25 struct kmessages kmess
;
27 static void tty_timed_out(minix_timer_t
*tp
);
28 static void settimer(tty_t
*tty_ptr
, int enable
);
29 static void in_transfer(tty_t
*tp
);
30 static int tty_echo(tty_t
*tp
, int ch
);
31 static void rawecho(tty_t
*tp
, int ch
);
32 static int back_over(tty_t
*tp
);
33 static void reprint(tty_t
*tp
);
34 static void dev_ioctl(tty_t
*tp
);
35 static void setattr(tty_t
*tp
);
36 static void tty_icancel(tty_t
*tp
);
38 static int do_open(devminor_t minor
, int access
, endpoint_t user_endpt
);
39 static int do_close(devminor_t minor
);
40 static ssize_t
do_read(devminor_t minor
, u64_t position
, endpoint_t endpt
,
41 cp_grant_id_t grant
, size_t size
, int flags
, cdev_id_t id
);
42 static ssize_t
do_write(devminor_t minor
, u64_t position
, endpoint_t endpt
,
43 cp_grant_id_t grant
, size_t size
, int flags
, cdev_id_t id
);
44 static int do_ioctl(devminor_t minor
, unsigned long request
, endpoint_t endpt
,
45 cp_grant_id_t grant
, int flags
, endpoint_t user_endpt
, cdev_id_t id
);
46 static int do_cancel(devminor_t minor
, endpoint_t endpt
, cdev_id_t id
);
47 static int do_select(devminor_t minor
, unsigned int ops
, endpoint_t endpt
);
49 static struct chardriver tty_tab
= {
51 .cdr_close
= do_close
,
53 .cdr_write
= do_write
,
54 .cdr_ioctl
= do_ioctl
,
55 .cdr_cancel
= do_cancel
,
56 .cdr_select
= do_select
59 /* Default attributes. */
60 static struct termios termios_defaults
= {
61 .c_iflag
= TTYDEF_IFLAG
,
62 .c_oflag
= TTYDEF_OFLAG
,
63 .c_cflag
= TTYDEF_CFLAG
,
64 .c_lflag
= TTYDEF_LFLAG
,
65 .c_ispeed
= TTYDEF_SPEED
,
66 .c_ospeed
= TTYDEF_SPEED
,
79 [VREPRINT
] = CREPRINT
,
81 [VDISCARD
] = CDISCARD
,
85 static struct winsize winsize_defaults
; /* = all zeroes */
87 /* Global variables for the TTY task (declared extern in tty.h). */
88 tty_t tty_table
[NR_PTYS
];
91 static void tty_startup(void);
92 static int tty_init(int, sef_init_info_t
*);
94 /*===========================================================================*
96 *===========================================================================*/
99 /* Main routine of the terminal task. */
101 message tty_mess
; /* buffer for all incoming messages */
110 /* Check for and handle any events on any of the ttys. */
111 for (tp
= FIRST_TTY
; tp
< END_TTY
; tp
++) {
112 if (tp
->tty_events
) handle_events(tp
);
115 /* Get a request message. */
116 r
= driver_receive(ANY
, &tty_mess
, &ipc_status
);
118 panic("driver_receive failed with: %d", r
);
120 if (is_ipc_notify(ipc_status
)) {
121 switch (_ENDPOINT_P(tty_mess
.m_source
)) {
123 /* run watchdogs of expired timers */
124 expire_timers(tty_mess
.m_notify
.timestamp
);
131 /* done, get new message */
135 if (!IS_CDEV_RQ(tty_mess
.m_type
)) {
136 chardriver_process(&tty_tab
, &tty_mess
, ipc_status
);
140 /* Only device requests should get to this point.
141 * All requests have a minor device number.
143 if (OK
!= chardriver_get_minor(&tty_mess
, &line
))
146 if ((line
>= PTYPX_MINOR
) && (line
< (PTYPX_MINOR
+ NR_PTYS
)) &&
147 tty_mess
.m_type
!= CDEV_IOCTL
) {
148 /* Terminals and pseudo terminals belong together. We can only
149 * make a distinction between the two based on position in the
150 * tty_table and not on minor number. Hence this special case.
152 do_pty(&tty_mess
, ipc_status
);
156 /* Execute the requested device driver function. */
157 chardriver_process(&tty_tab
, &tty_mess
, ipc_status
);
164 line2tty(devminor_t line
)
166 /* Convert a terminal line to tty_table pointer */
170 if ((line
- TTYPX_MINOR
) < NR_PTYS
) {
171 tp
= tty_addr(line
- TTYPX_MINOR
);
172 } else if ((line
- PTYPX_MINOR
) < NR_PTYS
) {
173 tp
= tty_addr(line
- PTYPX_MINOR
);
181 /*===========================================================================*
183 *===========================================================================*/
184 static void tty_startup(void)
186 /* Register init callbacks. */
187 sef_setcb_init_fresh(tty_init
);
188 sef_setcb_init_restart(tty_init
);
190 /* No live update support for now. */
192 /* No signal support for now. */
194 /* Let SEF perform startup. */
198 /*===========================================================================*
200 *===========================================================================*/
201 static ssize_t
do_read(devminor_t minor
, u64_t
UNUSED(position
),
202 endpoint_t endpt
, cp_grant_id_t grant
, size_t size
, int flags
,
205 /* A process wants to read from a terminal. */
209 if ((tp
= line2tty(minor
)) == NULL
)
212 /* Check if there is already a process hanging in a read, check if the
213 * parameters are correct, do I/O.
215 if (tp
->tty_incaller
!= NONE
|| tp
->tty_inleft
> 0)
220 /* Copy information from the message to the tty struct. */
221 tp
->tty_incaller
= endpt
;
223 tp
->tty_ingrant
= grant
;
224 assert(tp
->tty_incum
== 0);
225 tp
->tty_inleft
= size
;
227 if (!(tp
->tty_termios
.c_lflag
& ICANON
) && tp
->tty_termios
.c_cc
[VTIME
] > 0) {
228 if (tp
->tty_termios
.c_cc
[VMIN
] == 0) {
229 /* MIN & TIME specify a read timer that finishes the
230 * read in TIME/10 seconds if no bytes are available.
235 /* MIN & TIME specify an inter-byte timer that may
236 * have to be cancelled if there are no bytes yet.
238 if (tp
->tty_eotct
== 0) {
240 tp
->tty_min
= tp
->tty_termios
.c_cc
[VMIN
];
245 /* Anything waiting in the input buffer? Clear it out... */
247 /* ...then go back for more. */
249 if (tp
->tty_inleft
== 0)
250 return EDONTREPLY
; /* already done */
252 /* There were no bytes in the input queue available. */
253 if (flags
& CDEV_NONBLOCK
) {
255 r
= tp
->tty_incum
> 0 ? tp
->tty_incum
: EAGAIN
;
256 tp
->tty_inleft
= tp
->tty_incum
= 0;
257 tp
->tty_incaller
= NONE
;
261 if (tp
->tty_select_ops
)
264 return EDONTREPLY
; /* suspend the caller */
267 /*===========================================================================*
269 *===========================================================================*/
270 static ssize_t
do_write(devminor_t minor
, u64_t
UNUSED(position
),
271 endpoint_t endpt
, cp_grant_id_t grant
, size_t size
, int flags
,
274 /* A process wants to write on a terminal. */
278 if ((tp
= line2tty(minor
)) == NULL
)
281 /* Check if there is already a process hanging in a write, check if the
282 * parameters are correct, do I/O.
284 if (tp
->tty_outcaller
!= NONE
|| tp
->tty_outleft
> 0)
289 /* Copy message parameters to the tty structure. */
290 tp
->tty_outcaller
= endpt
;
292 tp
->tty_outgrant
= grant
;
293 assert(tp
->tty_outcum
== 0);
294 tp
->tty_outleft
= size
;
298 if (tp
->tty_outleft
== 0)
299 return EDONTREPLY
; /* already done */
301 /* None or not all the bytes could be written. */
302 if (flags
& CDEV_NONBLOCK
) {
303 r
= tp
->tty_outcum
> 0 ? tp
->tty_outcum
: EAGAIN
;
304 tp
->tty_outleft
= tp
->tty_outcum
= 0;
305 tp
->tty_outcaller
= NONE
;
309 if (tp
->tty_select_ops
)
312 return EDONTREPLY
; /* suspend the caller */
315 /*===========================================================================*
317 *===========================================================================*/
318 static int do_ioctl(devminor_t minor
, unsigned long request
, endpoint_t endpt
,
319 cp_grant_id_t grant
, int flags
, endpoint_t user_endpt
, cdev_id_t id
)
321 /* Perform an IOCTL on this terminal. POSIX termios calls are handled
322 * by the IOCTL system call.
327 if ((tp
= line2tty(minor
)) == NULL
)
333 /* Get the termios attributes. */
334 r
= sys_safecopyto(endpt
, grant
, 0, (vir_bytes
) &tp
->tty_termios
,
335 sizeof(struct termios
));
341 if (tp
->tty_outleft
> 0) {
342 if (flags
& CDEV_NONBLOCK
)
344 /* Wait for all ongoing output processing to finish. */
345 tp
->tty_iocaller
= endpt
;
347 tp
->tty_ioreq
= request
;
348 tp
->tty_iogrant
= grant
;
349 return EDONTREPLY
; /* suspend the caller */
351 if (request
== TIOCDRAIN
) break;
352 if (request
== TIOCSETAF
) tty_icancel(tp
);
355 /* Set the termios attributes. */
356 r
= sys_safecopyfrom(endpt
, grant
, 0, (vir_bytes
) &tp
->tty_termios
,
357 sizeof(struct termios
));
363 r
= sys_safecopyfrom(endpt
, grant
, 0, (vir_bytes
) &i
, sizeof(i
));
365 if(i
& FREAD
) { tty_icancel(tp
); }
366 if(i
& FWRITE
) { (*tp
->tty_ocancel
)(tp
, 0); }
369 tp
->tty_inhibited
= 0;
373 tp
->tty_inhibited
= 1;
376 case TIOCSBRK
: /* tcsendbreak - turn break on */
377 if (tp
->tty_break_on
!= NULL
) (*tp
->tty_break_on
)(tp
,0);
379 case TIOCCBRK
: /* tcsendbreak - turn break off */
380 if (tp
->tty_break_off
!= NULL
) (*tp
->tty_break_off
)(tp
,0);
384 r
= sys_safecopyto(endpt
, grant
, 0, (vir_bytes
) &tp
->tty_winsize
,
385 sizeof(struct winsize
));
389 r
= sys_safecopyfrom(endpt
, grant
, 0, (vir_bytes
) &tp
->tty_winsize
,
390 sizeof(struct winsize
));
391 sigchar(tp
, SIGWINCH
, 0);
393 case TIOCGETD
: /* get line discipline */
396 r
= sys_safecopyto(endpt
, grant
, 0,
397 (vir_bytes
) &disc
, (vir_bytes
) sizeof(disc
));
400 case TIOCSETD
: /* set line discipline */
401 printf("TTY: TIOCSETD: can't set any other line discipline.\n");
406 /* Process sets this tty as its controlling tty */
407 tp
->tty_pgrp
= user_endpt
;
410 /* These Posix functions are allowed to fail if _POSIX_JOB_CONTROL is
422 /*===========================================================================*
424 *===========================================================================*/
425 static int do_open(devminor_t minor
, int access
, endpoint_t user_endpt
)
427 /* A tty line has been opened. Make it the callers controlling tty if
428 * CDEV_NOCTTY is *not* set and it is not the log device. CDEV_CTTY is returned
429 * if the tty is made the controlling tty, otherwise OK or an error code.
434 if ((tp
= line2tty(minor
)) == NULL
)
437 if (!(access
& CDEV_NOCTTY
)) {
438 tp
->tty_pgrp
= user_endpt
;
442 if (tp
->tty_openct
== 1) {
443 /* Tell the device that the tty is opened */
444 (*tp
->tty_open
)(tp
, 0);
450 /*===========================================================================*
452 *===========================================================================*/
453 static int do_close(devminor_t minor
)
455 /* A tty line has been closed. Clean up the line if it is the last close. */
458 if ((tp
= line2tty(minor
)) == NULL
)
461 if (--tp
->tty_openct
== 0) {
464 (*tp
->tty_ocancel
)(tp
, 0);
465 (*tp
->tty_close
)(tp
, 0);
466 tp
->tty_termios
= termios_defaults
;
467 tp
->tty_winsize
= winsize_defaults
;
474 /*===========================================================================*
476 *===========================================================================*/
477 static int do_cancel(devminor_t minor
, endpoint_t endpt
, cdev_id_t id
)
479 /* A signal has been sent to a process that is hanging trying to read or write.
480 * The pending read or write must be finished off immediately.
485 if ((tp
= line2tty(minor
)) == NULL
)
488 /* Check the parameters carefully, to avoid cancelling twice. */
490 if (tp
->tty_inleft
!= 0 && endpt
== tp
->tty_incaller
&& id
== tp
->tty_inid
) {
491 /* Process was reading when killed. Clean up input. */
493 r
= tp
->tty_incum
> 0 ? tp
->tty_incum
: EAGAIN
;
494 tp
->tty_inleft
= tp
->tty_incum
= 0;
495 tp
->tty_incaller
= NONE
;
496 } else if (tp
->tty_outleft
!= 0 && endpt
== tp
->tty_outcaller
&&
497 id
== tp
->tty_outid
) {
498 /* Process was writing when killed. Clean up output. */
499 r
= tp
->tty_outcum
> 0 ? tp
->tty_outcum
: EAGAIN
;
500 tp
->tty_outleft
= tp
->tty_outcum
= 0;
501 tp
->tty_outcaller
= NONE
;
502 } else if (tp
->tty_ioreq
!= 0 && endpt
== tp
->tty_iocaller
&&
503 id
== tp
->tty_ioid
) {
504 /* Process was waiting for output to drain. */
507 tp
->tty_iocaller
= NONE
;
511 /* Only reply if we found a matching request. */
515 int select_try(struct tty
*tp
, int ops
)
519 /* Special case. If line is hung up, no operations will block.
520 * (and it can be seen as an exceptional condition.)
522 if (tp
->tty_termios
.c_ospeed
== B0
) {
526 if (ops
& CDEV_OP_RD
) {
527 /* will i/o not block on read? */
528 if (tp
->tty_inleft
> 0) {
529 ready_ops
|= CDEV_OP_RD
; /* EIO - no blocking */
530 } else if (tp
->tty_incount
> 0) {
531 /* Is a regular read possible? tty_incount
532 * says there is data. But a read will only succeed
533 * in canonical mode if a newline has been seen.
535 if (!(tp
->tty_termios
.c_lflag
& ICANON
) ||
537 ready_ops
|= CDEV_OP_RD
;
542 if (ops
& CDEV_OP_WR
) {
543 if (tp
->tty_outleft
> 0) ready_ops
|= CDEV_OP_WR
;
544 else if ((*tp
->tty_devwrite
)(tp
, 1)) ready_ops
|= CDEV_OP_WR
;
549 int select_retry(struct tty
*tp
)
553 if (tp
->tty_select_ops
&& (ops
= select_try(tp
, tp
->tty_select_ops
))) {
554 chardriver_reply_select(tp
->tty_select_proc
,
555 tp
->tty_select_minor
, ops
);
556 tp
->tty_select_ops
&= ~ops
;
561 /*===========================================================================*
563 *===========================================================================*/
564 static int do_select(devminor_t minor
, unsigned int ops
, endpoint_t endpt
)
567 int ready_ops
, watch
;
569 if ((tp
= line2tty(minor
)) == NULL
)
572 watch
= (ops
& CDEV_NOTIFY
);
573 ops
&= (CDEV_OP_RD
| CDEV_OP_WR
| CDEV_OP_ERR
);
575 ready_ops
= select_try(tp
, ops
);
579 /* Translated minor numbers are a problem with late select replies. We
580 * have to save the minor number used to do the select, since otherwise
581 * VFS won't be able to make sense of those late replies. We do not
582 * support selecting on two different minors for the same object.
584 if (tp
->tty_select_ops
!= 0 && tp
->tty_select_minor
!= minor
) {
585 printf("TTY: select on one object with two minors (%d, %d)\n",
586 tp
->tty_select_minor
, minor
);
589 tp
->tty_select_ops
|= ops
;
590 tp
->tty_select_proc
= endpt
;
591 tp
->tty_select_minor
= minor
;
597 /*===========================================================================*
599 *===========================================================================*/
600 void handle_events(tp
)
601 tty_t
*tp
; /* TTY to check for events. */
603 /* Handle any events pending on a TTY. */
608 /* Read input and perform input processing. */
609 (*tp
->tty_devread
)(tp
, 0);
611 /* Perform output processing and write output. */
612 (*tp
->tty_devwrite
)(tp
, 0);
614 /* Ioctl waiting for some event? */
615 if (tp
->tty_ioreq
!= 0) dev_ioctl(tp
);
616 } while (tp
->tty_events
);
618 /* Transfer characters from the input queue to a waiting process. */
621 /* Reply if enough bytes are available. */
622 if (tp
->tty_incum
>= tp
->tty_min
&& tp
->tty_inleft
> 0) {
623 chardriver_reply_task(tp
->tty_incaller
, tp
->tty_inid
, tp
->tty_incum
);
624 tp
->tty_inleft
= tp
->tty_incum
= 0;
625 tp
->tty_incaller
= NONE
;
627 if (tp
->tty_select_ops
)
631 select_retry_pty(tp
);
634 /*===========================================================================*
636 *===========================================================================*/
637 static void in_transfer(tp
)
638 register tty_t
*tp
; /* pointer to terminal to read from */
640 /* Transfer bytes from the input queue to a process reading from a terminal. */
646 /* Force read to succeed if the line is hung up, looks like EOF to reader. */
647 if (tp
->tty_termios
.c_ospeed
== B0
) tp
->tty_min
= 0;
649 /* Anything to do? */
650 if (tp
->tty_inleft
== 0 || tp
->tty_eotct
< tp
->tty_min
) return;
653 while (tp
->tty_inleft
> 0 && tp
->tty_eotct
> 0) {
654 ch
= *tp
->tty_intail
;
656 if (!(ch
& IN_EOF
)) {
657 /* One character to be delivered to the user. */
660 if (++bp
== bufend(buf
)) {
661 /* Temp buffer full, copy to user space. */
662 sys_safecopyto(tp
->tty_incaller
,
663 tp
->tty_ingrant
, tp
->tty_incum
,
664 (vir_bytes
) buf
, (vir_bytes
) buflen(buf
));
665 tp
->tty_incum
+= buflen(buf
);
670 /* Remove the character from the input queue. */
671 if (++tp
->tty_intail
== bufend(tp
->tty_inbuf
))
672 tp
->tty_intail
= tp
->tty_inbuf
;
676 /* Don't read past a line break in canonical mode. */
677 if (tp
->tty_termios
.c_lflag
& ICANON
) tp
->tty_inleft
= 0;
682 /* Leftover characters in the buffer. */
684 sys_safecopyto(tp
->tty_incaller
, tp
->tty_ingrant
, tp
->tty_incum
,
685 (vir_bytes
) buf
, (vir_bytes
) count
);
686 tp
->tty_incum
+= count
;
689 /* Usually reply to the reader, possibly even if incum == 0 (EOF). */
690 if (tp
->tty_inleft
== 0) {
691 chardriver_reply_task(tp
->tty_incaller
, tp
->tty_inid
, tp
->tty_incum
);
692 tp
->tty_inleft
= tp
->tty_incum
= 0;
693 tp
->tty_incaller
= NONE
;
697 /*===========================================================================*
699 *===========================================================================*/
700 int in_process(tp
, buf
, count
)
701 register tty_t
*tp
; /* terminal on which character has arrived */
702 char *buf
; /* buffer with input characters */
703 int count
; /* number of input characters */
705 /* Characters have just been typed in. Process, save, and echo them. Return
706 * the number of characters processed.
712 for (ct
= 0; ct
< count
; ct
++) {
713 /* Take one character. */
716 /* Strip to seven bits? */
717 if (tp
->tty_termios
.c_iflag
& ISTRIP
) ch
&= 0x7F;
719 /* Input extensions? */
720 if (tp
->tty_termios
.c_lflag
& IEXTEN
) {
722 /* Previous character was a character escape? */
723 if (tp
->tty_escaped
) {
724 tp
->tty_escaped
= NOT_ESCAPED
;
725 ch
|= IN_ESC
; /* protect character */
728 /* LNEXT (^V) to escape the next character? */
729 if (ch
== tp
->tty_termios
.c_cc
[VLNEXT
]) {
730 tp
->tty_escaped
= ESCAPED
;
733 continue; /* do not store the escape */
736 /* REPRINT (^R) to reprint echoed characters? */
737 if (ch
== tp
->tty_termios
.c_cc
[VREPRINT
]) {
743 /* _POSIX_VDISABLE is a normal character value, so better escape it. */
744 if (ch
== _POSIX_VDISABLE
) ch
|= IN_ESC
;
746 /* Map CR to LF, ignore CR, or map LF to CR. */
748 if (tp
->tty_termios
.c_iflag
& IGNCR
) continue;
749 if (tp
->tty_termios
.c_iflag
& ICRNL
) ch
= '\n';
752 if (tp
->tty_termios
.c_iflag
& INLCR
) ch
= '\r';
755 /* Canonical mode? */
756 if (tp
->tty_termios
.c_lflag
& ICANON
) {
758 /* Erase processing (rub out of last character). */
759 if (ch
== tp
->tty_termios
.c_cc
[VERASE
]) {
760 (void) back_over(tp
);
761 if (!(tp
->tty_termios
.c_lflag
& ECHOE
)) {
762 (void) tty_echo(tp
, ch
);
767 /* Kill processing (remove current line). */
768 if (ch
== tp
->tty_termios
.c_cc
[VKILL
]) {
769 while (back_over(tp
)) {}
770 if (!(tp
->tty_termios
.c_lflag
& ECHOE
)) {
771 (void) tty_echo(tp
, ch
);
772 if (tp
->tty_termios
.c_lflag
& ECHOK
)
778 /* EOF (^D) means end-of-file, an invisible "line break". */
779 if (ch
== tp
->tty_termios
.c_cc
[VEOF
]) ch
|= IN_EOT
| IN_EOF
;
781 /* The line may be returned to the user after an LF. */
782 if (ch
== '\n') ch
|= IN_EOT
;
784 /* Same thing with EOL, whatever it may be. */
785 if (ch
== tp
->tty_termios
.c_cc
[VEOL
]) ch
|= IN_EOT
;
788 /* Start/stop input control? */
789 if (tp
->tty_termios
.c_iflag
& IXON
) {
791 /* Output stops on STOP (^S). */
792 if (ch
== tp
->tty_termios
.c_cc
[VSTOP
]) {
793 tp
->tty_inhibited
= STOPPED
;
798 /* Output restarts on START (^Q) or any character if IXANY. */
799 if (tp
->tty_inhibited
) {
800 if (ch
== tp
->tty_termios
.c_cc
[VSTART
]
801 || (tp
->tty_termios
.c_iflag
& IXANY
)) {
802 tp
->tty_inhibited
= RUNNING
;
804 if (ch
== tp
->tty_termios
.c_cc
[VSTART
])
810 if (tp
->tty_termios
.c_lflag
& ISIG
) {
811 /* Check for INTR, QUIT and STATUS characters. */
813 if (ch
== tp
->tty_termios
.c_cc
[VINTR
])
815 else if(ch
== tp
->tty_termios
.c_cc
[VQUIT
])
817 else if(ch
== tp
->tty_termios
.c_cc
[VSTATUS
])
822 (void) tty_echo(tp
, ch
);
827 /* Is there space in the input buffer? */
828 if (tp
->tty_incount
== buflen(tp
->tty_inbuf
)) {
829 /* No space; discard in canonical mode, keep in raw mode. */
830 if (tp
->tty_termios
.c_lflag
& ICANON
) continue;
834 if (!(tp
->tty_termios
.c_lflag
& ICANON
)) {
835 /* In raw mode all characters are "line breaks". */
838 /* Start an inter-byte timer? */
839 if (!timeset
&& tp
->tty_termios
.c_cc
[VMIN
] > 0
840 && tp
->tty_termios
.c_cc
[VTIME
] > 0) {
846 /* Perform the intricate function of echoing. */
847 if (tp
->tty_termios
.c_lflag
& (ECHO
|ECHONL
)) ch
= tty_echo(tp
, ch
);
849 /* Save the character in the input queue. */
850 *tp
->tty_inhead
++ = ch
;
851 if (tp
->tty_inhead
== bufend(tp
->tty_inbuf
))
852 tp
->tty_inhead
= tp
->tty_inbuf
;
854 if (ch
& IN_EOT
) tp
->tty_eotct
++;
856 /* Try to finish input if the queue threatens to overflow. */
857 if (tp
->tty_incount
== buflen(tp
->tty_inbuf
)) in_transfer(tp
);
862 /*===========================================================================*
864 *===========================================================================*/
865 static int tty_echo(tp
, ch
)
866 register tty_t
*tp
; /* terminal on which to echo */
867 register int ch
; /* pointer to character to echo */
869 /* Echo the character if echoing is on. Some control characters are echoed
870 * with their normal effect, other control characters are echoed as "^X",
871 * normal characters are echoed normally. EOF (^D) is echoed, but immediately
872 * backspaced over. Return the character with the echoed length added to its
878 if (!(tp
->tty_termios
.c_lflag
& ECHO
)) {
879 if (ch
== ('\n' | IN_EOT
) && (tp
->tty_termios
.c_lflag
880 & (ICANON
|ECHONL
)) == (ICANON
|ECHONL
))
881 (*tp
->tty_echo
)(tp
, '\n');
885 /* "Reprint" tells if the echo output has been messed up by other output. */
886 rp
= tp
->tty_incount
== 0 ? FALSE
: tp
->tty_reprint
;
888 if ((ch
& IN_CHAR
) < ' ') {
889 switch (ch
& (IN_ESC
|IN_EOF
|IN_EOT
|IN_CHAR
)) {
893 (*tp
->tty_echo
)(tp
, ' ');
895 } while (len
< TAB_SIZE
&& (tp
->tty_position
& TAB_MASK
) != 0);
899 (*tp
->tty_echo
)(tp
, ch
& IN_CHAR
);
903 (*tp
->tty_echo
)(tp
, '^');
904 (*tp
->tty_echo
)(tp
, '@' + (ch
& IN_CHAR
));
908 if ((ch
& IN_CHAR
) == '\177') {
909 /* A DEL prints as "^?". */
910 (*tp
->tty_echo
)(tp
, '^');
911 (*tp
->tty_echo
)(tp
, '?');
914 (*tp
->tty_echo
)(tp
, ch
& IN_CHAR
);
917 if (ch
& IN_EOF
) while (len
> 0) { (*tp
->tty_echo
)(tp
, '\b'); len
--; }
919 tp
->tty_reprint
= rp
;
920 return(ch
| (len
<< IN_LSHIFT
));
923 /*===========================================================================*
925 *===========================================================================*/
926 static void rawecho(tp
, ch
)
930 /* Echo without interpretation if ECHO is set. */
931 int rp
= tp
->tty_reprint
;
932 if (tp
->tty_termios
.c_lflag
& ECHO
) (*tp
->tty_echo
)(tp
, ch
);
933 tp
->tty_reprint
= rp
;
936 /*===========================================================================*
938 *===========================================================================*/
939 static int back_over(tp
)
942 /* Backspace to previous character on screen and erase it. */
946 if (tp
->tty_incount
== 0) return(0); /* queue empty */
947 head
= tp
->tty_inhead
;
948 if (head
== tp
->tty_inbuf
) head
= bufend(tp
->tty_inbuf
);
949 if (*--head
& IN_EOT
) return(0); /* can't erase "line breaks" */
950 if (tp
->tty_reprint
) reprint(tp
); /* reprint if messed up */
951 tp
->tty_inhead
= head
;
953 if (tp
->tty_termios
.c_lflag
& ECHOE
) {
954 len
= (*head
& IN_LEN
) >> IN_LSHIFT
;
962 return(1); /* one character erased */
965 /*===========================================================================*
967 *===========================================================================*/
968 static void reprint(tp
)
969 register tty_t
*tp
; /* pointer to tty struct */
971 /* Restore what has been echoed to screen before if the user input has been
972 * messed up by output, or if REPRINT (^R) is typed.
977 tp
->tty_reprint
= FALSE
;
979 /* Find the last line break in the input. */
980 head
= tp
->tty_inhead
;
981 count
= tp
->tty_incount
;
983 if (head
== tp
->tty_inbuf
) head
= bufend(tp
->tty_inbuf
);
984 if (head
[-1] & IN_EOT
) break;
988 if (count
== tp
->tty_incount
) return; /* no reason to reprint */
990 /* Show REPRINT (^R) and move to a new line. */
991 (void) tty_echo(tp
, tp
->tty_termios
.c_cc
[VREPRINT
] | IN_ESC
);
995 /* Reprint from the last break onwards. */
997 if (head
== bufend(tp
->tty_inbuf
)) head
= tp
->tty_inbuf
;
998 *head
= tty_echo(tp
, *head
);
1001 } while (count
< tp
->tty_incount
);
1004 /*===========================================================================*
1006 *===========================================================================*/
1007 void out_process(tp
, bstart
, bpos
, bend
, icount
, ocount
)
1009 char *bstart
, *bpos
, *bend
; /* start/pos/end of circular buffer */
1010 int *icount
; /* # input chars / input chars used */
1011 int *ocount
; /* max output chars / output chars used */
1013 /* Perform output processing on a circular buffer. *icount is the number of
1014 * bytes to process, and the number of bytes actually processed on return.
1015 * *ocount is the space available on input and the space used on output.
1016 * (Naturally *icount < *ocount.) The column position is updated modulo
1017 * the TAB size, because we really only need it for tabs.
1023 int pos
= tp
->tty_position
;
1036 if ((tp
->tty_termios
.c_oflag
& (OPOST
|ONLCR
))
1038 /* Map LF to CR+LF if there is space. Note that the
1039 * next character in the buffer is overwritten, so
1040 * we stop at this point.
1044 if (++bpos
== bend
) bpos
= bstart
;
1050 goto out_done
; /* no space or buffer got changed */
1054 /* Best guess for the tab length. */
1055 tablen
= TAB_SIZE
- (pos
& TAB_MASK
);
1057 if ((tp
->tty_termios
.c_oflag
& (OPOST
|OXTABS
))
1058 == (OPOST
|OXTABS
)) {
1059 /* Tabs must be expanded. */
1060 if (oct
>= tablen
) {
1066 if (++bpos
== bend
) bpos
= bstart
;
1067 } while (--tablen
!= 0);
1071 /* Tabs are output directly. */
1075 /* Assume any other character prints as one character. */
1078 if (++bpos
== bend
) bpos
= bstart
;
1083 tp
->tty_position
= pos
& TAB_MASK
;
1085 *icount
-= ict
; /* [io]ct are the number of chars not used */
1086 *ocount
-= oct
; /* *[io]count are the number of chars that are used */
1089 /*===========================================================================*
1091 *===========================================================================*/
1092 static void dev_ioctl(tp
)
1095 /* The ioctl's TCSETSW, TCSETSF and TIOCDRAIN wait for output to finish to make
1096 * sure that an attribute change doesn't affect the processing of current
1097 * output. Once output finishes the ioctl is executed as in do_ioctl().
1099 int result
= EINVAL
;
1101 if (tp
->tty_outleft
> 0) return; /* output not finished */
1103 if (tp
->tty_ioreq
!= TIOCDRAIN
) {
1104 if (tp
->tty_ioreq
== TIOCSETAF
) tty_icancel(tp
);
1105 result
= sys_safecopyfrom(tp
->tty_iocaller
, tp
->tty_iogrant
, 0,
1106 (vir_bytes
) &tp
->tty_termios
,
1107 (vir_bytes
) sizeof(tp
->tty_termios
));
1108 if (result
== OK
) setattr(tp
);
1111 chardriver_reply_task(tp
->tty_iocaller
, tp
->tty_ioid
, result
);
1112 tp
->tty_iocaller
= NONE
;
1115 /*===========================================================================*
1117 *===========================================================================*/
1118 static void setattr(tp
)
1121 /* Apply the new line attributes (raw/canonical, line speed, etc.) */
1125 if (!(tp
->tty_termios
.c_lflag
& ICANON
)) {
1126 /* Raw mode; put a "line break" on all characters in the input queue.
1127 * It is undefined what happens to the input queue when ICANON is
1128 * switched off, a process should use TCSAFLUSH to flush the queue.
1129 * Keeping the queue to preserve typeahead is the Right Thing, however
1130 * when a process does use TCSANOW to switch to raw mode.
1132 count
= tp
->tty_eotct
= tp
->tty_incount
;
1133 inp
= tp
->tty_intail
;
1136 if (++inp
== bufend(tp
->tty_inbuf
)) inp
= tp
->tty_inbuf
;
1141 /* Inspect MIN and TIME. */
1142 settimer(tp
, FALSE
);
1143 if (tp
->tty_termios
.c_lflag
& ICANON
) {
1144 /* No MIN & TIME in canonical mode. */
1147 /* In raw mode MIN is the number of chars wanted, and TIME how long
1148 * to wait for them. With interesting exceptions if either is zero.
1150 tp
->tty_min
= tp
->tty_termios
.c_cc
[VMIN
];
1151 if (tp
->tty_min
== 0 && tp
->tty_termios
.c_cc
[VTIME
] > 0)
1155 if (!(tp
->tty_termios
.c_iflag
& IXON
)) {
1156 /* No start/stop output control, so don't leave output inhibited. */
1157 tp
->tty_inhibited
= RUNNING
;
1161 /* Setting the output speed to zero hangs up the phone. */
1162 if (tp
->tty_termios
.c_ospeed
== B0
) sigchar(tp
, SIGHUP
, 1);
1164 /* Set new line speed, character size, etc at the device level. */
1165 (*tp
->tty_ioctl
)(tp
, 0);
1168 /*===========================================================================*
1170 *===========================================================================*/
1171 void sigchar(tp
, sig
, mayflush
)
1173 int sig
; /* SIGINT, SIGQUIT, SIGKILL or SIGHUP */
1176 /* Process a SIGINT, SIGQUIT or SIGKILL char from the keyboard or SIGHUP from
1177 * a tty close, "stty 0", or a real RS-232 hangup. PM will send the signal to
1178 * the process group (INT, QUIT), all processes (KILL), or the session leader
1183 if (tp
->tty_pgrp
!= 0) {
1184 if (OK
!= (status
= sys_kill(tp
->tty_pgrp
, sig
))) {
1185 panic("Error; call to sys_kill failed: %d", status
);
1189 if (mayflush
&& !(tp
->tty_termios
.c_lflag
& NOFLSH
)) {
1190 tp
->tty_incount
= tp
->tty_eotct
= 0; /* kill earlier input */
1191 tp
->tty_intail
= tp
->tty_inhead
;
1192 (*tp
->tty_ocancel
)(tp
, 0); /* kill all output */
1193 tp
->tty_inhibited
= RUNNING
;
1198 /*===========================================================================*
1200 *===========================================================================*/
1201 static void tty_icancel(tp
)
1204 /* Discard all pending input, tty buffer or device. */
1206 tp
->tty_incount
= tp
->tty_eotct
= 0;
1207 tp
->tty_intail
= tp
->tty_inhead
;
1208 (*tp
->tty_icancel
)(tp
, 0);
1211 /*===========================================================================*
1213 *===========================================================================*/
1214 static int tty_devnop(tty_t
*UNUSED(tp
), int UNUSED(try))
1216 /* Some functions need not be implemented at the device level. */
1220 /*===========================================================================*
1222 *===========================================================================*/
1223 static int tty_init(int UNUSED(type
), sef_init_info_t
*UNUSED(info
))
1225 /* Initialize tty structure and call device initialization routines. */
1230 system_hz
= sys_hz();
1232 /* Initialize the terminal lines. */
1233 memset(tty_table
, '\0' , sizeof(tty_table
));
1235 for (tp
= FIRST_TTY
,s
=0; tp
< END_TTY
; tp
++,s
++) {
1238 init_timer(&tp
->tty_tmr
);
1240 tp
->tty_intail
= tp
->tty_inhead
= tp
->tty_inbuf
;
1242 tp
->tty_incaller
= tp
->tty_outcaller
= tp
->tty_iocaller
= NONE
;
1243 tp
->tty_termios
= termios_defaults
;
1244 tp
->tty_icancel
= tp
->tty_ocancel
= tp
->tty_ioctl
= tp
->tty_close
=
1245 tp
->tty_open
= tty_devnop
;
1247 tp
->tty_minor
= s
+ TTYPX_MINOR
;
1253 /*===========================================================================*
1255 *===========================================================================*/
1256 static void tty_timed_out(minix_timer_t
*tp
)
1258 /* This timer has expired. Set the events flag, to force processing. */
1260 tty_ptr
= &tty_table
[tmr_arg(tp
)->ta_int
];
1261 tty_ptr
->tty_min
= 0; /* force read to succeed */
1262 tty_ptr
->tty_events
= 1;
1265 /*===========================================================================*
1267 *===========================================================================*/
1268 static void settimer(tty_ptr
, enable
)
1269 tty_t
*tty_ptr
; /* line to set or unset a timer on */
1270 int enable
; /* set timer if true, otherwise unset */
1275 ticks
= tty_ptr
->tty_termios
.c_cc
[VTIME
] * (system_hz
/10);
1277 /* Set a new timer for enabling the TTY events flags. */
1278 set_timer(&tty_ptr
->tty_tmr
, ticks
, tty_timed_out
, tty_ptr
->tty_index
);
1280 /* Remove the timer from the active and expired lists. */
1281 cancel_timer(&tty_ptr
->tty_tmr
);