source: trunk/minix/drivers/tty/pty.c@ 15

Last change on this file since 15 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 16.2 KB
Line 
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:
5 *
6 * keyboard -> rlogin -> in.rld -> /dev/ptypX -> /dev/ttypX -> shell
7 * shell -> /dev/ttypX -> /dev/ptypX -> in.rld -> rlogin -> screen
8 *
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.
17 */
18
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"
27
28#if NR_PTYS > 0
29
30/* PTY bookkeeping structure, one per pty/tty pair. */
31typedef struct pty {
32 tty_t *tty; /* associated TTY structure */
33 char state; /* flags: busy, closed, ... */
34
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; /* virtual address in readers address space */
40 int rdleft; /* # bytes yet to be read */
41 int rdcum; /* # bytes written so far */
42
43 /* Write call to /dev/ptypX. */
44 char wrsendreply; /* send a reply (instead of notify) */
45 int wrcaller; /* process making the call (usually FS) */
46 int wrproc; /* process that wants to write to the pty */
47 vir_bytes wrvir; /* virtual address in writers address space */
48 int wrleft; /* # bytes yet to be written */
49 int wrcum; /* # bytes written so far */
50
51 /* Output buffer. */
52 int ocount; /* # characters in the buffer */
53 char *ohead, *otail; /* head and tail of the circular buffer */
54 char obuf[128]; /* buffer for bytes going to the pty reader */
55
56 /* select() data. */
57 int select_ops, /* Which operations do we want to know about? */
58 select_proc, /* Who wants to know about it? */
59 select_ready_ops; /* For callback. */
60} pty_t;
61
62#define PTY_ACTIVE 0x01 /* pty is open/active */
63#define TTY_CLOSED 0x02 /* tty side has closed down */
64#define PTY_CLOSED 0x04 /* pty side has closed down */
65
66PRIVATE pty_t pty_table[NR_PTYS]; /* PTY bookkeeping */
67
68FORWARD _PROTOTYPE( int pty_write, (tty_t *tp, int try) );
69FORWARD _PROTOTYPE( void pty_echo, (tty_t *tp, int c) );
70FORWARD _PROTOTYPE( void pty_start, (pty_t *pp) );
71FORWARD _PROTOTYPE( void pty_finish, (pty_t *pp) );
72FORWARD _PROTOTYPE( int pty_read, (tty_t *tp, int try) );
73FORWARD _PROTOTYPE( int pty_close, (tty_t *tp, int try) );
74FORWARD _PROTOTYPE( int pty_icancel, (tty_t *tp, int try) );
75FORWARD _PROTOTYPE( int pty_ocancel, (tty_t *tp, int try) );
76FORWARD _PROTOTYPE( int pty_select, (tty_t *tp, message *m) );
77
78/*===========================================================================*
79 * do_pty *
80 *===========================================================================*/
81PUBLIC void do_pty(tp, m_ptr)
82tty_t *tp;
83message *m_ptr;
84{
85/* Perform an open/close/read/write call on a /dev/ptypX device. */
86 pty_t *pp = tp->tty_priv;
87 int r;
88 phys_bytes p;
89
90 switch (m_ptr->m_type) {
91 case DEV_READ:
92 /* Check, store information on the reader, do I/O. */
93 if (pp->state & TTY_CLOSED) {
94 r = 0;
95 break;
96 }
97 if (pp->rdleft != 0 || pp->rdcum != 0) {
98 r = EIO;
99 break;
100 }
101 if (m_ptr->COUNT <= 0) {
102 r = EINVAL;
103 break;
104 }
105#if DEAD_CODE
106 if (numap_local(m_ptr->IO_ENDPT, (vir_bytes) m_ptr->ADDRESS,
107 m_ptr->COUNT) == 0) {
108#else
109 if ((r = sys_umap(m_ptr->IO_ENDPT, D, (vir_bytes) m_ptr->ADDRESS,
110 m_ptr->COUNT, &p)) != OK) {
111#endif
112 break;
113 }
114 pp->rdsendreply = TRUE;
115 pp->rdcaller = m_ptr->m_source;
116 pp->rdproc = m_ptr->IO_ENDPT;
117 pp->rdvir = (vir_bytes) m_ptr->ADDRESS;
118 pp->rdleft = m_ptr->COUNT;
119 pty_start(pp);
120 handle_events(tp);
121 if (pp->rdleft == 0) return; /* already done */
122
123 if (m_ptr->TTY_FLAGS & O_NONBLOCK) {
124 r = EAGAIN; /* don't suspend */
125 pp->rdleft = pp->rdcum = 0;
126 } else {
127 r = SUSPEND; /* do suspend */
128 pp->rdsendreply = FALSE;
129 }
130 break;
131
132 case DEV_WRITE:
133 /* Check, store information on the writer, do I/O. */
134 if (pp->state & TTY_CLOSED) {
135 r = EIO;
136 break;
137 }
138 if (pp->wrleft != 0 || pp->wrcum != 0) {
139 r = EIO;
140 break;
141 }
142 if (m_ptr->COUNT <= 0) {
143 r = EINVAL;
144 break;
145 }
146#if DEAD_CODE
147 if (numap_local(m_ptr->IO_ENDPT, (vir_bytes) m_ptr->ADDRESS,
148 m_ptr->COUNT) == 0) {
149 r = EFAULT;
150#else
151 if ((r = sys_umap(m_ptr->IO_ENDPT, D, (vir_bytes) m_ptr->ADDRESS,
152 m_ptr->COUNT, &p)) != OK) {
153#endif
154 break;
155 }
156 pp->wrsendreply = TRUE;
157 pp->wrcaller = m_ptr->m_source;
158 pp->wrproc = m_ptr->IO_ENDPT;
159 pp->wrvir = (vir_bytes) m_ptr->ADDRESS;
160 pp->wrleft = m_ptr->COUNT;
161 handle_events(tp);
162 if (pp->wrleft == 0) return; /* already done */
163
164 if (m_ptr->TTY_FLAGS & O_NONBLOCK) { /* don't suspend */
165 r = pp->wrcum > 0 ? pp->wrcum : EAGAIN;
166 pp->wrleft = pp->wrcum = 0;
167 } else {
168 pp->wrsendreply = FALSE; /* do suspend */
169 r = SUSPEND;
170 }
171 break;
172
173 case DEV_OPEN:
174 r = pp->state != 0 ? EIO : OK;
175 pp->state |= PTY_ACTIVE;
176 pp->rdcum = 0;
177 pp->wrcum = 0;
178 break;
179
180 case DEV_CLOSE:
181 r = OK;
182 if (pp->state & TTY_CLOSED) {
183 pp->state = 0;
184 } else {
185 pp->state |= PTY_CLOSED;
186 sigchar(tp, SIGHUP);
187 }
188 break;
189
190 case DEV_SELECT:
191 r = pty_select(tp, m_ptr);
192 break;
193
194 case CANCEL:
195 if (m_ptr->IO_ENDPT == pp->rdproc) {
196 /* Cancel a read from a PTY. */
197 pp->rdleft = pp->rdcum = 0;
198 }
199 if (m_ptr->IO_ENDPT == pp->wrproc) {
200 /* Cancel a write to a PTY. */
201 pp->wrleft = pp->wrcum = 0;
202 }
203 r = EINTR;
204 break;
205
206 default:
207 r = EINVAL;
208 }
209 tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, r);
210}
211
212/*===========================================================================*
213 * pty_write *
214 *===========================================================================*/
215PRIVATE int pty_write(tp, try)
216tty_t *tp;
217int try;
218{
219/* (*dev_write)() routine for PTYs. Transfer bytes from the writer on
220 * /dev/ttypX to the output buffer.
221 */
222 pty_t *pp = tp->tty_priv;
223 int count, ocount, s;
224 phys_bytes user_phys;
225
226 /* PTY closed down? */
227 if (pp->state & PTY_CLOSED) {
228 if (try) return 1;
229 if (tp->tty_outleft > 0) {
230 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
231 tp->tty_outproc, EIO);
232 tp->tty_outleft = tp->tty_outcum = 0;
233 }
234 return;
235 }
236
237 /* While there is something to do. */
238 for (;;) {
239 ocount = buflen(pp->obuf) - pp->ocount;
240 if (try) return (ocount > 0);
241 count = bufend(pp->obuf) - pp->ohead;
242 if (count > ocount) count = ocount;
243 if (count > tp->tty_outleft) count = tp->tty_outleft;
244 if (count == 0 || tp->tty_inhibited)
245 break;
246
247 /* Copy from user space to the PTY output buffer. */
248 if ((s = sys_vircopy(tp->tty_outproc, D, (vir_bytes) tp->tty_out_vir,
249 SELF, D, (vir_bytes) pp->ohead, (phys_bytes) count)) != OK) {
250 printf("pty tty%d: copy failed (error %d)\n", s);
251 break;
252 }
253
254 /* Perform output processing on the output buffer. */
255 out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
256 if (count == 0) break;
257
258 /* Assume echoing messed up by output. */
259 tp->tty_reprint = TRUE;
260
261 /* Bookkeeping. */
262 pp->ocount += ocount;
263 if ((pp->ohead += ocount) >= bufend(pp->obuf))
264 pp->ohead -= buflen(pp->obuf);
265 pty_start(pp);
266 tp->tty_out_vir += count;
267 tp->tty_outcum += count;
268 if ((tp->tty_outleft -= count) == 0) {
269 /* Output is finished, reply to the writer. */
270 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
271 tp->tty_outproc, tp->tty_outcum);
272 tp->tty_outcum = 0;
273 }
274 }
275 pty_finish(pp);
276 return 1;
277}
278
279/*===========================================================================*
280 * pty_echo *
281 *===========================================================================*/
282PRIVATE void pty_echo(tp, c)
283tty_t *tp;
284int c;
285{
286/* Echo one character. (Like pty_write, but only one character, optionally.) */
287
288 pty_t *pp = tp->tty_priv;
289 int count, ocount;
290
291 ocount = buflen(pp->obuf) - pp->ocount;
292 if (ocount == 0) return; /* output buffer full */
293 count = 1;
294 *pp->ohead = c; /* add one character */
295
296 out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
297 if (count == 0) return;
298
299 pp->ocount += ocount;
300 if ((pp->ohead += ocount) >= bufend(pp->obuf)) pp->ohead -= buflen(pp->obuf);
301 pty_start(pp);
302}
303
304/*===========================================================================*
305 * pty_start *
306 *===========================================================================*/
307PRIVATE void pty_start(pp)
308pty_t *pp;
309{
310/* Transfer bytes written to the output buffer to the PTY reader. */
311 int count;
312
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;
320
321 /* Copy from the output buffer to the readers address space. */
322 if ((s = sys_vircopy(SELF, D, (vir_bytes)pp->otail,
323 (vir_bytes) pp->rdproc, D, (vir_bytes) pp->rdvir, (phys_bytes) count)) != OK) {
324 printf("pty tty%d: copy failed (error %d)\n", s);
325 break;
326 }
327
328 /* Bookkeeping. */
329 pp->ocount -= count;
330 if ((pp->otail += count) == bufend(pp->obuf)) pp->otail = pp->obuf;
331 pp->rdvir += count;
332 pp->rdcum += count;
333 pp->rdleft -= count;
334 }
335}
336
337/*===========================================================================*
338 * pty_finish *
339 *===========================================================================*/
340PRIVATE void pty_finish(pp)
341pty_t *pp;
342{
343/* Finish the read request of a PTY reader if there is at least one byte
344 * transferred.
345 */
346 if (pp->rdcum > 0) {
347 if (pp->rdsendreply) {
348 tty_reply(TASK_REPLY, pp->rdcaller, pp->rdproc, pp->rdcum);
349 pp->rdleft = pp->rdcum = 0;
350 }
351 else
352 notify(pp->rdcaller);
353 }
354
355}
356
357/*===========================================================================*
358 * pty_read *
359 *===========================================================================*/
360PRIVATE int pty_read(tp, try)
361tty_t *tp;
362int try;
363{
364/* Offer bytes from the PTY writer for input on the TTY. (Do it one byte at
365 * a time, 99% of the writes will be for one byte, so no sense in being smart.)
366 */
367 pty_t *pp = tp->tty_priv;
368 char c;
369
370 if (pp->state & PTY_CLOSED) {
371 if (try) return 1;
372 if (tp->tty_inleft > 0) {
373 tty_reply(tp->tty_inrepcode, tp->tty_incaller, tp->tty_inproc,
374 tp->tty_incum);
375 tp->tty_inleft = tp->tty_incum = 0;
376 }
377 return 1;
378 }
379
380 if (try) {
381 if (pp->wrleft > 0)
382 return 1;
383 return 0;
384 }
385
386 while (pp->wrleft > 0) {
387 int s;
388
389 /* Transfer one character to 'c'. */
390 if ((s = sys_vircopy(pp->wrproc, D, (vir_bytes) pp->wrvir,
391 SELF, D, (vir_bytes) &c, (phys_bytes) 1)) != OK) {
392 printf("pty: copy failed (error %d)\n", s);
393 break;
394 }
395
396 /* Input processing. */
397 if (in_process(tp, &c, 1) == 0) break;
398
399 /* PTY writer bookkeeping. */
400 pp->wrvir++;
401 pp->wrcum++;
402 if (--pp->wrleft == 0) {
403 if (pp->wrsendreply) {
404 tty_reply(TASK_REPLY, pp->wrcaller, pp->wrproc,
405 pp->wrcum);
406 pp->wrcum = 0;
407 }
408 else
409 notify(pp->wrcaller);
410 }
411 }
412}
413
414/*===========================================================================*
415 * pty_close *
416 *===========================================================================*/
417PRIVATE int pty_close(tp, try)
418tty_t *tp;
419int try;
420{
421/* The tty side has closed, so shut down the pty side. */
422 pty_t *pp = tp->tty_priv;
423
424 if (!(pp->state & PTY_ACTIVE)) return;
425
426 if (pp->rdleft > 0) {
427 assert(!pp->rdsendreply);
428 notify(pp->rdcaller);
429 }
430
431 if (pp->wrleft > 0) {
432 assert(!pp->wrsendreply);
433 notify(pp->wrcaller);
434 }
435
436 if (pp->state & PTY_CLOSED) pp->state = 0; else pp->state |= TTY_CLOSED;
437}
438
439/*===========================================================================*
440 * pty_icancel *
441 *===========================================================================*/
442PRIVATE int pty_icancel(tp, try)
443tty_t *tp;
444int try;
445{
446/* Discard waiting input. */
447 pty_t *pp = tp->tty_priv;
448
449 if (pp->wrleft > 0) {
450 assert(!pp->wrsendreply);
451 pp->wrcum += pp->wrleft;
452 pp->wrleft= 0;
453 notify(pp->wrcaller);
454 }
455}
456
457/*===========================================================================*
458 * pty_ocancel *
459 *===========================================================================*/
460PRIVATE int pty_ocancel(tp, try)
461tty_t *tp;
462int try;
463{
464/* Drain the output buffer. */
465 pty_t *pp = tp->tty_priv;
466
467 pp->ocount = 0;
468 pp->otail = pp->ohead;
469}
470
471/*===========================================================================*
472 * pty_init *
473 *===========================================================================*/
474PUBLIC void pty_init(tp)
475tty_t *tp;
476{
477 pty_t *pp;
478 int line;
479
480 /* Associate PTY and TTY structures. */
481 line = tp - &tty_table[NR_CONS + NR_RS_LINES];
482 pp = tp->tty_priv = &pty_table[line];
483 pp->tty = tp;
484 pp->select_ops = 0;
485
486 /* Set up output queue. */
487 pp->ohead = pp->otail = pp->obuf;
488
489 /* Fill in TTY function hooks. */
490 tp->tty_devread = pty_read;
491 tp->tty_devwrite = pty_write;
492 tp->tty_echo = pty_echo;
493 tp->tty_icancel = pty_icancel;
494 tp->tty_ocancel = pty_ocancel;
495 tp->tty_close = pty_close;
496 tp->tty_select_ops = 0;
497}
498
499/*===========================================================================*
500 * pty_status *
501 *===========================================================================*/
502PUBLIC int pty_status(message *m_ptr)
503{
504 int i, event_found;
505 pty_t *pp;
506
507 event_found = 0;
508 for (i= 0, pp = pty_table; i<NR_PTYS; i++, pp++) {
509 if ((((pp->state & TTY_CLOSED) && pp->rdleft > 0) ||
510 pp->rdcum > 0) &&
511 pp->rdcaller == m_ptr->m_source)
512 {
513 m_ptr->m_type = DEV_REVIVE;
514 m_ptr->REP_ENDPT = pp->rdproc;
515 m_ptr->REP_STATUS = pp->rdcum;
516
517 pp->rdleft = pp->rdcum = 0;
518 event_found = 1;
519 break;
520 }
521
522 if ((((pp->state & TTY_CLOSED) && pp->wrleft > 0) ||
523 pp->wrcum > 0) &&
524 pp->wrcaller == m_ptr->m_source)
525 {
526 m_ptr->m_type = DEV_REVIVE;
527 m_ptr->REP_ENDPT = pp->wrproc;
528 if (pp->wrcum == 0)
529 m_ptr->REP_STATUS = EIO;
530 else
531 m_ptr->REP_STATUS = pp->wrcum;
532
533 pp->wrleft = pp->wrcum = 0;
534 event_found = 1;
535 break;
536 }
537
538 if (pp->select_ready_ops && pp->select_proc == m_ptr->m_source) {
539 m_ptr->m_type = DEV_IO_READY;
540 m_ptr->DEV_MINOR = PTYPX_MINOR + i;
541 m_ptr->DEV_SEL_OPS = pp->select_ready_ops;
542 pp->select_ready_ops = 0;
543 event_found = 1;
544 break;
545 }
546 }
547 return event_found;
548}
549
550/*===========================================================================*
551 * select_try_pty *
552 *===========================================================================*/
553PRIVATE int select_try_pty(tty_t *tp, int ops)
554{
555 pty_t *pp = tp->tty_priv;
556 int r = 0;
557
558 if (ops & SEL_WR) {
559 /* Write won't block on error. */
560 if (pp->state & TTY_CLOSED) r |= SEL_WR;
561 else if (pp->wrleft != 0 || pp->wrcum != 0) r |= SEL_WR;
562 else r |= SEL_WR;
563 }
564
565 if (ops & SEL_RD) {
566 /* Read won't block on error. */
567 if (pp->state & TTY_CLOSED) r |= SEL_RD;
568 else if (pp->rdleft != 0 || pp->rdcum != 0) r |= SEL_RD;
569 else if (pp->ocount > 0) r |= SEL_RD; /* Actual data. */
570 }
571
572 return r;
573}
574
575/*===========================================================================*
576 * select_retry_pty *
577 *===========================================================================*/
578PUBLIC void select_retry_pty(tty_t *tp)
579{
580 pty_t *pp = tp->tty_priv;
581 int r;
582
583 /* See if the pty side of a pty is ready to return a select. */
584 if (pp->select_ops && (r=select_try_pty(tp, pp->select_ops))) {
585 pp->select_ops &= ~r;
586 pp->select_ready_ops |= r;
587 notify(pp->select_proc);
588 }
589}
590
591/*===========================================================================*
592 * pty_select *
593 *===========================================================================*/
594PRIVATE int pty_select(tty_t *tp, message *m)
595{
596 pty_t *pp = tp->tty_priv;
597 int ops, ready_ops = 0, watch;
598
599 ops = m->IO_ENDPT & (SEL_RD|SEL_WR|SEL_ERR);
600 watch = (m->IO_ENDPT & SEL_NOTIFY) ? 1 : 0;
601
602 ready_ops = select_try_pty(tp, ops);
603
604 if (!ready_ops && ops && watch) {
605 pp->select_ops |= ops;
606 pp->select_proc = m->m_source;
607 }
608
609 return ready_ops;
610}
611
612#endif /* NR_PTYS > 0 */
Note: See TracBrowser for help on using the repository browser.