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