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 <minix/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 cp_grant_id_t rdgrant
; /* grant for readers address space */
40 vir_bytes rdoffset
; /* offset in above grant */
41 int rdleft
; /* # bytes yet to be read */
42 int rdcum
; /* # bytes written so far */
44 /* Write call to /dev/ptypX. */
45 char wrsendreply
; /* send a reply (instead of notify) */
46 int wrcaller
; /* process making the call (usually FS) */
47 int wrproc
; /* process that wants to write to the pty */
48 cp_grant_id_t wrgrant
; /* grant for writers address space */
49 vir_bytes wroffset
; /* offset in above grant */
50 int wrleft
; /* # bytes yet to be written */
51 int wrcum
; /* # bytes written so far */
54 int ocount
; /* # characters in the buffer */
55 char *ohead
, *otail
; /* head and tail of the circular buffer */
56 char obuf
[2048]; /* buffer for bytes going to the pty reader */
59 int select_ops
, /* Which operations do we want to know about? */
60 select_proc
, /* Who wants to know about it? */
61 select_ready_ops
; /* For callback. */
64 #define PTY_ACTIVE 0x01 /* pty is open/active */
65 #define TTY_CLOSED 0x02 /* tty side has closed down */
66 #define PTY_CLOSED 0x04 /* pty side has closed down */
68 static pty_t pty_table
[NR_PTYS
]; /* PTY bookkeeping */
70 static int pty_write(tty_t
*tp
, int try);
71 static void pty_echo(tty_t
*tp
, int c
);
72 static void pty_start(pty_t
*pp
);
73 static void pty_finish(pty_t
*pp
);
74 static int pty_read(tty_t
*tp
, int try);
75 static int pty_close(tty_t
*tp
, int try);
76 static int pty_icancel(tty_t
*tp
, int try);
77 static int pty_ocancel(tty_t
*tp
, int try);
78 static int pty_select(tty_t
*tp
, message
*m
);
80 /*===========================================================================*
82 *===========================================================================*/
83 void do_pty(tty_t
*tp
, message
*m_ptr
)
85 /* Perform an open/close/read/write call on a /dev/ptypX device. */
86 pty_t
*pp
= tp
->tty_priv
;
89 switch (m_ptr
->m_type
) {
91 /* Check, store information on the reader, do I/O. */
92 if (pp
->state
& TTY_CLOSED
) {
96 if (pp
->rdleft
!= 0 || pp
->rdcum
!= 0) {
100 if (m_ptr
->COUNT
<= 0) {
104 if (pp
->rdgrant
!= GRANT_INVALID
) {
108 pp
->rdsendreply
= TRUE
;
109 pp
->rdcaller
= m_ptr
->m_source
;
110 pp
->rdproc
= m_ptr
->USER_ENDPT
;
111 pp
->rdgrant
= (cp_grant_id_t
) m_ptr
->IO_GRANT
;
113 pp
->rdleft
= m_ptr
->COUNT
;
116 if (pp
->rdleft
== 0) {
117 pp
->rdgrant
= GRANT_INVALID
;
118 return; /* already done */
122 r
= SUSPEND
; /* do suspend */
123 pp
->rdsendreply
= FALSE
;
128 /* Check, store information on the writer, do I/O. */
129 if (pp
->state
& TTY_CLOSED
) {
133 if (pp
->wrleft
!= 0 || pp
->wrcum
!= 0) {
137 if (m_ptr
->COUNT
<= 0) {
141 if (pp
->wrgrant
!= GRANT_INVALID
) {
145 pp
->wrsendreply
= TRUE
;
146 pp
->wrcaller
= m_ptr
->m_source
;
147 pp
->wrproc
= m_ptr
->USER_ENDPT
;
148 pp
->wrgrant
= (cp_grant_id_t
) m_ptr
->IO_GRANT
;
150 pp
->wrleft
= m_ptr
->COUNT
;
152 if (pp
->wrleft
== 0) {
153 pp
->wrgrant
= GRANT_INVALID
;
154 return; /* already done */
158 pp
->wrsendreply
= FALSE
; /* do suspend */
164 r
= pp
->state
!= 0 ? EIO
: OK
;
165 pp
->state
|= PTY_ACTIVE
;
172 if (pp
->state
& TTY_CLOSED
) {
175 pp
->state
|= PTY_CLOSED
;
176 sigchar(tp
, SIGHUP
, 1);
181 r
= pty_select(tp
, m_ptr
);
186 if (m_ptr
->USER_ENDPT
== pp
->rdproc
) {
187 /* Cancel a read from a PTY. */
188 r
= pp
->rdcum
> 0 ? pp
->rdcum
: EAGAIN
;
189 pp
->rdleft
= pp
->rdcum
= 0;
190 pp
->rdgrant
= GRANT_INVALID
;
192 if (m_ptr
->USER_ENDPT
== pp
->wrproc
) {
193 /* Cancel a write to a PTY. */
194 r
= pp
->wrcum
> 0 ? pp
->wrcum
: EAGAIN
;
195 pp
->wrleft
= pp
->wrcum
= 0;
196 pp
->wrgrant
= GRANT_INVALID
;
203 tty_reply(TASK_REPLY
, m_ptr
->m_source
, m_ptr
->USER_ENDPT
, r
);
206 /*===========================================================================*
208 *===========================================================================*/
209 static int pty_write(tty_t
*tp
, int try)
211 /* (*dev_write)() routine for PTYs. Transfer bytes from the writer on
212 * /dev/ttypX to the output buffer.
214 pty_t
*pp
= tp
->tty_priv
;
215 int count
, ocount
, s
;
218 /* PTY closed down? */
219 if (pp
->state
& PTY_CLOSED
) {
221 if (tp
->tty_outleft
> 0) {
222 if(tp
->tty_outrepcode
== TTY_REVIVE
) {
223 notify(tp
->tty_outcaller
);
224 tp
->tty_outrevived
= 1;
226 tty_reply(tp
->tty_outrepcode
, tp
->tty_outcaller
,
227 tp
->tty_outproc
, EIO
);
228 tp
->tty_outleft
= tp
->tty_outcum
= 0;
234 /* While there is something to do. */
236 ocount
= buflen(pp
->obuf
) - pp
->ocount
;
237 if (try) return (ocount
> 0);
238 count
= bufend(pp
->obuf
) - pp
->ohead
;
239 if (count
> ocount
) count
= ocount
;
240 if (count
> tp
->tty_outleft
) count
= tp
->tty_outleft
;
241 if (count
== 0 || tp
->tty_inhibited
)
244 /* Copy from user space to the PTY output buffer. */
245 if ((s
= sys_safecopyfrom(tp
->tty_outcaller
, tp
->tty_outgrant
,
246 tp
->tty_outoffset
, (vir_bytes
) pp
->ohead
, count
))!=OK
) {
250 /* Perform output processing on the output buffer. */
251 out_process(tp
, pp
->obuf
, pp
->ohead
, bufend(pp
->obuf
), &count
, &ocount
);
252 if (count
== 0) break;
254 /* Assume echoing messed up by output. */
255 tp
->tty_reprint
= TRUE
;
258 pp
->ocount
+= ocount
;
259 if ((pp
->ohead
+= ocount
) >= bufend(pp
->obuf
))
260 pp
->ohead
-= buflen(pp
->obuf
);
263 tp
->tty_outoffset
+= count
;
265 tp
->tty_outcum
+= count
;
266 if ((tp
->tty_outleft
-= count
) == 0) {
267 /* Output is finished, reply to the writer. */
268 if(tp
->tty_outrepcode
== TTY_REVIVE
) {
269 notify(tp
->tty_outcaller
);
270 tp
->tty_outrevived
= 1;
272 tty_reply(tp
->tty_outrepcode
, tp
->tty_outcaller
,
273 tp
->tty_outproc
, tp
->tty_outcum
);
282 /*===========================================================================*
284 *===========================================================================*/
285 static void pty_echo(tty_t
*tp
, int c
)
287 /* Echo one character. (Like pty_write, but only one character, optionally.) */
289 pty_t
*pp
= tp
->tty_priv
;
292 ocount
= buflen(pp
->obuf
) - pp
->ocount
;
293 if (ocount
== 0) return; /* output buffer full */
295 *pp
->ohead
= c
; /* add one character */
297 out_process(tp
, pp
->obuf
, pp
->ohead
, bufend(pp
->obuf
), &count
, &ocount
);
298 if (count
== 0) return;
300 pp
->ocount
+= ocount
;
301 if ((pp
->ohead
+= ocount
) >= bufend(pp
->obuf
)) pp
->ohead
-= buflen(pp
->obuf
);
305 /*===========================================================================*
307 *===========================================================================*/
308 static void pty_start(pty_t
*pp
)
310 /* Transfer bytes written to the output buffer to the PTY reader. */
313 /* While there are things to do. */
316 count
= bufend(pp
->obuf
) - pp
->otail
;
317 if (count
> pp
->ocount
) count
= pp
->ocount
;
318 if (count
> pp
->rdleft
) count
= pp
->rdleft
;
319 if (count
== 0) break;
321 /* Copy from the output buffer to the readers address space. */
322 if((s
= sys_safecopyto(pp
->rdcaller
, pp
->rdgrant
,
323 pp
->rdoffset
, (vir_bytes
) pp
->otail
, count
)) != OK
) {
326 pp
->rdoffset
+= count
;
330 if ((pp
->otail
+= count
) == bufend(pp
->obuf
)) pp
->otail
= pp
->obuf
;
336 /*===========================================================================*
338 *===========================================================================*/
339 static void pty_finish(pty_t
*pp
)
341 /* Finish the read request of a PTY reader if there is at least one byte
345 if (pp
->rdsendreply
) {
346 tty_reply(TASK_REPLY
, pp
->rdcaller
, pp
->rdproc
, pp
->rdcum
);
347 pp
->rdleft
= pp
->rdcum
= 0;
350 notify(pp
->rdcaller
);
355 /*===========================================================================*
357 *===========================================================================*/
358 static int pty_read(tty_t
*tp
, int try)
360 /* Offer bytes from the PTY writer for input on the TTY. (Do it one byte at
361 * a time, 99% of the writes will be for one byte, so no sense in being smart.)
363 pty_t
*pp
= tp
->tty_priv
;
366 if (pp
->state
& PTY_CLOSED
) {
368 if (tp
->tty_inleft
> 0) {
369 if(tp
->tty_inrepcode
== TTY_REVIVE
) {
370 notify(tp
->tty_incaller
);
371 tp
->tty_inrevived
= 1;
373 tty_reply(tp
->tty_inrepcode
, tp
->tty_incaller
,
374 tp
->tty_inproc
, tp
->tty_incum
);
375 tp
->tty_inleft
= tp
->tty_incum
= 0;
387 while (pp
->wrleft
> 0) {
390 /* Transfer one character to 'c'. */
391 if ((s
= sys_safecopyfrom(pp
->wrcaller
, pp
->wrgrant
, pp
->wroffset
,
392 (vir_bytes
) &c
, 1)) != OK
) {
393 printf("pty: safecopy failed (error %d)\n", s
);
398 /* Input processing. */
399 if (in_process(tp
, &c
, 1, -1) == 0) break;
401 /* PTY writer bookkeeping. */
403 if (--pp
->wrleft
== 0) {
404 if (pp
->wrsendreply
) {
405 tty_reply(TASK_REPLY
, pp
->wrcaller
, pp
->wrproc
,
410 notify(pp
->wrcaller
);
417 /*===========================================================================*
419 *===========================================================================*/
420 static int pty_close(tty_t
*tp
, int UNUSED(try))
422 /* The tty side has closed, so shut down the pty side. */
423 pty_t
*pp
= tp
->tty_priv
;
425 if (!(pp
->state
& PTY_ACTIVE
)) return 0;
427 if (pp
->rdleft
> 0) {
428 assert(!pp
->rdsendreply
);
429 notify(pp
->rdcaller
);
432 if (pp
->wrleft
> 0) {
433 assert(!pp
->wrsendreply
);
434 notify(pp
->wrcaller
);
437 if (pp
->state
& PTY_CLOSED
) pp
->state
= 0; else pp
->state
|= TTY_CLOSED
;
442 /*===========================================================================*
444 *===========================================================================*/
445 static int pty_icancel(tty_t
*tp
, int UNUSED(try))
447 /* Discard waiting input. */
448 pty_t
*pp
= tp
->tty_priv
;
450 if (pp
->wrleft
> 0) {
451 pp
->wrcum
+= pp
->wrleft
;
453 notify(pp
->wrcaller
);
459 /*===========================================================================*
461 *===========================================================================*/
462 static int pty_ocancel(tty_t
*tp
, int UNUSED(try))
464 /* Drain the output buffer. */
465 pty_t
*pp
= tp
->tty_priv
;
468 pp
->otail
= pp
->ohead
;
473 /*===========================================================================*
475 *===========================================================================*/
476 void pty_init(tty_t
*tp
)
481 /* Associate PTY and TTY structures. */
482 line
= tp
- &tty_table
[NR_CONS
+ NR_RS_LINES
];
483 pp
= tp
->tty_priv
= &pty_table
[line
];
486 pp
->rdgrant
= GRANT_INVALID
;
487 pp
->wrgrant
= GRANT_INVALID
;
489 /* Set up output queue. */
490 pp
->ohead
= pp
->otail
= pp
->obuf
;
492 /* Fill in TTY function hooks. */
493 tp
->tty_devread
= pty_read
;
494 tp
->tty_devwrite
= pty_write
;
495 tp
->tty_echo
= pty_echo
;
496 tp
->tty_icancel
= pty_icancel
;
497 tp
->tty_ocancel
= pty_ocancel
;
498 tp
->tty_close
= pty_close
;
499 tp
->tty_select_ops
= 0;
502 /*===========================================================================*
504 *===========================================================================*/
505 int pty_status(message
*m_ptr
)
511 for (i
= 0, pp
= pty_table
; i
<NR_PTYS
; i
++, pp
++) {
512 if ((((pp
->state
& TTY_CLOSED
) && pp
->rdleft
> 0) ||
514 pp
->rdcaller
== m_ptr
->m_source
)
516 m_ptr
->m_type
= DEV_REVIVE
;
517 m_ptr
->REP_ENDPT
= pp
->rdproc
;
518 m_ptr
->REP_IO_GRANT
= pp
->rdgrant
;
519 m_ptr
->REP_STATUS
= pp
->rdcum
;
520 pp
->rdleft
= pp
->rdcum
= 0;
521 pp
->rdgrant
= GRANT_INVALID
;
526 if ((((pp
->state
& TTY_CLOSED
) && pp
->wrleft
> 0) ||
528 pp
->wrcaller
== m_ptr
->m_source
)
530 m_ptr
->m_type
= DEV_REVIVE
;
531 m_ptr
->REP_ENDPT
= pp
->wrproc
;
532 m_ptr
->REP_IO_GRANT
= pp
->wrgrant
;
534 m_ptr
->REP_STATUS
= EIO
;
536 m_ptr
->REP_STATUS
= pp
->wrcum
;
538 pp
->wrleft
= pp
->wrcum
= 0;
539 pp
->wrgrant
= GRANT_INVALID
;
544 if (pp
->select_ready_ops
&& pp
->select_proc
== m_ptr
->m_source
) {
545 m_ptr
->m_type
= DEV_IO_READY
;
546 m_ptr
->DEV_MINOR
= PTYPX_MINOR
+ i
;
547 m_ptr
->DEV_SEL_OPS
= pp
->select_ready_ops
;
548 pp
->select_ready_ops
= 0;
556 /*===========================================================================*
558 *===========================================================================*/
559 static int select_try_pty(tty_t
*tp
, int ops
)
561 pty_t
*pp
= tp
->tty_priv
;
565 /* Write won't block on error. */
566 if (pp
->state
& TTY_CLOSED
) r
|= SEL_WR
;
567 else if (pp
->wrleft
!= 0 || pp
->wrcum
!= 0) r
|= SEL_WR
;
572 /* Read won't block on error. */
573 if (pp
->state
& TTY_CLOSED
) r
|= SEL_RD
;
574 else if (pp
->rdleft
!= 0 || pp
->rdcum
!= 0) r
|= SEL_RD
;
575 else if (pp
->ocount
> 0) r
|= SEL_RD
; /* Actual data. */
581 /*===========================================================================*
583 *===========================================================================*/
584 void select_retry_pty(tty_t
*tp
)
586 pty_t
*pp
= tp
->tty_priv
;
589 /* See if the pty side of a pty is ready to return a select. */
590 if (pp
->select_ops
&& (r
=select_try_pty(tp
, pp
->select_ops
))) {
591 pp
->select_ops
&= ~r
;
592 pp
->select_ready_ops
|= r
;
593 notify(pp
->select_proc
);
597 /*===========================================================================*
599 *===========================================================================*/
600 static int pty_select(tty_t
*tp
, message
*m
)
602 pty_t
*pp
= tp
->tty_priv
;
603 int ops
, ready_ops
= 0, watch
;
605 ops
= m
->USER_ENDPT
& (SEL_RD
|SEL_WR
|SEL_ERR
);
606 watch
= (m
->USER_ENDPT
& SEL_NOTIFY
) ? 1 : 0;
608 ready_ops
= select_try_pty(tp
, ops
);
610 if (!ready_ops
&& ops
&& watch
) {
611 pp
->select_ops
|= ops
;
612 pp
->select_proc
= m
->m_source
;
618 #endif /* NR_PTYS > 0 */