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 "kernel.h"
20 #include <termios.h>
21 #include <signal.h>
22 #include <minix/com.h>
23 #include <minix/callnr.h>
24 #include "tty.h"
25 #include "proc.h"
26
27 #if NR_PTYS > 0
28
29 /* PTY bookkeeping structure, one per pty/tty pair. */
30 typedef struct pty {
31 tty_t *tty; /* associated TTY structure */
32 char state; /* flags: busy, closed, ... */
33
34 /* Read call on /dev/ptypX. */
35 char rdrepcode; /* reply code, TASK_REPLY or REVIVE */
36 char rdcaller; /* process making the call (usually FS) */
37 char rdproc; /* process that wants to read from the pty */
38 vir_bytes rdvir; /* virtual address in readers address space */
39 int rdleft; /* # bytes yet to be read */
40 int rdcum; /* # bytes written so far */
41
42 /* Write call to /dev/ptypX. */
43 char wrrepcode; /* reply code, TASK_REPLY or REVIVE */
44 char wrcaller; /* process making the call (usually FS) */
45 char wrproc; /* process that wants to write to the pty */
46 vir_bytes wrvir; /* virtual address in writers address space */
47 int wrleft; /* # bytes yet to be written */
48 int wrcum; /* # bytes written so far */
49
50 /* Output buffer. */
51 int ocount; /* # characters in the buffer */
52 char *ohead, *otail; /* head and tail of the circular buffer */
53 char obuf[128]; /* buffer for bytes going to the pty reader */
54 } pty_t;
55
56 #define PTY_ACTIVE 0x01 /* pty is open/active */
57 #define TTY_CLOSED 0x02 /* tty side has closed down */
58 #define PTY_CLOSED 0x04 /* pty side has closed down */
59
60 PRIVATE pty_t pty_table[NR_PTYS]; /* PTY bookkeeping */
61
62
63 FORWARD _PROTOTYPE( void pty_write, (tty_t *tp) );
64 FORWARD _PROTOTYPE( void pty_echo, (tty_t *tp, int c) );
65 FORWARD _PROTOTYPE( void pty_start, (pty_t *pp) );
66 FORWARD _PROTOTYPE( void pty_finish, (pty_t *pp) );
67 FORWARD _PROTOTYPE( void pty_read, (tty_t *tp) );
68 FORWARD _PROTOTYPE( void pty_close, (tty_t *tp) );
69 FORWARD _PROTOTYPE( void pty_icancel, (tty_t *tp) );
70 FORWARD _PROTOTYPE( void pty_ocancel, (tty_t *tp) );
71
72
73 /*==========================================================================*
74 * do_pty *
75 *==========================================================================*/
76 PUBLIC void do_pty(tp, m_ptr)
77 tty_t *tp;
78 message *m_ptr;
79 {
80 /* Perform an open/close/read/write call on a /dev/ptypX device. */
81 pty_t *pp = tp->tty_priv;
82 int r;
83
84 switch (m_ptr->m_type) {
85 case DEV_READ:
86 /* Check, store information on the reader, do I/O. */
87 if (pp->state & TTY_CLOSED) {
88 r = 0;
89 break;
90 }
91 if (pp->rdleft != 0) {
92 r = EIO;
93 break;
94 }
95 if (m_ptr->COUNT <= 0) {
96 r = EINVAL;
97 break;
98 }
99 if (numap(m_ptr->PROC_NR, (vir_bytes) m_ptr->ADDRESS,
100 m_ptr->COUNT) == 0) {
101 r = EFAULT;
102 break;
103 }
104 pp->rdrepcode = TASK_REPLY;
105 pp->rdcaller = m_ptr->m_source;
106 pp->rdproc = m_ptr->PROC_NR;
107 pp->rdvir = (vir_bytes) m_ptr->ADDRESS;
108 pp->rdleft = m_ptr->COUNT;
109 pty_start(pp);
110 handle_events(tp);
111 if (pp->rdleft == 0) return; /* already done */
112
113 if (m_ptr->TTY_FLAGS & O_NONBLOCK) {
114 r = EAGAIN; /* don't suspend */
115 pp->rdleft = pp->rdcum = 0;
116 } else {
117 r = SUSPEND; /* do suspend */
118 pp->rdrepcode = REVIVE;
119 }
120 break;
121
122 case DEV_WRITE:
123 /* Check, store information on the writer, do I/O. */
124 if (pp->state & TTY_CLOSED) {
125 r = EIO;
126 break;
127 }
128 if (pp->wrleft != 0) {
129 r = EIO;
130 break;
131 }
132 if (m_ptr->COUNT <= 0) {
133 r = EINVAL;
134 break;
135 }
136 if (numap(m_ptr->PROC_NR, (vir_bytes) m_ptr->ADDRESS,
137 m_ptr->COUNT) == 0) {
138 r = EFAULT;
139 break;
140 }
141 pp->wrrepcode = TASK_REPLY;
142 pp->wrcaller = m_ptr->m_source;
143 pp->wrproc = m_ptr->PROC_NR;
144 pp->wrvir = (vir_bytes) m_ptr->ADDRESS;
145 pp->wrleft = m_ptr->COUNT;
146 handle_events(tp);
147 if (pp->wrleft == 0) return; /* already done */
148
149 if (m_ptr->TTY_FLAGS & O_NONBLOCK) { /* don't suspend */
150 r = pp->wrcum > 0 ? pp->wrcum : EAGAIN;
151 pp->wrleft = pp->wrcum = 0;
152 } else {
153 pp->wrrepcode = REVIVE; /* do suspend */
154 r = SUSPEND;
155 }
156 break;
157
158 case DEV_IOCTL:
159 /* No ioctl's allowed on the pty side. */
160 r = ENOTTY;
161 break;
162
163 case DEV_OPEN:
164 r = pp->state != 0 ? EIO : OK;
165 pp->state |= PTY_ACTIVE;
166 break;
167
168 case DEV_CLOSE:
169 r = OK;
170 if (pp->state & TTY_CLOSED) {
171 pp->state = 0;
172 } else {
173 pp->state |= PTY_CLOSED;
174 sigchar(tp, SIGHUP);
175 }
176 break;
177
178 case CANCEL:
179 if (m_ptr->PROC_NR == pp->rdproc) {
180 /* Cancel a read from a PTY. */
181 pp->rdleft = pp->rdcum = 0;
182 }
183 if (m_ptr->PROC_NR == pp->wrproc) {
184 /* Cancel a write to a PTY. */
185 pp->wrleft = pp->wrcum = 0;
186 }
187 r = EINTR;
188 break;
189
190 default:
191 r = EINVAL;
192 }
193 tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r);
194 }
195
196
197 /*==========================================================================*
198 * pty_write *
199 *==========================================================================*/
200 PRIVATE void pty_write(tp)
201 tty_t *tp;
202 {
203 /* (*dev_write)() routine for PTYs. Transfer bytes from the writer on
204 * /dev/ttypX to the output buffer.
205 */
206 pty_t *pp = tp->tty_priv;
207 int count, ocount;
208 phys_bytes user_phys;
209
210 /* PTY closed down? */
211 if (pp->state & PTY_CLOSED) {
212 if (tp->tty_outleft > 0) {
213 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
214 tp->tty_outproc, EIO);
215 tp->tty_outleft = tp->tty_outcum = 0;
216 }
217 return;
218 }
219
220 /* While there is something to do. */
221 for (;;) {
222 ocount = buflen(pp->obuf) - pp->ocount;
223 count = bufend(pp->obuf) - pp->ohead;
224 if (count > ocount) count = ocount;
225 if (count > tp->tty_outleft) count = tp->tty_outleft;
226 if (count == 0 || tp->tty_inhibited) break;
227
228 /* Copy from user space to the PTY output buffer. */
229 user_phys = proc_vir2phys(proc_addr(tp->tty_outproc), tp->tty_out_vir);
230 phys_copy(user_phys, vir2phys(pp->ohead), (phys_bytes) count);
231
232 /* Perform output processing on the output buffer. */
233 out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
234 if (count == 0) break;
235
236 /* Assume echoing messed up by output. */
237 tp->tty_reprint = TRUE;
238
239 /* Bookkeeping. */
240 pp->ocount += ocount;
241 if ((pp->ohead += ocount) >= bufend(pp->obuf))
242 pp->ohead -= buflen(pp->obuf);
243 pty_start(pp);
244 tp->tty_out_vir += count;
245 tp->tty_outcum += count;
246 if ((tp->tty_outleft -= count) == 0) {
247 /* Output is finished, reply to the writer. */
248 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
249 tp->tty_outproc, tp->tty_outcum);
250 tp->tty_outcum = 0;
251 }
252 }
253 pty_finish(pp);
254 }
255
256
257 /*==========================================================================*
258 * pty_echo *
259 *==========================================================================*/
260 PRIVATE void pty_echo(tp, c)
261 tty_t *tp;
262 int c;
263 {
264 /* Echo one character. (Like pty_write, but only one character, optionally.) */
265
266 pty_t *pp = tp->tty_priv;
267 int count, ocount;
268
269 ocount = buflen(pp->obuf) - pp->ocount;
270 if (ocount == 0) return; /* output buffer full */
271 count = 1;
272 *pp->ohead = c; /* add one character */
273
274 out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
275 if (count == 0) return;
276
277 pp->ocount += ocount;
278 if ((pp->ohead += ocount) >= bufend(pp->obuf)) pp->ohead -= buflen(pp->obuf);
279 pty_start(pp);
280 }
281
282
283 /*==========================================================================*
284 * pty_start *
285 *==========================================================================*/
286 PRIVATE void pty_start(pp)
287 pty_t *pp;
288 {
289 /* Transfer bytes written to the output buffer to the PTY reader. */
290 int count;
291 phys_bytes user_phys;
292
293 /* While there are things to do. */
294 for (;;) {
295 count = bufend(pp->obuf) - pp->otail;
296 if (count > pp->ocount) count = pp->ocount;
297 if (count > pp->rdleft) count = pp->rdleft;
298 if (count == 0) break;
299
300 /* Copy from the output buffer to the readers address space. */
301 user_phys = proc_vir2phys(proc_addr(pp->rdproc), pp->rdvir);
302 phys_copy(vir2phys(pp->otail), user_phys, (phys_bytes) count);
303
304 /* Bookkeeping. */
305 pp->ocount -= count;
306 if ((pp->otail += count) == bufend(pp->obuf)) pp->otail = pp->obuf;
307 pp->rdvir += count;
308 pp->rdcum += count;
309 pp->rdleft -= count;
310 }
311 }
312
313
314 /*==========================================================================*
315 * pty_finish *
316 *==========================================================================*/
317 PRIVATE void pty_finish(pp)
318 pty_t *pp;
319 {
320 /* Finish the read request of a PTY reader if there is at least one byte
321 * transferred.
322 */
323
324 if (pp->rdcum > 0) {
325 tty_reply(pp->rdrepcode, pp->rdcaller, pp->rdproc, pp->rdcum);
326 pp->rdleft = pp->rdcum = 0;
327 }
328 }
329
330
331 /*==========================================================================*
332 * pty_read *
333 *==========================================================================*/
334 PRIVATE void pty_read(tp)
335 tty_t *tp;
336 {
337 /* Offer bytes from the PTY writer for input on the TTY. (Do it one byte at
338 * a time, 99% of the writes will be for one byte, so no sense in being smart.)
339 */
340 pty_t *pp = tp->tty_priv;
341 phys_bytes user_phys;
342 char c;
343
344 if (pp->state & PTY_CLOSED) {
345 if (tp->tty_inleft > 0) {
346 tty_reply(tp->tty_inrepcode, tp->tty_incaller, tp->tty_inproc,
347 tp->tty_incum);
348 tp->tty_inleft = tp->tty_incum = 0;
349 }
350 return;
351 }
352
353 while (pp->wrleft > 0) {
354 /* Transfer one character to 'c'. */
355 user_phys = proc_vir2phys(proc_addr(pp->wrproc), pp->wrvir);
356 phys_copy(user_phys, vir2phys(&c), 1L);
357
358 /* Input processing. */
359 if (in_process(tp, &c, 1) == 0) break;
360
361 /* PTY writer bookkeeping. */
362 pp->wrvir++;
363 pp->wrcum++;
364 if (--pp->wrleft == 0) {
365 tty_reply(pp->wrrepcode, pp->wrcaller, pp->wrproc, pp->wrcum);
366 pp->wrcum = 0;
367 }
368 }
369 }
370
371
372 /*==========================================================================*
373 * pty_close *
374 *==========================================================================*/
375 PRIVATE void pty_close(tp)
376 tty_t *tp;
377 {
378 /* The tty side has closed, so shut down the pty side. */
379 pty_t *pp = tp->tty_priv;
380
381 if (!(pp->state & PTY_ACTIVE)) return;
382
383 if (pp->rdleft > 0) {
384 tty_reply(pp->rdrepcode, pp->rdcaller, pp->rdproc, 0);
385 pp->rdleft = pp->rdcum = 0;
386 }
387
388 if (pp->wrleft > 0) {
389 tty_reply(pp->wrrepcode, pp->wrcaller, pp->wrproc, EIO);
390 pp->wrleft = pp->wrcum = 0;
391 }
392
393 if (pp->state & PTY_CLOSED) pp->state = 0; else pp->state |= TTY_CLOSED;
394 }
395
396
397 /*==========================================================================*
398 * pty_icancel *
399 *==========================================================================*/
400 PRIVATE void pty_icancel(tp)
401 tty_t *tp;
402 {
403 /* Discard waiting input. */
404 pty_t *pp = tp->tty_priv;
405
406 if (pp->wrleft > 0) {
407 tty_reply(pp->wrrepcode, pp->wrcaller, pp->wrproc,
408 pp->wrcum + pp->wrleft);
409 pp->wrleft = pp->wrcum = 0;
410 }
411 }
412
413
414 /*==========================================================================*
415 * pty_ocancel *
416 *==========================================================================*/
417 PRIVATE void pty_ocancel(tp)
418 tty_t *tp;
419 {
420 /* Drain the output buffer. */
421 pty_t *pp = tp->tty_priv;
422
423 pp->ocount = 0;
424 pp->otail = pp->ohead;
425 }
426
427
428 /*==========================================================================*
429 * pty_init *
430 *==========================================================================*/
431 PUBLIC void pty_init(tp)
432 tty_t *tp;
433 {
434 pty_t *pp;
435 int line;
436
437 /* Associate PTY and TTY structures. */
438 line = tp - &tty_table[NR_CONS + NR_RS_LINES];
439 pp = tp->tty_priv = &pty_table[line];
440 pp->tty = tp;
441
442 /* Set up output queue. */
443 pp->ohead = pp->otail = pp->obuf;
444
445 /* Fill in TTY function hooks. */
446 tp->tty_devread = pty_read;
447 tp->tty_devwrite = pty_write;
448 tp->tty_echo = pty_echo;
449 tp->tty_icancel = pty_icancel;
450 tp->tty_ocancel = pty_ocancel;
451 tp->tty_close = pty_close;
452 }
453 #endif /* NR_PTYS > 0 */
454
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.