xref: /minix3/minix/drivers/tty/pty/pty.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1*433d6423SLionel Sambuc /*	pty.c - pseudo terminal driver			Author: Kees J. Bot
2*433d6423SLionel Sambuc  *								30 Dec 1995
3*433d6423SLionel Sambuc  * PTYs can be seen as a bidirectional pipe with TTY
4*433d6423SLionel Sambuc  * input and output processing.  For example a simple rlogin session:
5*433d6423SLionel Sambuc  *
6*433d6423SLionel Sambuc  *	keyboard -> rlogin -> in.rld -> /dev/ptypX -> /dev/ttypX -> shell
7*433d6423SLionel Sambuc  *	shell -> /dev/ttypX -> /dev/ptypX -> in.rld -> rlogin -> screen
8*433d6423SLionel Sambuc  *
9*433d6423SLionel Sambuc  * This file takes care of copying data between the tty/pty device pairs and
10*433d6423SLionel Sambuc  * the open/read/write/close calls on the pty devices.  The TTY task takes
11*433d6423SLionel Sambuc  * care of the input and output processing (interrupt, backspace, raw I/O,
12*433d6423SLionel Sambuc  * etc.) using the pty_slave_read() and pty_slave_write() functions as the
13*433d6423SLionel Sambuc  * "keyboard" and "screen" functions of the ttypX devices.
14*433d6423SLionel Sambuc  * Be careful when reading this code, the terms "reading" and "writing" are
15*433d6423SLionel Sambuc  * used both for the tty (slave) and the pty (master) end of the pseudo tty.
16*433d6423SLionel Sambuc  * Writes to one end are to be read at the other end and vice-versa.
17*433d6423SLionel Sambuc  */
18*433d6423SLionel Sambuc 
19*433d6423SLionel Sambuc #include <minix/drivers.h>
20*433d6423SLionel Sambuc #include <termios.h>
21*433d6423SLionel Sambuc #include <assert.h>
22*433d6423SLionel Sambuc #include <sys/termios.h>
23*433d6423SLionel Sambuc #include <signal.h>
24*433d6423SLionel Sambuc #include "tty.h"
25*433d6423SLionel Sambuc 
26*433d6423SLionel Sambuc /* PTY bookkeeping structure, one per pty/tty pair. */
27*433d6423SLionel Sambuc typedef struct pty {
28*433d6423SLionel Sambuc   tty_t		*tty;		/* associated TTY structure */
29*433d6423SLionel Sambuc   char		state;		/* flags: busy, closed, ... */
30*433d6423SLionel Sambuc 
31*433d6423SLionel Sambuc   /* Read call on master (/dev/ptypX). */
32*433d6423SLionel Sambuc   endpoint_t	rdcaller;	/* process making the call, or NONE if none */
33*433d6423SLionel Sambuc   cdev_id_t	rdid;		/* ID of suspended read request */
34*433d6423SLionel Sambuc   cp_grant_id_t	rdgrant;	/* grant for reader's address space */
35*433d6423SLionel Sambuc   size_t	rdleft;		/* # bytes yet to be read */
36*433d6423SLionel Sambuc   size_t	rdcum;		/* # bytes written so far */
37*433d6423SLionel Sambuc 
38*433d6423SLionel Sambuc   /* Write call to master (/dev/ptypX). */
39*433d6423SLionel Sambuc   endpoint_t	wrcaller;	/* process making the call, or NONE if none*/
40*433d6423SLionel Sambuc   cdev_id_t	wrid;		/* ID of suspended write request */
41*433d6423SLionel Sambuc   cp_grant_id_t	wrgrant;	/* grant for writer's address space */
42*433d6423SLionel Sambuc   size_t	wrleft;		/* # bytes yet to be written */
43*433d6423SLionel Sambuc   size_t	wrcum;		/* # bytes written so far */
44*433d6423SLionel Sambuc 
45*433d6423SLionel Sambuc   /* Output buffer. */
46*433d6423SLionel Sambuc   int		ocount;		/* # characters in the buffer */
47*433d6423SLionel Sambuc   char		*ohead, *otail;	/* head and tail of the circular buffer */
48*433d6423SLionel Sambuc   char		obuf[2048];	/* buffer for bytes going to the pty reader */
49*433d6423SLionel Sambuc 
50*433d6423SLionel Sambuc   /* select() data. */
51*433d6423SLionel Sambuc   unsigned int	select_ops;	/* Which operations do we want to know about? */
52*433d6423SLionel Sambuc   endpoint_t	select_proc;	/* Who wants to know about it? */
53*433d6423SLionel Sambuc } pty_t;
54*433d6423SLionel Sambuc 
55*433d6423SLionel Sambuc #define TTY_ACTIVE	0x01	/* tty is open/active */
56*433d6423SLionel Sambuc #define PTY_ACTIVE	0x02	/* pty is open/active */
57*433d6423SLionel Sambuc #define TTY_CLOSED	0x04	/* tty side has closed down */
58*433d6423SLionel Sambuc #define PTY_CLOSED	0x08	/* pty side has closed down */
59*433d6423SLionel Sambuc 
60*433d6423SLionel Sambuc static pty_t pty_table[NR_PTYS];	/* PTY bookkeeping */
61*433d6423SLionel Sambuc 
62*433d6423SLionel Sambuc static void pty_start(pty_t *pp);
63*433d6423SLionel Sambuc static void pty_finish(pty_t *pp);
64*433d6423SLionel Sambuc 
65*433d6423SLionel Sambuc static int pty_master_open(devminor_t minor, int access,
66*433d6423SLionel Sambuc 	endpoint_t user_endpt);
67*433d6423SLionel Sambuc static int pty_master_close(devminor_t minor);
68*433d6423SLionel Sambuc static ssize_t pty_master_read(devminor_t minor, u64_t position,
69*433d6423SLionel Sambuc 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
70*433d6423SLionel Sambuc 	cdev_id_t id);
71*433d6423SLionel Sambuc static ssize_t pty_master_write(devminor_t minor, u64_t position,
72*433d6423SLionel Sambuc 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
73*433d6423SLionel Sambuc 	cdev_id_t id);
74*433d6423SLionel Sambuc static int pty_master_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id);
75*433d6423SLionel Sambuc static int pty_master_select(devminor_t minor, unsigned int ops,
76*433d6423SLionel Sambuc 	endpoint_t endpt);
77*433d6423SLionel Sambuc 
78*433d6423SLionel Sambuc static struct chardriver pty_master_tab = {
79*433d6423SLionel Sambuc   .cdr_open	= pty_master_open,
80*433d6423SLionel Sambuc   .cdr_close	= pty_master_close,
81*433d6423SLionel Sambuc   .cdr_read	= pty_master_read,
82*433d6423SLionel Sambuc   .cdr_write	= pty_master_write,
83*433d6423SLionel Sambuc   .cdr_cancel	= pty_master_cancel,
84*433d6423SLionel Sambuc   .cdr_select	= pty_master_select
85*433d6423SLionel Sambuc };
86*433d6423SLionel Sambuc 
87*433d6423SLionel Sambuc /*===========================================================================*
88*433d6423SLionel Sambuc  *				pty_master_open				     *
89*433d6423SLionel Sambuc  *===========================================================================*/
90*433d6423SLionel Sambuc static int pty_master_open(devminor_t minor, int UNUSED(access),
91*433d6423SLionel Sambuc 	endpoint_t UNUSED(user_endpt))
92*433d6423SLionel Sambuc {
93*433d6423SLionel Sambuc   tty_t *tp;
94*433d6423SLionel Sambuc   pty_t *pp;
95*433d6423SLionel Sambuc 
96*433d6423SLionel Sambuc   assert(minor >= PTYPX_MINOR && minor < PTYPX_MINOR + NR_PTYS);
97*433d6423SLionel Sambuc 
98*433d6423SLionel Sambuc   if ((tp = line2tty(minor)) == NULL)
99*433d6423SLionel Sambuc 	return ENXIO;
100*433d6423SLionel Sambuc   pp = tp->tty_priv;
101*433d6423SLionel Sambuc 
102*433d6423SLionel Sambuc   if (pp->state & PTY_ACTIVE)
103*433d6423SLionel Sambuc 	return EIO;
104*433d6423SLionel Sambuc 
105*433d6423SLionel Sambuc   pp->state |= PTY_ACTIVE;
106*433d6423SLionel Sambuc   pp->rdcum = 0;
107*433d6423SLionel Sambuc   pp->wrcum = 0;
108*433d6423SLionel Sambuc 
109*433d6423SLionel Sambuc   return OK;
110*433d6423SLionel Sambuc }
111*433d6423SLionel Sambuc 
112*433d6423SLionel Sambuc /*===========================================================================*
113*433d6423SLionel Sambuc  *				pty_master_close			     *
114*433d6423SLionel Sambuc  *===========================================================================*/
115*433d6423SLionel Sambuc static int pty_master_close(devminor_t minor)
116*433d6423SLionel Sambuc {
117*433d6423SLionel Sambuc   tty_t *tp;
118*433d6423SLionel Sambuc   pty_t *pp;
119*433d6423SLionel Sambuc 
120*433d6423SLionel Sambuc   if ((tp = line2tty(minor)) == NULL)
121*433d6423SLionel Sambuc 	return ENXIO;
122*433d6423SLionel Sambuc   pp = tp->tty_priv;
123*433d6423SLionel Sambuc 
124*433d6423SLionel Sambuc   if ((pp->state & (TTY_ACTIVE | TTY_CLOSED)) != TTY_ACTIVE) {
125*433d6423SLionel Sambuc 	pp->state = 0;
126*433d6423SLionel Sambuc   } else {
127*433d6423SLionel Sambuc 	pp->state |= PTY_CLOSED;
128*433d6423SLionel Sambuc 	sigchar(tp, SIGHUP, 1);
129*433d6423SLionel Sambuc   }
130*433d6423SLionel Sambuc 
131*433d6423SLionel Sambuc   return OK;
132*433d6423SLionel Sambuc }
133*433d6423SLionel Sambuc 
134*433d6423SLionel Sambuc /*===========================================================================*
135*433d6423SLionel Sambuc  *				pty_master_read				     *
136*433d6423SLionel Sambuc  *===========================================================================*/
137*433d6423SLionel Sambuc static ssize_t pty_master_read(devminor_t minor, u64_t UNUSED(position),
138*433d6423SLionel Sambuc 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
139*433d6423SLionel Sambuc 	cdev_id_t id)
140*433d6423SLionel Sambuc {
141*433d6423SLionel Sambuc   tty_t *tp;
142*433d6423SLionel Sambuc   pty_t *pp;
143*433d6423SLionel Sambuc   ssize_t r;
144*433d6423SLionel Sambuc 
145*433d6423SLionel Sambuc   if ((tp = line2tty(minor)) == NULL)
146*433d6423SLionel Sambuc 	return ENXIO;
147*433d6423SLionel Sambuc   pp = tp->tty_priv;
148*433d6423SLionel Sambuc 
149*433d6423SLionel Sambuc   /* Check, store information on the reader, do I/O. */
150*433d6423SLionel Sambuc   if (pp->state & TTY_CLOSED)
151*433d6423SLionel Sambuc 	return 0; /* EOF */
152*433d6423SLionel Sambuc 
153*433d6423SLionel Sambuc   if (pp->rdcaller != NONE || pp->rdleft != 0 || pp->rdcum != 0)
154*433d6423SLionel Sambuc 	return EIO;
155*433d6423SLionel Sambuc 
156*433d6423SLionel Sambuc   if (size <= 0)
157*433d6423SLionel Sambuc 	return EINVAL;
158*433d6423SLionel Sambuc 
159*433d6423SLionel Sambuc   pp->rdcaller = endpt;
160*433d6423SLionel Sambuc   pp->rdid = id;
161*433d6423SLionel Sambuc   pp->rdgrant = grant;
162*433d6423SLionel Sambuc   pp->rdleft = size;
163*433d6423SLionel Sambuc   pty_start(pp);
164*433d6423SLionel Sambuc 
165*433d6423SLionel Sambuc   handle_events(tp);
166*433d6423SLionel Sambuc 
167*433d6423SLionel Sambuc   if (pp->rdleft == 0) {
168*433d6423SLionel Sambuc 	pp->rdcaller = NONE;
169*433d6423SLionel Sambuc 	return EDONTREPLY;		/* already done */
170*433d6423SLionel Sambuc   }
171*433d6423SLionel Sambuc 
172*433d6423SLionel Sambuc   if (flags & CDEV_NONBLOCK) {
173*433d6423SLionel Sambuc 	r = pp->rdcum > 0 ? pp->rdcum : EAGAIN;
174*433d6423SLionel Sambuc 	pp->rdleft = pp->rdcum = 0;
175*433d6423SLionel Sambuc 	pp->rdcaller = NONE;
176*433d6423SLionel Sambuc 	return r;
177*433d6423SLionel Sambuc   }
178*433d6423SLionel Sambuc 
179*433d6423SLionel Sambuc   return EDONTREPLY;			/* do suspend */
180*433d6423SLionel Sambuc }
181*433d6423SLionel Sambuc 
182*433d6423SLionel Sambuc /*===========================================================================*
183*433d6423SLionel Sambuc  *				pty_master_write			     *
184*433d6423SLionel Sambuc  *===========================================================================*/
185*433d6423SLionel Sambuc static ssize_t pty_master_write(devminor_t minor, u64_t UNUSED(position),
186*433d6423SLionel Sambuc 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
187*433d6423SLionel Sambuc 	cdev_id_t id)
188*433d6423SLionel Sambuc {
189*433d6423SLionel Sambuc   tty_t *tp;
190*433d6423SLionel Sambuc   pty_t *pp;
191*433d6423SLionel Sambuc   ssize_t r;
192*433d6423SLionel Sambuc 
193*433d6423SLionel Sambuc   if ((tp = line2tty(minor)) == NULL)
194*433d6423SLionel Sambuc 	return ENXIO;
195*433d6423SLionel Sambuc   pp = tp->tty_priv;
196*433d6423SLionel Sambuc 
197*433d6423SLionel Sambuc   /* Check, store information on the writer, do I/O. */
198*433d6423SLionel Sambuc   if (pp->state & TTY_CLOSED)
199*433d6423SLionel Sambuc 	return EIO;
200*433d6423SLionel Sambuc 
201*433d6423SLionel Sambuc   if (pp->wrcaller != NONE || pp->wrleft != 0 || pp->wrcum != 0)
202*433d6423SLionel Sambuc 	return EIO;
203*433d6423SLionel Sambuc 
204*433d6423SLionel Sambuc   if (size <= 0)
205*433d6423SLionel Sambuc 	return EINVAL;
206*433d6423SLionel Sambuc 
207*433d6423SLionel Sambuc   pp->wrcaller = endpt;
208*433d6423SLionel Sambuc   pp->wrid = id;
209*433d6423SLionel Sambuc   pp->wrgrant = grant;
210*433d6423SLionel Sambuc   pp->wrleft = size;
211*433d6423SLionel Sambuc 
212*433d6423SLionel Sambuc   handle_events(tp);
213*433d6423SLionel Sambuc 
214*433d6423SLionel Sambuc   if (pp->wrleft == 0) {
215*433d6423SLionel Sambuc 	pp->wrcaller = NONE;
216*433d6423SLionel Sambuc 	return EDONTREPLY;		/* already done */
217*433d6423SLionel Sambuc   }
218*433d6423SLionel Sambuc 
219*433d6423SLionel Sambuc   if (flags & CDEV_NONBLOCK) {
220*433d6423SLionel Sambuc 	r = pp->wrcum > 0 ? pp->wrcum : EAGAIN;
221*433d6423SLionel Sambuc 	pp->wrleft = pp->wrcum = 0;
222*433d6423SLionel Sambuc 	pp->wrcaller = NONE;
223*433d6423SLionel Sambuc 	return r;
224*433d6423SLionel Sambuc   }
225*433d6423SLionel Sambuc 
226*433d6423SLionel Sambuc   return EDONTREPLY;			/* do suspend */
227*433d6423SLionel Sambuc }
228*433d6423SLionel Sambuc 
229*433d6423SLionel Sambuc /*===========================================================================*
230*433d6423SLionel Sambuc  *				pty_master_cancel			     *
231*433d6423SLionel Sambuc  *===========================================================================*/
232*433d6423SLionel Sambuc static int pty_master_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id)
233*433d6423SLionel Sambuc {
234*433d6423SLionel Sambuc   tty_t *tp;
235*433d6423SLionel Sambuc   pty_t *pp;
236*433d6423SLionel Sambuc   int r;
237*433d6423SLionel Sambuc 
238*433d6423SLionel Sambuc   if ((tp = line2tty(minor)) == NULL)
239*433d6423SLionel Sambuc 	return ENXIO;
240*433d6423SLionel Sambuc   pp = tp->tty_priv;
241*433d6423SLionel Sambuc 
242*433d6423SLionel Sambuc   if (pp->rdcaller == endpt && pp->rdid == id) {
243*433d6423SLionel Sambuc 	/* Cancel a read from a PTY. */
244*433d6423SLionel Sambuc 	r = pp->rdcum > 0 ? pp->rdcum : EINTR;
245*433d6423SLionel Sambuc 	pp->rdleft = pp->rdcum = 0;
246*433d6423SLionel Sambuc 	pp->rdcaller = NONE;
247*433d6423SLionel Sambuc 	return r;
248*433d6423SLionel Sambuc   }
249*433d6423SLionel Sambuc 
250*433d6423SLionel Sambuc   if (pp->wrcaller == endpt && pp->wrid == id) {
251*433d6423SLionel Sambuc 	/* Cancel a write to a PTY. */
252*433d6423SLionel Sambuc 	r = pp->wrcum > 0 ? pp->wrcum : EINTR;
253*433d6423SLionel Sambuc 	pp->wrleft = pp->wrcum = 0;
254*433d6423SLionel Sambuc 	pp->wrcaller = NONE;
255*433d6423SLionel Sambuc 	return r;
256*433d6423SLionel Sambuc   }
257*433d6423SLionel Sambuc 
258*433d6423SLionel Sambuc   /* Request not found. */
259*433d6423SLionel Sambuc   return EDONTREPLY;
260*433d6423SLionel Sambuc }
261*433d6423SLionel Sambuc 
262*433d6423SLionel Sambuc /*===========================================================================*
263*433d6423SLionel Sambuc  *				select_try_pty				     *
264*433d6423SLionel Sambuc  *===========================================================================*/
265*433d6423SLionel Sambuc static int select_try_pty(tty_t *tp, int ops)
266*433d6423SLionel Sambuc {
267*433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
268*433d6423SLionel Sambuc   int r = 0;
269*433d6423SLionel Sambuc 
270*433d6423SLionel Sambuc   if (ops & CDEV_OP_WR)  {
271*433d6423SLionel Sambuc 	/* Write won't block on error. */
272*433d6423SLionel Sambuc 	if (pp->state & TTY_CLOSED) r |= CDEV_OP_WR;
273*433d6423SLionel Sambuc 	else if (pp->wrleft != 0 || pp->wrcum != 0) r |= CDEV_OP_WR;
274*433d6423SLionel Sambuc 	else if (tp->tty_incount < buflen(tp->tty_inbuf)) r |= CDEV_OP_WR;
275*433d6423SLionel Sambuc   }
276*433d6423SLionel Sambuc 
277*433d6423SLionel Sambuc   if (ops & CDEV_OP_RD) {
278*433d6423SLionel Sambuc 	/* Read won't block on error. */
279*433d6423SLionel Sambuc 	if (pp->state & TTY_CLOSED) r |= CDEV_OP_RD;
280*433d6423SLionel Sambuc 	else if (pp->rdleft != 0 || pp->rdcum != 0) r |= CDEV_OP_RD;
281*433d6423SLionel Sambuc 	else if (pp->ocount > 0) r |= CDEV_OP_RD;	/* Actual data. */
282*433d6423SLionel Sambuc   }
283*433d6423SLionel Sambuc 
284*433d6423SLionel Sambuc   return r;
285*433d6423SLionel Sambuc }
286*433d6423SLionel Sambuc 
287*433d6423SLionel Sambuc /*===========================================================================*
288*433d6423SLionel Sambuc  *				select_retry_pty			     *
289*433d6423SLionel Sambuc  *===========================================================================*/
290*433d6423SLionel Sambuc void select_retry_pty(tty_t *tp)
291*433d6423SLionel Sambuc {
292*433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
293*433d6423SLionel Sambuc   devminor_t minor;
294*433d6423SLionel Sambuc   int r;
295*433d6423SLionel Sambuc 
296*433d6423SLionel Sambuc   /* See if the pty side of a pty is ready to return a select. */
297*433d6423SLionel Sambuc   if (pp->select_ops && (r = select_try_pty(tp, pp->select_ops))) {
298*433d6423SLionel Sambuc 	minor = PTYPX_MINOR + (int) (pp - pty_table);
299*433d6423SLionel Sambuc 	chardriver_reply_select(pp->select_proc, minor, r);
300*433d6423SLionel Sambuc 	pp->select_ops &= ~r;
301*433d6423SLionel Sambuc   }
302*433d6423SLionel Sambuc }
303*433d6423SLionel Sambuc 
304*433d6423SLionel Sambuc /*===========================================================================*
305*433d6423SLionel Sambuc  *				pty_master_select			     *
306*433d6423SLionel Sambuc  *===========================================================================*/
307*433d6423SLionel Sambuc static int pty_master_select(devminor_t minor, unsigned int ops,
308*433d6423SLionel Sambuc 	endpoint_t endpt)
309*433d6423SLionel Sambuc {
310*433d6423SLionel Sambuc   tty_t *tp;
311*433d6423SLionel Sambuc   pty_t *pp;
312*433d6423SLionel Sambuc   int ready_ops, watch;
313*433d6423SLionel Sambuc 
314*433d6423SLionel Sambuc   if ((tp = line2tty(minor)) == NULL)
315*433d6423SLionel Sambuc 	return ENXIO;
316*433d6423SLionel Sambuc   pp = tp->tty_priv;
317*433d6423SLionel Sambuc 
318*433d6423SLionel Sambuc   watch = (ops & CDEV_NOTIFY);
319*433d6423SLionel Sambuc   ops &= (CDEV_OP_RD | CDEV_OP_WR | CDEV_OP_ERR);
320*433d6423SLionel Sambuc 
321*433d6423SLionel Sambuc   ready_ops = select_try_pty(tp, ops);
322*433d6423SLionel Sambuc 
323*433d6423SLionel Sambuc   ops &= ~ready_ops;
324*433d6423SLionel Sambuc   if (ops && watch) {
325*433d6423SLionel Sambuc 	pp->select_ops |= ops;
326*433d6423SLionel Sambuc 	pp->select_proc = endpt;
327*433d6423SLionel Sambuc   }
328*433d6423SLionel Sambuc 
329*433d6423SLionel Sambuc   return ready_ops;
330*433d6423SLionel Sambuc }
331*433d6423SLionel Sambuc 
332*433d6423SLionel Sambuc /*===========================================================================*
333*433d6423SLionel Sambuc  *				do_pty					     *
334*433d6423SLionel Sambuc  *===========================================================================*/
335*433d6423SLionel Sambuc void do_pty(message *m_ptr, int ipc_status)
336*433d6423SLionel Sambuc {
337*433d6423SLionel Sambuc /* Process a request for a PTY master (/dev/ptypX) device. */
338*433d6423SLionel Sambuc 
339*433d6423SLionel Sambuc   chardriver_process(&pty_master_tab, m_ptr, ipc_status);
340*433d6423SLionel Sambuc }
341*433d6423SLionel Sambuc 
342*433d6423SLionel Sambuc /*===========================================================================*
343*433d6423SLionel Sambuc  *				pty_slave_write				     *
344*433d6423SLionel Sambuc  *===========================================================================*/
345*433d6423SLionel Sambuc static int pty_slave_write(tty_t *tp, int try)
346*433d6423SLionel Sambuc {
347*433d6423SLionel Sambuc /* (*dev_write)() routine for PTYs.  Transfer bytes from the writer on
348*433d6423SLionel Sambuc  * /dev/ttypX to the output buffer.
349*433d6423SLionel Sambuc  */
350*433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
351*433d6423SLionel Sambuc   int count, ocount, s;
352*433d6423SLionel Sambuc 
353*433d6423SLionel Sambuc   /* PTY closed down? */
354*433d6423SLionel Sambuc   if (pp->state & PTY_CLOSED) {
355*433d6423SLionel Sambuc   	if (try) return 1;
356*433d6423SLionel Sambuc 	if (tp->tty_outleft > 0) {
357*433d6423SLionel Sambuc 		chardriver_reply_task(tp->tty_outcaller, tp->tty_outid, EIO);
358*433d6423SLionel Sambuc 		tp->tty_outleft = tp->tty_outcum = 0;
359*433d6423SLionel Sambuc 		tp->tty_outcaller = NONE;
360*433d6423SLionel Sambuc 	}
361*433d6423SLionel Sambuc 	return 0;
362*433d6423SLionel Sambuc   }
363*433d6423SLionel Sambuc 
364*433d6423SLionel Sambuc   /* While there is something to do. */
365*433d6423SLionel Sambuc   for (;;) {
366*433d6423SLionel Sambuc 	ocount = buflen(pp->obuf) - pp->ocount;
367*433d6423SLionel Sambuc 	if (try) return (ocount > 0);
368*433d6423SLionel Sambuc 	count = bufend(pp->obuf) - pp->ohead;
369*433d6423SLionel Sambuc 	if (count > ocount) count = ocount;
370*433d6423SLionel Sambuc 	if (count > tp->tty_outleft) count = tp->tty_outleft;
371*433d6423SLionel Sambuc 	if (count == 0 || tp->tty_inhibited)
372*433d6423SLionel Sambuc 		break;
373*433d6423SLionel Sambuc 
374*433d6423SLionel Sambuc 	/* Copy from user space to the PTY output buffer. */
375*433d6423SLionel Sambuc 	if (tp->tty_outcaller == KERNEL) {
376*433d6423SLionel Sambuc 		/* We're trying to print on kernel's behalf */
377*433d6423SLionel Sambuc 		memcpy(pp->ohead, (void *) tp->tty_outgrant + tp->tty_outcum,
378*433d6423SLionel Sambuc 			count);
379*433d6423SLionel Sambuc 	} else {
380*433d6423SLionel Sambuc 		if ((s = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant,
381*433d6423SLionel Sambuc 				tp->tty_outcum, (vir_bytes) pp->ohead,
382*433d6423SLionel Sambuc 				count)) != OK) {
383*433d6423SLionel Sambuc 			break;
384*433d6423SLionel Sambuc 		}
385*433d6423SLionel Sambuc 	}
386*433d6423SLionel Sambuc 
387*433d6423SLionel Sambuc 	/* Perform output processing on the output buffer. */
388*433d6423SLionel Sambuc 	out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
389*433d6423SLionel Sambuc 	if (count == 0) break;
390*433d6423SLionel Sambuc 
391*433d6423SLionel Sambuc 	/* Assume echoing messed up by output. */
392*433d6423SLionel Sambuc 	tp->tty_reprint = TRUE;
393*433d6423SLionel Sambuc 
394*433d6423SLionel Sambuc 	/* Bookkeeping. */
395*433d6423SLionel Sambuc 	pp->ocount += ocount;
396*433d6423SLionel Sambuc 	if ((pp->ohead += ocount) >= bufend(pp->obuf))
397*433d6423SLionel Sambuc 		pp->ohead -= buflen(pp->obuf);
398*433d6423SLionel Sambuc 	pty_start(pp);
399*433d6423SLionel Sambuc 
400*433d6423SLionel Sambuc 	tp->tty_outcum += count;
401*433d6423SLionel Sambuc 	if ((tp->tty_outleft -= count) == 0) {
402*433d6423SLionel Sambuc 		/* Output is finished, reply to the writer. */
403*433d6423SLionel Sambuc 		chardriver_reply_task(tp->tty_outcaller, tp->tty_outid,
404*433d6423SLionel Sambuc 			tp->tty_outcum);
405*433d6423SLionel Sambuc 		tp->tty_outcum = 0;
406*433d6423SLionel Sambuc 		tp->tty_outcaller = NONE;
407*433d6423SLionel Sambuc 	}
408*433d6423SLionel Sambuc   }
409*433d6423SLionel Sambuc   pty_finish(pp);
410*433d6423SLionel Sambuc   return 1;
411*433d6423SLionel Sambuc }
412*433d6423SLionel Sambuc 
413*433d6423SLionel Sambuc /*===========================================================================*
414*433d6423SLionel Sambuc  *				pty_slave_echo				     *
415*433d6423SLionel Sambuc  *===========================================================================*/
416*433d6423SLionel Sambuc static void pty_slave_echo(tty_t *tp, int c)
417*433d6423SLionel Sambuc {
418*433d6423SLionel Sambuc /* Echo one character.  (Like pty_write, but only one character, optionally.) */
419*433d6423SLionel Sambuc 
420*433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
421*433d6423SLionel Sambuc   int count, ocount;
422*433d6423SLionel Sambuc 
423*433d6423SLionel Sambuc   ocount = buflen(pp->obuf) - pp->ocount;
424*433d6423SLionel Sambuc   if (ocount == 0) return;		/* output buffer full */
425*433d6423SLionel Sambuc   count = 1;
426*433d6423SLionel Sambuc   *pp->ohead = c;			/* add one character */
427*433d6423SLionel Sambuc 
428*433d6423SLionel Sambuc   out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
429*433d6423SLionel Sambuc   if (count == 0) return;
430*433d6423SLionel Sambuc 
431*433d6423SLionel Sambuc   pp->ocount += ocount;
432*433d6423SLionel Sambuc   if ((pp->ohead += ocount) >= bufend(pp->obuf)) pp->ohead -= buflen(pp->obuf);
433*433d6423SLionel Sambuc   pty_start(pp);
434*433d6423SLionel Sambuc }
435*433d6423SLionel Sambuc 
436*433d6423SLionel Sambuc /*===========================================================================*
437*433d6423SLionel Sambuc  *				pty_start				     *
438*433d6423SLionel Sambuc  *===========================================================================*/
439*433d6423SLionel Sambuc static void pty_start(pty_t *pp)
440*433d6423SLionel Sambuc {
441*433d6423SLionel Sambuc /* Transfer bytes written to the output buffer to the PTY reader. */
442*433d6423SLionel Sambuc   int count;
443*433d6423SLionel Sambuc 
444*433d6423SLionel Sambuc   /* While there are things to do. */
445*433d6423SLionel Sambuc   for (;;) {
446*433d6423SLionel Sambuc   	int s;
447*433d6423SLionel Sambuc 	count = bufend(pp->obuf) - pp->otail;
448*433d6423SLionel Sambuc 	if (count > pp->ocount) count = pp->ocount;
449*433d6423SLionel Sambuc 	if (count > pp->rdleft) count = pp->rdleft;
450*433d6423SLionel Sambuc 	if (count == 0) break;
451*433d6423SLionel Sambuc 
452*433d6423SLionel Sambuc 	/* Copy from the output buffer to the readers address space. */
453*433d6423SLionel Sambuc 	if((s = sys_safecopyto(pp->rdcaller, pp->rdgrant, pp->rdcum,
454*433d6423SLionel Sambuc 		(vir_bytes) pp->otail, count)) != OK) {
455*433d6423SLionel Sambuc 		break;
456*433d6423SLionel Sambuc  	}
457*433d6423SLionel Sambuc 
458*433d6423SLionel Sambuc 	/* Bookkeeping. */
459*433d6423SLionel Sambuc 	pp->ocount -= count;
460*433d6423SLionel Sambuc 	if ((pp->otail += count) == bufend(pp->obuf)) pp->otail = pp->obuf;
461*433d6423SLionel Sambuc 	pp->rdcum += count;
462*433d6423SLionel Sambuc 	pp->rdleft -= count;
463*433d6423SLionel Sambuc   }
464*433d6423SLionel Sambuc }
465*433d6423SLionel Sambuc 
466*433d6423SLionel Sambuc /*===========================================================================*
467*433d6423SLionel Sambuc  *				pty_finish				     *
468*433d6423SLionel Sambuc  *===========================================================================*/
469*433d6423SLionel Sambuc static void pty_finish(pty_t *pp)
470*433d6423SLionel Sambuc {
471*433d6423SLionel Sambuc /* Finish the read request of a PTY reader if there is at least one byte
472*433d6423SLionel Sambuc  * transferred.
473*433d6423SLionel Sambuc  */
474*433d6423SLionel Sambuc 
475*433d6423SLionel Sambuc   if (pp->rdcum > 0) {
476*433d6423SLionel Sambuc 	chardriver_reply_task(pp->rdcaller, pp->rdid, pp->rdcum);
477*433d6423SLionel Sambuc 	pp->rdleft = pp->rdcum = 0;
478*433d6423SLionel Sambuc 	pp->rdcaller = NONE;
479*433d6423SLionel Sambuc   }
480*433d6423SLionel Sambuc }
481*433d6423SLionel Sambuc 
482*433d6423SLionel Sambuc /*===========================================================================*
483*433d6423SLionel Sambuc  *				pty_slave_read				     *
484*433d6423SLionel Sambuc  *===========================================================================*/
485*433d6423SLionel Sambuc static int pty_slave_read(tty_t *tp, int try)
486*433d6423SLionel Sambuc {
487*433d6423SLionel Sambuc /* Offer bytes from the PTY writer for input on the TTY.  (Do it one byte at
488*433d6423SLionel Sambuc  * a time, 99% of the writes will be for one byte, so no sense in being smart.)
489*433d6423SLionel Sambuc  */
490*433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
491*433d6423SLionel Sambuc   char c;
492*433d6423SLionel Sambuc 
493*433d6423SLionel Sambuc   if (pp->state & PTY_CLOSED) {
494*433d6423SLionel Sambuc 	if (try) return 1;
495*433d6423SLionel Sambuc 	if (tp->tty_inleft > 0) {
496*433d6423SLionel Sambuc 		chardriver_reply_task(tp->tty_incaller, tp->tty_inid,
497*433d6423SLionel Sambuc 			tp->tty_incum);
498*433d6423SLionel Sambuc 		tp->tty_inleft = tp->tty_incum = 0;
499*433d6423SLionel Sambuc 		tp->tty_incaller = NONE;
500*433d6423SLionel Sambuc 	}
501*433d6423SLionel Sambuc 	return 1;
502*433d6423SLionel Sambuc   }
503*433d6423SLionel Sambuc 
504*433d6423SLionel Sambuc   if (try) {
505*433d6423SLionel Sambuc   	if (pp->wrleft > 0)
506*433d6423SLionel Sambuc   		return 1;
507*433d6423SLionel Sambuc   	return 0;
508*433d6423SLionel Sambuc   }
509*433d6423SLionel Sambuc 
510*433d6423SLionel Sambuc   while (pp->wrleft > 0) {
511*433d6423SLionel Sambuc   	int s;
512*433d6423SLionel Sambuc 
513*433d6423SLionel Sambuc 	/* Transfer one character to 'c'. */
514*433d6423SLionel Sambuc 	if ((s = sys_safecopyfrom(pp->wrcaller, pp->wrgrant, pp->wrcum,
515*433d6423SLionel Sambuc 		(vir_bytes) &c, 1)) != OK) {
516*433d6423SLionel Sambuc 		printf("pty: safecopy failed (error %d)\n", s);
517*433d6423SLionel Sambuc 		break;
518*433d6423SLionel Sambuc 	}
519*433d6423SLionel Sambuc 
520*433d6423SLionel Sambuc 	/* Input processing. */
521*433d6423SLionel Sambuc 	if (in_process(tp, &c, 1) == 0) break;
522*433d6423SLionel Sambuc 
523*433d6423SLionel Sambuc 	/* PTY writer bookkeeping. */
524*433d6423SLionel Sambuc 	pp->wrcum++;
525*433d6423SLionel Sambuc 	if (--pp->wrleft == 0) {
526*433d6423SLionel Sambuc 		chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum);
527*433d6423SLionel Sambuc 		pp->wrcum = 0;
528*433d6423SLionel Sambuc 		pp->wrcaller = NONE;
529*433d6423SLionel Sambuc 	}
530*433d6423SLionel Sambuc   }
531*433d6423SLionel Sambuc 
532*433d6423SLionel Sambuc   return 0;
533*433d6423SLionel Sambuc }
534*433d6423SLionel Sambuc 
535*433d6423SLionel Sambuc /*===========================================================================*
536*433d6423SLionel Sambuc  *				pty_slave_open				     *
537*433d6423SLionel Sambuc  *===========================================================================*/
538*433d6423SLionel Sambuc static int pty_slave_open(tty_t *tp, int UNUSED(try))
539*433d6423SLionel Sambuc {
540*433d6423SLionel Sambuc /* The tty side has been opened. */
541*433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
542*433d6423SLionel Sambuc 
543*433d6423SLionel Sambuc   assert(tp->tty_minor >= TTYPX_MINOR && tp->tty_minor < TTYPX_MINOR + NR_PTYS);
544*433d6423SLionel Sambuc 
545*433d6423SLionel Sambuc   /* TTY_ACTIVE may already be set, which would indicate that the slave is
546*433d6423SLionel Sambuc    * reopened after being fully closed while the master is still open. In that
547*433d6423SLionel Sambuc    * case TTY_CLOSED will also be set, so clear that one.
548*433d6423SLionel Sambuc    */
549*433d6423SLionel Sambuc   pp->state |= TTY_ACTIVE;
550*433d6423SLionel Sambuc   pp->state &= ~TTY_CLOSED;
551*433d6423SLionel Sambuc 
552*433d6423SLionel Sambuc   return 0;
553*433d6423SLionel Sambuc }
554*433d6423SLionel Sambuc 
555*433d6423SLionel Sambuc /*===========================================================================*
556*433d6423SLionel Sambuc  *				pty_slave_close				     *
557*433d6423SLionel Sambuc  *===========================================================================*/
558*433d6423SLionel Sambuc static int pty_slave_close(tty_t *tp, int UNUSED(try))
559*433d6423SLionel Sambuc {
560*433d6423SLionel Sambuc /* The tty side has closed, so shut down the pty side. */
561*433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
562*433d6423SLionel Sambuc 
563*433d6423SLionel Sambuc   if (!(pp->state & PTY_ACTIVE)) return 0;
564*433d6423SLionel Sambuc 
565*433d6423SLionel Sambuc   if (pp->rdleft > 0) {
566*433d6423SLionel Sambuc 	chardriver_reply_task(pp->rdcaller, pp->rdid, pp->rdcum);
567*433d6423SLionel Sambuc 	pp->rdleft = pp->rdcum = 0;
568*433d6423SLionel Sambuc 	pp->rdcaller = NONE;
569*433d6423SLionel Sambuc   }
570*433d6423SLionel Sambuc 
571*433d6423SLionel Sambuc   if (pp->wrleft > 0) {
572*433d6423SLionel Sambuc 	chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum);
573*433d6423SLionel Sambuc 	pp->wrleft = pp->wrcum = 0;
574*433d6423SLionel Sambuc 	pp->wrcaller = NONE;
575*433d6423SLionel Sambuc   }
576*433d6423SLionel Sambuc 
577*433d6423SLionel Sambuc   if (pp->state & PTY_CLOSED) pp->state = 0;
578*433d6423SLionel Sambuc   else pp->state |= TTY_CLOSED;
579*433d6423SLionel Sambuc 
580*433d6423SLionel Sambuc   return 0;
581*433d6423SLionel Sambuc }
582*433d6423SLionel Sambuc 
583*433d6423SLionel Sambuc /*===========================================================================*
584*433d6423SLionel Sambuc  *				pty_slave_icancel			     *
585*433d6423SLionel Sambuc  *===========================================================================*/
586*433d6423SLionel Sambuc static int pty_slave_icancel(tty_t *tp, int UNUSED(try))
587*433d6423SLionel Sambuc {
588*433d6423SLionel Sambuc /* Discard waiting input. */
589*433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
590*433d6423SLionel Sambuc 
591*433d6423SLionel Sambuc   if (pp->wrleft > 0) {
592*433d6423SLionel Sambuc 	chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum + pp->wrleft);
593*433d6423SLionel Sambuc 	pp->wrcum = pp->wrleft = 0;
594*433d6423SLionel Sambuc 	pp->wrcaller = NONE;
595*433d6423SLionel Sambuc   }
596*433d6423SLionel Sambuc 
597*433d6423SLionel Sambuc   return 0;
598*433d6423SLionel Sambuc }
599*433d6423SLionel Sambuc 
600*433d6423SLionel Sambuc /*===========================================================================*
601*433d6423SLionel Sambuc  *				pty_slave_ocancel			     *
602*433d6423SLionel Sambuc  *===========================================================================*/
603*433d6423SLionel Sambuc static int pty_slave_ocancel(tty_t *tp, int UNUSED(try))
604*433d6423SLionel Sambuc {
605*433d6423SLionel Sambuc /* Drain the output buffer. */
606*433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
607*433d6423SLionel Sambuc 
608*433d6423SLionel Sambuc   pp->ocount = 0;
609*433d6423SLionel Sambuc   pp->otail = pp->ohead;
610*433d6423SLionel Sambuc 
611*433d6423SLionel Sambuc   return 0;
612*433d6423SLionel Sambuc }
613*433d6423SLionel Sambuc 
614*433d6423SLionel Sambuc /*===========================================================================*
615*433d6423SLionel Sambuc  *				pty_init				     *
616*433d6423SLionel Sambuc  *===========================================================================*/
617*433d6423SLionel Sambuc void pty_init(tty_t *tp)
618*433d6423SLionel Sambuc {
619*433d6423SLionel Sambuc   pty_t *pp;
620*433d6423SLionel Sambuc   int line;
621*433d6423SLionel Sambuc 
622*433d6423SLionel Sambuc   /* Associate PTY and TTY structures. */
623*433d6423SLionel Sambuc   line = tp - tty_table;
624*433d6423SLionel Sambuc   pp = tp->tty_priv = &pty_table[line];
625*433d6423SLionel Sambuc   pp->tty = tp;
626*433d6423SLionel Sambuc   pp->select_ops = 0;
627*433d6423SLionel Sambuc   pp->rdcaller = NONE;
628*433d6423SLionel Sambuc   pp->wrcaller = NONE;
629*433d6423SLionel Sambuc 
630*433d6423SLionel Sambuc   /* Set up output queue. */
631*433d6423SLionel Sambuc   pp->ohead = pp->otail = pp->obuf;
632*433d6423SLionel Sambuc 
633*433d6423SLionel Sambuc   /* Fill in TTY function hooks. */
634*433d6423SLionel Sambuc   tp->tty_devread = pty_slave_read;
635*433d6423SLionel Sambuc   tp->tty_devwrite = pty_slave_write;
636*433d6423SLionel Sambuc   tp->tty_echo = pty_slave_echo;
637*433d6423SLionel Sambuc   tp->tty_icancel = pty_slave_icancel;
638*433d6423SLionel Sambuc   tp->tty_ocancel = pty_slave_ocancel;
639*433d6423SLionel Sambuc   tp->tty_open = pty_slave_open;
640*433d6423SLionel Sambuc   tp->tty_close = pty_slave_close;
641*433d6423SLionel Sambuc   tp->tty_select_ops = 0;
642*433d6423SLionel Sambuc }
643