~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Minix Cross Reference
Minix/kernel/pty.c


  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.