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