can't get_block(NO_DEV) any more
[minix.git] / drivers / tty / pty.c
blob72e4724eb66df92c8f65a5b5565c1a8254183bd8
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 <minix/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 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 */
53 /* Output buffer. */
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 */
58 /* select() data. */
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. */
62 } pty_t;
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 /*===========================================================================*
81 * do_pty *
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;
87 int r;
89 switch (m_ptr->m_type) {
90 case DEV_READ_S:
91 /* Check, store information on the reader, do I/O. */
92 if (pp->state & TTY_CLOSED) {
93 r = 0;
94 break;
96 if (pp->rdleft != 0 || pp->rdcum != 0) {
97 r = EIO;
98 break;
100 if (m_ptr->COUNT <= 0) {
101 r = EINVAL;
102 break;
104 if (pp->rdgrant != GRANT_INVALID) {
105 r = ENOBUFS;
106 break;
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;
112 pp->rdoffset = 0;
113 pp->rdleft = m_ptr->COUNT;
114 pty_start(pp);
115 handle_events(tp);
116 if (pp->rdleft == 0) {
117 pp->rdgrant = GRANT_INVALID;
118 return; /* already done */
122 r = SUSPEND; /* do suspend */
123 pp->rdsendreply = FALSE;
125 break;
127 case DEV_WRITE_S:
128 /* Check, store information on the writer, do I/O. */
129 if (pp->state & TTY_CLOSED) {
130 r = EIO;
131 break;
133 if (pp->wrleft != 0 || pp->wrcum != 0) {
134 r = EIO;
135 break;
137 if (m_ptr->COUNT <= 0) {
138 r = EINVAL;
139 break;
141 if (pp->wrgrant != GRANT_INVALID) {
142 r = ENOBUFS;
143 break;
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;
149 pp->wroffset = 0;
150 pp->wrleft = m_ptr->COUNT;
151 handle_events(tp);
152 if (pp->wrleft == 0) {
153 pp->wrgrant = GRANT_INVALID;
154 return; /* already done */
158 pp->wrsendreply = FALSE; /* do suspend */
159 r = SUSPEND;
161 break;
163 case DEV_OPEN:
164 r = pp->state != 0 ? EIO : OK;
165 pp->state |= PTY_ACTIVE;
166 pp->rdcum = 0;
167 pp->wrcum = 0;
168 break;
170 case DEV_CLOSE:
171 r = OK;
172 if (pp->state & TTY_CLOSED) {
173 pp->state = 0;
174 } else {
175 pp->state |= PTY_CLOSED;
176 sigchar(tp, SIGHUP, 1);
178 break;
180 case DEV_SELECT:
181 r = pty_select(tp, m_ptr);
182 break;
184 case CANCEL:
185 r = EINTR;
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;
198 break;
200 default:
201 r = EINVAL;
203 tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->USER_ENDPT, r);
206 /*===========================================================================*
207 * pty_write *
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) {
220 if (try) return 1;
221 if (tp->tty_outleft > 0) {
222 if(tp->tty_outrepcode == TTY_REVIVE) {
223 notify(tp->tty_outcaller);
224 tp->tty_outrevived = 1;
225 } else {
226 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
227 tp->tty_outproc, EIO);
228 tp->tty_outleft = tp->tty_outcum = 0;
231 return 0;
234 /* While there is something to do. */
235 for (;;) {
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)
242 break;
244 /* Copy from user space to the PTY output buffer. */
245 if (tp->tty_outcaller == KERNEL) {
246 /* We're trying to print on kernel's behalf */
247 memcpy(pp->ohead, (void *) tp->tty_outgrant + tp->tty_outoffset,
248 count);
249 } else {
250 if ((s = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant,
251 tp->tty_outoffset, (vir_bytes) pp->ohead,
252 count)) != OK) {
253 break;
257 /* Perform output processing on the output buffer. */
258 out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
259 if (count == 0) break;
261 /* Assume echoing messed up by output. */
262 tp->tty_reprint = TRUE;
264 /* Bookkeeping. */
265 pp->ocount += ocount;
266 if ((pp->ohead += ocount) >= bufend(pp->obuf))
267 pp->ohead -= buflen(pp->obuf);
268 pty_start(pp);
270 tp->tty_outoffset += count;
272 tp->tty_outcum += count;
273 if ((tp->tty_outleft -= count) == 0) {
274 /* Output is finished, reply to the writer. */
275 if(tp->tty_outrepcode == TTY_REVIVE) {
276 notify(tp->tty_outcaller);
277 tp->tty_outrevived = 1;
278 } else {
279 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
280 tp->tty_outproc, tp->tty_outcum);
281 tp->tty_outcum = 0;
285 pty_finish(pp);
286 return 1;
289 /*===========================================================================*
290 * pty_echo *
291 *===========================================================================*/
292 static void pty_echo(tty_t *tp, int c)
294 /* Echo one character. (Like pty_write, but only one character, optionally.) */
296 pty_t *pp = tp->tty_priv;
297 int count, ocount;
299 ocount = buflen(pp->obuf) - pp->ocount;
300 if (ocount == 0) return; /* output buffer full */
301 count = 1;
302 *pp->ohead = c; /* add one character */
304 out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
305 if (count == 0) return;
307 pp->ocount += ocount;
308 if ((pp->ohead += ocount) >= bufend(pp->obuf)) pp->ohead -= buflen(pp->obuf);
309 pty_start(pp);
312 /*===========================================================================*
313 * pty_start *
314 *===========================================================================*/
315 static void pty_start(pty_t *pp)
317 /* Transfer bytes written to the output buffer to the PTY reader. */
318 int count;
320 /* While there are things to do. */
321 for (;;) {
322 int s;
323 count = bufend(pp->obuf) - pp->otail;
324 if (count > pp->ocount) count = pp->ocount;
325 if (count > pp->rdleft) count = pp->rdleft;
326 if (count == 0) break;
328 /* Copy from the output buffer to the readers address space. */
329 if((s = sys_safecopyto(pp->rdcaller, pp->rdgrant,
330 pp->rdoffset, (vir_bytes) pp->otail, count)) != OK) {
331 break;
333 pp->rdoffset += count;
335 /* Bookkeeping. */
336 pp->ocount -= count;
337 if ((pp->otail += count) == bufend(pp->obuf)) pp->otail = pp->obuf;
338 pp->rdcum += count;
339 pp->rdleft -= count;
343 /*===========================================================================*
344 * pty_finish *
345 *===========================================================================*/
346 static void pty_finish(pty_t *pp)
348 /* Finish the read request of a PTY reader if there is at least one byte
349 * transferred.
351 if (pp->rdcum > 0) {
352 if (pp->rdsendreply) {
353 tty_reply(TASK_REPLY, pp->rdcaller, pp->rdproc, pp->rdcum);
354 pp->rdleft = pp->rdcum = 0;
356 else
357 notify(pp->rdcaller);
362 /*===========================================================================*
363 * pty_read *
364 *===========================================================================*/
365 static int pty_read(tty_t *tp, int try)
367 /* Offer bytes from the PTY writer for input on the TTY. (Do it one byte at
368 * a time, 99% of the writes will be for one byte, so no sense in being smart.)
370 pty_t *pp = tp->tty_priv;
371 char c;
373 if (pp->state & PTY_CLOSED) {
374 if (try) return 1;
375 if (tp->tty_inleft > 0) {
376 if(tp->tty_inrepcode == TTY_REVIVE) {
377 notify(tp->tty_incaller);
378 tp->tty_inrevived = 1;
379 } else {
380 tty_reply(tp->tty_inrepcode, tp->tty_incaller,
381 tp->tty_inproc, tp->tty_incum);
382 tp->tty_inleft = tp->tty_incum = 0;
385 return 1;
388 if (try) {
389 if (pp->wrleft > 0)
390 return 1;
391 return 0;
394 while (pp->wrleft > 0) {
395 int s;
397 /* Transfer one character to 'c'. */
398 if ((s = sys_safecopyfrom(pp->wrcaller, pp->wrgrant, pp->wroffset,
399 (vir_bytes) &c, 1)) != OK) {
400 printf("pty: safecopy failed (error %d)\n", s);
401 break;
403 pp->wroffset++;
405 /* Input processing. */
406 if (in_process(tp, &c, 1, -1) == 0) break;
408 /* PTY writer bookkeeping. */
409 pp->wrcum++;
410 if (--pp->wrleft == 0) {
411 if (pp->wrsendreply) {
412 tty_reply(TASK_REPLY, pp->wrcaller, pp->wrproc,
413 pp->wrcum);
414 pp->wrcum = 0;
416 else
417 notify(pp->wrcaller);
421 return 0;
424 /*===========================================================================*
425 * pty_close *
426 *===========================================================================*/
427 static int pty_close(tty_t *tp, int UNUSED(try))
429 /* The tty side has closed, so shut down the pty side. */
430 pty_t *pp = tp->tty_priv;
432 if (!(pp->state & PTY_ACTIVE)) return 0;
434 if (pp->rdleft > 0) {
435 assert(!pp->rdsendreply);
436 notify(pp->rdcaller);
439 if (pp->wrleft > 0) {
440 assert(!pp->wrsendreply);
441 notify(pp->wrcaller);
444 if (pp->state & PTY_CLOSED) pp->state = 0; else pp->state |= TTY_CLOSED;
446 return 0;
449 /*===========================================================================*
450 * pty_icancel *
451 *===========================================================================*/
452 static int pty_icancel(tty_t *tp, int UNUSED(try))
454 /* Discard waiting input. */
455 pty_t *pp = tp->tty_priv;
457 if (pp->wrleft > 0) {
458 pp->wrcum += pp->wrleft;
459 pp->wrleft= 0;
460 notify(pp->wrcaller);
463 return 0;
466 /*===========================================================================*
467 * pty_ocancel *
468 *===========================================================================*/
469 static int pty_ocancel(tty_t *tp, int UNUSED(try))
471 /* Drain the output buffer. */
472 pty_t *pp = tp->tty_priv;
474 pp->ocount = 0;
475 pp->otail = pp->ohead;
477 return 0;
480 /*===========================================================================*
481 * pty_init *
482 *===========================================================================*/
483 void pty_init(tty_t *tp)
485 pty_t *pp;
486 int line;
488 /* Associate PTY and TTY structures. */
489 line = tp - &tty_table[NR_CONS + NR_RS_LINES];
490 pp = tp->tty_priv = &pty_table[line];
491 pp->tty = tp;
492 pp->select_ops = 0;
493 pp->rdgrant = GRANT_INVALID;
494 pp->wrgrant = GRANT_INVALID;
496 /* Set up output queue. */
497 pp->ohead = pp->otail = pp->obuf;
499 /* Fill in TTY function hooks. */
500 tp->tty_devread = pty_read;
501 tp->tty_devwrite = pty_write;
502 tp->tty_echo = pty_echo;
503 tp->tty_icancel = pty_icancel;
504 tp->tty_ocancel = pty_ocancel;
505 tp->tty_close = pty_close;
506 tp->tty_select_ops = 0;
509 /*===========================================================================*
510 * pty_status *
511 *===========================================================================*/
512 int pty_status(message *m_ptr)
514 int i, event_found;
515 pty_t *pp;
517 event_found = 0;
518 for (i= 0, pp = pty_table; i<NR_PTYS; i++, pp++) {
519 if ((((pp->state & TTY_CLOSED) && pp->rdleft > 0) ||
520 pp->rdcum > 0) &&
521 pp->rdcaller == m_ptr->m_source)
523 m_ptr->m_type = DEV_REVIVE;
524 m_ptr->REP_ENDPT = pp->rdproc;
525 m_ptr->REP_IO_GRANT = pp->rdgrant;
526 m_ptr->REP_STATUS = pp->rdcum;
527 pp->rdleft = pp->rdcum = 0;
528 pp->rdgrant = GRANT_INVALID;
529 event_found = 1;
530 break;
533 if ((((pp->state & TTY_CLOSED) && pp->wrleft > 0) ||
534 pp->wrcum > 0) &&
535 pp->wrcaller == m_ptr->m_source)
537 m_ptr->m_type = DEV_REVIVE;
538 m_ptr->REP_ENDPT = pp->wrproc;
539 m_ptr->REP_IO_GRANT = pp->wrgrant;
540 if (pp->wrcum == 0)
541 m_ptr->REP_STATUS = EIO;
542 else
543 m_ptr->REP_STATUS = pp->wrcum;
545 pp->wrleft = pp->wrcum = 0;
546 pp->wrgrant = GRANT_INVALID;
547 event_found = 1;
548 break;
551 if (pp->select_ready_ops && pp->select_proc == m_ptr->m_source) {
552 m_ptr->m_type = DEV_IO_READY;
553 m_ptr->DEV_MINOR = PTYPX_MINOR + i;
554 m_ptr->DEV_SEL_OPS = pp->select_ready_ops;
555 pp->select_ready_ops = 0;
556 event_found = 1;
557 break;
560 return event_found;
563 /*===========================================================================*
564 * select_try_pty *
565 *===========================================================================*/
566 static int select_try_pty(tty_t *tp, int ops)
568 pty_t *pp = tp->tty_priv;
569 int r = 0;
571 if (ops & SEL_WR) {
572 /* Write won't block on error. */
573 if (pp->state & TTY_CLOSED) r |= SEL_WR;
574 else if (pp->wrleft != 0 || pp->wrcum != 0) r |= SEL_WR;
575 else r |= SEL_WR;
578 if (ops & SEL_RD) {
579 /* Read won't block on error. */
580 if (pp->state & TTY_CLOSED) r |= SEL_RD;
581 else if (pp->rdleft != 0 || pp->rdcum != 0) r |= SEL_RD;
582 else if (pp->ocount > 0) r |= SEL_RD; /* Actual data. */
585 return r;
588 /*===========================================================================*
589 * select_retry_pty *
590 *===========================================================================*/
591 void select_retry_pty(tty_t *tp)
593 pty_t *pp = tp->tty_priv;
594 int r;
596 /* See if the pty side of a pty is ready to return a select. */
597 if (pp->select_ops && (r=select_try_pty(tp, pp->select_ops))) {
598 pp->select_ops &= ~r;
599 pp->select_ready_ops |= r;
600 notify(pp->select_proc);
604 /*===========================================================================*
605 * pty_select *
606 *===========================================================================*/
607 static int pty_select(tty_t *tp, message *m)
609 pty_t *pp = tp->tty_priv;
610 int ops, ready_ops = 0, watch;
612 ops = m->USER_ENDPT & (SEL_RD|SEL_WR|SEL_ERR);
613 watch = (m->USER_ENDPT & SEL_NOTIFY) ? 1 : 0;
615 ready_ops = select_try_pty(tp, ops);
617 if (!ready_ops && ops && watch) {
618 pp->select_ops |= ops;
619 pp->select_proc = m->m_source;
622 return ready_ops;
625 #endif /* NR_PTYS > 0 */