| 1 | /* tty_pty.c 4.5 %G% */ |
| 2 | |
| 3 | /* |
| 4 | * Pseudo-teletype Driver |
| 5 | * (Actually two drivers, requiring two entries in 'cdevsw') |
| 6 | */ |
| 7 | #include "pty.h" |
| 8 | |
| 9 | #if WANTPTY > 0 |
| 10 | |
| 11 | #include "../h/param.h" |
| 12 | #include "../h/systm.h" |
| 13 | #include "../h/tty.h" |
| 14 | #include "../h/dir.h" |
| 15 | #include "../h/user.h" |
| 16 | #include "../h/conf.h" |
| 17 | #include "../h/buf.h" |
| 18 | #include "../h/file.h" |
| 19 | |
| 20 | #define NPTY 16 /* Number of pseudo-teletypes */ |
| 21 | #define BUFSIZ 100 /* Chunk size iomoved from user */ |
| 22 | #define ALLDELAYS (NLDELAY|TBDELAY|XTABS|CRDELAY|VTDELAY) |
| 23 | /* |
| 24 | * A pseudo-teletype is a special device which is not unlike a pipe. |
| 25 | * It is used to communicate between two processes. However, it allows |
| 26 | * one to simulate a teletype, including mode setting, interrupt, and |
| 27 | * multiple end of files (all not possible on a pipe). There are |
| 28 | * really two drivers here. One is the device which looks like a TTY |
| 29 | * and can be thought of as the slave device, and hence its routines |
| 30 | * are prefixed with 'pts' (PTY Slave). The other driver can be |
| 31 | * thought of as the controlling device, and its routines are prefixed |
| 32 | * by 'ptc' (PTY Controller). To type on the simulated keyboard of the |
| 33 | * PTY, one does a 'write' to the controlling device. To get the |
| 34 | * simulated printout from the PTY, one does a 'read' on the controlling |
| 35 | * device. Normally, the controlling device is called 'ptyx' and the |
| 36 | * slave device is called 'ttyx' (to make programs like 'who' happy). |
| 37 | */ |
| 38 | |
| 39 | struct tty pt_tty[NPTY]; /* TTY headers for PTYs */ |
| 40 | |
| 41 | /*ARGSUSED*/ |
| 42 | ptsopen(dev, flag) |
| 43 | dev_t dev; |
| 44 | { /* Open for PTY Slave */ |
| 45 | register struct tty *tp; |
| 46 | |
| 47 | if(minor(dev) >= NPTY) { |
| 48 | u.u_error = ENXIO; |
| 49 | return; |
| 50 | } |
| 51 | tp = &pt_tty[minor(dev)]; |
| 52 | if((tp->t_state & ISOPEN) == 0) { |
| 53 | ttychars(tp); /* Set up default chars */ |
| 54 | tp->t_flags = 0; /* No features (nor raw mode) */ |
| 55 | } else if(tp->t_state&XCLUDE && u.u_uid != 0) { |
| 56 | u.u_error = EBUSY; |
| 57 | return; |
| 58 | } |
| 59 | if(tp->t_oproc) /* Ctrlr still around. */ |
| 60 | tp->t_state |= CARR_ON; |
| 61 | while((tp->t_state & CARR_ON) == 0) { |
| 62 | tp->t_state |= WOPEN; |
| 63 | sleep((caddr_t)&tp->t_rawq, TTIPRI); |
| 64 | } |
| 65 | (*linesw[tp->t_line].l_open)(dev, tp); |
| 66 | } |
| 67 | |
| 68 | ptsclose(dev) |
| 69 | dev_t dev; |
| 70 | { /* Close slave part of PTY */ |
| 71 | register struct tty *tp; |
| 72 | |
| 73 | tp = &pt_tty[minor(dev)]; |
| 74 | (*linesw[tp->t_line].l_close)(tp); |
| 75 | } |
| 76 | |
| 77 | ptsread(dev) |
| 78 | dev_t dev; |
| 79 | { /* Read from PTY, i.e. from data written by controlling device */ |
| 80 | register struct tty *tp; |
| 81 | |
| 82 | tp = &pt_tty[minor(dev)]; |
| 83 | if(tp->t_oproc) { |
| 84 | (*linesw[tp->t_line].l_read)(tp); |
| 85 | /* Wakeup other half if sleeping */ |
| 86 | wakeup((caddr_t)&tp->t_rawq.c_cf); |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | ptswrite(dev) |
| 91 | dev_t dev; |
| 92 | { /* Write on PTY, i.e. to be read from |
| 93 | controlling device */ |
| 94 | register struct tty *tp; |
| 95 | |
| 96 | tp = &pt_tty[minor(dev)]; |
| 97 | /* Wait for controlling device to be opened */ |
| 98 | if(tp->t_oproc) |
| 99 | (*linesw[tp->t_line].l_write)(tp); |
| 100 | } |
| 101 | |
| 102 | ptsstart(tp) |
| 103 | struct tty *tp; |
| 104 | { /* Called by 'ttstart' to output a character. |
| 105 | Merely wakes up controlling half, which |
| 106 | does actual work */ |
| 107 | if(tp->t_state & TTSTOP) |
| 108 | return; |
| 109 | wakeup((caddr_t)&tp->t_outq.c_cf); |
| 110 | } |
| 111 | |
| 112 | /*ARGSUSED*/ |
| 113 | ptcopen(dev, flag) |
| 114 | dev_t dev; |
| 115 | { /* Open for PTY Controller */ |
| 116 | register struct tty *tp; |
| 117 | |
| 118 | if(minor(dev) >= NPTY) { |
| 119 | u.u_error = ENXIO; |
| 120 | return; |
| 121 | } |
| 122 | tp = &pt_tty[minor(dev)]; |
| 123 | if(tp->t_oproc) { |
| 124 | u.u_error = EIO; |
| 125 | return; |
| 126 | } |
| 127 | tp->t_oproc = ptsstart; /* Set address of start routine */ |
| 128 | tp->t_iproc = 0; |
| 129 | if(tp->t_state & WOPEN) |
| 130 | wakeup((caddr_t)&tp->t_rawq); |
| 131 | tp->t_state |= CARR_ON; |
| 132 | } |
| 133 | |
| 134 | ptcclose(dev) |
| 135 | dev_t dev; |
| 136 | { /* Close controlling part of PTY */ |
| 137 | register struct tty *tp; |
| 138 | |
| 139 | tp = &pt_tty[minor(dev)]; |
| 140 | if(tp->t_state & ISOPEN) |
| 141 | gsignal(tp->t_pgrp, SIGHUP); |
| 142 | tp->t_state &= ~CARR_ON; /* Virtual carrier is gone */ |
| 143 | flushtty(tp, FREAD|FWRITE); /* Clean things up */ |
| 144 | tp->t_oproc = 0; /* Mark as closed */ |
| 145 | } |
| 146 | |
| 147 | ptcread(dev) |
| 148 | dev_t dev; |
| 149 | { /* Read from PTY's output buffer */ |
| 150 | register struct tty *tp; |
| 151 | |
| 152 | tp = &pt_tty[minor(dev)]; |
| 153 | if((tp->t_state&(CARR_ON|ISOPEN)) == 0) |
| 154 | return; |
| 155 | while(tp->t_outq.c_cc == 0 || /* Wait for something to arrive */ |
| 156 | (tp->t_state&TTSTOP)) /* (Woken by ptsstart) */ |
| 157 | sleep((caddr_t)&tp->t_outq.c_cf, TTIPRI); |
| 158 | while(tp->t_outq.c_cc && passc(getc(&tp->t_outq)) >= 0); |
| 159 | if(tp->t_outq.c_cc <= TTLOWAT(tp) && (tp->t_state&ASLEEP)) { |
| 160 | tp->t_state &= ~ASLEEP; |
| 161 | if(tp->t_chan) |
| 162 | mcstart(tp->t_chan, (caddr_t)&tp->t_outq); |
| 163 | else |
| 164 | wakeup((caddr_t)&tp->t_outq); |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | ptcwrite(dev) |
| 169 | dev_t dev; |
| 170 | { /* Stuff characters into PTY's input buffer */ |
| 171 | register struct tty *tp; |
| 172 | register char *cp, *ce; |
| 173 | register int cc; |
| 174 | char locbuf[BUFSIZ]; |
| 175 | |
| 176 | tp = &pt_tty[minor(dev)]; |
| 177 | if((tp->t_state&(CARR_ON|ISOPEN)) == 0) |
| 178 | return; |
| 179 | while(u.u_count) { |
| 180 | cc = MIN(u.u_count, BUFSIZ); |
| 181 | cp = locbuf; |
| 182 | iomove(cp, (unsigned)cc, B_WRITE); |
| 183 | if(u.u_error) |
| 184 | break; |
| 185 | ce = cp + cc; |
| 186 | while(cp < ce) { |
| 187 | while(tp->t_delct && tp->t_rawq.c_cc >= TTYHOG - 2) { |
| 188 | wakeup((caddr_t)&tp->t_rawq); |
| 189 | /* Better than just flushing it! */ |
| 190 | /* Wait for something to be read */ |
| 191 | sleep((caddr_t)&tp->t_rawq.c_cf, TTOPRI); |
| 192 | } |
| 193 | ttyinput(*cp++, tp); |
| 194 | } |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | /* Note: Both slave and controlling device have the same routine for */ |
| 199 | /* 'ioctl' (but note check for controller - 4/12/78:mob)*/ |
| 200 | /*ARGSUSED*/ |
| 201 | ptyioctl(dev, cmd, addr, flag) |
| 202 | caddr_t addr; |
| 203 | dev_t dev; |
| 204 | { /* Read and write status bits */ |
| 205 | register struct tty *tp; |
| 206 | register int tbd; |
| 207 | #ifdef BLAND |
| 208 | register int nld; |
| 209 | #endif |
| 210 | |
| 211 | tp = &pt_tty[minor(dev)]; |
| 212 | /* if controller stty then must flush to prevent a hang */ |
| 213 | if(cdevsw[major(dev)].d_open == ptcopen && cmd == TIOCSETP) |
| 214 | while(getc(&tp->t_outq) >= 0); |
| 215 | if(ttioctl(tp, cmd, addr, dev)) { |
| 216 | if(cmd == TIOCSETP || cmd == TIOCSETN) { |
| 217 | #ifdef BLAND |
| 218 | nld = tp->t_flags & NLDELAY; |
| 219 | #endif |
| 220 | tbd = tp->t_flags & TBDELAY; |
| 221 | tp->t_flags &= ~ALLDELAYS; |
| 222 | if(tbd == TBDELAY) /* Wants tab expansion */ |
| 223 | tp->t_flags |= tbd; |
| 224 | #ifdef BLAND |
| 225 | if(nld == NLDELAY) /* Allow ANN ARBOR mode. */ |
| 226 | tp->t_flags |= nld; |
| 227 | #endif |
| 228 | } |
| 229 | } else |
| 230 | u.u_error = ENOTTY; |
| 231 | } |
| 232 | #endif |