panic() cleanup.
[minix.git] / drivers / tty / pty.c
blob3949a70158f196a3dd391e00cacfbdce3058bee4
1 /* pty.c - pseudo terminal driver Author: Kees J. Bot
2 * 30 Dec 1995
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"
20 #include <assert.h>
21 #include <termios.h>
22 #include <signal.h>
23 #include <minix/com.h>
24 #include <minix/callnr.h>
25 #include <sys/select.h>
26 #include "tty.h"
28 #if NR_PTYS > 0
30 /* PTY bookkeeping structure, one per pty/tty pair. */
31 typedef struct pty {
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 */
55 /* Output buffer. */
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 */
60 /* select() data. */
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. */
64 } pty_t;
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 /*===========================================================================*
83 * do_pty *
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;
89 int r;
90 int safe = 0;
92 switch (m_ptr->m_type) {
93 case DEV_READ_S:
94 safe=1;
95 /* Check, store information on the reader, do I/O. */
96 if (pp->state & TTY_CLOSED) {
97 r = 0;
98 break;
100 if (pp->rdleft != 0 || pp->rdcum != 0) {
101 r = EIO;
102 break;
104 if (m_ptr->COUNT <= 0) {
105 r = EINVAL;
106 break;
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;
113 pp->rdsafe = safe;
114 pp->rdleft = m_ptr->COUNT;
115 pty_start(pp);
116 handle_events(tp);
117 if (pp->rdleft == 0) {
118 return; /* already done */
122 r = SUSPEND; /* do suspend */
123 pp->rdsendreply = FALSE;
125 break;
127 case DEV_WRITE_S:
128 safe=1;
129 /* Check, store information on the writer, do I/O. */
130 if (pp->state & TTY_CLOSED) {
131 r = EIO;
132 break;
134 if (pp->wrleft != 0 || pp->wrcum != 0) {
135 r = EIO;
136 break;
138 if (m_ptr->COUNT <= 0) {
139 r = EINVAL;
140 break;
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;
147 pp->wrsafe = safe;
148 pp->wrleft = m_ptr->COUNT;
149 handle_events(tp);
150 if (pp->wrleft == 0) {
151 return; /* already done */
155 pp->wrsendreply = FALSE; /* do suspend */
156 r = SUSPEND;
158 break;
160 case DEV_OPEN:
161 r = pp->state != 0 ? EIO : OK;
162 pp->state |= PTY_ACTIVE;
163 pp->rdcum = 0;
164 pp->wrcum = 0;
165 break;
167 case DEV_CLOSE:
168 r = OK;
169 if (pp->state & TTY_CLOSED) {
170 pp->state = 0;
171 } else {
172 pp->state |= PTY_CLOSED;
173 sigchar(tp, SIGHUP, 1);
175 break;
177 case DEV_SELECT:
178 r = pty_select(tp, m_ptr);
179 break;
181 case CANCEL:
182 r = EINTR;
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;
193 break;
195 default:
196 r = EINVAL;
198 tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, r);
201 /*===========================================================================*
202 * pty_write *
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) {
215 if (try) return 1;
216 if (tp->tty_outleft > 0) {
217 if(tp->tty_outrepcode == TTY_REVIVE) {
218 notify(tp->tty_outcaller);
219 tp->tty_outrevived = 1;
220 } else {
221 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
222 tp->tty_outproc, EIO);
223 tp->tty_outleft = tp->tty_outcum = 0;
226 return 0;
229 /* While there is something to do. */
230 for (;;) {
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)
237 break;
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) {
243 break;
245 } else {
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) {
248 break;
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;
259 /* Bookkeeping. */
260 pp->ocount += ocount;
261 if ((pp->ohead += ocount) >= bufend(pp->obuf))
262 pp->ohead -= buflen(pp->obuf);
263 pty_start(pp);
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;
274 } else {
275 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
276 tp->tty_outproc, tp->tty_outcum);
277 tp->tty_outcum = 0;
281 pty_finish(pp);
282 return 1;
285 /*===========================================================================*
286 * pty_echo *
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;
293 int count, ocount;
295 ocount = buflen(pp->obuf) - pp->ocount;
296 if (ocount == 0) return; /* output buffer full */
297 count = 1;
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);
305 pty_start(pp);
308 /*===========================================================================*
309 * pty_start *
310 *===========================================================================*/
311 PRIVATE void pty_start(pty_t *pp)
313 /* Transfer bytes written to the output buffer to the PTY reader. */
314 int count;
316 /* While there are things to do. */
317 for (;;) {
318 int s;
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. */
325 if (pp->rdsafe) {
326 if((s = sys_safecopyto(pp->rdproc, pp->rdvir_g,
327 pp->rdvir_offset, (vir_bytes) pp->otail, count, D)) != OK) {
328 break;
330 pp->rdvir_offset += count;
331 } else {
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);
335 break;
337 pp->rdvir_g += count;
340 /* Bookkeeping. */
341 pp->ocount -= count;
342 if ((pp->otail += count) == bufend(pp->obuf)) pp->otail = pp->obuf;
343 pp->rdcum += count;
344 pp->rdleft -= count;
348 /*===========================================================================*
349 * pty_finish *
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
354 * transferred.
356 if (pp->rdcum > 0) {
357 if (pp->rdsendreply) {
358 tty_reply(TASK_REPLY, pp->rdcaller, pp->rdproc, pp->rdcum);
359 pp->rdleft = pp->rdcum = 0;
361 else
362 notify(pp->rdcaller);
367 /*===========================================================================*
368 * pty_read *
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;
376 char c;
378 if (pp->state & PTY_CLOSED) {
379 if (try) return 1;
380 if (tp->tty_inleft > 0) {
381 if(tp->tty_inrepcode == TTY_REVIVE) {
382 notify(tp->tty_incaller);
383 tp->tty_inrevived = 1;
384 } else {
385 tty_reply(tp->tty_inrepcode, tp->tty_incaller,
386 tp->tty_inproc, tp->tty_incum);
387 tp->tty_inleft = tp->tty_incum = 0;
390 return 1;
393 if (try) {
394 if (pp->wrleft > 0)
395 return 1;
396 return 0;
399 while (pp->wrleft > 0) {
400 int s;
402 /* Transfer one character to 'c'. */
403 if(pp->wrsafe) {
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);
407 break;
409 pp->wrvir_offset++;
410 } else {
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);
414 break;
416 pp->wrvir_g++;
419 /* Input processing. */
420 if (in_process(tp, &c, 1) == 0) break;
422 /* PTY writer bookkeeping. */
423 pp->wrcum++;
424 if (--pp->wrleft == 0) {
425 if (pp->wrsendreply) {
426 tty_reply(TASK_REPLY, pp->wrcaller, pp->wrproc,
427 pp->wrcum);
428 pp->wrcum = 0;
430 else
431 notify(pp->wrcaller);
435 return 0;
438 /*===========================================================================*
439 * pty_close *
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;
460 return 0;
463 /*===========================================================================*
464 * pty_icancel *
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;
473 pp->wrleft= 0;
474 notify(pp->wrcaller);
477 return 0;
480 /*===========================================================================*
481 * pty_ocancel *
482 *===========================================================================*/
483 PRIVATE int pty_ocancel(tty_t *tp, int try)
485 /* Drain the output buffer. */
486 pty_t *pp = tp->tty_priv;
488 pp->ocount = 0;
489 pp->otail = pp->ohead;
491 return 0;
494 /*===========================================================================*
495 * pty_init *
496 *===========================================================================*/
497 PUBLIC void pty_init(tty_t *tp)
499 pty_t *pp;
500 int line;
502 /* Associate PTY and TTY structures. */
503 line = tp - &tty_table[NR_CONS + NR_RS_LINES];
504 pp = tp->tty_priv = &pty_table[line];
505 pp->tty = tp;
506 pp->select_ops = 0;
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 /*===========================================================================*
522 * pty_status *
523 *===========================================================================*/
524 PUBLIC int pty_status(message *m_ptr)
526 int i, event_found;
527 pty_t *pp;
529 event_found = 0;
530 for (i= 0, pp = pty_table; i<NR_PTYS; i++, pp++) {
531 if ((((pp->state & TTY_CLOSED) && pp->rdleft > 0) ||
532 pp->rdcum > 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;
541 event_found = 1;
542 break;
545 if ((((pp->state & TTY_CLOSED) && pp->wrleft > 0) ||
546 pp->wrcum > 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;
552 if (pp->wrcum == 0)
553 m_ptr->REP_STATUS = EIO;
554 else
555 m_ptr->REP_STATUS = pp->wrcum;
557 pp->wrleft = pp->wrcum = 0;
558 event_found = 1;
559 break;
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;
567 event_found = 1;
568 break;
571 return event_found;
574 /*===========================================================================*
575 * select_try_pty *
576 *===========================================================================*/
577 PRIVATE int select_try_pty(tty_t *tp, int ops)
579 pty_t *pp = tp->tty_priv;
580 int r = 0;
582 if (ops & SEL_WR) {
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;
586 else r |= SEL_WR;
589 if (ops & SEL_RD) {
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. */
596 return r;
599 /*===========================================================================*
600 * select_retry_pty *
601 *===========================================================================*/
602 PUBLIC void select_retry_pty(tty_t *tp)
604 pty_t *pp = tp->tty_priv;
605 int r;
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 /*===========================================================================*
616 * pty_select *
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;
633 return ready_ops;
636 #endif /* NR_PTYS > 0 */