Retired DEV_{READ,WRITE,GATHER,SCATTER,IOCTL} (safe versions *_S are to
[minix3.git] / drivers / tty / pty.c
blobb90fe6e8043add760f8e260d3c76da6487713e57
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(tp, m_ptr)
86 tty_t *tp;
87 message *m_ptr;
89 /* Perform an open/close/read/write call on a /dev/ptypX device. */
90 pty_t *pp = tp->tty_priv;
91 int r;
92 phys_bytes p;
93 int safe = 0;
95 switch (m_ptr->m_type) {
96 case DEV_READ_S:
97 safe=1;
98 /* Check, store information on the reader, do I/O. */
99 if (pp->state & TTY_CLOSED) {
100 r = 0;
101 break;
103 if (pp->rdleft != 0 || pp->rdcum != 0) {
104 r = EIO;
105 break;
107 if (m_ptr->COUNT <= 0) {
108 r = EINVAL;
109 break;
111 #if DEAD_CODE
112 if (numap_local(m_ptr->IO_ENDPT, (vir_bytes) m_ptr->ADDRESS,
113 m_ptr->COUNT) == 0) {
114 #else
115 if ((r = sys_umap(m_ptr->IO_ENDPT, D, (vir_bytes) m_ptr->ADDRESS,
116 m_ptr->COUNT, &p)) != OK) {
117 #endif
118 break;
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;
125 pp->rdsafe = safe;
126 pp->rdleft = m_ptr->COUNT;
127 pty_start(pp);
128 handle_events(tp);
129 if (pp->rdleft == 0) {
130 return; /* already done */
133 #if DEAD_CODE
134 if (m_ptr->TTY_FLAGS & O_NONBLOCK) {
135 r = EAGAIN; /* don't suspend */
136 pp->rdleft = pp->rdcum = 0;
137 } else
138 #endif
140 r = SUSPEND; /* do suspend */
141 pp->rdsendreply = FALSE;
143 break;
145 case DEV_WRITE_S:
146 safe=1;
147 /* Check, store information on the writer, do I/O. */
148 if (pp->state & TTY_CLOSED) {
149 r = EIO;
150 break;
152 if (pp->wrleft != 0 || pp->wrcum != 0) {
153 r = EIO;
154 break;
156 if (m_ptr->COUNT <= 0) {
157 r = EINVAL;
158 break;
160 #if DEAD_CODE
161 if (numap_local(m_ptr->IO_ENDPT, (vir_bytes) m_ptr->ADDRESS,
162 m_ptr->COUNT) == 0) {
163 r = EFAULT;
164 #else
165 if ((r = sys_umap(m_ptr->IO_ENDPT, D, (vir_bytes) m_ptr->ADDRESS,
166 m_ptr->COUNT, &p)) != OK) {
167 #endif
168 break;
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;
175 pp->wrsafe = safe;
176 pp->wrleft = m_ptr->COUNT;
177 handle_events(tp);
178 if (pp->wrleft == 0) {
179 return; /* already done */
182 #if DEAD_CODE
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;
186 } else
187 #endif
189 pp->wrsendreply = FALSE; /* do suspend */
190 r = SUSPEND;
192 break;
194 case DEV_OPEN:
195 r = pp->state != 0 ? EIO : OK;
196 pp->state |= PTY_ACTIVE;
197 pp->rdcum = 0;
198 pp->wrcum = 0;
199 break;
201 case DEV_CLOSE:
202 r = OK;
203 if (pp->state & TTY_CLOSED) {
204 pp->state = 0;
205 } else {
206 pp->state |= PTY_CLOSED;
207 sigchar(tp, SIGHUP);
209 break;
211 case DEV_SELECT:
212 r = pty_select(tp, m_ptr);
213 break;
215 case CANCEL:
216 r = EINTR;
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;
227 break;
229 default:
230 r = EINVAL;
232 tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, r);
235 /*===========================================================================*
236 * pty_write *
237 *===========================================================================*/
238 PRIVATE int pty_write(tp, try)
239 tty_t *tp;
240 int 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) {
251 if (try) return 1;
252 if (tp->tty_outleft > 0) {
253 if(tp->tty_outrepcode == TTY_REVIVE) {
254 notify(tp->tty_outcaller);
255 tp->tty_outrevived = 1;
256 } else {
257 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
258 tp->tty_outproc, EIO);
259 tp->tty_outleft = tp->tty_outcum = 0;
262 return;
265 /* While there is something to do. */
266 for (;;) {
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)
273 break;
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) {
279 break;
281 } else {
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) {
284 break;
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;
295 /* Bookkeeping. */
296 pp->ocount += ocount;
297 if ((pp->ohead += ocount) >= bufend(pp->obuf))
298 pp->ohead -= buflen(pp->obuf);
299 pty_start(pp);
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;
310 } else {
311 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
312 tp->tty_outproc, tp->tty_outcum);
313 tp->tty_outcum = 0;
317 pty_finish(pp);
318 return 1;
321 /*===========================================================================*
322 * pty_echo *
323 *===========================================================================*/
324 PRIVATE void pty_echo(tp, c)
325 tty_t *tp;
326 int c;
328 /* Echo one character. (Like pty_write, but only one character, optionally.) */
330 pty_t *pp = tp->tty_priv;
331 int count, ocount;
333 ocount = buflen(pp->obuf) - pp->ocount;
334 if (ocount == 0) return; /* output buffer full */
335 count = 1;
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);
343 pty_start(pp);
346 /*===========================================================================*
347 * pty_start *
348 *===========================================================================*/
349 PRIVATE void pty_start(pp)
350 pty_t *pp;
352 /* Transfer bytes written to the output buffer to the PTY reader. */
353 int count;
355 /* While there are things to do. */
356 for (;;) {
357 int s;
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. */
364 if (pp->rdsafe) {
365 if((s = sys_safecopyto(pp->rdproc, pp->rdvir_g,
366 pp->rdvir_offset, (vir_bytes) pp->otail, count, D)) != OK) {
367 break;
369 pp->rdvir_offset += count;
370 } else {
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);
374 break;
376 pp->rdvir_g += count;
379 /* Bookkeeping. */
380 pp->ocount -= count;
381 if ((pp->otail += count) == bufend(pp->obuf)) pp->otail = pp->obuf;
382 pp->rdcum += count;
383 pp->rdleft -= count;
387 /*===========================================================================*
388 * pty_finish *
389 *===========================================================================*/
390 PRIVATE void pty_finish(pp)
391 pty_t *pp;
393 /* Finish the read request of a PTY reader if there is at least one byte
394 * transferred.
396 if (pp->rdcum > 0) {
397 if (pp->rdsendreply) {
398 tty_reply(TASK_REPLY, pp->rdcaller, pp->rdproc, pp->rdcum);
399 pp->rdleft = pp->rdcum = 0;
401 else
402 notify(pp->rdcaller);
407 /*===========================================================================*
408 * pty_read *
409 *===========================================================================*/
410 PRIVATE int pty_read(tp, try)
411 tty_t *tp;
412 int 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;
418 char c;
420 if (pp->state & PTY_CLOSED) {
421 if (try) return 1;
422 if (tp->tty_inleft > 0) {
423 if(tp->tty_inrepcode == TTY_REVIVE) {
424 notify(tp->tty_incaller);
425 tp->tty_inrevived = 1;
426 } else {
427 tty_reply(tp->tty_inrepcode, tp->tty_incaller,
428 tp->tty_inproc, tp->tty_incum);
429 tp->tty_inleft = tp->tty_incum = 0;
432 return 1;
435 if (try) {
436 if (pp->wrleft > 0)
437 return 1;
438 return 0;
441 while (pp->wrleft > 0) {
442 int s;
444 /* Transfer one character to 'c'. */
445 if(pp->wrsafe) {
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);
449 break;
451 pp->wrvir_offset++;
452 } else {
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);
456 break;
458 pp->wrvir_g++;
461 /* Input processing. */
462 if (in_process(tp, &c, 1) == 0) break;
464 /* PTY writer bookkeeping. */
465 pp->wrcum++;
466 if (--pp->wrleft == 0) {
467 if (pp->wrsendreply) {
468 tty_reply(TASK_REPLY, pp->wrcaller, pp->wrproc,
469 pp->wrcum);
470 pp->wrcum = 0;
472 else
473 notify(pp->wrcaller);
478 /*===========================================================================*
479 * pty_close *
480 *===========================================================================*/
481 PRIVATE int pty_close(tp, try)
482 tty_t *tp;
483 int 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 /*===========================================================================*
504 * pty_icancel *
505 *===========================================================================*/
506 PRIVATE int pty_icancel(tp, try)
507 tty_t *tp;
508 int try;
510 /* Discard waiting input. */
511 pty_t *pp = tp->tty_priv;
513 if (pp->wrleft > 0) {
514 pp->wrcum += pp->wrleft;
515 pp->wrleft= 0;
516 notify(pp->wrcaller);
520 /*===========================================================================*
521 * pty_ocancel *
522 *===========================================================================*/
523 PRIVATE int pty_ocancel(tp, try)
524 tty_t *tp;
525 int try;
527 /* Drain the output buffer. */
528 pty_t *pp = tp->tty_priv;
530 pp->ocount = 0;
531 pp->otail = pp->ohead;
534 /*===========================================================================*
535 * pty_init *
536 *===========================================================================*/
537 PUBLIC void pty_init(tp)
538 tty_t *tp;
540 pty_t *pp;
541 int line;
543 /* Associate PTY and TTY structures. */
544 line = tp - &tty_table[NR_CONS + NR_RS_LINES];
545 pp = tp->tty_priv = &pty_table[line];
546 pp->tty = tp;
547 pp->select_ops = 0;
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 /*===========================================================================*
563 * pty_status *
564 *===========================================================================*/
565 PUBLIC int pty_status(message *m_ptr)
567 int i, event_found;
568 pty_t *pp;
570 event_found = 0;
571 for (i= 0, pp = pty_table; i<NR_PTYS; i++, pp++) {
572 if ((((pp->state & TTY_CLOSED) && pp->rdleft > 0) ||
573 pp->rdcum > 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;
582 event_found = 1;
583 break;
586 if ((((pp->state & TTY_CLOSED) && pp->wrleft > 0) ||
587 pp->wrcum > 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;
593 if (pp->wrcum == 0)
594 m_ptr->REP_STATUS = EIO;
595 else
596 m_ptr->REP_STATUS = pp->wrcum;
598 pp->wrleft = pp->wrcum = 0;
599 event_found = 1;
600 break;
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;
608 event_found = 1;
609 break;
612 return event_found;
615 /*===========================================================================*
616 * select_try_pty *
617 *===========================================================================*/
618 PRIVATE int select_try_pty(tty_t *tp, int ops)
620 pty_t *pp = tp->tty_priv;
621 int r = 0;
623 if (ops & SEL_WR) {
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;
627 else r |= SEL_WR;
630 if (ops & SEL_RD) {
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. */
637 return r;
640 /*===========================================================================*
641 * select_retry_pty *
642 *===========================================================================*/
643 PUBLIC void select_retry_pty(tty_t *tp)
645 pty_t *pp = tp->tty_priv;
646 int r;
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 /*===========================================================================*
657 * pty_select *
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;
674 return ready_ops;
677 #endif /* NR_PTYS > 0 */