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(tty_t
*tp
, message
*m_ptr
)
87 /* Perform an open/close/read/write call on a /dev/ptypX device. */
88 pty_t
*pp
= tp
->tty_priv
;
92 switch (m_ptr
->m_type
) {
95 /* Check, store information on the reader, do I/O. */
96 if (pp
->state
& TTY_CLOSED
) {
100 if (pp
->rdleft
!= 0 || pp
->rdcum
!= 0) {
104 if (m_ptr
->COUNT
<= 0) {
108 pp
->rdsendreply
= TRUE
;
109 pp
->rdcaller
= m_ptr
->m_source
;
110 pp
->rdproc
= m_ptr
->IO_ENDPT
;
111 pp
->rdvir_g
= (vir_bytes
) m_ptr
->ADDRESS
;
112 pp
->rdvir_offset
= 0;
114 pp
->rdleft
= m_ptr
->COUNT
;
117 if (pp
->rdleft
== 0) {
118 return; /* already done */
122 r
= SUSPEND
; /* do suspend */
123 pp
->rdsendreply
= FALSE
;
129 /* Check, store information on the writer, do I/O. */
130 if (pp
->state
& TTY_CLOSED
) {
134 if (pp
->wrleft
!= 0 || pp
->wrcum
!= 0) {
138 if (m_ptr
->COUNT
<= 0) {
142 pp
->wrsendreply
= TRUE
;
143 pp
->wrcaller
= m_ptr
->m_source
;
144 pp
->wrproc
= m_ptr
->IO_ENDPT
;
145 pp
->wrvir_g
= (vir_bytes
) m_ptr
->ADDRESS
;
146 pp
->wrvir_offset
= 0;
148 pp
->wrleft
= m_ptr
->COUNT
;
150 if (pp
->wrleft
== 0) {
151 return; /* already done */
155 pp
->wrsendreply
= FALSE
; /* do suspend */
161 r
= pp
->state
!= 0 ? EIO
: OK
;
162 pp
->state
|= PTY_ACTIVE
;
169 if (pp
->state
& TTY_CLOSED
) {
172 pp
->state
|= PTY_CLOSED
;
173 sigchar(tp
, SIGHUP
, 1);
178 r
= pty_select(tp
, m_ptr
);
183 if (m_ptr
->IO_ENDPT
== pp
->rdproc
) {
184 /* Cancel a read from a PTY. */
185 r
= pp
->rdcum
> 0 ? pp
->rdcum
: EAGAIN
;
186 pp
->rdleft
= pp
->rdcum
= 0;
188 if (m_ptr
->IO_ENDPT
== pp
->wrproc
) {
189 /* Cancel a write to a PTY. */
190 r
= pp
->wrcum
> 0 ? pp
->wrcum
: EAGAIN
;
191 pp
->wrleft
= pp
->wrcum
= 0;
198 tty_reply(TASK_REPLY
, m_ptr
->m_source
, m_ptr
->IO_ENDPT
, r
);
201 /*===========================================================================*
203 *===========================================================================*/
204 PRIVATE
int pty_write(tty_t
*tp
, int try)
206 /* (*dev_write)() routine for PTYs. Transfer bytes from the writer on
207 * /dev/ttypX to the output buffer.
209 pty_t
*pp
= tp
->tty_priv
;
210 int count
, ocount
, s
;
213 /* PTY closed down? */
214 if (pp
->state
& PTY_CLOSED
) {
216 if (tp
->tty_outleft
> 0) {
217 if(tp
->tty_outrepcode
== TTY_REVIVE
) {
218 notify(tp
->tty_outcaller
);
219 tp
->tty_outrevived
= 1;
221 tty_reply(tp
->tty_outrepcode
, tp
->tty_outcaller
,
222 tp
->tty_outproc
, EIO
);
223 tp
->tty_outleft
= tp
->tty_outcum
= 0;
229 /* While there is something to do. */
231 ocount
= buflen(pp
->obuf
) - pp
->ocount
;
232 if (try) return (ocount
> 0);
233 count
= bufend(pp
->obuf
) - pp
->ohead
;
234 if (count
> ocount
) count
= ocount
;
235 if (count
> tp
->tty_outleft
) count
= tp
->tty_outleft
;
236 if (count
== 0 || tp
->tty_inhibited
)
239 /* Copy from user space to the PTY output buffer. */
240 if(tp
->tty_out_safe
) {
241 if ((s
= sys_safecopyfrom(tp
->tty_outproc
, tp
->tty_out_vir_g
,
242 tp
->tty_out_vir_offset
, (vir_bytes
) pp
->ohead
, count
, D
))!=OK
) {
246 if ((s
= sys_vircopy(tp
->tty_outproc
, D
, (vir_bytes
) tp
->tty_out_vir_g
,
247 SELF
, D
, (vir_bytes
) pp
->ohead
, (phys_bytes
) count
)) != OK
) {
252 /* Perform output processing on the output buffer. */
253 out_process(tp
, pp
->obuf
, pp
->ohead
, bufend(pp
->obuf
), &count
, &ocount
);
254 if (count
== 0) break;
256 /* Assume echoing messed up by output. */
257 tp
->tty_reprint
= TRUE
;
260 pp
->ocount
+= ocount
;
261 if ((pp
->ohead
+= ocount
) >= bufend(pp
->obuf
))
262 pp
->ohead
-= buflen(pp
->obuf
);
265 if(tp
->tty_out_safe
) tp
->tty_out_vir_offset
+= count
;
266 else tp
->tty_out_vir_g
+= count
;
268 tp
->tty_outcum
+= count
;
269 if ((tp
->tty_outleft
-= count
) == 0) {
270 /* Output is finished, reply to the writer. */
271 if(tp
->tty_outrepcode
== TTY_REVIVE
) {
272 notify(tp
->tty_outcaller
);
273 tp
->tty_outrevived
= 1;
275 tty_reply(tp
->tty_outrepcode
, tp
->tty_outcaller
,
276 tp
->tty_outproc
, tp
->tty_outcum
);
285 /*===========================================================================*
287 *===========================================================================*/
288 PRIVATE
void pty_echo(tty_t
*tp
, int c
)
290 /* Echo one character. (Like pty_write, but only one character, optionally.) */
292 pty_t
*pp
= tp
->tty_priv
;
295 ocount
= buflen(pp
->obuf
) - pp
->ocount
;
296 if (ocount
== 0) return; /* output buffer full */
298 *pp
->ohead
= c
; /* add one character */
300 out_process(tp
, pp
->obuf
, pp
->ohead
, bufend(pp
->obuf
), &count
, &ocount
);
301 if (count
== 0) return;
303 pp
->ocount
+= ocount
;
304 if ((pp
->ohead
+= ocount
) >= bufend(pp
->obuf
)) pp
->ohead
-= buflen(pp
->obuf
);
308 /*===========================================================================*
310 *===========================================================================*/
311 PRIVATE
void pty_start(pty_t
*pp
)
313 /* Transfer bytes written to the output buffer to the PTY reader. */
316 /* While there are things to do. */
319 count
= bufend(pp
->obuf
) - pp
->otail
;
320 if (count
> pp
->ocount
) count
= pp
->ocount
;
321 if (count
> pp
->rdleft
) count
= pp
->rdleft
;
322 if (count
== 0) break;
324 /* Copy from the output buffer to the readers address space. */
326 if((s
= sys_safecopyto(pp
->rdproc
, pp
->rdvir_g
,
327 pp
->rdvir_offset
, (vir_bytes
) pp
->otail
, count
, D
)) != OK
) {
330 pp
->rdvir_offset
+= count
;
332 if ((s
= sys_vircopy(SELF
, D
, (vir_bytes
)pp
->otail
,
333 (vir_bytes
) pp
->rdproc
, D
, (vir_bytes
) pp
->rdvir_g
, (phys_bytes
) count
)) != OK
) {
334 printf("pty tty: copy failed (error %d)\n", s
);
337 pp
->rdvir_g
+= count
;
342 if ((pp
->otail
+= count
) == bufend(pp
->obuf
)) pp
->otail
= pp
->obuf
;
348 /*===========================================================================*
350 *===========================================================================*/
351 PRIVATE
void pty_finish(pty_t
*pp
)
353 /* Finish the read request of a PTY reader if there is at least one byte
357 if (pp
->rdsendreply
) {
358 tty_reply(TASK_REPLY
, pp
->rdcaller
, pp
->rdproc
, pp
->rdcum
);
359 pp
->rdleft
= pp
->rdcum
= 0;
362 notify(pp
->rdcaller
);
367 /*===========================================================================*
369 *===========================================================================*/
370 PRIVATE
int pty_read(tty_t
*tp
, int try)
372 /* Offer bytes from the PTY writer for input on the TTY. (Do it one byte at
373 * a time, 99% of the writes will be for one byte, so no sense in being smart.)
375 pty_t
*pp
= tp
->tty_priv
;
378 if (pp
->state
& PTY_CLOSED
) {
380 if (tp
->tty_inleft
> 0) {
381 if(tp
->tty_inrepcode
== TTY_REVIVE
) {
382 notify(tp
->tty_incaller
);
383 tp
->tty_inrevived
= 1;
385 tty_reply(tp
->tty_inrepcode
, tp
->tty_incaller
,
386 tp
->tty_inproc
, tp
->tty_incum
);
387 tp
->tty_inleft
= tp
->tty_incum
= 0;
399 while (pp
->wrleft
> 0) {
402 /* Transfer one character to 'c'. */
404 if ((s
= sys_safecopyfrom(pp
->wrproc
, pp
->wrvir_g
,
405 pp
->wrvir_offset
, (vir_bytes
) &c
, 1, D
)) != OK
) {
406 printf("pty: safecopy failed (error %d)\n", s
);
411 if ((s
= sys_vircopy(pp
->wrproc
, D
, (vir_bytes
) pp
->wrvir_g
,
412 SELF
, D
, (vir_bytes
) &c
, (phys_bytes
) 1)) != OK
) {
413 printf("pty: copy failed (error %d)\n", s
);
419 /* Input processing. */
420 if (in_process(tp
, &c
, 1) == 0) break;
422 /* PTY writer bookkeeping. */
424 if (--pp
->wrleft
== 0) {
425 if (pp
->wrsendreply
) {
426 tty_reply(TASK_REPLY
, pp
->wrcaller
, pp
->wrproc
,
431 notify(pp
->wrcaller
);
438 /*===========================================================================*
440 *===========================================================================*/
441 PRIVATE
int pty_close(tty_t
*tp
, int try)
443 /* The tty side has closed, so shut down the pty side. */
444 pty_t
*pp
= tp
->tty_priv
;
446 if (!(pp
->state
& PTY_ACTIVE
)) return 0;
448 if (pp
->rdleft
> 0) {
449 assert(!pp
->rdsendreply
);
450 notify(pp
->rdcaller
);
453 if (pp
->wrleft
> 0) {
454 assert(!pp
->wrsendreply
);
455 notify(pp
->wrcaller
);
458 if (pp
->state
& PTY_CLOSED
) pp
->state
= 0; else pp
->state
|= TTY_CLOSED
;
463 /*===========================================================================*
465 *===========================================================================*/
466 PRIVATE
int pty_icancel(tty_t
*tp
, int try)
468 /* Discard waiting input. */
469 pty_t
*pp
= tp
->tty_priv
;
471 if (pp
->wrleft
> 0) {
472 pp
->wrcum
+= pp
->wrleft
;
474 notify(pp
->wrcaller
);
480 /*===========================================================================*
482 *===========================================================================*/
483 PRIVATE
int pty_ocancel(tty_t
*tp
, int try)
485 /* Drain the output buffer. */
486 pty_t
*pp
= tp
->tty_priv
;
489 pp
->otail
= pp
->ohead
;
494 /*===========================================================================*
496 *===========================================================================*/
497 PUBLIC
void pty_init(tty_t
*tp
)
502 /* Associate PTY and TTY structures. */
503 line
= tp
- &tty_table
[NR_CONS
+ NR_RS_LINES
];
504 pp
= tp
->tty_priv
= &pty_table
[line
];
508 /* Set up output queue. */
509 pp
->ohead
= pp
->otail
= pp
->obuf
;
511 /* Fill in TTY function hooks. */
512 tp
->tty_devread
= pty_read
;
513 tp
->tty_devwrite
= pty_write
;
514 tp
->tty_echo
= pty_echo
;
515 tp
->tty_icancel
= pty_icancel
;
516 tp
->tty_ocancel
= pty_ocancel
;
517 tp
->tty_close
= pty_close
;
518 tp
->tty_select_ops
= 0;
521 /*===========================================================================*
523 *===========================================================================*/
524 PUBLIC
int pty_status(message
*m_ptr
)
530 for (i
= 0, pp
= pty_table
; i
<NR_PTYS
; i
++, pp
++) {
531 if ((((pp
->state
& TTY_CLOSED
) && pp
->rdleft
> 0) ||
533 pp
->rdcaller
== m_ptr
->m_source
)
535 m_ptr
->m_type
= DEV_REVIVE
;
536 m_ptr
->REP_ENDPT
= pp
->rdproc
;
537 m_ptr
->REP_IO_GRANT
= pp
->rdvir_g
;
538 m_ptr
->REP_STATUS
= pp
->rdcum
;
540 pp
->rdleft
= pp
->rdcum
= 0;
545 if ((((pp
->state
& TTY_CLOSED
) && pp
->wrleft
> 0) ||
547 pp
->wrcaller
== m_ptr
->m_source
)
549 m_ptr
->m_type
= DEV_REVIVE
;
550 m_ptr
->REP_ENDPT
= pp
->wrproc
;
551 m_ptr
->REP_IO_GRANT
= pp
->wrvir_g
;
553 m_ptr
->REP_STATUS
= EIO
;
555 m_ptr
->REP_STATUS
= pp
->wrcum
;
557 pp
->wrleft
= pp
->wrcum
= 0;
562 if (pp
->select_ready_ops
&& pp
->select_proc
== m_ptr
->m_source
) {
563 m_ptr
->m_type
= DEV_IO_READY
;
564 m_ptr
->DEV_MINOR
= PTYPX_MINOR
+ i
;
565 m_ptr
->DEV_SEL_OPS
= pp
->select_ready_ops
;
566 pp
->select_ready_ops
= 0;
574 /*===========================================================================*
576 *===========================================================================*/
577 PRIVATE
int select_try_pty(tty_t
*tp
, int ops
)
579 pty_t
*pp
= tp
->tty_priv
;
583 /* Write won't block on error. */
584 if (pp
->state
& TTY_CLOSED
) r
|= SEL_WR
;
585 else if (pp
->wrleft
!= 0 || pp
->wrcum
!= 0) r
|= SEL_WR
;
590 /* Read won't block on error. */
591 if (pp
->state
& TTY_CLOSED
) r
|= SEL_RD
;
592 else if (pp
->rdleft
!= 0 || pp
->rdcum
!= 0) r
|= SEL_RD
;
593 else if (pp
->ocount
> 0) r
|= SEL_RD
; /* Actual data. */
599 /*===========================================================================*
601 *===========================================================================*/
602 PUBLIC
void select_retry_pty(tty_t
*tp
)
604 pty_t
*pp
= tp
->tty_priv
;
607 /* See if the pty side of a pty is ready to return a select. */
608 if (pp
->select_ops
&& (r
=select_try_pty(tp
, pp
->select_ops
))) {
609 pp
->select_ops
&= ~r
;
610 pp
->select_ready_ops
|= r
;
611 notify(pp
->select_proc
);
615 /*===========================================================================*
617 *===========================================================================*/
618 PRIVATE
int pty_select(tty_t
*tp
, message
*m
)
620 pty_t
*pp
= tp
->tty_priv
;
621 int ops
, ready_ops
= 0, watch
;
623 ops
= m
->IO_ENDPT
& (SEL_RD
|SEL_WR
|SEL_ERR
);
624 watch
= (m
->IO_ENDPT
& SEL_NOTIFY
) ? 1 : 0;
626 ready_ops
= select_try_pty(tp
, ops
);
628 if (!ready_ops
&& ops
&& watch
) {
629 pp
->select_ops
|= ops
;
630 pp
->select_proc
= m
->m_source
;
636 #endif /* NR_PTYS > 0 */