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
;
95 switch (m_ptr
->m_type
) {
98 /* Check, store information on the reader, do I/O. */
99 if (pp
->state
& TTY_CLOSED
) {
103 if (pp
->rdleft
!= 0 || pp
->rdcum
!= 0) {
107 if (m_ptr
->COUNT
<= 0) {
112 if (numap_local(m_ptr
->IO_ENDPT
, (vir_bytes
) m_ptr
->ADDRESS
,
113 m_ptr
->COUNT
) == 0) {
115 if ((r
= sys_umap(m_ptr
->IO_ENDPT
, D
, (vir_bytes
) m_ptr
->ADDRESS
,
116 m_ptr
->COUNT
, &p
)) != OK
) {
120 pp
->rdsendreply
= TRUE
;
121 pp
->rdcaller
= m_ptr
->m_source
;
122 pp
->rdproc
= m_ptr
->IO_ENDPT
;
123 pp
->rdvir_g
= (vir_bytes
) m_ptr
->ADDRESS
;
124 pp
->rdvir_offset
= 0;
126 pp
->rdleft
= m_ptr
->COUNT
;
129 if (pp
->rdleft
== 0) {
130 return; /* already done */
134 if (m_ptr
->TTY_FLAGS
& O_NONBLOCK
) {
135 r
= EAGAIN
; /* don't suspend */
136 pp
->rdleft
= pp
->rdcum
= 0;
140 r
= SUSPEND
; /* do suspend */
141 pp
->rdsendreply
= FALSE
;
147 /* Check, store information on the writer, do I/O. */
148 if (pp
->state
& TTY_CLOSED
) {
152 if (pp
->wrleft
!= 0 || pp
->wrcum
!= 0) {
156 if (m_ptr
->COUNT
<= 0) {
161 if (numap_local(m_ptr
->IO_ENDPT
, (vir_bytes
) m_ptr
->ADDRESS
,
162 m_ptr
->COUNT
) == 0) {
165 if ((r
= sys_umap(m_ptr
->IO_ENDPT
, D
, (vir_bytes
) m_ptr
->ADDRESS
,
166 m_ptr
->COUNT
, &p
)) != OK
) {
170 pp
->wrsendreply
= TRUE
;
171 pp
->wrcaller
= m_ptr
->m_source
;
172 pp
->wrproc
= m_ptr
->IO_ENDPT
;
173 pp
->wrvir_g
= (vir_bytes
) m_ptr
->ADDRESS
;
174 pp
->wrvir_offset
= 0;
176 pp
->wrleft
= m_ptr
->COUNT
;
178 if (pp
->wrleft
== 0) {
179 return; /* already done */
183 if (m_ptr
->TTY_FLAGS
& O_NONBLOCK
) { /* don't suspend */
184 r
= pp
->wrcum
> 0 ? pp
->wrcum
: EAGAIN
;
185 pp
->wrleft
= pp
->wrcum
= 0;
189 pp
->wrsendreply
= FALSE
; /* do suspend */
195 r
= pp
->state
!= 0 ? EIO
: OK
;
196 pp
->state
|= PTY_ACTIVE
;
203 if (pp
->state
& TTY_CLOSED
) {
206 pp
->state
|= PTY_CLOSED
;
212 r
= pty_select(tp
, m_ptr
);
217 if (m_ptr
->IO_ENDPT
== pp
->rdproc
) {
218 /* Cancel a read from a PTY. */
219 r
= pp
->rdcum
> 0 ? pp
->rdcum
: EAGAIN
;
220 pp
->rdleft
= pp
->rdcum
= 0;
222 if (m_ptr
->IO_ENDPT
== pp
->wrproc
) {
223 /* Cancel a write to a PTY. */
224 r
= pp
->wrcum
> 0 ? pp
->wrcum
: EAGAIN
;
225 pp
->wrleft
= pp
->wrcum
= 0;
232 tty_reply(TASK_REPLY
, m_ptr
->m_source
, m_ptr
->IO_ENDPT
, r
);
235 /*===========================================================================*
237 *===========================================================================*/
238 PRIVATE
int pty_write(tp
, try)
242 /* (*dev_write)() routine for PTYs. Transfer bytes from the writer on
243 * /dev/ttypX to the output buffer.
245 pty_t
*pp
= tp
->tty_priv
;
246 int count
, ocount
, s
;
247 phys_bytes user_phys
;
249 /* PTY closed down? */
250 if (pp
->state
& PTY_CLOSED
) {
252 if (tp
->tty_outleft
> 0) {
253 if(tp
->tty_outrepcode
== TTY_REVIVE
) {
254 notify(tp
->tty_outcaller
);
255 tp
->tty_outrevived
= 1;
257 tty_reply(tp
->tty_outrepcode
, tp
->tty_outcaller
,
258 tp
->tty_outproc
, EIO
);
259 tp
->tty_outleft
= tp
->tty_outcum
= 0;
265 /* While there is something to do. */
267 ocount
= buflen(pp
->obuf
) - pp
->ocount
;
268 if (try) return (ocount
> 0);
269 count
= bufend(pp
->obuf
) - pp
->ohead
;
270 if (count
> ocount
) count
= ocount
;
271 if (count
> tp
->tty_outleft
) count
= tp
->tty_outleft
;
272 if (count
== 0 || tp
->tty_inhibited
)
275 /* Copy from user space to the PTY output buffer. */
276 if(tp
->tty_out_safe
) {
277 if ((s
= sys_safecopyfrom(tp
->tty_outproc
, tp
->tty_out_vir_g
,
278 tp
->tty_out_vir_offset
, (vir_bytes
) pp
->ohead
, count
, D
))!=OK
) {
282 if ((s
= sys_vircopy(tp
->tty_outproc
, D
, (vir_bytes
) tp
->tty_out_vir_g
,
283 SELF
, D
, (vir_bytes
) pp
->ohead
, (phys_bytes
) count
)) != OK
) {
288 /* Perform output processing on the output buffer. */
289 out_process(tp
, pp
->obuf
, pp
->ohead
, bufend(pp
->obuf
), &count
, &ocount
);
290 if (count
== 0) break;
292 /* Assume echoing messed up by output. */
293 tp
->tty_reprint
= TRUE
;
296 pp
->ocount
+= ocount
;
297 if ((pp
->ohead
+= ocount
) >= bufend(pp
->obuf
))
298 pp
->ohead
-= buflen(pp
->obuf
);
301 if(tp
->tty_out_safe
) tp
->tty_out_vir_offset
+= count
;
302 else tp
->tty_out_vir_g
+= count
;
304 tp
->tty_outcum
+= count
;
305 if ((tp
->tty_outleft
-= count
) == 0) {
306 /* Output is finished, reply to the writer. */
307 if(tp
->tty_outrepcode
== TTY_REVIVE
) {
308 notify(tp
->tty_outcaller
);
309 tp
->tty_outrevived
= 1;
311 tty_reply(tp
->tty_outrepcode
, tp
->tty_outcaller
,
312 tp
->tty_outproc
, tp
->tty_outcum
);
321 /*===========================================================================*
323 *===========================================================================*/
324 PRIVATE
void pty_echo(tp
, c
)
328 /* Echo one character. (Like pty_write, but only one character, optionally.) */
330 pty_t
*pp
= tp
->tty_priv
;
333 ocount
= buflen(pp
->obuf
) - pp
->ocount
;
334 if (ocount
== 0) return; /* output buffer full */
336 *pp
->ohead
= c
; /* add one character */
338 out_process(tp
, pp
->obuf
, pp
->ohead
, bufend(pp
->obuf
), &count
, &ocount
);
339 if (count
== 0) return;
341 pp
->ocount
+= ocount
;
342 if ((pp
->ohead
+= ocount
) >= bufend(pp
->obuf
)) pp
->ohead
-= buflen(pp
->obuf
);
346 /*===========================================================================*
348 *===========================================================================*/
349 PRIVATE
void pty_start(pp
)
352 /* Transfer bytes written to the output buffer to the PTY reader. */
355 /* While there are things to do. */
358 count
= bufend(pp
->obuf
) - pp
->otail
;
359 if (count
> pp
->ocount
) count
= pp
->ocount
;
360 if (count
> pp
->rdleft
) count
= pp
->rdleft
;
361 if (count
== 0) break;
363 /* Copy from the output buffer to the readers address space. */
365 if((s
= sys_safecopyto(pp
->rdproc
, pp
->rdvir_g
,
366 pp
->rdvir_offset
, (vir_bytes
) pp
->otail
, count
, D
)) != OK
) {
369 pp
->rdvir_offset
+= count
;
371 if ((s
= sys_vircopy(SELF
, D
, (vir_bytes
)pp
->otail
,
372 (vir_bytes
) pp
->rdproc
, D
, (vir_bytes
) pp
->rdvir_g
, (phys_bytes
) count
)) != OK
) {
373 printf("pty tty: copy failed (error %d)\n", s
);
376 pp
->rdvir_g
+= count
;
381 if ((pp
->otail
+= count
) == bufend(pp
->obuf
)) pp
->otail
= pp
->obuf
;
387 /*===========================================================================*
389 *===========================================================================*/
390 PRIVATE
void pty_finish(pp
)
393 /* Finish the read request of a PTY reader if there is at least one byte
397 if (pp
->rdsendreply
) {
398 tty_reply(TASK_REPLY
, pp
->rdcaller
, pp
->rdproc
, pp
->rdcum
);
399 pp
->rdleft
= pp
->rdcum
= 0;
402 notify(pp
->rdcaller
);
407 /*===========================================================================*
409 *===========================================================================*/
410 PRIVATE
int pty_read(tp
, try)
414 /* Offer bytes from the PTY writer for input on the TTY. (Do it one byte at
415 * a time, 99% of the writes will be for one byte, so no sense in being smart.)
417 pty_t
*pp
= tp
->tty_priv
;
420 if (pp
->state
& PTY_CLOSED
) {
422 if (tp
->tty_inleft
> 0) {
423 if(tp
->tty_inrepcode
== TTY_REVIVE
) {
424 notify(tp
->tty_incaller
);
425 tp
->tty_inrevived
= 1;
427 tty_reply(tp
->tty_inrepcode
, tp
->tty_incaller
,
428 tp
->tty_inproc
, tp
->tty_incum
);
429 tp
->tty_inleft
= tp
->tty_incum
= 0;
441 while (pp
->wrleft
> 0) {
444 /* Transfer one character to 'c'. */
446 if ((s
= sys_safecopyfrom(pp
->wrproc
, pp
->wrvir_g
,
447 pp
->wrvir_offset
, (vir_bytes
) &c
, 1, D
)) != OK
) {
448 printf("pty: safecopy failed (error %d)\n", s
);
453 if ((s
= sys_vircopy(pp
->wrproc
, D
, (vir_bytes
) pp
->wrvir_g
,
454 SELF
, D
, (vir_bytes
) &c
, (phys_bytes
) 1)) != OK
) {
455 printf("pty: copy failed (error %d)\n", s
);
461 /* Input processing. */
462 if (in_process(tp
, &c
, 1) == 0) break;
464 /* PTY writer bookkeeping. */
466 if (--pp
->wrleft
== 0) {
467 if (pp
->wrsendreply
) {
468 tty_reply(TASK_REPLY
, pp
->wrcaller
, pp
->wrproc
,
473 notify(pp
->wrcaller
);
478 /*===========================================================================*
480 *===========================================================================*/
481 PRIVATE
int pty_close(tp
, try)
485 /* The tty side has closed, so shut down the pty side. */
486 pty_t
*pp
= tp
->tty_priv
;
488 if (!(pp
->state
& PTY_ACTIVE
)) return;
490 if (pp
->rdleft
> 0) {
491 assert(!pp
->rdsendreply
);
492 notify(pp
->rdcaller
);
495 if (pp
->wrleft
> 0) {
496 assert(!pp
->wrsendreply
);
497 notify(pp
->wrcaller
);
500 if (pp
->state
& PTY_CLOSED
) pp
->state
= 0; else pp
->state
|= TTY_CLOSED
;
503 /*===========================================================================*
505 *===========================================================================*/
506 PRIVATE
int pty_icancel(tp
, try)
510 /* Discard waiting input. */
511 pty_t
*pp
= tp
->tty_priv
;
513 if (pp
->wrleft
> 0) {
514 pp
->wrcum
+= pp
->wrleft
;
516 notify(pp
->wrcaller
);
520 /*===========================================================================*
522 *===========================================================================*/
523 PRIVATE
int pty_ocancel(tp
, try)
527 /* Drain the output buffer. */
528 pty_t
*pp
= tp
->tty_priv
;
531 pp
->otail
= pp
->ohead
;
534 /*===========================================================================*
536 *===========================================================================*/
537 PUBLIC
void pty_init(tp
)
543 /* Associate PTY and TTY structures. */
544 line
= tp
- &tty_table
[NR_CONS
+ NR_RS_LINES
];
545 pp
= tp
->tty_priv
= &pty_table
[line
];
549 /* Set up output queue. */
550 pp
->ohead
= pp
->otail
= pp
->obuf
;
552 /* Fill in TTY function hooks. */
553 tp
->tty_devread
= pty_read
;
554 tp
->tty_devwrite
= pty_write
;
555 tp
->tty_echo
= pty_echo
;
556 tp
->tty_icancel
= pty_icancel
;
557 tp
->tty_ocancel
= pty_ocancel
;
558 tp
->tty_close
= pty_close
;
559 tp
->tty_select_ops
= 0;
562 /*===========================================================================*
564 *===========================================================================*/
565 PUBLIC
int pty_status(message
*m_ptr
)
571 for (i
= 0, pp
= pty_table
; i
<NR_PTYS
; i
++, pp
++) {
572 if ((((pp
->state
& TTY_CLOSED
) && pp
->rdleft
> 0) ||
574 pp
->rdcaller
== m_ptr
->m_source
)
576 m_ptr
->m_type
= DEV_REVIVE
;
577 m_ptr
->REP_ENDPT
= pp
->rdproc
;
578 m_ptr
->REP_IO_GRANT
= pp
->rdvir_g
;
579 m_ptr
->REP_STATUS
= pp
->rdcum
;
581 pp
->rdleft
= pp
->rdcum
= 0;
586 if ((((pp
->state
& TTY_CLOSED
) && pp
->wrleft
> 0) ||
588 pp
->wrcaller
== m_ptr
->m_source
)
590 m_ptr
->m_type
= DEV_REVIVE
;
591 m_ptr
->REP_ENDPT
= pp
->wrproc
;
592 m_ptr
->REP_IO_GRANT
= pp
->wrvir_g
;
594 m_ptr
->REP_STATUS
= EIO
;
596 m_ptr
->REP_STATUS
= pp
->wrcum
;
598 pp
->wrleft
= pp
->wrcum
= 0;
603 if (pp
->select_ready_ops
&& pp
->select_proc
== m_ptr
->m_source
) {
604 m_ptr
->m_type
= DEV_IO_READY
;
605 m_ptr
->DEV_MINOR
= PTYPX_MINOR
+ i
;
606 m_ptr
->DEV_SEL_OPS
= pp
->select_ready_ops
;
607 pp
->select_ready_ops
= 0;
615 /*===========================================================================*
617 *===========================================================================*/
618 PRIVATE
int select_try_pty(tty_t
*tp
, int ops
)
620 pty_t
*pp
= tp
->tty_priv
;
624 /* Write won't block on error. */
625 if (pp
->state
& TTY_CLOSED
) r
|= SEL_WR
;
626 else if (pp
->wrleft
!= 0 || pp
->wrcum
!= 0) r
|= SEL_WR
;
631 /* Read won't block on error. */
632 if (pp
->state
& TTY_CLOSED
) r
|= SEL_RD
;
633 else if (pp
->rdleft
!= 0 || pp
->rdcum
!= 0) r
|= SEL_RD
;
634 else if (pp
->ocount
> 0) r
|= SEL_RD
; /* Actual data. */
640 /*===========================================================================*
642 *===========================================================================*/
643 PUBLIC
void select_retry_pty(tty_t
*tp
)
645 pty_t
*pp
= tp
->tty_priv
;
648 /* See if the pty side of a pty is ready to return a select. */
649 if (pp
->select_ops
&& (r
=select_try_pty(tp
, pp
->select_ops
))) {
650 pp
->select_ops
&= ~r
;
651 pp
->select_ready_ops
|= r
;
652 notify(pp
->select_proc
);
656 /*===========================================================================*
658 *===========================================================================*/
659 PRIVATE
int pty_select(tty_t
*tp
, message
*m
)
661 pty_t
*pp
= tp
->tty_priv
;
662 int ops
, ready_ops
= 0, watch
;
664 ops
= m
->IO_ENDPT
& (SEL_RD
|SEL_WR
|SEL_ERR
);
665 watch
= (m
->IO_ENDPT
& SEL_NOTIFY
) ? 1 : 0;
667 ready_ops
= select_try_pty(tp
, ops
);
669 if (!ready_ops
&& ops
&& watch
) {
670 pp
->select_ops
|= ops
;
671 pp
->select_proc
= m
->m_source
;
677 #endif /* NR_PTYS > 0 */