memory: use sys_safememset() for /dev/zero
[minix.git] / drivers / tty / pty.c
blob9d718214bff8c4e9d908952ca1fad7933d2109bc
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 ((s = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant,
246 tp->tty_outoffset, (vir_bytes) pp->ohead, count))!=OK) {
247 break;
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;
257 /* Bookkeeping. */
258 pp->ocount += ocount;
259 if ((pp->ohead += ocount) >= bufend(pp->obuf))
260 pp->ohead -= buflen(pp->obuf);
261 pty_start(pp);
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;
271 } else {
272 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
273 tp->tty_outproc, tp->tty_outcum);
274 tp->tty_outcum = 0;
278 pty_finish(pp);
279 return 1;
282 /*===========================================================================*
283 * pty_echo *
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;
290 int count, ocount;
292 ocount = buflen(pp->obuf) - pp->ocount;
293 if (ocount == 0) return; /* output buffer full */
294 count = 1;
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);
302 pty_start(pp);
305 /*===========================================================================*
306 * pty_start *
307 *===========================================================================*/
308 static void pty_start(pty_t *pp)
310 /* Transfer bytes written to the output buffer to the PTY reader. */
311 int count;
313 /* While there are things to do. */
314 for (;;) {
315 int s;
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) {
324 break;
326 pp->rdoffset += count;
328 /* Bookkeeping. */
329 pp->ocount -= count;
330 if ((pp->otail += count) == bufend(pp->obuf)) pp->otail = pp->obuf;
331 pp->rdcum += count;
332 pp->rdleft -= count;
336 /*===========================================================================*
337 * pty_finish *
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
342 * transferred.
344 if (pp->rdcum > 0) {
345 if (pp->rdsendreply) {
346 tty_reply(TASK_REPLY, pp->rdcaller, pp->rdproc, pp->rdcum);
347 pp->rdleft = pp->rdcum = 0;
349 else
350 notify(pp->rdcaller);
355 /*===========================================================================*
356 * pty_read *
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;
364 char c;
366 if (pp->state & PTY_CLOSED) {
367 if (try) return 1;
368 if (tp->tty_inleft > 0) {
369 if(tp->tty_inrepcode == TTY_REVIVE) {
370 notify(tp->tty_incaller);
371 tp->tty_inrevived = 1;
372 } else {
373 tty_reply(tp->tty_inrepcode, tp->tty_incaller,
374 tp->tty_inproc, tp->tty_incum);
375 tp->tty_inleft = tp->tty_incum = 0;
378 return 1;
381 if (try) {
382 if (pp->wrleft > 0)
383 return 1;
384 return 0;
387 while (pp->wrleft > 0) {
388 int s;
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);
394 break;
396 pp->wroffset++;
398 /* Input processing. */
399 if (in_process(tp, &c, 1, -1) == 0) break;
401 /* PTY writer bookkeeping. */
402 pp->wrcum++;
403 if (--pp->wrleft == 0) {
404 if (pp->wrsendreply) {
405 tty_reply(TASK_REPLY, pp->wrcaller, pp->wrproc,
406 pp->wrcum);
407 pp->wrcum = 0;
409 else
410 notify(pp->wrcaller);
414 return 0;
417 /*===========================================================================*
418 * pty_close *
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;
439 return 0;
442 /*===========================================================================*
443 * pty_icancel *
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;
452 pp->wrleft= 0;
453 notify(pp->wrcaller);
456 return 0;
459 /*===========================================================================*
460 * pty_ocancel *
461 *===========================================================================*/
462 static int pty_ocancel(tty_t *tp, int UNUSED(try))
464 /* Drain the output buffer. */
465 pty_t *pp = tp->tty_priv;
467 pp->ocount = 0;
468 pp->otail = pp->ohead;
470 return 0;
473 /*===========================================================================*
474 * pty_init *
475 *===========================================================================*/
476 void pty_init(tty_t *tp)
478 pty_t *pp;
479 int line;
481 /* Associate PTY and TTY structures. */
482 line = tp - &tty_table[NR_CONS + NR_RS_LINES];
483 pp = tp->tty_priv = &pty_table[line];
484 pp->tty = tp;
485 pp->select_ops = 0;
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 /*===========================================================================*
503 * pty_status *
504 *===========================================================================*/
505 int pty_status(message *m_ptr)
507 int i, event_found;
508 pty_t *pp;
510 event_found = 0;
511 for (i= 0, pp = pty_table; i<NR_PTYS; i++, pp++) {
512 if ((((pp->state & TTY_CLOSED) && pp->rdleft > 0) ||
513 pp->rdcum > 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;
522 event_found = 1;
523 break;
526 if ((((pp->state & TTY_CLOSED) && pp->wrleft > 0) ||
527 pp->wrcum > 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;
533 if (pp->wrcum == 0)
534 m_ptr->REP_STATUS = EIO;
535 else
536 m_ptr->REP_STATUS = pp->wrcum;
538 pp->wrleft = pp->wrcum = 0;
539 pp->wrgrant = GRANT_INVALID;
540 event_found = 1;
541 break;
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;
549 event_found = 1;
550 break;
553 return event_found;
556 /*===========================================================================*
557 * select_try_pty *
558 *===========================================================================*/
559 static int select_try_pty(tty_t *tp, int ops)
561 pty_t *pp = tp->tty_priv;
562 int r = 0;
564 if (ops & SEL_WR) {
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;
568 else r |= SEL_WR;
571 if (ops & SEL_RD) {
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. */
578 return r;
581 /*===========================================================================*
582 * select_retry_pty *
583 *===========================================================================*/
584 void select_retry_pty(tty_t *tp)
586 pty_t *pp = tp->tty_priv;
587 int r;
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 /*===========================================================================*
598 * pty_select *
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;
615 return ready_ops;
618 #endif /* NR_PTYS > 0 */