dec21140A ethernet driver for virtualpc, contributed by nicolas tittley.
[minix.git] / drivers / tty / pty.c
blobd7949f8c5d0b6e852c18b4ad83e425835dbdcf54
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 int safe = 0;
94 switch (m_ptr->m_type) {
95 case DEV_READ_S:
96 safe=1;
97 /* Check, store information on the reader, do I/O. */
98 if (pp->state & TTY_CLOSED) {
99 r = 0;
100 break;
102 if (pp->rdleft != 0 || pp->rdcum != 0) {
103 r = EIO;
104 break;
106 if (m_ptr->COUNT <= 0) {
107 r = EINVAL;
108 break;
110 pp->rdsendreply = TRUE;
111 pp->rdcaller = m_ptr->m_source;
112 pp->rdproc = m_ptr->IO_ENDPT;
113 pp->rdvir_g = (vir_bytes) m_ptr->ADDRESS;
114 pp->rdvir_offset = 0;
115 pp->rdsafe = safe;
116 pp->rdleft = m_ptr->COUNT;
117 pty_start(pp);
118 handle_events(tp);
119 if (pp->rdleft == 0) {
120 return; /* already done */
124 r = SUSPEND; /* do suspend */
125 pp->rdsendreply = FALSE;
127 break;
129 case DEV_WRITE_S:
130 safe=1;
131 /* Check, store information on the writer, do I/O. */
132 if (pp->state & TTY_CLOSED) {
133 r = EIO;
134 break;
136 if (pp->wrleft != 0 || pp->wrcum != 0) {
137 r = EIO;
138 break;
140 if (m_ptr->COUNT <= 0) {
141 r = EINVAL;
142 break;
144 pp->wrsendreply = TRUE;
145 pp->wrcaller = m_ptr->m_source;
146 pp->wrproc = m_ptr->IO_ENDPT;
147 pp->wrvir_g = (vir_bytes) m_ptr->ADDRESS;
148 pp->wrvir_offset = 0;
149 pp->wrsafe = safe;
150 pp->wrleft = m_ptr->COUNT;
151 handle_events(tp);
152 if (pp->wrleft == 0) {
153 return; /* already done */
157 pp->wrsendreply = FALSE; /* do suspend */
158 r = SUSPEND;
160 break;
162 case DEV_OPEN:
163 r = pp->state != 0 ? EIO : OK;
164 pp->state |= PTY_ACTIVE;
165 pp->rdcum = 0;
166 pp->wrcum = 0;
167 break;
169 case DEV_CLOSE:
170 r = OK;
171 if (pp->state & TTY_CLOSED) {
172 pp->state = 0;
173 } else {
174 pp->state |= PTY_CLOSED;
175 sigchar(tp, SIGHUP, 1);
177 break;
179 case DEV_SELECT:
180 r = pty_select(tp, m_ptr);
181 break;
183 case CANCEL:
184 r = EINTR;
185 if (m_ptr->IO_ENDPT == pp->rdproc) {
186 /* Cancel a read from a PTY. */
187 r = pp->rdcum > 0 ? pp->rdcum : EAGAIN;
188 pp->rdleft = pp->rdcum = 0;
190 if (m_ptr->IO_ENDPT == pp->wrproc) {
191 /* Cancel a write to a PTY. */
192 r = pp->wrcum > 0 ? pp->wrcum : EAGAIN;
193 pp->wrleft = pp->wrcum = 0;
195 break;
197 default:
198 r = EINVAL;
200 tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, r);
203 /*===========================================================================*
204 * pty_write *
205 *===========================================================================*/
206 PRIVATE int pty_write(tp, try)
207 tty_t *tp;
208 int try;
210 /* (*dev_write)() routine for PTYs. Transfer bytes from the writer on
211 * /dev/ttypX to the output buffer.
213 pty_t *pp = tp->tty_priv;
214 int count, ocount, s;
217 /* PTY closed down? */
218 if (pp->state & PTY_CLOSED) {
219 if (try) return 1;
220 if (tp->tty_outleft > 0) {
221 if(tp->tty_outrepcode == TTY_REVIVE) {
222 notify(tp->tty_outcaller);
223 tp->tty_outrevived = 1;
224 } else {
225 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
226 tp->tty_outproc, EIO);
227 tp->tty_outleft = tp->tty_outcum = 0;
230 return 0;
233 /* While there is something to do. */
234 for (;;) {
235 ocount = buflen(pp->obuf) - pp->ocount;
236 if (try) return (ocount > 0);
237 count = bufend(pp->obuf) - pp->ohead;
238 if (count > ocount) count = ocount;
239 if (count > tp->tty_outleft) count = tp->tty_outleft;
240 if (count == 0 || tp->tty_inhibited)
241 break;
243 /* Copy from user space to the PTY output buffer. */
244 if(tp->tty_out_safe) {
245 if ((s = sys_safecopyfrom(tp->tty_outproc, tp->tty_out_vir_g,
246 tp->tty_out_vir_offset, (vir_bytes) pp->ohead, count, D))!=OK) {
247 break;
249 } else {
250 if ((s = sys_vircopy(tp->tty_outproc, D, (vir_bytes) tp->tty_out_vir_g,
251 SELF, D, (vir_bytes) pp->ohead, (phys_bytes) count)) != OK) {
252 break;
256 /* Perform output processing on the output buffer. */
257 out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
258 if (count == 0) break;
260 /* Assume echoing messed up by output. */
261 tp->tty_reprint = TRUE;
263 /* Bookkeeping. */
264 pp->ocount += ocount;
265 if ((pp->ohead += ocount) >= bufend(pp->obuf))
266 pp->ohead -= buflen(pp->obuf);
267 pty_start(pp);
269 if(tp->tty_out_safe) tp->tty_out_vir_offset += count;
270 else tp->tty_out_vir_g += 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 PRIVATE void pty_echo(tp, c)
293 tty_t *tp;
294 int c;
296 /* Echo one character. (Like pty_write, but only one character, optionally.) */
298 pty_t *pp = tp->tty_priv;
299 int count, ocount;
301 ocount = buflen(pp->obuf) - pp->ocount;
302 if (ocount == 0) return; /* output buffer full */
303 count = 1;
304 *pp->ohead = c; /* add one character */
306 out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
307 if (count == 0) return;
309 pp->ocount += ocount;
310 if ((pp->ohead += ocount) >= bufend(pp->obuf)) pp->ohead -= buflen(pp->obuf);
311 pty_start(pp);
314 /*===========================================================================*
315 * pty_start *
316 *===========================================================================*/
317 PRIVATE void pty_start(pp)
318 pty_t *pp;
320 /* Transfer bytes written to the output buffer to the PTY reader. */
321 int count;
323 /* While there are things to do. */
324 for (;;) {
325 int s;
326 count = bufend(pp->obuf) - pp->otail;
327 if (count > pp->ocount) count = pp->ocount;
328 if (count > pp->rdleft) count = pp->rdleft;
329 if (count == 0) break;
331 /* Copy from the output buffer to the readers address space. */
332 if (pp->rdsafe) {
333 if((s = sys_safecopyto(pp->rdproc, pp->rdvir_g,
334 pp->rdvir_offset, (vir_bytes) pp->otail, count, D)) != OK) {
335 break;
337 pp->rdvir_offset += count;
338 } else {
339 if ((s = sys_vircopy(SELF, D, (vir_bytes)pp->otail,
340 (vir_bytes) pp->rdproc, D, (vir_bytes) pp->rdvir_g, (phys_bytes) count)) != OK) {
341 printf("pty tty: copy failed (error %d)\n", s);
342 break;
344 pp->rdvir_g += count;
347 /* Bookkeeping. */
348 pp->ocount -= count;
349 if ((pp->otail += count) == bufend(pp->obuf)) pp->otail = pp->obuf;
350 pp->rdcum += count;
351 pp->rdleft -= count;
355 /*===========================================================================*
356 * pty_finish *
357 *===========================================================================*/
358 PRIVATE void pty_finish(pp)
359 pty_t *pp;
361 /* Finish the read request of a PTY reader if there is at least one byte
362 * transferred.
364 if (pp->rdcum > 0) {
365 if (pp->rdsendreply) {
366 tty_reply(TASK_REPLY, pp->rdcaller, pp->rdproc, pp->rdcum);
367 pp->rdleft = pp->rdcum = 0;
369 else
370 notify(pp->rdcaller);
375 /*===========================================================================*
376 * pty_read *
377 *===========================================================================*/
378 PRIVATE int pty_read(tp, try)
379 tty_t *tp;
380 int try;
382 /* Offer bytes from the PTY writer for input on the TTY. (Do it one byte at
383 * a time, 99% of the writes will be for one byte, so no sense in being smart.)
385 pty_t *pp = tp->tty_priv;
386 char c;
388 if (pp->state & PTY_CLOSED) {
389 if (try) return 1;
390 if (tp->tty_inleft > 0) {
391 if(tp->tty_inrepcode == TTY_REVIVE) {
392 notify(tp->tty_incaller);
393 tp->tty_inrevived = 1;
394 } else {
395 tty_reply(tp->tty_inrepcode, tp->tty_incaller,
396 tp->tty_inproc, tp->tty_incum);
397 tp->tty_inleft = tp->tty_incum = 0;
400 return 1;
403 if (try) {
404 if (pp->wrleft > 0)
405 return 1;
406 return 0;
409 while (pp->wrleft > 0) {
410 int s;
412 /* Transfer one character to 'c'. */
413 if(pp->wrsafe) {
414 if ((s = sys_safecopyfrom(pp->wrproc, pp->wrvir_g,
415 pp->wrvir_offset, (vir_bytes) &c, 1, D)) != OK) {
416 printf("pty: safecopy failed (error %d)\n", s);
417 break;
419 pp->wrvir_offset++;
420 } else {
421 if ((s = sys_vircopy(pp->wrproc, D, (vir_bytes) pp->wrvir_g,
422 SELF, D, (vir_bytes) &c, (phys_bytes) 1)) != OK) {
423 printf("pty: copy failed (error %d)\n", s);
424 break;
426 pp->wrvir_g++;
429 /* Input processing. */
430 if (in_process(tp, &c, 1) == 0) break;
432 /* PTY writer bookkeeping. */
433 pp->wrcum++;
434 if (--pp->wrleft == 0) {
435 if (pp->wrsendreply) {
436 tty_reply(TASK_REPLY, pp->wrcaller, pp->wrproc,
437 pp->wrcum);
438 pp->wrcum = 0;
440 else
441 notify(pp->wrcaller);
445 return 0;
448 /*===========================================================================*
449 * pty_close *
450 *===========================================================================*/
451 PRIVATE int pty_close(tp, try)
452 tty_t *tp;
453 int try;
455 /* The tty side has closed, so shut down the pty side. */
456 pty_t *pp = tp->tty_priv;
458 if (!(pp->state & PTY_ACTIVE)) return 0;
460 if (pp->rdleft > 0) {
461 assert(!pp->rdsendreply);
462 notify(pp->rdcaller);
465 if (pp->wrleft > 0) {
466 assert(!pp->wrsendreply);
467 notify(pp->wrcaller);
470 if (pp->state & PTY_CLOSED) pp->state = 0; else pp->state |= TTY_CLOSED;
472 return 0;
475 /*===========================================================================*
476 * pty_icancel *
477 *===========================================================================*/
478 PRIVATE int pty_icancel(tp, try)
479 tty_t *tp;
480 int try;
482 /* Discard waiting input. */
483 pty_t *pp = tp->tty_priv;
485 if (pp->wrleft > 0) {
486 pp->wrcum += pp->wrleft;
487 pp->wrleft= 0;
488 notify(pp->wrcaller);
491 return 0;
494 /*===========================================================================*
495 * pty_ocancel *
496 *===========================================================================*/
497 PRIVATE int pty_ocancel(tp, try)
498 tty_t *tp;
499 int try;
501 /* Drain the output buffer. */
502 pty_t *pp = tp->tty_priv;
504 pp->ocount = 0;
505 pp->otail = pp->ohead;
507 return 0;
510 /*===========================================================================*
511 * pty_init *
512 *===========================================================================*/
513 PUBLIC void pty_init(tp)
514 tty_t *tp;
516 pty_t *pp;
517 int line;
519 /* Associate PTY and TTY structures. */
520 line = tp - &tty_table[NR_CONS + NR_RS_LINES];
521 pp = tp->tty_priv = &pty_table[line];
522 pp->tty = tp;
523 pp->select_ops = 0;
525 /* Set up output queue. */
526 pp->ohead = pp->otail = pp->obuf;
528 /* Fill in TTY function hooks. */
529 tp->tty_devread = pty_read;
530 tp->tty_devwrite = pty_write;
531 tp->tty_echo = pty_echo;
532 tp->tty_icancel = pty_icancel;
533 tp->tty_ocancel = pty_ocancel;
534 tp->tty_close = pty_close;
535 tp->tty_select_ops = 0;
538 /*===========================================================================*
539 * pty_status *
540 *===========================================================================*/
541 PUBLIC int pty_status(message *m_ptr)
543 int i, event_found;
544 pty_t *pp;
546 event_found = 0;
547 for (i= 0, pp = pty_table; i<NR_PTYS; i++, pp++) {
548 if ((((pp->state & TTY_CLOSED) && pp->rdleft > 0) ||
549 pp->rdcum > 0) &&
550 pp->rdcaller == m_ptr->m_source)
552 m_ptr->m_type = DEV_REVIVE;
553 m_ptr->REP_ENDPT = pp->rdproc;
554 m_ptr->REP_IO_GRANT = pp->rdvir_g;
555 m_ptr->REP_STATUS = pp->rdcum;
557 pp->rdleft = pp->rdcum = 0;
558 event_found = 1;
559 break;
562 if ((((pp->state & TTY_CLOSED) && pp->wrleft > 0) ||
563 pp->wrcum > 0) &&
564 pp->wrcaller == m_ptr->m_source)
566 m_ptr->m_type = DEV_REVIVE;
567 m_ptr->REP_ENDPT = pp->wrproc;
568 m_ptr->REP_IO_GRANT = pp->wrvir_g;
569 if (pp->wrcum == 0)
570 m_ptr->REP_STATUS = EIO;
571 else
572 m_ptr->REP_STATUS = pp->wrcum;
574 pp->wrleft = pp->wrcum = 0;
575 event_found = 1;
576 break;
579 if (pp->select_ready_ops && pp->select_proc == m_ptr->m_source) {
580 m_ptr->m_type = DEV_IO_READY;
581 m_ptr->DEV_MINOR = PTYPX_MINOR + i;
582 m_ptr->DEV_SEL_OPS = pp->select_ready_ops;
583 pp->select_ready_ops = 0;
584 event_found = 1;
585 break;
588 return event_found;
591 /*===========================================================================*
592 * select_try_pty *
593 *===========================================================================*/
594 PRIVATE int select_try_pty(tty_t *tp, int ops)
596 pty_t *pp = tp->tty_priv;
597 int r = 0;
599 if (ops & SEL_WR) {
600 /* Write won't block on error. */
601 if (pp->state & TTY_CLOSED) r |= SEL_WR;
602 else if (pp->wrleft != 0 || pp->wrcum != 0) r |= SEL_WR;
603 else r |= SEL_WR;
606 if (ops & SEL_RD) {
607 /* Read won't block on error. */
608 if (pp->state & TTY_CLOSED) r |= SEL_RD;
609 else if (pp->rdleft != 0 || pp->rdcum != 0) r |= SEL_RD;
610 else if (pp->ocount > 0) r |= SEL_RD; /* Actual data. */
613 return r;
616 /*===========================================================================*
617 * select_retry_pty *
618 *===========================================================================*/
619 PUBLIC void select_retry_pty(tty_t *tp)
621 pty_t *pp = tp->tty_priv;
622 int r;
624 /* See if the pty side of a pty is ready to return a select. */
625 if (pp->select_ops && (r=select_try_pty(tp, pp->select_ops))) {
626 pp->select_ops &= ~r;
627 pp->select_ready_ops |= r;
628 notify(pp->select_proc);
632 /*===========================================================================*
633 * pty_select *
634 *===========================================================================*/
635 PRIVATE int pty_select(tty_t *tp, message *m)
637 pty_t *pp = tp->tty_priv;
638 int ops, ready_ops = 0, watch;
640 ops = m->IO_ENDPT & (SEL_RD|SEL_WR|SEL_ERR);
641 watch = (m->IO_ENDPT & SEL_NOTIFY) ? 1 : 0;
643 ready_ops = select_try_pty(tp, ops);
645 if (!ready_ops && ops && watch) {
646 pp->select_ops |= ops;
647 pp->select_proc = m->m_source;
650 return ready_ops;
653 #endif /* NR_PTYS > 0 */