1 /* This file contains the terminal driver, both for the IBM console and regular
2 * ASCII terminals. It handles only the device-independent part of a TTY, the
3 * device dependent parts are in console.c, rs232.c, etc. This file contains
4 * two main entry points, tty_task() and tty_wakeup(), and several minor entry
5 * points for use by the device-dependent code.
7 * The device-independent part accepts "keyboard" input from the device-
8 * dependent part, performs input processing (special key interpretation),
9 * and sends the input to a process reading from the TTY. Output to a TTY
10 * is sent to the device-dependent code for output processing and "screen"
11 * display. Input processing is done by the device by calling 'in_process'
12 * on the input characters, output processing may be done by the device itself
13 * or by calling 'out_process'. The TTY takes care of input queuing, the
14 * device does the output queuing. If a device receives an external signal,
15 * like an interrupt, then it causes tty_wakeup() to be run by the CLOCK task
16 * to, you guessed it, wake up the TTY to check if input or output can
19 * The valid messages and their parameters are:
21 * notify from HARDWARE: output has been completed or input has arrived
22 * notify from SYSTEM : e.g., MINIX wants to shutdown; run code to
24 * DEV_READ: a process wants to read from a terminal
25 * DEV_WRITE: a process wants to write on a terminal
26 * DEV_IOCTL: a process wants to change a terminal's parameters
27 * DEV_OPEN: a tty line has been opened
28 * DEV_CLOSE: a tty line has been closed
29 * DEV_SELECT: start select notification request
30 * DEV_STATUS: FS wants to know status for SELECT or REVIVE
31 * CANCEL: terminate a previous incomplete system call immediately
33 * m_type TTY_LINE IO_ENDPT COUNT TTY_SPEKS ADDRESS
34 * -----------------------------------------------------------------
35 * | HARD_INT | | | | | |
36 * |-------------+---------+---------+---------+---------+---------|
37 * | SYS_SIG | sig set | | | | |
38 * |-------------+---------+---------+---------+---------+---------|
39 * | DEV_READ |minor dev| proc nr | count | | buf ptr |
40 * |-------------+---------+---------+---------+---------+---------|
41 * | DEV_WRITE |minor dev| proc nr | count | | buf ptr |
42 * |-------------+---------+---------+---------+---------+---------|
43 * | DEV_IOCTL |minor dev| proc nr |func code|erase etc| |
44 * |-------------+---------+---------+---------+---------+---------|
45 * | DEV_OPEN |minor dev| proc nr | O_NOCTTY| | |
46 * |-------------+---------+---------+---------+---------+---------|
47 * | DEV_CLOSE |minor dev| proc nr | | | |
48 * |-------------+---------+---------+---------+---------+---------|
49 * | DEV_STATUS | | | | | |
50 * |-------------+---------+---------+---------+---------+---------|
51 * | CANCEL |minor dev| proc nr | | | |
52 * -----------------------------------------------------------------
55 * Jan 20, 2004 moved TTY driver to user-space (Jorrit N. Herder)
56 * Sep 20, 2004 local timer management/ sync alarms (Jorrit N. Herder)
57 * Jul 13, 2004 support for function key observers (Jorrit N. Herder)
60 #include <minix/drivers.h>
61 #include <minix/driver.h>
63 #include <sys/ioc_tty.h>
65 #include <minix/callnr.h>
66 #include <minix/sys_config.h>
67 #include <minix/tty.h>
68 #include <minix/keymap.h>
69 #include <minix/endpoint.h>
73 #include <sys/select.h>
75 unsigned long kbd_irq_set
= 0;
76 unsigned long rs_irq_set
= 0;
78 /* Address of a tty structure. */
79 #define tty_addr(line) (&tty_table[line])
81 /* Macros for magic tty types. */
82 #define isconsole(tp) ((tp) < tty_addr(NR_CONS))
83 #define ispty(tp) ((tp) >= tty_addr(NR_CONS+NR_RS_LINES))
85 /* Macros for magic tty structure pointers. */
86 #define FIRST_TTY tty_addr(0)
87 #define END_TTY tty_addr(sizeof(tty_table) / sizeof(tty_table[0]))
89 /* A device exists if at least its 'devread' function is defined. */
90 #define tty_active(tp) ((tp)->tty_devread != NULL)
92 /* RS232 lines or pseudo terminals can be completely configured out. */
94 #define rs_init(tp) ((void) 0)
98 #define pty_init(tp) ((void) 0)
99 #define do_pty(tp, mp) ((void) 0)
102 struct kmessages kmess
;
104 FORWARD
_PROTOTYPE( void tty_timed_out
, (timer_t
*tp
) );
105 FORWARD
_PROTOTYPE( void expire_timers
, (void) );
106 FORWARD
_PROTOTYPE( void settimer
, (tty_t
*tty_ptr
, int enable
) );
107 FORWARD
_PROTOTYPE( void do_cancel
, (tty_t
*tp
, message
*m_ptr
) );
108 FORWARD
_PROTOTYPE( void do_ioctl
, (tty_t
*tp
, message
*m_ptr
, int s
) );
109 FORWARD
_PROTOTYPE( void do_open
, (tty_t
*tp
, message
*m_ptr
) );
110 FORWARD
_PROTOTYPE( void do_close
, (tty_t
*tp
, message
*m_ptr
) );
111 FORWARD
_PROTOTYPE( void do_read
, (tty_t
*tp
, message
*m_ptr
, int s
) );
112 FORWARD
_PROTOTYPE( void do_write
, (tty_t
*tp
, message
*m_ptr
, int s
) );
113 FORWARD
_PROTOTYPE( void do_select
, (tty_t
*tp
, message
*m_ptr
) );
114 FORWARD
_PROTOTYPE( void do_status
, (message
*m_ptr
) );
115 FORWARD
_PROTOTYPE( void in_transfer
, (tty_t
*tp
) );
116 FORWARD
_PROTOTYPE( int tty_echo
, (tty_t
*tp
, int ch
) );
117 FORWARD
_PROTOTYPE( void rawecho
, (tty_t
*tp
, int ch
) );
118 FORWARD
_PROTOTYPE( int back_over
, (tty_t
*tp
) );
119 FORWARD
_PROTOTYPE( void reprint
, (tty_t
*tp
) );
120 FORWARD
_PROTOTYPE( void dev_ioctl
, (tty_t
*tp
) );
121 FORWARD
_PROTOTYPE( void setattr
, (tty_t
*tp
) );
122 FORWARD
_PROTOTYPE( void tty_icancel
, (tty_t
*tp
) );
123 FORWARD
_PROTOTYPE( void tty_init
, (void) );
125 /* Default attributes. */
126 PRIVATE
struct termios termios_defaults
= {
127 TINPUT_DEF
, TOUTPUT_DEF
, TCTRL_DEF
, TLOCAL_DEF
, TSPEED_DEF
, TSPEED_DEF
,
129 TEOF_DEF
, TEOL_DEF
, TERASE_DEF
, TINTR_DEF
, TKILL_DEF
, TMIN_DEF
,
130 TQUIT_DEF
, TTIME_DEF
, TSUSP_DEF
, TSTART_DEF
, TSTOP_DEF
,
131 TREPRINT_DEF
, TLNEXT_DEF
, TDISCARD_DEF
,
134 PRIVATE
struct winsize winsize_defaults
; /* = all zeroes */
136 /* Global variables for the TTY task (declared extern in tty.h). */
137 PUBLIC tty_t tty_table
[NR_CONS
+NR_RS_LINES
+NR_PTYS
];
138 PUBLIC
int ccurrent
; /* currently active console */
139 PUBLIC timer_t
*tty_timers
; /* queue of TTY timers */
140 PUBLIC
clock_t tty_next_timeout
; /* time that the next alarm is due */
141 PUBLIC
struct machine machine
; /* kernel environment variables */
142 PUBLIC u32_t system_hz
;
144 /* SEF functions and variables. */
145 FORWARD
_PROTOTYPE( void sef_local_startup
, (void) );
146 FORWARD
_PROTOTYPE( int sef_cb_init_fresh
, (int type
, sef_init_info_t
*info
) );
147 FORWARD
_PROTOTYPE( void sef_cb_signal_handler
, (int signo
) );
149 /*===========================================================================*
151 *===========================================================================*/
152 PUBLIC
int main(void)
154 /* Main routine of the terminal task. */
156 message tty_mess
; /* buffer for all incoming messages */
162 /* SEF local startup. */
166 /* Check for and handle any events on any of the ttys. */
167 for (tp
= FIRST_TTY
; tp
< END_TTY
; tp
++) {
168 if (tp
->tty_events
) handle_events(tp
);
171 /* Get a request message. */
172 r
= driver_receive(ANY
, &tty_mess
, &ipc_status
);
174 panic("driver_receive failed with: %d", r
);
176 /* First handle all kernel notification types that the TTY supports.
177 * - An alarm went off, expire all timers and handle the events.
178 * - A hardware interrupt also is an invitation to check for events.
179 * - A new kernel message is available for printing.
180 * - Reset the console on system shutdown.
181 * Then see if this message is different from a normal device driver
182 * request and should be handled separately. These extra functions
183 * do not operate on a device, in constrast to the driver requests.
186 if (is_ipc_notify(ipc_status
)) {
187 switch (_ENDPOINT_P(tty_mess
.m_source
)) {
189 /* run watchdogs of expired timers */
193 /* hardware interrupt notification */
195 /* fetch chars from keyboard */
196 if (tty_mess
.NOTIFY_ARG
& kbd_irq_set
)
197 kbd_interrupt(&tty_mess
);
200 if (tty_mess
.NOTIFY_ARG
& rs_irq_set
)
201 rs_interrupt(&tty_mess
);
203 /* run watchdogs of expired timers */
211 /* done, get new message */
215 switch (tty_mess
.m_type
) {
216 case DIAGNOSTICS_OLD
: /* a server wants to print some */
218 if (tty_mess
.m_source
!= LOG_PROC_NR
)
220 printf("[%d ", tty_mess
.m_source
);
223 do_diagnostics(&tty_mess
, 0);
225 case DIAGNOSTICS_S_OLD
:
226 case ASYN_DIAGNOSTICS_OLD
:
227 do_diagnostics(&tty_mess
, 1);
230 do_get_kmess(&tty_mess
);
233 do_get_kmess_s(&tty_mess
);
235 case FKEY_CONTROL
: /* (un)register a fkey observer */
236 do_fkey_ctl(&tty_mess
);
238 default: /* should be a driver request */
239 ; /* do nothing; end switch */
242 /* Only device requests should get to this point. All requests,
243 * except DEV_STATUS, have a minor device number. Check this
244 * exception and get the minor device number otherwise.
246 if (tty_mess
.m_type
== DEV_STATUS
) {
247 do_status(&tty_mess
);
250 line
= tty_mess
.TTY_LINE
;
251 if (line
== KBD_MINOR
) {
254 } else if (line
== KBDAUX_MINOR
) {
255 do_kbdaux(&tty_mess
);
257 } else if (line
== VIDEO_MINOR
) {
260 } else if ((line
- CONS_MINOR
) < NR_CONS
) {
261 tp
= tty_addr(line
- CONS_MINOR
);
262 } else if (line
== LOG_MINOR
) {
264 } else if ((line
- RS232_MINOR
) < NR_RS_LINES
) {
265 tp
= tty_addr(line
- RS232_MINOR
+ NR_CONS
);
266 } else if ((line
- TTYPX_MINOR
) < NR_PTYS
) {
267 tp
= tty_addr(line
- TTYPX_MINOR
+ NR_CONS
+ NR_RS_LINES
);
268 } else if ((line
- PTYPX_MINOR
) < NR_PTYS
) {
269 tp
= tty_addr(line
- PTYPX_MINOR
+ NR_CONS
+ NR_RS_LINES
);
270 if (tty_mess
.m_type
!= DEV_IOCTL_S
) {
271 do_pty(tp
, &tty_mess
);
278 /* If the device doesn't exist or is not configured return ENXIO. */
279 if (tp
== NULL
|| ! tty_active(tp
)) {
280 printf("Warning, TTY got illegal request %d from %d\n",
281 tty_mess
.m_type
, tty_mess
.m_source
);
282 if (tty_mess
.m_source
!= LOG_PROC_NR
)
284 tty_reply(TASK_REPLY
, tty_mess
.m_source
,
285 tty_mess
.IO_ENDPT
, ENXIO
);
290 /* Execute the requested device driver function. */
291 switch (tty_mess
.m_type
) {
292 case DEV_READ_S
: do_read(tp
, &tty_mess
, 1); break;
293 case DEV_WRITE_S
: do_write(tp
, &tty_mess
, 1); break;
294 case DEV_IOCTL_S
: do_ioctl(tp
, &tty_mess
, 1); break;
295 case DEV_OPEN
: do_open(tp
, &tty_mess
); break;
296 case DEV_CLOSE
: do_close(tp
, &tty_mess
); break;
297 case DEV_SELECT
: do_select(tp
, &tty_mess
); break;
298 case CANCEL
: do_cancel(tp
, &tty_mess
); break;
300 printf("Warning, TTY got unexpected request %d from %d\n",
301 tty_mess
.m_type
, tty_mess
.m_source
);
302 tty_reply(TASK_REPLY
, tty_mess
.m_source
,
303 tty_mess
.IO_ENDPT
, EINVAL
);
310 /*===========================================================================*
311 * sef_local_startup *
312 *===========================================================================*/
313 PRIVATE
void sef_local_startup()
315 /* Register init callbacks. */
316 sef_setcb_init_fresh(sef_cb_init_fresh
);
317 sef_setcb_init_restart(sef_cb_init_fresh
);
319 /* No live update support for now. */
321 /* Register signal callbacks. */
322 sef_setcb_signal_handler(sef_cb_signal_handler
);
324 /* Let SEF perform startup. */
328 /*===========================================================================*
329 * sef_cb_init_fresh *
330 *===========================================================================*/
331 PRIVATE
int sef_cb_init_fresh(int type
, sef_init_info_t
*info
)
333 /* Initialize the tty driver. */
336 /* Get kernel environment (protected_mode, pc_at and ega are needed). */
337 if (OK
!= (r
=sys_getmachine(&machine
))) {
338 panic("Couldn't obtain kernel environment: %d", r
);
341 /* Initialize the TTY driver. */
344 /* Final one-time keyboard initialization. */
350 /*===========================================================================*
351 * sef_cb_signal_handler *
352 *===========================================================================*/
353 PRIVATE
void sef_cb_signal_handler(int signo
)
355 /* Check for known signals, ignore anything else. */
357 /* There is a pending message from the kernel. */
361 /* Switch to primary console on termination. */
368 /*===========================================================================*
370 *===========================================================================*/
371 PRIVATE
void do_status(m_ptr
)
374 register struct tty
*tp
;
379 /* Check for select or revive events on any of the ttys. If we found an,
380 * event return a single status message for it. The FS will make another
381 * call to see if there is more.
384 for (tp
= FIRST_TTY
; tp
< END_TTY
; tp
++) {
385 if ((ops
= select_try(tp
, tp
->tty_select_ops
)) &&
386 tp
->tty_select_proc
== m_ptr
->m_source
) {
388 /* I/O for a selected minor device is ready. */
389 m_ptr
->m_type
= DEV_IO_READY
;
390 m_ptr
->DEV_MINOR
= tp
->tty_minor
;
391 m_ptr
->DEV_SEL_OPS
= ops
;
393 tp
->tty_select_ops
&= ~ops
; /* unmark select event */
397 else if (tp
->tty_inrevived
&& tp
->tty_incaller
== m_ptr
->m_source
) {
399 /* Suspended request finished. Send a REVIVE. */
400 m_ptr
->m_type
= DEV_REVIVE
;
401 m_ptr
->REP_ENDPT
= tp
->tty_inproc
;
402 m_ptr
->REP_IO_GRANT
= tp
->tty_in_vir_g
;
403 m_ptr
->REP_STATUS
= tp
->tty_incum
;
405 tp
->tty_inleft
= tp
->tty_incum
= 0;
406 tp
->tty_inrevived
= 0; /* unmark revive event */
410 else if (tp
->tty_outrevived
&& tp
->tty_outcaller
== m_ptr
->m_source
) {
412 /* Suspended request finished. Send a REVIVE. */
413 m_ptr
->m_type
= DEV_REVIVE
;
414 m_ptr
->REP_ENDPT
= tp
->tty_outproc
;
415 m_ptr
->REP_IO_GRANT
= tp
->tty_out_vir_g
;
416 m_ptr
->REP_STATUS
= tp
->tty_outcum
;
419 tp
->tty_outrevived
= 0; /* unmark revive event */
423 else if (tp
->tty_iorevived
&& tp
->tty_iocaller
== m_ptr
->m_source
) {
424 /* Suspended request finished. Send a REVIVE. */
425 m_ptr
->m_type
= DEV_REVIVE
;
426 m_ptr
->REP_ENDPT
= tp
->tty_ioproc
;
427 m_ptr
->REP_IO_GRANT
= tp
->tty_iovir_g
;
428 m_ptr
->REP_STATUS
= tp
->tty_iostatus
;
429 tp
->tty_iorevived
= 0; /* unmark revive event */
437 event_found
= pty_status(m_ptr
);
440 event_found
= kbd_status(m_ptr
);
443 /* No events of interest were found. Return an empty message. */
444 m_ptr
->m_type
= DEV_NO_STATUS
;
447 /* Almost done. Send back the reply message to the caller. */
448 status
= sendnb(m_ptr
->m_source
, m_ptr
);
450 printf("tty`do_status: send to %d failed: %d\n",
451 m_ptr
->m_source
, status
);
455 /*===========================================================================*
457 *===========================================================================*/
458 PRIVATE
void do_read(tp
, m_ptr
, safe
)
459 register tty_t
*tp
; /* pointer to tty struct */
460 register message
*m_ptr
; /* pointer to message sent to the task */
461 int safe
; /* use safecopies? */
463 /* A process wants to read from a terminal. */
466 /* Check if there is already a process hanging in a read, check if the
467 * parameters are correct, do I/O.
469 if (tp
->tty_inleft
> 0) {
472 if (m_ptr
->COUNT
<= 0) {
475 /* Copy information from the message to the tty struct. */
476 tp
->tty_inrepcode
= TASK_REPLY
;
477 tp
->tty_incaller
= m_ptr
->m_source
;
478 tp
->tty_inproc
= m_ptr
->IO_ENDPT
;
479 tp
->tty_in_vir_g
= (vir_bytes
) m_ptr
->ADDRESS
;
480 tp
->tty_in_vir_offset
= 0;
481 tp
->tty_in_safe
= safe
;
482 tp
->tty_inleft
= m_ptr
->COUNT
;
484 if (!(tp
->tty_termios
.c_lflag
& ICANON
)
485 && tp
->tty_termios
.c_cc
[VTIME
] > 0) {
486 if (tp
->tty_termios
.c_cc
[VMIN
] == 0) {
487 /* MIN & TIME specify a read timer that finishes the
488 * read in TIME/10 seconds if no bytes are available.
493 /* MIN & TIME specify an inter-byte timer that may
494 * have to be cancelled if there are no bytes yet.
496 if (tp
->tty_eotct
== 0) {
498 tp
->tty_min
= tp
->tty_termios
.c_cc
[VMIN
];
503 /* Anything waiting in the input buffer? Clear it out... */
505 /* ...then go back for more. */
507 if (tp
->tty_inleft
== 0) {
508 if (tp
->tty_select_ops
)
510 return; /* already done */
513 /* There were no bytes in the input queue available, so suspend
516 r
= SUSPEND
; /* suspend the caller */
517 tp
->tty_inrepcode
= TTY_REVIVE
;
519 tty_reply(TASK_REPLY
, m_ptr
->m_source
, m_ptr
->IO_ENDPT
, r
);
520 if (tp
->tty_select_ops
)
524 /*===========================================================================*
526 *===========================================================================*/
527 PRIVATE
void do_write(tp
, m_ptr
, safe
)
529 register message
*m_ptr
; /* pointer to message sent to the task */
532 /* A process wants to write on a terminal. */
535 /* Check if there is already a process hanging in a write, check if the
536 * parameters are correct, do I/O.
538 if (tp
->tty_outleft
> 0) {
541 if (m_ptr
->COUNT
<= 0) {
544 /* Copy message parameters to the tty structure. */
545 tp
->tty_outrepcode
= TASK_REPLY
;
546 tp
->tty_outcaller
= m_ptr
->m_source
;
547 tp
->tty_outproc
= m_ptr
->IO_ENDPT
;
548 tp
->tty_out_vir_g
= (vir_bytes
) m_ptr
->ADDRESS
;
549 tp
->tty_out_vir_offset
= 0;
550 tp
->tty_out_safe
= safe
;
551 tp
->tty_outleft
= m_ptr
->COUNT
;
555 if (tp
->tty_outleft
== 0)
556 return; /* already done */
558 /* None or not all the bytes could be written, so suspend the
561 r
= SUSPEND
; /* suspend the caller */
562 tp
->tty_outrepcode
= TTY_REVIVE
;
564 tty_reply(TASK_REPLY
, m_ptr
->m_source
, m_ptr
->IO_ENDPT
, r
);
567 /*===========================================================================*
569 *===========================================================================*/
570 PRIVATE
void do_ioctl(tp
, m_ptr
, safe
)
572 message
*m_ptr
; /* pointer to message sent to task */
575 /* Perform an IOCTL on this terminal. Posix termios calls are handled
576 * by the IOCTL system call
585 /* Size of the ioctl parameter. */
586 switch (m_ptr
->TTY_REQUEST
) {
587 case TCGETS
: /* Posix tcgetattr function */
588 case TCSETS
: /* Posix tcsetattr function, TCSANOW option */
589 case TCSETSW
: /* Posix tcsetattr function, TCSADRAIN option */
590 case TCSETSF
: /* Posix tcsetattr function, TCSAFLUSH option */
591 size
= sizeof(struct termios
);
594 case TCSBRK
: /* Posix tcsendbreak function */
595 case TCFLOW
: /* Posix tcflow function */
596 case TCFLSH
: /* Posix tcflush function */
597 case TIOCGPGRP
: /* Posix tcgetpgrp function */
598 case TIOCSPGRP
: /* Posix tcsetpgrp function */
602 case TIOCGWINSZ
: /* get window size (not Posix) */
603 case TIOCSWINSZ
: /* set window size (not Posix) */
604 size
= sizeof(struct winsize
);
607 #if (MACHINE == IBM_PC)
608 case KIOCSMAP
: /* load keymap (Minix extension) */
609 size
= sizeof(keymap_t
);
612 case TIOCSFON
: /* load font (Minix extension) */
613 size
= sizeof(u8_t
[8192]);
617 case TCDRAIN
: /* Posix tcdrain function -- no parameter */
622 switch (m_ptr
->TTY_REQUEST
) {
624 /* Get the termios attributes. */
626 r
= sys_safecopyto(m_ptr
->IO_ENDPT
, (vir_bytes
) m_ptr
->ADDRESS
, 0,
627 (vir_bytes
) &tp
->tty_termios
, (vir_bytes
) size
, D
);
629 r
= sys_vircopy(SELF
, D
, (vir_bytes
) &tp
->tty_termios
,
630 m_ptr
->IO_ENDPT
, D
, (vir_bytes
) m_ptr
->ADDRESS
,
638 if (tp
->tty_outleft
> 0) {
639 /* Wait for all ongoing output processing to finish. */
640 tp
->tty_iocaller
= m_ptr
->m_source
;
641 tp
->tty_ioproc
= m_ptr
->IO_ENDPT
;
642 tp
->tty_ioreq
= m_ptr
->REQUEST
;
643 tp
->tty_iovir_g
= (vir_bytes
) m_ptr
->ADDRESS
;
644 tp
->tty_io_safe
= safe
;
648 if (m_ptr
->TTY_REQUEST
== TCDRAIN
) break;
649 if (m_ptr
->TTY_REQUEST
== TCSETSF
) tty_icancel(tp
);
652 /* Set the termios attributes. */
654 r
= sys_safecopyfrom(m_ptr
->IO_ENDPT
, (vir_bytes
) m_ptr
->ADDRESS
, 0,
655 (vir_bytes
) &tp
->tty_termios
, (vir_bytes
) size
, D
);
657 r
= sys_vircopy( m_ptr
->IO_ENDPT
, D
, (vir_bytes
) m_ptr
->ADDRESS
,
658 SELF
, D
, (vir_bytes
) &tp
->tty_termios
, (vir_bytes
) size
);
666 r
= sys_safecopyfrom(m_ptr
->IO_ENDPT
, (vir_bytes
) m_ptr
->ADDRESS
, 0,
667 (vir_bytes
) ¶m
.i
, (vir_bytes
) size
, D
);
669 r
= sys_vircopy(m_ptr
->IO_ENDPT
, D
, (vir_bytes
) m_ptr
->ADDRESS
,
670 SELF
, D
, (vir_bytes
) ¶m
.i
, (vir_bytes
) size
);
674 case TCIFLUSH
: tty_icancel(tp
); break;
675 case TCOFLUSH
: (*tp
->tty_ocancel
)(tp
, 0); break;
676 case TCIOFLUSH
: tty_icancel(tp
); (*tp
->tty_ocancel
)(tp
, 0); break;
683 r
= sys_safecopyfrom(m_ptr
->IO_ENDPT
, (vir_bytes
) m_ptr
->ADDRESS
, 0,
684 (vir_bytes
) ¶m
.i
, (vir_bytes
) size
, D
);
686 r
= sys_vircopy( m_ptr
->IO_ENDPT
, D
, (vir_bytes
) m_ptr
->ADDRESS
,
687 SELF
, D
, (vir_bytes
) ¶m
.i
, (vir_bytes
) size
);
693 tp
->tty_inhibited
= (param
.i
== TCOOFF
);
697 (*tp
->tty_echo
)(tp
, tp
->tty_termios
.c_cc
[VSTOP
]);
700 (*tp
->tty_echo
)(tp
, tp
->tty_termios
.c_cc
[VSTART
]);
708 if (tp
->tty_break
!= NULL
) (*tp
->tty_break
)(tp
,0);
713 r
= sys_safecopyto(m_ptr
->IO_ENDPT
, (vir_bytes
) m_ptr
->ADDRESS
, 0,
714 (vir_bytes
) &tp
->tty_winsize
, (vir_bytes
) size
, D
);
716 r
= sys_vircopy(SELF
, D
, (vir_bytes
) &tp
->tty_winsize
,
717 m_ptr
->IO_ENDPT
, D
, (vir_bytes
) m_ptr
->ADDRESS
,
724 r
= sys_safecopyfrom(m_ptr
->IO_ENDPT
, (vir_bytes
) m_ptr
->ADDRESS
, 0,
725 (vir_bytes
) &tp
->tty_winsize
, (vir_bytes
) size
, D
);
727 r
= sys_vircopy( m_ptr
->IO_ENDPT
, D
, (vir_bytes
) m_ptr
->ADDRESS
,
728 SELF
, D
, (vir_bytes
) &tp
->tty_winsize
, (vir_bytes
) size
);
730 sigchar(tp
, SIGWINCH
, 0);
733 #if (MACHINE == IBM_PC)
735 /* Load a new keymap (only /dev/console). */
736 if (isconsole(tp
)) r
= kbd_loadmap(m_ptr
, safe
);
740 printf("TTY: old TIOCSFON ignored.\n");
743 /* Load a font into an EGA or VGA card (hs@hck.hr) */
744 if (isconsole(tp
)) r
= con_loadfont(m_ptr
);
748 #if (MACHINE == ATARI)
750 r
= vdu_loadfont(m_ptr
);
754 /* These Posix functions are allowed to fail if _POSIX_JOB_CONTROL is
763 /* Send the reply. */
764 tty_reply(TASK_REPLY
, m_ptr
->m_source
, m_ptr
->IO_ENDPT
, r
);
767 /*===========================================================================*
769 *===========================================================================*/
770 PRIVATE
void do_open(tp
, m_ptr
)
772 message
*m_ptr
; /* pointer to message sent to task */
774 /* A tty line has been opened. Make it the callers controlling tty if
775 * O_NOCTTY is *not* set and it is not the log device. 1 is returned if
776 * the tty is made the controlling tty, otherwise OK or an error code.
780 if (m_ptr
->TTY_LINE
== LOG_MINOR
) {
781 /* The log device is a write-only diagnostics device. */
782 if (m_ptr
->COUNT
& R_BIT
) r
= EACCES
;
784 if (!(m_ptr
->COUNT
& O_NOCTTY
)) {
785 tp
->tty_pgrp
= m_ptr
->IO_ENDPT
;
790 tty_reply(TASK_REPLY
, m_ptr
->m_source
, m_ptr
->IO_ENDPT
, r
);
793 /*===========================================================================*
795 *===========================================================================*/
796 PRIVATE
void do_close(tp
, m_ptr
)
798 message
*m_ptr
; /* pointer to message sent to task */
800 /* A tty line has been closed. Clean up the line if it is the last close. */
802 if (m_ptr
->TTY_LINE
!= LOG_MINOR
&& --tp
->tty_openct
== 0) {
805 (*tp
->tty_ocancel
)(tp
, 0);
806 (*tp
->tty_close
)(tp
, 0);
807 tp
->tty_termios
= termios_defaults
;
808 tp
->tty_winsize
= winsize_defaults
;
811 tty_reply(TASK_REPLY
, m_ptr
->m_source
, m_ptr
->IO_ENDPT
, OK
);
814 /*===========================================================================*
816 *===========================================================================*/
817 PRIVATE
void do_cancel(tp
, m_ptr
)
819 message
*m_ptr
; /* pointer to message sent to task */
821 /* A signal has been sent to a process that is hanging trying to read or write.
822 * The pending read or write must be finished off immediately.
829 /* Check the parameters carefully, to avoid cancelling twice. */
830 proc_nr
= m_ptr
->IO_ENDPT
;
832 if ((mode
& R_BIT
) && tp
->tty_inleft
!= 0 && proc_nr
== tp
->tty_inproc
&&
833 (!tp
->tty_in_safe
|| tp
->tty_in_vir_g
==(vir_bytes
)m_ptr
->IO_GRANT
)) {
834 /* Process was reading when killed. Clean up input. */
836 r
= tp
->tty_incum
> 0 ? tp
->tty_incum
: EAGAIN
;
837 tp
->tty_inleft
= tp
->tty_incum
= tp
->tty_inrevived
= 0;
839 if ((mode
& W_BIT
) && tp
->tty_outleft
!= 0 && proc_nr
== tp
->tty_outproc
&&
840 (!tp
->tty_out_safe
|| tp
->tty_out_vir_g
==(vir_bytes
)m_ptr
->IO_GRANT
)) {
841 /* Process was writing when killed. Clean up output. */
842 r
= tp
->tty_outcum
> 0 ? tp
->tty_outcum
: EAGAIN
;
843 tp
->tty_outleft
= tp
->tty_outcum
= tp
->tty_outrevived
= 0;
845 if (tp
->tty_ioreq
!= 0 && proc_nr
== tp
->tty_ioproc
) {
846 /* Process was waiting for output to drain. */
850 tty_reply(TASK_REPLY
, m_ptr
->m_source
, proc_nr
, r
);
853 PUBLIC
int select_try(struct tty
*tp
, int ops
)
857 /* Special case. If line is hung up, no operations will block.
858 * (and it can be seen as an exceptional condition.)
860 if (tp
->tty_termios
.c_ospeed
== B0
) {
865 /* will i/o not block on read? */
866 if (tp
->tty_inleft
> 0) {
867 ready_ops
|= SEL_RD
; /* EIO - no blocking */
868 } else if (tp
->tty_incount
> 0) {
869 /* Is a regular read possible? tty_incount
870 * says there is data. But a read will only succeed
871 * in canonical mode if a newline has been seen.
873 if (!(tp
->tty_termios
.c_lflag
& ICANON
) ||
881 if (tp
->tty_outleft
> 0) ready_ops
|= SEL_WR
;
882 else if ((*tp
->tty_devwrite
)(tp
, 1)) ready_ops
|= SEL_WR
;
887 PUBLIC
int select_retry(struct tty
*tp
)
889 if (tp
->tty_select_ops
&& select_try(tp
, tp
->tty_select_ops
))
890 notify(tp
->tty_select_proc
);
894 /*===========================================================================*
896 *===========================================================================*/
897 PUBLIC
void handle_events(tp
)
898 tty_t
*tp
; /* TTY to check for events. */
900 /* Handle any events pending on a TTY. These events are usually device
903 * Two kinds of events are prominent:
904 * - a character has been received from the console or an RS232 line.
905 * - an RS232 line has completed a write request (on behalf of a user).
906 * The interrupt handler may delay the interrupt message at its discretion
907 * to avoid swamping the TTY task. Messages may be overwritten when the
908 * lines are fast or when there are races between different lines, input
909 * and output, because MINIX only provides single buffering for interrupt
910 * messages (in proc.c). This is handled by explicitly checking each line
911 * for fresh input and completed output on each interrupt.
917 /* Read input and perform input processing. */
918 (*tp
->tty_devread
)(tp
, 0);
920 /* Perform output processing and write output. */
921 (*tp
->tty_devwrite
)(tp
, 0);
923 /* Ioctl waiting for some event? */
924 if (tp
->tty_ioreq
!= 0) dev_ioctl(tp
);
925 } while (tp
->tty_events
);
927 /* Transfer characters from the input queue to a waiting process. */
930 /* Reply if enough bytes are available. */
931 if (tp
->tty_incum
>= tp
->tty_min
&& tp
->tty_inleft
> 0) {
932 if (tp
->tty_inrepcode
== TTY_REVIVE
) {
933 notify(tp
->tty_incaller
);
934 tp
->tty_inrevived
= 1;
936 tty_reply(tp
->tty_inrepcode
, tp
->tty_incaller
,
937 tp
->tty_inproc
, tp
->tty_incum
);
938 tp
->tty_inleft
= tp
->tty_incum
= 0;
941 if (tp
->tty_select_ops
)
947 select_retry_pty(tp
);
951 /*===========================================================================*
953 *===========================================================================*/
954 PRIVATE
void in_transfer(tp
)
955 register tty_t
*tp
; /* pointer to terminal to read from */
957 /* Transfer bytes from the input queue to a process reading from a terminal. */
963 /* Force read to succeed if the line is hung up, looks like EOF to reader. */
964 if (tp
->tty_termios
.c_ospeed
== B0
) tp
->tty_min
= 0;
966 /* Anything to do? */
967 if (tp
->tty_inleft
== 0 || tp
->tty_eotct
< tp
->tty_min
) return;
970 while (tp
->tty_inleft
> 0 && tp
->tty_eotct
> 0) {
971 ch
= *tp
->tty_intail
;
973 if (!(ch
& IN_EOF
)) {
974 /* One character to be delivered to the user. */
977 if (++bp
== bufend(buf
)) {
978 /* Temp buffer full, copy to user space. */
979 if(tp
->tty_in_safe
) {
980 sys_safecopyto(tp
->tty_inproc
,
981 tp
->tty_in_vir_g
, tp
->tty_in_vir_offset
,
983 (vir_bytes
) buflen(buf
), D
);
984 tp
->tty_in_vir_offset
+= buflen(buf
);
986 sys_vircopy(SELF
, D
, (vir_bytes
) buf
,
987 tp
->tty_inproc
, D
, tp
->tty_in_vir_g
,
988 (vir_bytes
) buflen(buf
));
989 tp
->tty_in_vir_g
+= buflen(buf
);
991 tp
->tty_incum
+= buflen(buf
);
996 /* Remove the character from the input queue. */
997 if (++tp
->tty_intail
== bufend(tp
->tty_inbuf
))
998 tp
->tty_intail
= tp
->tty_inbuf
;
1002 /* Don't read past a line break in canonical mode. */
1003 if (tp
->tty_termios
.c_lflag
& ICANON
) tp
->tty_inleft
= 0;
1008 /* Leftover characters in the buffer. */
1010 if(tp
->tty_in_safe
) {
1011 sys_safecopyto(tp
->tty_inproc
,
1012 tp
->tty_in_vir_g
, tp
->tty_in_vir_offset
,
1013 (vir_bytes
) buf
, (vir_bytes
) count
, D
);
1014 tp
->tty_in_vir_offset
+= count
;
1016 sys_vircopy(SELF
, D
, (vir_bytes
) buf
,
1017 tp
->tty_inproc
, D
, tp
->tty_in_vir_g
, (vir_bytes
) count
);
1018 tp
->tty_in_vir_g
+= count
;
1020 tp
->tty_incum
+= count
;
1023 /* Usually reply to the reader, possibly even if incum == 0 (EOF). */
1024 if (tp
->tty_inleft
== 0) {
1025 if (tp
->tty_inrepcode
== TTY_REVIVE
) {
1026 notify(tp
->tty_incaller
);
1027 tp
->tty_inrevived
= 1;
1029 tty_reply(tp
->tty_inrepcode
, tp
->tty_incaller
,
1030 tp
->tty_inproc
, tp
->tty_incum
);
1031 tp
->tty_inleft
= tp
->tty_incum
= 0;
1036 /*===========================================================================*
1038 *===========================================================================*/
1039 PRIVATE
void in_process_send_byte(
1040 tty_t
*tp
, /* terminal on which character has arrived */
1041 int ch
/* input character */
1044 /* Save the character in the input queue. */
1045 *tp
->tty_inhead
++ = ch
;
1046 if (tp
->tty_inhead
== bufend(tp
->tty_inbuf
))
1047 tp
->tty_inhead
= tp
->tty_inbuf
;
1049 if (ch
& IN_EOT
) tp
->tty_eotct
++;
1051 /* Try to finish input if the queue threatens to overflow. */
1052 if (tp
->tty_incount
== buflen(tp
->tty_inbuf
)) in_transfer(tp
);
1055 PUBLIC
int in_process(tp
, buf
, count
, scode
)
1056 register tty_t
*tp
; /* terminal on which character has arrived */
1057 char *buf
; /* buffer with input characters */
1058 int count
; /* number of input characters */
1059 int scode
; /* scan code */
1061 /* Characters have just been typed in. Process, save, and echo them. Return
1062 * the number of characters processed.
1066 int timeset
= FALSE
;
1068 /* Send scancode if requested */
1069 if (tp
->tty_termios
.c_iflag
& SCANCODES
) {
1070 in_process_send_byte(tp
, (scode
& BYTE
) | IN_EOT
);
1073 for (ct
= 0; ct
< count
; ct
++) {
1074 /* Take one character. */
1077 /* Strip to seven bits? */
1078 if (tp
->tty_termios
.c_iflag
& ISTRIP
) ch
&= 0x7F;
1080 /* Input extensions? */
1081 if (tp
->tty_termios
.c_lflag
& IEXTEN
) {
1083 /* Previous character was a character escape? */
1084 if (tp
->tty_escaped
) {
1085 tp
->tty_escaped
= NOT_ESCAPED
;
1086 ch
|= IN_ESC
; /* protect character */
1089 /* LNEXT (^V) to escape the next character? */
1090 if (ch
== tp
->tty_termios
.c_cc
[VLNEXT
]) {
1091 tp
->tty_escaped
= ESCAPED
;
1094 continue; /* do not store the escape */
1097 /* REPRINT (^R) to reprint echoed characters? */
1098 if (ch
== tp
->tty_termios
.c_cc
[VREPRINT
]) {
1104 /* _POSIX_VDISABLE is a normal character value, so better escape it. */
1105 if (ch
== _POSIX_VDISABLE
) ch
|= IN_ESC
;
1107 /* Map CR to LF, ignore CR, or map LF to CR. */
1109 if (tp
->tty_termios
.c_iflag
& IGNCR
) continue;
1110 if (tp
->tty_termios
.c_iflag
& ICRNL
) ch
= '\n';
1113 if (tp
->tty_termios
.c_iflag
& INLCR
) ch
= '\r';
1116 /* Canonical mode? */
1117 if (tp
->tty_termios
.c_lflag
& ICANON
) {
1119 /* Erase processing (rub out of last character). */
1120 if (ch
== tp
->tty_termios
.c_cc
[VERASE
]) {
1121 (void) back_over(tp
);
1122 if (!(tp
->tty_termios
.c_lflag
& ECHOE
)) {
1123 (void) tty_echo(tp
, ch
);
1128 /* Kill processing (remove current line). */
1129 if (ch
== tp
->tty_termios
.c_cc
[VKILL
]) {
1130 while (back_over(tp
)) {}
1131 if (!(tp
->tty_termios
.c_lflag
& ECHOE
)) {
1132 (void) tty_echo(tp
, ch
);
1133 if (tp
->tty_termios
.c_lflag
& ECHOK
)
1139 /* EOF (^D) means end-of-file, an invisible "line break". */
1140 if (ch
== tp
->tty_termios
.c_cc
[VEOF
]) ch
|= IN_EOT
| IN_EOF
;
1142 /* The line may be returned to the user after an LF. */
1143 if (ch
== '\n') ch
|= IN_EOT
;
1145 /* Same thing with EOL, whatever it may be. */
1146 if (ch
== tp
->tty_termios
.c_cc
[VEOL
]) ch
|= IN_EOT
;
1149 /* Start/stop input control? */
1150 if (tp
->tty_termios
.c_iflag
& IXON
) {
1152 /* Output stops on STOP (^S). */
1153 if (ch
== tp
->tty_termios
.c_cc
[VSTOP
]) {
1154 tp
->tty_inhibited
= STOPPED
;
1159 /* Output restarts on START (^Q) or any character if IXANY. */
1160 if (tp
->tty_inhibited
) {
1161 if (ch
== tp
->tty_termios
.c_cc
[VSTART
]
1162 || (tp
->tty_termios
.c_iflag
& IXANY
)) {
1163 tp
->tty_inhibited
= RUNNING
;
1165 if (ch
== tp
->tty_termios
.c_cc
[VSTART
])
1171 if (tp
->tty_termios
.c_lflag
& ISIG
) {
1172 /* Check for INTR (^?) and QUIT (^\) characters. */
1173 if (ch
== tp
->tty_termios
.c_cc
[VINTR
]
1174 || ch
== tp
->tty_termios
.c_cc
[VQUIT
]) {
1176 if (ch
== tp
->tty_termios
.c_cc
[VQUIT
]) sig
= SIGQUIT
;
1177 sigchar(tp
, sig
, 1);
1178 (void) tty_echo(tp
, ch
);
1183 /* Is there space in the input buffer? */
1184 if (tp
->tty_incount
== buflen(tp
->tty_inbuf
)) {
1185 /* No space; discard in canonical mode, keep in raw mode. */
1186 if (tp
->tty_termios
.c_lflag
& ICANON
) continue;
1190 if (!(tp
->tty_termios
.c_lflag
& ICANON
)) {
1191 /* In raw mode all characters are "line breaks". */
1194 /* Start an inter-byte timer? */
1195 if (!timeset
&& tp
->tty_termios
.c_cc
[VMIN
] > 0
1196 && tp
->tty_termios
.c_cc
[VTIME
] > 0) {
1202 /* Perform the intricate function of echoing. */
1203 if (tp
->tty_termios
.c_lflag
& (ECHO
|ECHONL
)) ch
= tty_echo(tp
, ch
);
1205 /* Send processed byte of input unless scancodes sent instead */
1206 if (!(tp
->tty_termios
.c_iflag
& SCANCODES
)) {
1207 in_process_send_byte(tp
, ch
);
1213 /*===========================================================================*
1215 *===========================================================================*/
1216 PRIVATE
int tty_echo(tp
, ch
)
1217 register tty_t
*tp
; /* terminal on which to echo */
1218 register int ch
; /* pointer to character to echo */
1220 /* Echo the character if echoing is on. Some control characters are echoed
1221 * with their normal effect, other control characters are echoed as "^X",
1222 * normal characters are echoed normally. EOF (^D) is echoed, but immediately
1223 * backspaced over. Return the character with the echoed length added to its
1229 if (!(tp
->tty_termios
.c_lflag
& ECHO
)) {
1230 if (ch
== ('\n' | IN_EOT
) && (tp
->tty_termios
.c_lflag
1231 & (ICANON
|ECHONL
)) == (ICANON
|ECHONL
))
1232 (*tp
->tty_echo
)(tp
, '\n');
1236 /* "Reprint" tells if the echo output has been messed up by other output. */
1237 rp
= tp
->tty_incount
== 0 ? FALSE
: tp
->tty_reprint
;
1239 if ((ch
& IN_CHAR
) < ' ') {
1240 switch (ch
& (IN_ESC
|IN_EOF
|IN_EOT
|IN_CHAR
)) {
1244 (*tp
->tty_echo
)(tp
, ' ');
1246 } while (len
< TAB_SIZE
&& (tp
->tty_position
& TAB_MASK
) != 0);
1250 (*tp
->tty_echo
)(tp
, ch
& IN_CHAR
);
1254 (*tp
->tty_echo
)(tp
, '^');
1255 (*tp
->tty_echo
)(tp
, '@' + (ch
& IN_CHAR
));
1259 if ((ch
& IN_CHAR
) == '\177') {
1260 /* A DEL prints as "^?". */
1261 (*tp
->tty_echo
)(tp
, '^');
1262 (*tp
->tty_echo
)(tp
, '?');
1265 (*tp
->tty_echo
)(tp
, ch
& IN_CHAR
);
1268 if (ch
& IN_EOF
) while (len
> 0) { (*tp
->tty_echo
)(tp
, '\b'); len
--; }
1270 tp
->tty_reprint
= rp
;
1271 return(ch
| (len
<< IN_LSHIFT
));
1274 /*===========================================================================*
1276 *===========================================================================*/
1277 PRIVATE
void rawecho(tp
, ch
)
1281 /* Echo without interpretation if ECHO is set. */
1282 int rp
= tp
->tty_reprint
;
1283 if (tp
->tty_termios
.c_lflag
& ECHO
) (*tp
->tty_echo
)(tp
, ch
);
1284 tp
->tty_reprint
= rp
;
1287 /*===========================================================================*
1289 *===========================================================================*/
1290 PRIVATE
int back_over(tp
)
1293 /* Backspace to previous character on screen and erase it. */
1297 if (tp
->tty_incount
== 0) return(0); /* queue empty */
1298 head
= tp
->tty_inhead
;
1299 if (head
== tp
->tty_inbuf
) head
= bufend(tp
->tty_inbuf
);
1300 if (*--head
& IN_EOT
) return(0); /* can't erase "line breaks" */
1301 if (tp
->tty_reprint
) reprint(tp
); /* reprint if messed up */
1302 tp
->tty_inhead
= head
;
1304 if (tp
->tty_termios
.c_lflag
& ECHOE
) {
1305 len
= (*head
& IN_LEN
) >> IN_LSHIFT
;
1313 return(1); /* one character erased */
1316 /*===========================================================================*
1318 *===========================================================================*/
1319 PRIVATE
void reprint(tp
)
1320 register tty_t
*tp
; /* pointer to tty struct */
1322 /* Restore what has been echoed to screen before if the user input has been
1323 * messed up by output, or if REPRINT (^R) is typed.
1328 tp
->tty_reprint
= FALSE
;
1330 /* Find the last line break in the input. */
1331 head
= tp
->tty_inhead
;
1332 count
= tp
->tty_incount
;
1334 if (head
== tp
->tty_inbuf
) head
= bufend(tp
->tty_inbuf
);
1335 if (head
[-1] & IN_EOT
) break;
1339 if (count
== tp
->tty_incount
) return; /* no reason to reprint */
1341 /* Show REPRINT (^R) and move to a new line. */
1342 (void) tty_echo(tp
, tp
->tty_termios
.c_cc
[VREPRINT
] | IN_ESC
);
1346 /* Reprint from the last break onwards. */
1348 if (head
== bufend(tp
->tty_inbuf
)) head
= tp
->tty_inbuf
;
1349 *head
= tty_echo(tp
, *head
);
1352 } while (count
< tp
->tty_incount
);
1355 /*===========================================================================*
1357 *===========================================================================*/
1358 PUBLIC
void out_process(tp
, bstart
, bpos
, bend
, icount
, ocount
)
1360 char *bstart
, *bpos
, *bend
; /* start/pos/end of circular buffer */
1361 int *icount
; /* # input chars / input chars used */
1362 int *ocount
; /* max output chars / output chars used */
1364 /* Perform output processing on a circular buffer. *icount is the number of
1365 * bytes to process, and the number of bytes actually processed on return.
1366 * *ocount is the space available on input and the space used on output.
1367 * (Naturally *icount < *ocount.) The column position is updated modulo
1368 * the TAB size, because we really only need it for tabs.
1374 int pos
= tp
->tty_position
;
1387 if ((tp
->tty_termios
.c_oflag
& (OPOST
|ONLCR
))
1389 /* Map LF to CR+LF if there is space. Note that the
1390 * next character in the buffer is overwritten, so
1391 * we stop at this point.
1395 if (++bpos
== bend
) bpos
= bstart
;
1401 goto out_done
; /* no space or buffer got changed */
1405 /* Best guess for the tab length. */
1406 tablen
= TAB_SIZE
- (pos
& TAB_MASK
);
1408 if ((tp
->tty_termios
.c_oflag
& (OPOST
|XTABS
))
1410 /* Tabs must be expanded. */
1411 if (oct
>= tablen
) {
1417 if (++bpos
== bend
) bpos
= bstart
;
1418 } while (--tablen
!= 0);
1422 /* Tabs are output directly. */
1426 /* Assume any other character prints as one character. */
1429 if (++bpos
== bend
) bpos
= bstart
;
1434 tp
->tty_position
= pos
& TAB_MASK
;
1436 *icount
-= ict
; /* [io]ct are the number of chars not used */
1437 *ocount
-= oct
; /* *[io]count are the number of chars that are used */
1440 /*===========================================================================*
1442 *===========================================================================*/
1443 PRIVATE
void dev_ioctl(tp
)
1446 /* The ioctl's TCSETSW, TCSETSF and TCDRAIN wait for output to finish to make
1447 * sure that an attribute change doesn't affect the processing of current
1448 * output. Once output finishes the ioctl is executed as in do_ioctl().
1450 int result
= EINVAL
;
1452 if (tp
->tty_outleft
> 0) return; /* output not finished */
1454 if (tp
->tty_ioreq
!= TCDRAIN
) {
1455 if (tp
->tty_ioreq
== TCSETSF
) tty_icancel(tp
);
1456 if(tp
->tty_io_safe
) {
1457 result
= sys_safecopyfrom(tp
->tty_ioproc
, tp
->tty_iovir_g
, 0,
1458 (vir_bytes
) &tp
->tty_termios
,
1459 (vir_bytes
) sizeof(tp
->tty_termios
), D
);
1461 result
= sys_vircopy(tp
->tty_ioproc
, D
, tp
->tty_iovir_g
,
1462 SELF
, D
, (vir_bytes
) &tp
->tty_termios
,
1463 (vir_bytes
) sizeof(tp
->tty_termios
));
1468 notify(tp
->tty_iocaller
);
1469 tp
->tty_iorevived
= 1;
1470 tp
->tty_iostatus
= result
;
1473 /*===========================================================================*
1475 *===========================================================================*/
1476 PRIVATE
void setattr(tp
)
1479 /* Apply the new line attributes (raw/canonical, line speed, etc.) */
1483 if (!(tp
->tty_termios
.c_lflag
& ICANON
)) {
1484 /* Raw mode; put a "line break" on all characters in the input queue.
1485 * It is undefined what happens to the input queue when ICANON is
1486 * switched off, a process should use TCSAFLUSH to flush the queue.
1487 * Keeping the queue to preserve typeahead is the Right Thing, however
1488 * when a process does use TCSANOW to switch to raw mode.
1490 count
= tp
->tty_eotct
= tp
->tty_incount
;
1491 inp
= tp
->tty_intail
;
1494 if (++inp
== bufend(tp
->tty_inbuf
)) inp
= tp
->tty_inbuf
;
1499 /* Inspect MIN and TIME. */
1500 settimer(tp
, FALSE
);
1501 if (tp
->tty_termios
.c_lflag
& ICANON
) {
1502 /* No MIN & TIME in canonical mode. */
1505 /* In raw mode MIN is the number of chars wanted, and TIME how long
1506 * to wait for them. With interesting exceptions if either is zero.
1508 tp
->tty_min
= tp
->tty_termios
.c_cc
[VMIN
];
1509 if (tp
->tty_min
== 0 && tp
->tty_termios
.c_cc
[VTIME
] > 0)
1513 if (!(tp
->tty_termios
.c_iflag
& IXON
)) {
1514 /* No start/stop output control, so don't leave output inhibited. */
1515 tp
->tty_inhibited
= RUNNING
;
1519 /* Setting the output speed to zero hangs up the phone. */
1520 if (tp
->tty_termios
.c_ospeed
== B0
) sigchar(tp
, SIGHUP
, 1);
1522 /* SCANCODES is supported only for the console */
1523 if (!isconsole(tp
)) tp
->tty_termios
.c_iflag
&= ~SCANCODES
;
1525 /* Set new line speed, character size, etc at the device level. */
1526 (*tp
->tty_ioctl
)(tp
, 0);
1529 /*===========================================================================*
1531 *===========================================================================*/
1534 file
, line
, code
, replyee
, proc_nr
, status
)
1537 int code
; /* TASK_REPLY or REVIVE */
1538 int replyee
; /* destination address for the reply */
1539 int proc_nr
; /* to whom should the reply go? */
1540 int status
; /* reply code */
1542 /* Send a reply to a process that wanted to read or write data. */
1545 tty_mess
.m_type
= code
;
1546 tty_mess
.REP_ENDPT
= proc_nr
;
1547 tty_mess
.REP_STATUS
= status
;
1549 /* TTY is not supposed to send a TTY_REVIVE message. The
1550 * REVIVE message is gone, TTY_REVIVE is only used as an internal
1551 * placeholder for something that is not supposed to be a message.
1553 if(code
== TTY_REVIVE
) {
1554 printf("%s:%d: ", file
, line
);
1555 panic("tty_reply sending TTY_REVIVE");
1558 status
= sendnb(replyee
, &tty_mess
);
1560 printf("tty`tty_reply: send to %d failed: %d\n", replyee
, status
);
1563 /*===========================================================================*
1565 *===========================================================================*/
1566 PUBLIC
void sigchar(tp
, sig
, mayflush
)
1568 int sig
; /* SIGINT, SIGQUIT, SIGKILL or SIGHUP */
1571 /* Process a SIGINT, SIGQUIT or SIGKILL char from the keyboard or SIGHUP from
1572 * a tty close, "stty 0", or a real RS-232 hangup. MM will send the signal to
1573 * the process group (INT, QUIT), all processes (KILL), or the session leader
1578 if (tp
->tty_pgrp
!= 0) {
1579 if (OK
!= (status
= sys_kill(tp
->tty_pgrp
, sig
))) {
1580 panic("Error; call to sys_kill failed: %d", status
);
1584 if (mayflush
&& !(tp
->tty_termios
.c_lflag
& NOFLSH
)) {
1585 tp
->tty_incount
= tp
->tty_eotct
= 0; /* kill earlier input */
1586 tp
->tty_intail
= tp
->tty_inhead
;
1587 (*tp
->tty_ocancel
)(tp
, 0); /* kill all output */
1588 tp
->tty_inhibited
= RUNNING
;
1593 /*===========================================================================*
1595 *===========================================================================*/
1596 PRIVATE
void tty_icancel(tp
)
1599 /* Discard all pending input, tty buffer or device. */
1601 tp
->tty_incount
= tp
->tty_eotct
= 0;
1602 tp
->tty_intail
= tp
->tty_inhead
;
1603 (*tp
->tty_icancel
)(tp
, 0);
1606 /*===========================================================================*
1608 *===========================================================================*/
1609 PRIVATE
int tty_devnop(tty_t
*tp
, int try)
1611 /* Some functions need not be implemented at the device level. */
1615 /*===========================================================================*
1617 *===========================================================================*/
1618 PRIVATE
void tty_init()
1620 /* Initialize tty structure and call device initialization routines. */
1625 system_hz
= sys_hz();
1627 /* Initialize the terminal lines. */
1628 for (tp
= FIRST_TTY
,s
=0; tp
< END_TTY
; tp
++,s
++) {
1632 tmr_inittimer(&tp
->tty_tmr
);
1634 tp
->tty_intail
= tp
->tty_inhead
= tp
->tty_inbuf
;
1636 tp
->tty_termios
= termios_defaults
;
1637 tp
->tty_icancel
= tp
->tty_ocancel
= tp
->tty_ioctl
= tp
->tty_close
=
1639 if (tp
< tty_addr(NR_CONS
)) {
1642 /* Initialize the keyboard driver. */
1645 tp
->tty_minor
= CONS_MINOR
+ s
;
1647 if (tp
< tty_addr(NR_CONS
+NR_RS_LINES
)) {
1649 tp
->tty_minor
= RS232_MINOR
+ s
-NR_CONS
;
1652 tp
->tty_minor
= s
- (NR_CONS
+NR_RS_LINES
) + TTYPX_MINOR
;
1658 /*===========================================================================*
1660 *===========================================================================*/
1661 PRIVATE
void tty_timed_out(timer_t
*tp
)
1663 /* This timer has expired. Set the events flag, to force processing. */
1665 tty_ptr
= &tty_table
[tmr_arg(tp
)->ta_int
];
1666 tty_ptr
->tty_min
= 0; /* force read to succeed */
1667 tty_ptr
->tty_events
= 1;
1670 /*===========================================================================*
1672 *===========================================================================*/
1673 PRIVATE
void expire_timers(void)
1675 /* A synchronous alarm message was received. Check if there are any expired
1676 * timers. Possibly set the event flag and reschedule another alarm.
1678 clock_t now
; /* current time */
1681 /* Get the current time to compare the timers against. */
1682 if ((s
=getuptime(&now
)) != OK
)
1683 panic("Couldn't get uptime from clock: %d", s
);
1685 /* Scan the queue of timers for expired timers. This dispatch the watchdog
1686 * functions of expired timers. Possibly a new alarm call must be scheduled.
1688 tmrs_exptimers(&tty_timers
, now
, NULL
);
1689 if (tty_timers
== NULL
) tty_next_timeout
= TMR_NEVER
;
1690 else { /* set new sync alarm */
1691 tty_next_timeout
= tty_timers
->tmr_exp_time
;
1692 if ((s
=sys_setalarm(tty_next_timeout
, 1)) != OK
)
1693 panic("Couldn't set synchronous alarm: %d", s
);
1697 /*===========================================================================*
1699 *===========================================================================*/
1700 PRIVATE
void settimer(tty_ptr
, enable
)
1701 tty_t
*tty_ptr
; /* line to set or unset a timer on */
1702 int enable
; /* set timer if true, otherwise unset */
1704 clock_t now
; /* current time */
1708 /* Get the current time to calculate the timeout time. */
1709 if ((s
=getuptime(&now
)) != OK
)
1710 panic("Couldn't get uptime from clock: %d", s
);
1712 exp_time
= now
+ tty_ptr
->tty_termios
.c_cc
[VTIME
] * (system_hz
/10);
1713 /* Set a new timer for enabling the TTY events flags. */
1714 tmrs_settimer(&tty_timers
, &tty_ptr
->tty_tmr
,
1715 exp_time
, tty_timed_out
, NULL
);
1717 /* Remove the timer from the active and expired lists. */
1718 tmrs_clrtimer(&tty_timers
, &tty_ptr
->tty_tmr
, NULL
);
1721 /* Now check if a new alarm must be scheduled. This happens when the front
1722 * of the timers queue was disabled or reinserted at another position, or
1723 * when a new timer was added to the front.
1725 if (tty_timers
== NULL
) tty_next_timeout
= TMR_NEVER
;
1726 else if (tty_timers
->tmr_exp_time
!= tty_next_timeout
) {
1727 tty_next_timeout
= tty_timers
->tmr_exp_time
;
1728 if ((s
=sys_setalarm(tty_next_timeout
, 1)) != OK
)
1729 panic("Couldn't set synchronous alarm: %d", s
);
1733 /*===========================================================================*
1735 *===========================================================================*/
1736 PRIVATE
void do_select(tp
, m_ptr
)
1737 register tty_t
*tp
; /* pointer to tty struct */
1738 register message
*m_ptr
; /* pointer to message sent to the task */
1740 int ops
, ready_ops
= 0, watch
;
1742 ops
= m_ptr
->IO_ENDPT
& (SEL_RD
|SEL_WR
|SEL_ERR
);
1743 watch
= (m_ptr
->IO_ENDPT
& SEL_NOTIFY
) ? 1 : 0;
1745 ready_ops
= select_try(tp
, ops
);
1747 if (!ready_ops
&& ops
&& watch
) {
1748 tp
->tty_select_ops
|= ops
;
1749 tp
->tty_select_proc
= m_ptr
->m_source
;
1752 tty_reply(TASK_REPLY
, m_ptr
->m_source
, m_ptr
->IO_ENDPT
, ready_ops
);