1 /* pty.c - pseudo terminal driver Author: Kees J. Bot
3 * PTYs can be seen as a bidirectional pipe with TTY
4 * input and output processing. For example a simple rlogin session:
6 * keyboard -> rlogin -> in.rld -> /dev/ptypX -> /dev/ttypX -> shell
7 * shell -> /dev/ttypX -> /dev/ptypX -> in.rld -> rlogin -> screen
9 * This file takes care of copying data between the tty/pty device pairs and
10 * the open/read/write/close calls on the pty devices. The TTY task takes
11 * care of the input and output processing (interrupt, backspace, raw I/O,
12 * etc.) using the pty_read() and pty_write() functions as the "keyboard" and
13 * "screen" functions of the ttypX devices.
14 * Be careful when reading this code, the terms "reading" and "writing" are
15 * used both for the tty and the pty end of the pseudo tty. Writes to one
16 * end are to be read at the other end and vice-versa.
19 #include "../drivers.h"
23 #include <minix/com.h>
24 #include <minix/callnr.h>
25 #include <sys/select.h>
30 /* PTY bookkeeping structure, one per pty/tty pair. */
32 tty_t
*tty
; /* associated TTY structure */
33 char state
; /* flags: busy, closed, ... */
35 /* Read call on /dev/ptypX. */
36 char rdsendreply
; /* send a reply (instead of notify) */
37 int rdcaller
; /* process making the call (usually FS) */
38 int rdproc
; /* process that wants to read from the pty */
39 vir_bytes rdvir_g
; /* virtual address in readers address space */
40 vir_bytes rdvir_offset
; /* offset in above grant */
41 int rdsafe
; /* safe read mode? */
42 int rdleft
; /* # bytes yet to be read */
43 int rdcum
; /* # bytes written so far */
45 /* Write call to /dev/ptypX. */
46 char wrsendreply
; /* send a reply (instead of notify) */
47 int wrcaller
; /* process making the call (usually FS) */
48 int wrproc
; /* process that wants to write to the pty */
49 vir_bytes wrvir_g
; /* virtual address in writers address space */
50 vir_bytes wrvir_offset
; /* offset in above grant */
51 int wrsafe
; /* safe write mode? */
52 int wrleft
; /* # bytes yet to be written */
53 int wrcum
; /* # bytes written so far */
56 int ocount
; /* # characters in the buffer */
57 char *ohead
, *otail
; /* head and tail of the circular buffer */
58 char obuf
[2048]; /* buffer for bytes going to the pty reader */
61 int select_ops
, /* Which operations do we want to know about? */
62 select_proc
, /* Who wants to know about it? */
63 select_ready_ops
; /* For callback. */
66 #define PTY_ACTIVE 0x01 /* pty is open/active */
67 #define TTY_CLOSED 0x02 /* tty side has closed down */
68 #define PTY_CLOSED 0x04 /* pty side has closed down */
70 PRIVATE pty_t pty_table
[NR_PTYS
]; /* PTY bookkeeping */
72 FORWARD
_PROTOTYPE( int pty_write
, (tty_t
*tp
, int try) );
73 FORWARD
_PROTOTYPE( void pty_echo
, (tty_t
*tp
, int c
) );
74 FORWARD
_PROTOTYPE( void pty_start
, (pty_t
*pp
) );
75 FORWARD
_PROTOTYPE( void pty_finish
, (pty_t
*pp
) );
76 FORWARD
_PROTOTYPE( int pty_read
, (tty_t
*tp
, int try) );
77 FORWARD
_PROTOTYPE( int pty_close
, (tty_t
*tp
, int try) );
78 FORWARD
_PROTOTYPE( int pty_icancel
, (tty_t
*tp
, int try) );
79 FORWARD
_PROTOTYPE( int pty_ocancel
, (tty_t
*tp
, int try) );
80 FORWARD
_PROTOTYPE( int pty_select
, (tty_t
*tp
, message
*m
) );
82 /*===========================================================================*
84 *===========================================================================*/
85 PUBLIC
void do_pty(tp
, m_ptr
)
89 /* Perform an open/close/read/write call on a /dev/ptypX device. */
90 pty_t
*pp
= tp
->tty_priv
;
94 switch (m_ptr
->m_type
) {
97 /* Check, store information on the reader, do I/O. */
98 if (pp
->state
& TTY_CLOSED
) {
102 if (pp
->rdleft
!= 0 || pp
->rdcum
!= 0) {
106 if (m_ptr
->COUNT
<= 0) {
110 pp
->rdsendreply
= TRUE
;
111 pp
->rdcaller
= m_ptr
->m_source
;
112 pp
->rdproc
= m_ptr
->IO_ENDPT
;
113 pp
->rdvir_g
= (vir_bytes
) m_ptr
->ADDRESS
;
114 pp
->rdvir_offset
= 0;
116 pp
->rdleft
= m_ptr
->COUNT
;
119 if (pp
->rdleft
== 0) {
120 return; /* already done */
124 r
= SUSPEND
; /* do suspend */
125 pp
->rdsendreply
= FALSE
;
131 /* Check, store information on the writer, do I/O. */
132 if (pp
->state
& TTY_CLOSED
) {
136 if (pp
->wrleft
!= 0 || pp
->wrcum
!= 0) {
140 if (m_ptr
->COUNT
<= 0) {
144 pp
->wrsendreply
= TRUE
;
145 pp
->wrcaller
= m_ptr
->m_source
;
146 pp
->wrproc
= m_ptr
->IO_ENDPT
;
147 pp
->wrvir_g
= (vir_bytes
) m_ptr
->ADDRESS
;
148 pp
->wrvir_offset
= 0;
150 pp
->wrleft
= m_ptr
->COUNT
;
152 if (pp
->wrleft
== 0) {
153 return; /* already done */
157 pp
->wrsendreply
= FALSE
; /* do suspend */
163 r
= pp
->state
!= 0 ? EIO
: OK
;
164 pp
->state
|= PTY_ACTIVE
;
171 if (pp
->state
& TTY_CLOSED
) {
174 pp
->state
|= PTY_CLOSED
;
175 sigchar(tp
, SIGHUP
, 1);
180 r
= pty_select(tp
, m_ptr
);
185 if (m_ptr
->IO_ENDPT
== pp
->rdproc
) {
186 /* Cancel a read from a PTY. */
187 r
= pp
->rdcum
> 0 ? pp
->rdcum
: EAGAIN
;
188 pp
->rdleft
= pp
->rdcum
= 0;
190 if (m_ptr
->IO_ENDPT
== pp
->wrproc
) {
191 /* Cancel a write to a PTY. */
192 r
= pp
->wrcum
> 0 ? pp
->wrcum
: EAGAIN
;
193 pp
->wrleft
= pp
->wrcum
= 0;
200 tty_reply(TASK_REPLY
, m_ptr
->m_source
, m_ptr
->IO_ENDPT
, r
);
203 /*===========================================================================*
205 *===========================================================================*/
206 PRIVATE
int pty_write(tp
, try)
210 /* (*dev_write)() routine for PTYs. Transfer bytes from the writer on
211 * /dev/ttypX to the output buffer.
213 pty_t
*pp
= tp
->tty_priv
;
214 int count
, ocount
, s
;
217 /* PTY closed down? */
218 if (pp
->state
& PTY_CLOSED
) {
220 if (tp
->tty_outleft
> 0) {
221 if(tp
->tty_outrepcode
== TTY_REVIVE
) {
222 notify(tp
->tty_outcaller
);
223 tp
->tty_outrevived
= 1;
225 tty_reply(tp
->tty_outrepcode
, tp
->tty_outcaller
,
226 tp
->tty_outproc
, EIO
);
227 tp
->tty_outleft
= tp
->tty_outcum
= 0;
233 /* While there is something to do. */
235 ocount
= buflen(pp
->obuf
) - pp
->ocount
;
236 if (try) return (ocount
> 0);
237 count
= bufend(pp
->obuf
) - pp
->ohead
;
238 if (count
> ocount
) count
= ocount
;
239 if (count
> tp
->tty_outleft
) count
= tp
->tty_outleft
;
240 if (count
== 0 || tp
->tty_inhibited
)
243 /* Copy from user space to the PTY output buffer. */
244 if(tp
->tty_out_safe
) {
245 if ((s
= sys_safecopyfrom(tp
->tty_outproc
, tp
->tty_out_vir_g
,
246 tp
->tty_out_vir_offset
, (vir_bytes
) pp
->ohead
, count
, D
))!=OK
) {
250 if ((s
= sys_vircopy(tp
->tty_outproc
, D
, (vir_bytes
) tp
->tty_out_vir_g
,
251 SELF
, D
, (vir_bytes
) pp
->ohead
, (phys_bytes
) count
)) != OK
) {
256 /* Perform output processing on the output buffer. */
257 out_process(tp
, pp
->obuf
, pp
->ohead
, bufend(pp
->obuf
), &count
, &ocount
);
258 if (count
== 0) break;
260 /* Assume echoing messed up by output. */
261 tp
->tty_reprint
= TRUE
;
264 pp
->ocount
+= ocount
;
265 if ((pp
->ohead
+= ocount
) >= bufend(pp
->obuf
))
266 pp
->ohead
-= buflen(pp
->obuf
);
269 if(tp
->tty_out_safe
) tp
->tty_out_vir_offset
+= count
;
270 else tp
->tty_out_vir_g
+= count
;
272 tp
->tty_outcum
+= count
;
273 if ((tp
->tty_outleft
-= count
) == 0) {
274 /* Output is finished, reply to the writer. */
275 if(tp
->tty_outrepcode
== TTY_REVIVE
) {
276 notify(tp
->tty_outcaller
);
277 tp
->tty_outrevived
= 1;
279 tty_reply(tp
->tty_outrepcode
, tp
->tty_outcaller
,
280 tp
->tty_outproc
, tp
->tty_outcum
);
289 /*===========================================================================*
291 *===========================================================================*/
292 PRIVATE
void pty_echo(tp
, c
)
296 /* Echo one character. (Like pty_write, but only one character, optionally.) */
298 pty_t
*pp
= tp
->tty_priv
;
301 ocount
= buflen(pp
->obuf
) - pp
->ocount
;
302 if (ocount
== 0) return; /* output buffer full */
304 *pp
->ohead
= c
; /* add one character */
306 out_process(tp
, pp
->obuf
, pp
->ohead
, bufend(pp
->obuf
), &count
, &ocount
);
307 if (count
== 0) return;
309 pp
->ocount
+= ocount
;
310 if ((pp
->ohead
+= ocount
) >= bufend(pp
->obuf
)) pp
->ohead
-= buflen(pp
->obuf
);
314 /*===========================================================================*
316 *===========================================================================*/
317 PRIVATE
void pty_start(pp
)
320 /* Transfer bytes written to the output buffer to the PTY reader. */
323 /* While there are things to do. */
326 count
= bufend(pp
->obuf
) - pp
->otail
;
327 if (count
> pp
->ocount
) count
= pp
->ocount
;
328 if (count
> pp
->rdleft
) count
= pp
->rdleft
;
329 if (count
== 0) break;
331 /* Copy from the output buffer to the readers address space. */
333 if((s
= sys_safecopyto(pp
->rdproc
, pp
->rdvir_g
,
334 pp
->rdvir_offset
, (vir_bytes
) pp
->otail
, count
, D
)) != OK
) {
337 pp
->rdvir_offset
+= count
;
339 if ((s
= sys_vircopy(SELF
, D
, (vir_bytes
)pp
->otail
,
340 (vir_bytes
) pp
->rdproc
, D
, (vir_bytes
) pp
->rdvir_g
, (phys_bytes
) count
)) != OK
) {
341 printf("pty tty: copy failed (error %d)\n", s
);
344 pp
->rdvir_g
+= count
;
349 if ((pp
->otail
+= count
) == bufend(pp
->obuf
)) pp
->otail
= pp
->obuf
;
355 /*===========================================================================*
357 *===========================================================================*/
358 PRIVATE
void pty_finish(pp
)
361 /* Finish the read request of a PTY reader if there is at least one byte
365 if (pp
->rdsendreply
) {
366 tty_reply(TASK_REPLY
, pp
->rdcaller
, pp
->rdproc
, pp
->rdcum
);
367 pp
->rdleft
= pp
->rdcum
= 0;
370 notify(pp
->rdcaller
);
375 /*===========================================================================*
377 *===========================================================================*/
378 PRIVATE
int pty_read(tp
, try)
382 /* Offer bytes from the PTY writer for input on the TTY. (Do it one byte at
383 * a time, 99% of the writes will be for one byte, so no sense in being smart.)
385 pty_t
*pp
= tp
->tty_priv
;
388 if (pp
->state
& PTY_CLOSED
) {
390 if (tp
->tty_inleft
> 0) {
391 if(tp
->tty_inrepcode
== TTY_REVIVE
) {
392 notify(tp
->tty_incaller
);
393 tp
->tty_inrevived
= 1;
395 tty_reply(tp
->tty_inrepcode
, tp
->tty_incaller
,
396 tp
->tty_inproc
, tp
->tty_incum
);
397 tp
->tty_inleft
= tp
->tty_incum
= 0;
409 while (pp
->wrleft
> 0) {
412 /* Transfer one character to 'c'. */
414 if ((s
= sys_safecopyfrom(pp
->wrproc
, pp
->wrvir_g
,
415 pp
->wrvir_offset
, (vir_bytes
) &c
, 1, D
)) != OK
) {
416 printf("pty: safecopy failed (error %d)\n", s
);
421 if ((s
= sys_vircopy(pp
->wrproc
, D
, (vir_bytes
) pp
->wrvir_g
,
422 SELF
, D
, (vir_bytes
) &c
, (phys_bytes
) 1)) != OK
) {
423 printf("pty: copy failed (error %d)\n", s
);
429 /* Input processing. */
430 if (in_process(tp
, &c
, 1) == 0) break;
432 /* PTY writer bookkeeping. */
434 if (--pp
->wrleft
== 0) {
435 if (pp
->wrsendreply
) {
436 tty_reply(TASK_REPLY
, pp
->wrcaller
, pp
->wrproc
,
441 notify(pp
->wrcaller
);
448 /*===========================================================================*
450 *===========================================================================*/
451 PRIVATE
int pty_close(tp
, try)
455 /* The tty side has closed, so shut down the pty side. */
456 pty_t
*pp
= tp
->tty_priv
;
458 if (!(pp
->state
& PTY_ACTIVE
)) return 0;
460 if (pp
->rdleft
> 0) {
461 assert(!pp
->rdsendreply
);
462 notify(pp
->rdcaller
);
465 if (pp
->wrleft
> 0) {
466 assert(!pp
->wrsendreply
);
467 notify(pp
->wrcaller
);
470 if (pp
->state
& PTY_CLOSED
) pp
->state
= 0; else pp
->state
|= TTY_CLOSED
;
475 /*===========================================================================*
477 *===========================================================================*/
478 PRIVATE
int pty_icancel(tp
, try)
482 /* Discard waiting input. */
483 pty_t
*pp
= tp
->tty_priv
;
485 if (pp
->wrleft
> 0) {
486 pp
->wrcum
+= pp
->wrleft
;
488 notify(pp
->wrcaller
);
494 /*===========================================================================*
496 *===========================================================================*/
497 PRIVATE
int pty_ocancel(tp
, try)
501 /* Drain the output buffer. */
502 pty_t
*pp
= tp
->tty_priv
;
505 pp
->otail
= pp
->ohead
;
510 /*===========================================================================*
512 *===========================================================================*/
513 PUBLIC
void pty_init(tp
)
519 /* Associate PTY and TTY structures. */
520 line
= tp
- &tty_table
[NR_CONS
+ NR_RS_LINES
];
521 pp
= tp
->tty_priv
= &pty_table
[line
];
525 /* Set up output queue. */
526 pp
->ohead
= pp
->otail
= pp
->obuf
;
528 /* Fill in TTY function hooks. */
529 tp
->tty_devread
= pty_read
;
530 tp
->tty_devwrite
= pty_write
;
531 tp
->tty_echo
= pty_echo
;
532 tp
->tty_icancel
= pty_icancel
;
533 tp
->tty_ocancel
= pty_ocancel
;
534 tp
->tty_close
= pty_close
;
535 tp
->tty_select_ops
= 0;
538 /*===========================================================================*
540 *===========================================================================*/
541 PUBLIC
int pty_status(message
*m_ptr
)
547 for (i
= 0, pp
= pty_table
; i
<NR_PTYS
; i
++, pp
++) {
548 if ((((pp
->state
& TTY_CLOSED
) && pp
->rdleft
> 0) ||
550 pp
->rdcaller
== m_ptr
->m_source
)
552 m_ptr
->m_type
= DEV_REVIVE
;
553 m_ptr
->REP_ENDPT
= pp
->rdproc
;
554 m_ptr
->REP_IO_GRANT
= pp
->rdvir_g
;
555 m_ptr
->REP_STATUS
= pp
->rdcum
;
557 pp
->rdleft
= pp
->rdcum
= 0;
562 if ((((pp
->state
& TTY_CLOSED
) && pp
->wrleft
> 0) ||
564 pp
->wrcaller
== m_ptr
->m_source
)
566 m_ptr
->m_type
= DEV_REVIVE
;
567 m_ptr
->REP_ENDPT
= pp
->wrproc
;
568 m_ptr
->REP_IO_GRANT
= pp
->wrvir_g
;
570 m_ptr
->REP_STATUS
= EIO
;
572 m_ptr
->REP_STATUS
= pp
->wrcum
;
574 pp
->wrleft
= pp
->wrcum
= 0;
579 if (pp
->select_ready_ops
&& pp
->select_proc
== m_ptr
->m_source
) {
580 m_ptr
->m_type
= DEV_IO_READY
;
581 m_ptr
->DEV_MINOR
= PTYPX_MINOR
+ i
;
582 m_ptr
->DEV_SEL_OPS
= pp
->select_ready_ops
;
583 pp
->select_ready_ops
= 0;
591 /*===========================================================================*
593 *===========================================================================*/
594 PRIVATE
int select_try_pty(tty_t
*tp
, int ops
)
596 pty_t
*pp
= tp
->tty_priv
;
600 /* Write won't block on error. */
601 if (pp
->state
& TTY_CLOSED
) r
|= SEL_WR
;
602 else if (pp
->wrleft
!= 0 || pp
->wrcum
!= 0) r
|= SEL_WR
;
607 /* Read won't block on error. */
608 if (pp
->state
& TTY_CLOSED
) r
|= SEL_RD
;
609 else if (pp
->rdleft
!= 0 || pp
->rdcum
!= 0) r
|= SEL_RD
;
610 else if (pp
->ocount
> 0) r
|= SEL_RD
; /* Actual data. */
616 /*===========================================================================*
618 *===========================================================================*/
619 PUBLIC
void select_retry_pty(tty_t
*tp
)
621 pty_t
*pp
= tp
->tty_priv
;
624 /* See if the pty side of a pty is ready to return a select. */
625 if (pp
->select_ops
&& (r
=select_try_pty(tp
, pp
->select_ops
))) {
626 pp
->select_ops
&= ~r
;
627 pp
->select_ready_ops
|= r
;
628 notify(pp
->select_proc
);
632 /*===========================================================================*
634 *===========================================================================*/
635 PRIVATE
int pty_select(tty_t
*tp
, message
*m
)
637 pty_t
*pp
= tp
->tty_priv
;
638 int ops
, ready_ops
= 0, watch
;
640 ops
= m
->IO_ENDPT
& (SEL_RD
|SEL_WR
|SEL_ERR
);
641 watch
= (m
->IO_ENDPT
& SEL_NOTIFY
) ? 1 : 0;
643 ready_ops
= select_try_pty(tp
, ops
);
645 if (!ready_ops
&& ops
&& watch
) {
646 pp
->select_ops
|= ops
;
647 pp
->select_proc
= m
->m_source
;
653 #endif /* NR_PTYS > 0 */