1 | #include <machine/rtems-bsd-kernel-space.h> |
---|
2 | |
---|
3 | /*- |
---|
4 | * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> |
---|
5 | * All rights reserved. |
---|
6 | * |
---|
7 | * Portions of this software were developed under sponsorship from Snow |
---|
8 | * B.V., the Netherlands. |
---|
9 | * |
---|
10 | * Redistribution and use in source and binary forms, with or without |
---|
11 | * modification, are permitted provided that the following conditions |
---|
12 | * are met: |
---|
13 | * 1. Redistributions of source code must retain the above copyright |
---|
14 | * notice, this list of conditions and the following disclaimer. |
---|
15 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
16 | * notice, this list of conditions and the following disclaimer in the |
---|
17 | * documentation and/or other materials provided with the distribution. |
---|
18 | * |
---|
19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
---|
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
---|
23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
---|
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
---|
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
---|
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
---|
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
---|
29 | * SUCH DAMAGE. |
---|
30 | */ |
---|
31 | |
---|
32 | #include <sys/cdefs.h> |
---|
33 | __FBSDID("$FreeBSD$"); |
---|
34 | |
---|
35 | #include <rtems/bsd/sys/param.h> |
---|
36 | #include <sys/fcntl.h> |
---|
37 | #include <sys/filio.h> |
---|
38 | #include <sys/kernel.h> |
---|
39 | #include <sys/signal.h> |
---|
40 | #include <sys/sysctl.h> |
---|
41 | #include <sys/systm.h> |
---|
42 | #include <sys/tty.h> |
---|
43 | #include <sys/ttycom.h> |
---|
44 | #include <sys/ttydefaults.h> |
---|
45 | #include <sys/uio.h> |
---|
46 | #include <sys/vnode.h> |
---|
47 | |
---|
48 | /* |
---|
49 | * Standard TTYDISC `termios' line discipline. |
---|
50 | */ |
---|
51 | |
---|
52 | /* Statistics. */ |
---|
53 | static unsigned long tty_nin = 0; |
---|
54 | SYSCTL_ULONG(_kern, OID_AUTO, tty_nin, CTLFLAG_RD, |
---|
55 | &tty_nin, 0, "Total amount of bytes received"); |
---|
56 | static unsigned long tty_nout = 0; |
---|
57 | SYSCTL_ULONG(_kern, OID_AUTO, tty_nout, CTLFLAG_RD, |
---|
58 | &tty_nout, 0, "Total amount of bytes transmitted"); |
---|
59 | |
---|
60 | /* termios comparison macro's. */ |
---|
61 | #define CMP_CC(v,c) (tp->t_termios.c_cc[v] != _POSIX_VDISABLE && \ |
---|
62 | tp->t_termios.c_cc[v] == (c)) |
---|
63 | #define CMP_FLAG(field,opt) (tp->t_termios.c_ ## field ## flag & (opt)) |
---|
64 | |
---|
65 | /* Characters that cannot be modified through c_cc. */ |
---|
66 | #define CTAB '\t' |
---|
67 | #define CNL '\n' |
---|
68 | #define CCR '\r' |
---|
69 | |
---|
70 | /* Character is a control character. */ |
---|
71 | #define CTL_VALID(c) ((c) == 0x7f || (unsigned char)(c) < 0x20) |
---|
72 | /* Control character should be processed on echo. */ |
---|
73 | #define CTL_ECHO(c,q) (!(q) && ((c) == CERASE2 || (c) == CTAB || \ |
---|
74 | (c) == CNL || (c) == CCR)) |
---|
75 | /* Control character should be printed using ^X notation. */ |
---|
76 | #define CTL_PRINT(c,q) ((c) == 0x7f || ((unsigned char)(c) < 0x20 && \ |
---|
77 | ((q) || ((c) != CTAB && (c) != CNL)))) |
---|
78 | /* Character is whitespace. */ |
---|
79 | #define CTL_WHITE(c) ((c) == ' ' || (c) == CTAB) |
---|
80 | /* Character is alphanumeric. */ |
---|
81 | #define CTL_ALNUM(c) (((c) >= '0' && (c) <= '9') || \ |
---|
82 | ((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) |
---|
83 | |
---|
84 | #define TTY_STACKBUF 256 |
---|
85 | |
---|
86 | void |
---|
87 | ttydisc_open(struct tty *tp) |
---|
88 | { |
---|
89 | ttydisc_optimize(tp); |
---|
90 | } |
---|
91 | |
---|
92 | void |
---|
93 | ttydisc_close(struct tty *tp) |
---|
94 | { |
---|
95 | |
---|
96 | /* Clean up our flags when leaving the discipline. */ |
---|
97 | tp->t_flags &= ~(TF_STOPPED|TF_HIWAT|TF_ZOMBIE); |
---|
98 | |
---|
99 | /* |
---|
100 | * POSIX states that we must drain output and flush input on |
---|
101 | * last close. Draining has already been done if possible. |
---|
102 | */ |
---|
103 | tty_flush(tp, FREAD | FWRITE); |
---|
104 | |
---|
105 | if (ttyhook_hashook(tp, close)) |
---|
106 | ttyhook_close(tp); |
---|
107 | } |
---|
108 | |
---|
109 | static int |
---|
110 | ttydisc_read_canonical(struct tty *tp, struct uio *uio, int ioflag) |
---|
111 | { |
---|
112 | char breakc[4] = { CNL }; /* enough to hold \n, VEOF and VEOL. */ |
---|
113 | int error; |
---|
114 | size_t clen, flen = 0, n = 1; |
---|
115 | unsigned char lastc = _POSIX_VDISABLE; |
---|
116 | |
---|
117 | #define BREAK_ADD(c) do { \ |
---|
118 | if (tp->t_termios.c_cc[c] != _POSIX_VDISABLE) \ |
---|
119 | breakc[n++] = tp->t_termios.c_cc[c]; \ |
---|
120 | } while (0) |
---|
121 | /* Determine which characters we should trigger on. */ |
---|
122 | BREAK_ADD(VEOF); |
---|
123 | BREAK_ADD(VEOL); |
---|
124 | #undef BREAK_ADD |
---|
125 | breakc[n] = '\0'; |
---|
126 | |
---|
127 | do { |
---|
128 | error = tty_wait_background(tp, curthread, SIGTTIN); |
---|
129 | if (error) |
---|
130 | return (error); |
---|
131 | |
---|
132 | /* |
---|
133 | * Quite a tricky case: unlike the old TTY |
---|
134 | * implementation, this implementation copies data back |
---|
135 | * to userspace in large chunks. Unfortunately, we can't |
---|
136 | * calculate the line length on beforehand if it crosses |
---|
137 | * ttyinq_block boundaries, because multiple reads could |
---|
138 | * then make this code read beyond the newline. |
---|
139 | * |
---|
140 | * This is why we limit the read to: |
---|
141 | * - The size the user has requested |
---|
142 | * - The blocksize (done in tty_inq.c) |
---|
143 | * - The amount of bytes until the newline |
---|
144 | * |
---|
145 | * This causes the line length to be recalculated after |
---|
146 | * each block has been copied to userspace. This will |
---|
147 | * cause the TTY layer to return data in chunks using |
---|
148 | * the blocksize (except the first and last blocks). |
---|
149 | */ |
---|
150 | clen = ttyinq_findchar(&tp->t_inq, breakc, uio->uio_resid, |
---|
151 | &lastc); |
---|
152 | |
---|
153 | /* No more data. */ |
---|
154 | if (clen == 0) { |
---|
155 | if (tp->t_flags & TF_ZOMBIE) |
---|
156 | return (0); |
---|
157 | else if (ioflag & IO_NDELAY) |
---|
158 | return (EWOULDBLOCK); |
---|
159 | |
---|
160 | error = tty_wait(tp, &tp->t_inwait); |
---|
161 | if (error) |
---|
162 | return (error); |
---|
163 | continue; |
---|
164 | } |
---|
165 | |
---|
166 | /* Don't send the EOF char back to userspace. */ |
---|
167 | if (CMP_CC(VEOF, lastc)) |
---|
168 | flen = 1; |
---|
169 | |
---|
170 | MPASS(flen <= clen); |
---|
171 | |
---|
172 | /* Read and throw away the EOF character. */ |
---|
173 | error = ttyinq_read_uio(&tp->t_inq, tp, uio, clen, flen); |
---|
174 | if (error) |
---|
175 | return (error); |
---|
176 | |
---|
177 | } while (uio->uio_resid > 0 && lastc == _POSIX_VDISABLE); |
---|
178 | |
---|
179 | return (0); |
---|
180 | } |
---|
181 | |
---|
182 | static int |
---|
183 | ttydisc_read_raw_no_timer(struct tty *tp, struct uio *uio, int ioflag) |
---|
184 | { |
---|
185 | size_t vmin = tp->t_termios.c_cc[VMIN]; |
---|
186 | ssize_t oresid = uio->uio_resid; |
---|
187 | int error; |
---|
188 | |
---|
189 | MPASS(tp->t_termios.c_cc[VTIME] == 0); |
---|
190 | |
---|
191 | /* |
---|
192 | * This routine implements the easy cases of read()s while in |
---|
193 | * non-canonical mode, namely case B and D, where we don't have |
---|
194 | * any timers at all. |
---|
195 | */ |
---|
196 | |
---|
197 | for (;;) { |
---|
198 | error = tty_wait_background(tp, curthread, SIGTTIN); |
---|
199 | if (error) |
---|
200 | return (error); |
---|
201 | |
---|
202 | error = ttyinq_read_uio(&tp->t_inq, tp, uio, |
---|
203 | uio->uio_resid, 0); |
---|
204 | if (error) |
---|
205 | return (error); |
---|
206 | if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin) |
---|
207 | return (0); |
---|
208 | |
---|
209 | /* We have to wait for more. */ |
---|
210 | if (tp->t_flags & TF_ZOMBIE) |
---|
211 | return (0); |
---|
212 | else if (ioflag & IO_NDELAY) |
---|
213 | return (EWOULDBLOCK); |
---|
214 | |
---|
215 | error = tty_wait(tp, &tp->t_inwait); |
---|
216 | if (error) |
---|
217 | return (error); |
---|
218 | } |
---|
219 | } |
---|
220 | |
---|
221 | static int |
---|
222 | ttydisc_read_raw_read_timer(struct tty *tp, struct uio *uio, int ioflag, |
---|
223 | int oresid) |
---|
224 | { |
---|
225 | size_t vmin = MAX(tp->t_termios.c_cc[VMIN], 1); |
---|
226 | unsigned int vtime = tp->t_termios.c_cc[VTIME]; |
---|
227 | struct timeval end, now, left; |
---|
228 | int error, hz; |
---|
229 | |
---|
230 | MPASS(tp->t_termios.c_cc[VTIME] != 0); |
---|
231 | |
---|
232 | /* Determine when the read should be expired. */ |
---|
233 | end.tv_sec = vtime / 10; |
---|
234 | end.tv_usec = (vtime % 10) * 100000; |
---|
235 | getmicrotime(&now); |
---|
236 | timevaladd(&end, &now); |
---|
237 | |
---|
238 | for (;;) { |
---|
239 | error = tty_wait_background(tp, curthread, SIGTTIN); |
---|
240 | if (error) |
---|
241 | return (error); |
---|
242 | |
---|
243 | error = ttyinq_read_uio(&tp->t_inq, tp, uio, |
---|
244 | uio->uio_resid, 0); |
---|
245 | if (error) |
---|
246 | return (error); |
---|
247 | if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin) |
---|
248 | return (0); |
---|
249 | |
---|
250 | /* Calculate how long we should wait. */ |
---|
251 | getmicrotime(&now); |
---|
252 | if (timevalcmp(&now, &end, >)) |
---|
253 | return (0); |
---|
254 | left = end; |
---|
255 | timevalsub(&left, &now); |
---|
256 | hz = tvtohz(&left); |
---|
257 | |
---|
258 | /* |
---|
259 | * We have to wait for more. If the timer expires, we |
---|
260 | * should return a 0-byte read. |
---|
261 | */ |
---|
262 | if (tp->t_flags & TF_ZOMBIE) |
---|
263 | return (0); |
---|
264 | else if (ioflag & IO_NDELAY) |
---|
265 | return (EWOULDBLOCK); |
---|
266 | |
---|
267 | error = tty_timedwait(tp, &tp->t_inwait, hz); |
---|
268 | if (error) |
---|
269 | return (error == EWOULDBLOCK ? 0 : error); |
---|
270 | } |
---|
271 | |
---|
272 | return (0); |
---|
273 | } |
---|
274 | |
---|
275 | static int |
---|
276 | ttydisc_read_raw_interbyte_timer(struct tty *tp, struct uio *uio, int ioflag) |
---|
277 | { |
---|
278 | size_t vmin = tp->t_termios.c_cc[VMIN]; |
---|
279 | ssize_t oresid = uio->uio_resid; |
---|
280 | int error; |
---|
281 | |
---|
282 | MPASS(tp->t_termios.c_cc[VMIN] != 0); |
---|
283 | MPASS(tp->t_termios.c_cc[VTIME] != 0); |
---|
284 | |
---|
285 | /* |
---|
286 | * When using the interbyte timer, the timer should be started |
---|
287 | * after the first byte has been received. We just call into the |
---|
288 | * generic read timer code after we've received the first byte. |
---|
289 | */ |
---|
290 | |
---|
291 | for (;;) { |
---|
292 | error = tty_wait_background(tp, curthread, SIGTTIN); |
---|
293 | if (error) |
---|
294 | return (error); |
---|
295 | |
---|
296 | error = ttyinq_read_uio(&tp->t_inq, tp, uio, |
---|
297 | uio->uio_resid, 0); |
---|
298 | if (error) |
---|
299 | return (error); |
---|
300 | if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin) |
---|
301 | return (0); |
---|
302 | |
---|
303 | /* |
---|
304 | * Not enough data, but we did receive some, which means |
---|
305 | * we'll now start using the interbyte timer. |
---|
306 | */ |
---|
307 | if (oresid != uio->uio_resid) |
---|
308 | break; |
---|
309 | |
---|
310 | /* We have to wait for more. */ |
---|
311 | if (tp->t_flags & TF_ZOMBIE) |
---|
312 | return (0); |
---|
313 | else if (ioflag & IO_NDELAY) |
---|
314 | return (EWOULDBLOCK); |
---|
315 | |
---|
316 | error = tty_wait(tp, &tp->t_inwait); |
---|
317 | if (error) |
---|
318 | return (error); |
---|
319 | } |
---|
320 | |
---|
321 | return ttydisc_read_raw_read_timer(tp, uio, ioflag, oresid); |
---|
322 | } |
---|
323 | |
---|
324 | int |
---|
325 | ttydisc_read(struct tty *tp, struct uio *uio, int ioflag) |
---|
326 | { |
---|
327 | int error; |
---|
328 | |
---|
329 | tty_lock_assert(tp, MA_OWNED); |
---|
330 | |
---|
331 | if (uio->uio_resid == 0) |
---|
332 | return (0); |
---|
333 | |
---|
334 | if (CMP_FLAG(l, ICANON)) |
---|
335 | error = ttydisc_read_canonical(tp, uio, ioflag); |
---|
336 | else if (tp->t_termios.c_cc[VTIME] == 0) |
---|
337 | error = ttydisc_read_raw_no_timer(tp, uio, ioflag); |
---|
338 | else if (tp->t_termios.c_cc[VMIN] == 0) |
---|
339 | error = ttydisc_read_raw_read_timer(tp, uio, ioflag, |
---|
340 | uio->uio_resid); |
---|
341 | else |
---|
342 | error = ttydisc_read_raw_interbyte_timer(tp, uio, ioflag); |
---|
343 | |
---|
344 | if (ttyinq_bytesleft(&tp->t_inq) >= tp->t_inlow || |
---|
345 | ttyinq_bytescanonicalized(&tp->t_inq) == 0) { |
---|
346 | /* Unset the input watermark when we've got enough space. */ |
---|
347 | tty_hiwat_in_unblock(tp); |
---|
348 | } |
---|
349 | |
---|
350 | return (error); |
---|
351 | } |
---|
352 | |
---|
353 | static __inline unsigned int |
---|
354 | ttydisc_findchar(const char *obstart, unsigned int oblen) |
---|
355 | { |
---|
356 | const char *c = obstart; |
---|
357 | |
---|
358 | while (oblen--) { |
---|
359 | if (CTL_VALID(*c)) |
---|
360 | break; |
---|
361 | c++; |
---|
362 | } |
---|
363 | |
---|
364 | return (c - obstart); |
---|
365 | } |
---|
366 | |
---|
367 | static int |
---|
368 | ttydisc_write_oproc(struct tty *tp, char c) |
---|
369 | { |
---|
370 | unsigned int scnt, error; |
---|
371 | |
---|
372 | MPASS(CMP_FLAG(o, OPOST)); |
---|
373 | MPASS(CTL_VALID(c)); |
---|
374 | |
---|
375 | #define PRINT_NORMAL() ttyoutq_write_nofrag(&tp->t_outq, &c, 1) |
---|
376 | switch (c) { |
---|
377 | case CEOF: |
---|
378 | /* End-of-text dropping. */ |
---|
379 | if (CMP_FLAG(o, ONOEOT)) |
---|
380 | return (0); |
---|
381 | return PRINT_NORMAL(); |
---|
382 | |
---|
383 | case CERASE2: |
---|
384 | /* Handle backspace to fix tab expansion. */ |
---|
385 | if (PRINT_NORMAL() != 0) |
---|
386 | return (-1); |
---|
387 | if (tp->t_column > 0) |
---|
388 | tp->t_column--; |
---|
389 | return (0); |
---|
390 | |
---|
391 | case CTAB: |
---|
392 | /* Tab expansion. */ |
---|
393 | scnt = 8 - (tp->t_column & 7); |
---|
394 | if (CMP_FLAG(o, TAB3)) { |
---|
395 | error = ttyoutq_write_nofrag(&tp->t_outq, |
---|
396 | " ", scnt); |
---|
397 | } else { |
---|
398 | error = PRINT_NORMAL(); |
---|
399 | } |
---|
400 | if (error) |
---|
401 | return (-1); |
---|
402 | |
---|
403 | tp->t_column += scnt; |
---|
404 | MPASS((tp->t_column % 8) == 0); |
---|
405 | return (0); |
---|
406 | |
---|
407 | case CNL: |
---|
408 | /* Newline conversion. */ |
---|
409 | if (CMP_FLAG(o, ONLCR)) { |
---|
410 | /* Convert \n to \r\n. */ |
---|
411 | error = ttyoutq_write_nofrag(&tp->t_outq, "\r\n", 2); |
---|
412 | } else { |
---|
413 | error = PRINT_NORMAL(); |
---|
414 | } |
---|
415 | if (error) |
---|
416 | return (-1); |
---|
417 | |
---|
418 | if (CMP_FLAG(o, ONLCR|ONLRET)) { |
---|
419 | tp->t_column = tp->t_writepos = 0; |
---|
420 | ttyinq_reprintpos_set(&tp->t_inq); |
---|
421 | } |
---|
422 | return (0); |
---|
423 | |
---|
424 | case CCR: |
---|
425 | /* Carriage return to newline conversion. */ |
---|
426 | if (CMP_FLAG(o, OCRNL)) |
---|
427 | c = CNL; |
---|
428 | /* Omit carriage returns on column 0. */ |
---|
429 | if (CMP_FLAG(o, ONOCR) && tp->t_column == 0) |
---|
430 | return (0); |
---|
431 | if (PRINT_NORMAL() != 0) |
---|
432 | return (-1); |
---|
433 | |
---|
434 | tp->t_column = tp->t_writepos = 0; |
---|
435 | ttyinq_reprintpos_set(&tp->t_inq); |
---|
436 | return (0); |
---|
437 | } |
---|
438 | |
---|
439 | /* |
---|
440 | * Invisible control character. Print it, but don't |
---|
441 | * increase the column count. |
---|
442 | */ |
---|
443 | return PRINT_NORMAL(); |
---|
444 | #undef PRINT_NORMAL |
---|
445 | } |
---|
446 | |
---|
447 | /* |
---|
448 | * Just like the old TTY implementation, we need to copy data in chunks |
---|
449 | * into a temporary buffer. One of the reasons why we need to do this, |
---|
450 | * is because output processing (only TAB3 though) may allow the buffer |
---|
451 | * to grow eight times. |
---|
452 | */ |
---|
453 | int |
---|
454 | ttydisc_write(struct tty *tp, struct uio *uio, int ioflag) |
---|
455 | { |
---|
456 | char ob[TTY_STACKBUF]; |
---|
457 | char *obstart; |
---|
458 | int error = 0; |
---|
459 | unsigned int oblen = 0; |
---|
460 | |
---|
461 | tty_lock_assert(tp, MA_OWNED); |
---|
462 | |
---|
463 | if (tp->t_flags & TF_ZOMBIE) |
---|
464 | return (EIO); |
---|
465 | |
---|
466 | /* |
---|
467 | * We don't need to check whether the process is the foreground |
---|
468 | * process group or if we have a carrier. This is already done |
---|
469 | * in ttydev_write(). |
---|
470 | */ |
---|
471 | |
---|
472 | while (uio->uio_resid > 0) { |
---|
473 | unsigned int nlen; |
---|
474 | |
---|
475 | MPASS(oblen == 0); |
---|
476 | |
---|
477 | /* Step 1: read data. */ |
---|
478 | obstart = ob; |
---|
479 | nlen = MIN(uio->uio_resid, sizeof ob); |
---|
480 | tty_unlock(tp); |
---|
481 | error = uiomove(ob, nlen, uio); |
---|
482 | tty_lock(tp); |
---|
483 | if (error != 0) |
---|
484 | break; |
---|
485 | oblen = nlen; |
---|
486 | |
---|
487 | if (tty_gone(tp)) { |
---|
488 | error = ENXIO; |
---|
489 | break; |
---|
490 | } |
---|
491 | |
---|
492 | MPASS(oblen > 0); |
---|
493 | |
---|
494 | /* Step 2: process data. */ |
---|
495 | do { |
---|
496 | unsigned int plen, wlen; |
---|
497 | |
---|
498 | /* Search for special characters for post processing. */ |
---|
499 | if (CMP_FLAG(o, OPOST)) { |
---|
500 | plen = ttydisc_findchar(obstart, oblen); |
---|
501 | } else { |
---|
502 | plen = oblen; |
---|
503 | } |
---|
504 | |
---|
505 | if (plen == 0) { |
---|
506 | /* |
---|
507 | * We're going to process a character |
---|
508 | * that needs processing |
---|
509 | */ |
---|
510 | if (ttydisc_write_oproc(tp, *obstart) == 0) { |
---|
511 | obstart++; |
---|
512 | oblen--; |
---|
513 | |
---|
514 | tp->t_writepos = tp->t_column; |
---|
515 | ttyinq_reprintpos_set(&tp->t_inq); |
---|
516 | continue; |
---|
517 | } |
---|
518 | } else { |
---|
519 | /* We're going to write regular data. */ |
---|
520 | wlen = ttyoutq_write(&tp->t_outq, obstart, plen); |
---|
521 | obstart += wlen; |
---|
522 | oblen -= wlen; |
---|
523 | tp->t_column += wlen; |
---|
524 | |
---|
525 | tp->t_writepos = tp->t_column; |
---|
526 | ttyinq_reprintpos_set(&tp->t_inq); |
---|
527 | |
---|
528 | if (wlen == plen) |
---|
529 | continue; |
---|
530 | } |
---|
531 | |
---|
532 | /* Watermark reached. Try to sleep. */ |
---|
533 | tp->t_flags |= TF_HIWAT_OUT; |
---|
534 | |
---|
535 | if (ioflag & IO_NDELAY) { |
---|
536 | error = EWOULDBLOCK; |
---|
537 | goto done; |
---|
538 | } |
---|
539 | |
---|
540 | /* |
---|
541 | * The driver may write back the data |
---|
542 | * synchronously. Be sure to check the high |
---|
543 | * water mark before going to sleep. |
---|
544 | */ |
---|
545 | ttydevsw_outwakeup(tp); |
---|
546 | if ((tp->t_flags & TF_HIWAT_OUT) == 0) |
---|
547 | continue; |
---|
548 | |
---|
549 | error = tty_wait(tp, &tp->t_outwait); |
---|
550 | if (error) |
---|
551 | goto done; |
---|
552 | |
---|
553 | if (tp->t_flags & TF_ZOMBIE) { |
---|
554 | error = EIO; |
---|
555 | goto done; |
---|
556 | } |
---|
557 | } while (oblen > 0); |
---|
558 | } |
---|
559 | |
---|
560 | done: |
---|
561 | if (!tty_gone(tp)) |
---|
562 | ttydevsw_outwakeup(tp); |
---|
563 | |
---|
564 | /* |
---|
565 | * Add the amount of bytes that we didn't process back to the |
---|
566 | * uio counters. We need to do this to make sure write() doesn't |
---|
567 | * count the bytes we didn't store in the queue. |
---|
568 | */ |
---|
569 | uio->uio_resid += oblen; |
---|
570 | return (error); |
---|
571 | } |
---|
572 | |
---|
573 | void |
---|
574 | ttydisc_optimize(struct tty *tp) |
---|
575 | { |
---|
576 | tty_lock_assert(tp, MA_OWNED); |
---|
577 | |
---|
578 | if (ttyhook_hashook(tp, rint_bypass)) { |
---|
579 | tp->t_flags |= TF_BYPASS; |
---|
580 | } else if (ttyhook_hashook(tp, rint)) { |
---|
581 | tp->t_flags &= ~TF_BYPASS; |
---|
582 | } else if (!CMP_FLAG(i, ICRNL|IGNCR|IMAXBEL|INLCR|ISTRIP|IXON) && |
---|
583 | (!CMP_FLAG(i, BRKINT) || CMP_FLAG(i, IGNBRK)) && |
---|
584 | (!CMP_FLAG(i, PARMRK) || |
---|
585 | CMP_FLAG(i, IGNPAR|IGNBRK) == (IGNPAR|IGNBRK)) && |
---|
586 | !CMP_FLAG(l, ECHO|ICANON|IEXTEN|ISIG|PENDIN)) { |
---|
587 | tp->t_flags |= TF_BYPASS; |
---|
588 | } else { |
---|
589 | tp->t_flags &= ~TF_BYPASS; |
---|
590 | } |
---|
591 | } |
---|
592 | |
---|
593 | void |
---|
594 | ttydisc_modem(struct tty *tp, int open) |
---|
595 | { |
---|
596 | |
---|
597 | tty_lock_assert(tp, MA_OWNED); |
---|
598 | |
---|
599 | if (open) |
---|
600 | cv_broadcast(&tp->t_dcdwait); |
---|
601 | |
---|
602 | /* |
---|
603 | * Ignore modem status lines when CLOCAL is turned on, but don't |
---|
604 | * enter the zombie state when the TTY isn't opened, because |
---|
605 | * that would cause the TTY to be in zombie state after being |
---|
606 | * opened. |
---|
607 | */ |
---|
608 | if (!tty_opened(tp) || CMP_FLAG(c, CLOCAL)) |
---|
609 | return; |
---|
610 | |
---|
611 | if (open == 0) { |
---|
612 | /* |
---|
613 | * Lost carrier. |
---|
614 | */ |
---|
615 | tp->t_flags |= TF_ZOMBIE; |
---|
616 | |
---|
617 | tty_signal_sessleader(tp, SIGHUP); |
---|
618 | tty_flush(tp, FREAD|FWRITE); |
---|
619 | } else { |
---|
620 | /* |
---|
621 | * Carrier is back again. |
---|
622 | */ |
---|
623 | |
---|
624 | /* XXX: what should we do here? */ |
---|
625 | } |
---|
626 | } |
---|
627 | |
---|
628 | static int |
---|
629 | ttydisc_echo_force(struct tty *tp, char c, int quote) |
---|
630 | { |
---|
631 | |
---|
632 | if (CMP_FLAG(o, OPOST) && CTL_ECHO(c, quote)) { |
---|
633 | /* |
---|
634 | * Only perform postprocessing when OPOST is turned on |
---|
635 | * and the character is an unquoted BS/TB/NL/CR. |
---|
636 | */ |
---|
637 | return ttydisc_write_oproc(tp, c); |
---|
638 | } else if (CMP_FLAG(l, ECHOCTL) && CTL_PRINT(c, quote)) { |
---|
639 | /* |
---|
640 | * Only use ^X notation when ECHOCTL is turned on and |
---|
641 | * we've got an quoted control character. |
---|
642 | * |
---|
643 | * Print backspaces when echoing an end-of-file. |
---|
644 | */ |
---|
645 | char ob[4] = "^?\b\b"; |
---|
646 | |
---|
647 | /* Print ^X notation. */ |
---|
648 | if (c != 0x7f) |
---|
649 | ob[1] = c + 'A' - 1; |
---|
650 | |
---|
651 | if (!quote && CMP_CC(VEOF, c)) { |
---|
652 | return ttyoutq_write_nofrag(&tp->t_outq, ob, 4); |
---|
653 | } else { |
---|
654 | tp->t_column += 2; |
---|
655 | return ttyoutq_write_nofrag(&tp->t_outq, ob, 2); |
---|
656 | } |
---|
657 | } else { |
---|
658 | /* Can just be printed. */ |
---|
659 | tp->t_column++; |
---|
660 | return ttyoutq_write_nofrag(&tp->t_outq, &c, 1); |
---|
661 | } |
---|
662 | } |
---|
663 | |
---|
664 | static int |
---|
665 | ttydisc_echo(struct tty *tp, char c, int quote) |
---|
666 | { |
---|
667 | |
---|
668 | /* |
---|
669 | * Only echo characters when ECHO is turned on, or ECHONL when |
---|
670 | * the character is an unquoted newline. |
---|
671 | */ |
---|
672 | if (!CMP_FLAG(l, ECHO) && |
---|
673 | (!CMP_FLAG(l, ECHONL) || c != CNL || quote)) |
---|
674 | return (0); |
---|
675 | |
---|
676 | return ttydisc_echo_force(tp, c, quote); |
---|
677 | } |
---|
678 | |
---|
679 | static void |
---|
680 | ttydisc_reprint_char(void *d, char c, int quote) |
---|
681 | { |
---|
682 | struct tty *tp = d; |
---|
683 | |
---|
684 | ttydisc_echo(tp, c, quote); |
---|
685 | } |
---|
686 | |
---|
687 | static void |
---|
688 | ttydisc_reprint(struct tty *tp) |
---|
689 | { |
---|
690 | cc_t c; |
---|
691 | |
---|
692 | /* Print ^R\n, followed by the line. */ |
---|
693 | c = tp->t_termios.c_cc[VREPRINT]; |
---|
694 | if (c != _POSIX_VDISABLE) |
---|
695 | ttydisc_echo(tp, c, 0); |
---|
696 | ttydisc_echo(tp, CNL, 0); |
---|
697 | ttyinq_reprintpos_reset(&tp->t_inq); |
---|
698 | |
---|
699 | ttyinq_line_iterate_from_linestart(&tp->t_inq, ttydisc_reprint_char, tp); |
---|
700 | } |
---|
701 | |
---|
702 | struct ttydisc_recalc_length { |
---|
703 | struct tty *tp; |
---|
704 | unsigned int curlen; |
---|
705 | }; |
---|
706 | |
---|
707 | static void |
---|
708 | ttydisc_recalc_charlength(void *d, char c, int quote) |
---|
709 | { |
---|
710 | struct ttydisc_recalc_length *data = d; |
---|
711 | struct tty *tp = data->tp; |
---|
712 | |
---|
713 | if (CTL_PRINT(c, quote)) { |
---|
714 | if (CMP_FLAG(l, ECHOCTL)) |
---|
715 | data->curlen += 2; |
---|
716 | } else if (c == CTAB) { |
---|
717 | data->curlen += 8 - (data->curlen & 7); |
---|
718 | } else { |
---|
719 | data->curlen++; |
---|
720 | } |
---|
721 | } |
---|
722 | |
---|
723 | static unsigned int |
---|
724 | ttydisc_recalc_linelength(struct tty *tp) |
---|
725 | { |
---|
726 | struct ttydisc_recalc_length data = { tp, tp->t_writepos }; |
---|
727 | |
---|
728 | ttyinq_line_iterate_from_reprintpos(&tp->t_inq, |
---|
729 | ttydisc_recalc_charlength, &data); |
---|
730 | return (data.curlen); |
---|
731 | } |
---|
732 | |
---|
733 | static int |
---|
734 | ttydisc_rubchar(struct tty *tp) |
---|
735 | { |
---|
736 | char c; |
---|
737 | int quote; |
---|
738 | unsigned int prevpos, tablen; |
---|
739 | |
---|
740 | if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0) |
---|
741 | return (-1); |
---|
742 | ttyinq_unputchar(&tp->t_inq); |
---|
743 | |
---|
744 | if (CMP_FLAG(l, ECHO)) { |
---|
745 | /* |
---|
746 | * Remove the character from the screen. This is even |
---|
747 | * safe for characters that span multiple characters |
---|
748 | * (tabs, quoted, etc). |
---|
749 | */ |
---|
750 | if (tp->t_writepos >= tp->t_column) { |
---|
751 | /* Retype the sentence. */ |
---|
752 | ttydisc_reprint(tp); |
---|
753 | } else if (CMP_FLAG(l, ECHOE)) { |
---|
754 | if (CTL_PRINT(c, quote)) { |
---|
755 | /* Remove ^X formatted chars. */ |
---|
756 | if (CMP_FLAG(l, ECHOCTL)) { |
---|
757 | tp->t_column -= 2; |
---|
758 | ttyoutq_write_nofrag(&tp->t_outq, |
---|
759 | "\b\b \b\b", 6); |
---|
760 | } |
---|
761 | } else if (c == ' ') { |
---|
762 | /* Space character needs no rubbing. */ |
---|
763 | tp->t_column -= 1; |
---|
764 | ttyoutq_write_nofrag(&tp->t_outq, "\b", 1); |
---|
765 | } else if (c == CTAB) { |
---|
766 | /* |
---|
767 | * Making backspace work with tabs is |
---|
768 | * quite hard. Recalculate the length of |
---|
769 | * this character and remove it. |
---|
770 | * |
---|
771 | * Because terminal settings could be |
---|
772 | * changed while the line is being |
---|
773 | * inserted, the calculations don't have |
---|
774 | * to be correct. Make sure we keep the |
---|
775 | * tab length within proper bounds. |
---|
776 | */ |
---|
777 | prevpos = ttydisc_recalc_linelength(tp); |
---|
778 | if (prevpos >= tp->t_column) |
---|
779 | tablen = 1; |
---|
780 | else |
---|
781 | tablen = tp->t_column - prevpos; |
---|
782 | if (tablen > 8) |
---|
783 | tablen = 8; |
---|
784 | |
---|
785 | tp->t_column = prevpos; |
---|
786 | ttyoutq_write_nofrag(&tp->t_outq, |
---|
787 | "\b\b\b\b\b\b\b\b", tablen); |
---|
788 | return (0); |
---|
789 | } else { |
---|
790 | /* |
---|
791 | * Remove a regular character by |
---|
792 | * punching a space over it. |
---|
793 | */ |
---|
794 | tp->t_column -= 1; |
---|
795 | ttyoutq_write_nofrag(&tp->t_outq, "\b \b", 3); |
---|
796 | } |
---|
797 | } else { |
---|
798 | /* Don't print spaces. */ |
---|
799 | ttydisc_echo(tp, tp->t_termios.c_cc[VERASE], 0); |
---|
800 | } |
---|
801 | } |
---|
802 | |
---|
803 | return (0); |
---|
804 | } |
---|
805 | |
---|
806 | static void |
---|
807 | ttydisc_rubword(struct tty *tp) |
---|
808 | { |
---|
809 | char c; |
---|
810 | int quote, alnum; |
---|
811 | |
---|
812 | /* Strip whitespace first. */ |
---|
813 | for (;;) { |
---|
814 | if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0) |
---|
815 | return; |
---|
816 | if (!CTL_WHITE(c)) |
---|
817 | break; |
---|
818 | ttydisc_rubchar(tp); |
---|
819 | } |
---|
820 | |
---|
821 | /* |
---|
822 | * Record whether the last character from the previous iteration |
---|
823 | * was alphanumeric or not. We need this to implement ALTWERASE. |
---|
824 | */ |
---|
825 | alnum = CTL_ALNUM(c); |
---|
826 | for (;;) { |
---|
827 | ttydisc_rubchar(tp); |
---|
828 | |
---|
829 | if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0) |
---|
830 | return; |
---|
831 | if (CTL_WHITE(c)) |
---|
832 | return; |
---|
833 | if (CMP_FLAG(l, ALTWERASE) && CTL_ALNUM(c) != alnum) |
---|
834 | return; |
---|
835 | } |
---|
836 | } |
---|
837 | |
---|
838 | int |
---|
839 | ttydisc_rint(struct tty *tp, char c, int flags) |
---|
840 | { |
---|
841 | int signal, quote = 0; |
---|
842 | char ob[3] = { 0xff, 0x00 }; |
---|
843 | size_t ol; |
---|
844 | |
---|
845 | tty_lock_assert(tp, MA_OWNED); |
---|
846 | |
---|
847 | atomic_add_long(&tty_nin, 1); |
---|
848 | |
---|
849 | if (ttyhook_hashook(tp, rint)) |
---|
850 | return ttyhook_rint(tp, c, flags); |
---|
851 | |
---|
852 | if (tp->t_flags & TF_BYPASS) |
---|
853 | goto processed; |
---|
854 | |
---|
855 | if (flags) { |
---|
856 | if (flags & TRE_BREAK) { |
---|
857 | if (CMP_FLAG(i, IGNBRK)) { |
---|
858 | /* Ignore break characters. */ |
---|
859 | return (0); |
---|
860 | } else if (CMP_FLAG(i, BRKINT)) { |
---|
861 | /* Generate SIGINT on break. */ |
---|
862 | tty_flush(tp, FREAD|FWRITE); |
---|
863 | tty_signal_pgrp(tp, SIGINT); |
---|
864 | return (0); |
---|
865 | } else { |
---|
866 | /* Just print it. */ |
---|
867 | goto parmrk; |
---|
868 | } |
---|
869 | } else if (flags & TRE_FRAMING || |
---|
870 | (flags & TRE_PARITY && CMP_FLAG(i, INPCK))) { |
---|
871 | if (CMP_FLAG(i, IGNPAR)) { |
---|
872 | /* Ignore bad characters. */ |
---|
873 | return (0); |
---|
874 | } else { |
---|
875 | /* Just print it. */ |
---|
876 | goto parmrk; |
---|
877 | } |
---|
878 | } |
---|
879 | } |
---|
880 | |
---|
881 | /* Allow any character to perform a wakeup. */ |
---|
882 | if (CMP_FLAG(i, IXANY)) |
---|
883 | tp->t_flags &= ~TF_STOPPED; |
---|
884 | |
---|
885 | /* Remove the top bit. */ |
---|
886 | if (CMP_FLAG(i, ISTRIP)) |
---|
887 | c &= ~0x80; |
---|
888 | |
---|
889 | /* Skip input processing when we want to print it literally. */ |
---|
890 | if (tp->t_flags & TF_LITERAL) { |
---|
891 | tp->t_flags &= ~TF_LITERAL; |
---|
892 | quote = 1; |
---|
893 | goto processed; |
---|
894 | } |
---|
895 | |
---|
896 | /* Special control characters that are implementation dependent. */ |
---|
897 | if (CMP_FLAG(l, IEXTEN)) { |
---|
898 | /* Accept the next character as literal. */ |
---|
899 | if (CMP_CC(VLNEXT, c)) { |
---|
900 | if (CMP_FLAG(l, ECHO)) { |
---|
901 | if (CMP_FLAG(l, ECHOE)) |
---|
902 | ttyoutq_write_nofrag(&tp->t_outq, "^\b", 2); |
---|
903 | else |
---|
904 | ttydisc_echo(tp, c, 0); |
---|
905 | } |
---|
906 | tp->t_flags |= TF_LITERAL; |
---|
907 | return (0); |
---|
908 | } |
---|
909 | } |
---|
910 | |
---|
911 | /* |
---|
912 | * Handle signal processing. |
---|
913 | */ |
---|
914 | if (CMP_FLAG(l, ISIG)) { |
---|
915 | if (CMP_FLAG(l, ICANON|IEXTEN) == (ICANON|IEXTEN)) { |
---|
916 | if (CMP_CC(VSTATUS, c)) { |
---|
917 | #ifndef __rtems__ |
---|
918 | tty_signal_pgrp(tp, SIGINFO); |
---|
919 | #endif /* __rtems__ */ |
---|
920 | return (0); |
---|
921 | } |
---|
922 | } |
---|
923 | |
---|
924 | /* |
---|
925 | * When compared to the old implementation, this |
---|
926 | * implementation also flushes the output queue. POSIX |
---|
927 | * is really brief about this, but does makes us assume |
---|
928 | * we have to do so. |
---|
929 | */ |
---|
930 | signal = 0; |
---|
931 | if (CMP_CC(VINTR, c)) { |
---|
932 | signal = SIGINT; |
---|
933 | } else if (CMP_CC(VQUIT, c)) { |
---|
934 | signal = SIGQUIT; |
---|
935 | } else if (CMP_CC(VSUSP, c)) { |
---|
936 | signal = SIGTSTP; |
---|
937 | } |
---|
938 | |
---|
939 | if (signal != 0) { |
---|
940 | /* |
---|
941 | * Echo the character before signalling the |
---|
942 | * processes. |
---|
943 | */ |
---|
944 | if (!CMP_FLAG(l, NOFLSH)) |
---|
945 | tty_flush(tp, FREAD|FWRITE); |
---|
946 | ttydisc_echo(tp, c, 0); |
---|
947 | tty_signal_pgrp(tp, signal); |
---|
948 | return (0); |
---|
949 | } |
---|
950 | } |
---|
951 | |
---|
952 | /* |
---|
953 | * Handle start/stop characters. |
---|
954 | */ |
---|
955 | if (CMP_FLAG(i, IXON)) { |
---|
956 | if (CMP_CC(VSTOP, c)) { |
---|
957 | /* Stop it if we aren't stopped yet. */ |
---|
958 | if ((tp->t_flags & TF_STOPPED) == 0) { |
---|
959 | tp->t_flags |= TF_STOPPED; |
---|
960 | return (0); |
---|
961 | } |
---|
962 | /* |
---|
963 | * Fallthrough: |
---|
964 | * When VSTART == VSTOP, we should make this key |
---|
965 | * toggle it. |
---|
966 | */ |
---|
967 | if (!CMP_CC(VSTART, c)) |
---|
968 | return (0); |
---|
969 | } |
---|
970 | if (CMP_CC(VSTART, c)) { |
---|
971 | tp->t_flags &= ~TF_STOPPED; |
---|
972 | return (0); |
---|
973 | } |
---|
974 | } |
---|
975 | |
---|
976 | /* Conversion of CR and NL. */ |
---|
977 | switch (c) { |
---|
978 | case CCR: |
---|
979 | if (CMP_FLAG(i, IGNCR)) |
---|
980 | return (0); |
---|
981 | if (CMP_FLAG(i, ICRNL)) |
---|
982 | c = CNL; |
---|
983 | break; |
---|
984 | case CNL: |
---|
985 | if (CMP_FLAG(i, INLCR)) |
---|
986 | c = CCR; |
---|
987 | break; |
---|
988 | } |
---|
989 | |
---|
990 | /* Canonical line editing. */ |
---|
991 | if (CMP_FLAG(l, ICANON)) { |
---|
992 | if (CMP_CC(VERASE, c) || CMP_CC(VERASE2, c)) { |
---|
993 | ttydisc_rubchar(tp); |
---|
994 | return (0); |
---|
995 | } else if (CMP_CC(VKILL, c)) { |
---|
996 | while (ttydisc_rubchar(tp) == 0); |
---|
997 | return (0); |
---|
998 | } else if (CMP_FLAG(l, IEXTEN)) { |
---|
999 | if (CMP_CC(VWERASE, c)) { |
---|
1000 | ttydisc_rubword(tp); |
---|
1001 | return (0); |
---|
1002 | } else if (CMP_CC(VREPRINT, c)) { |
---|
1003 | ttydisc_reprint(tp); |
---|
1004 | return (0); |
---|
1005 | } |
---|
1006 | } |
---|
1007 | } |
---|
1008 | |
---|
1009 | processed: |
---|
1010 | if (CMP_FLAG(i, PARMRK) && (unsigned char)c == 0xff) { |
---|
1011 | /* Print 0xff 0xff. */ |
---|
1012 | ob[1] = 0xff; |
---|
1013 | ol = 2; |
---|
1014 | quote = 1; |
---|
1015 | } else { |
---|
1016 | ob[0] = c; |
---|
1017 | ol = 1; |
---|
1018 | } |
---|
1019 | |
---|
1020 | goto print; |
---|
1021 | |
---|
1022 | parmrk: |
---|
1023 | if (CMP_FLAG(i, PARMRK)) { |
---|
1024 | /* Prepend 0xff 0x00 0x.. */ |
---|
1025 | ob[2] = c; |
---|
1026 | ol = 3; |
---|
1027 | quote = 1; |
---|
1028 | } else { |
---|
1029 | ob[0] = c; |
---|
1030 | ol = 1; |
---|
1031 | } |
---|
1032 | |
---|
1033 | print: |
---|
1034 | /* See if we can store this on the input queue. */ |
---|
1035 | if (ttyinq_write_nofrag(&tp->t_inq, ob, ol, quote) != 0) { |
---|
1036 | if (CMP_FLAG(i, IMAXBEL)) |
---|
1037 | ttyoutq_write_nofrag(&tp->t_outq, "\a", 1); |
---|
1038 | |
---|
1039 | /* |
---|
1040 | * Prevent a deadlock here. It may be possible that a |
---|
1041 | * user has entered so much data, there is no data |
---|
1042 | * available to read(), but the buffers are full anyway. |
---|
1043 | * |
---|
1044 | * Only enter the high watermark if the device driver |
---|
1045 | * can actually transmit something. |
---|
1046 | */ |
---|
1047 | if (ttyinq_bytescanonicalized(&tp->t_inq) == 0) |
---|
1048 | return (0); |
---|
1049 | |
---|
1050 | tty_hiwat_in_block(tp); |
---|
1051 | return (-1); |
---|
1052 | } |
---|
1053 | |
---|
1054 | /* |
---|
1055 | * In raw mode, we canonicalize after receiving a single |
---|
1056 | * character. Otherwise, we canonicalize when we receive a |
---|
1057 | * newline, VEOL or VEOF, but only when it isn't quoted. |
---|
1058 | */ |
---|
1059 | if (!CMP_FLAG(l, ICANON) || |
---|
1060 | (!quote && (c == CNL || CMP_CC(VEOL, c) || CMP_CC(VEOF, c)))) { |
---|
1061 | ttyinq_canonicalize(&tp->t_inq); |
---|
1062 | } |
---|
1063 | |
---|
1064 | ttydisc_echo(tp, c, quote); |
---|
1065 | |
---|
1066 | return (0); |
---|
1067 | } |
---|
1068 | |
---|
1069 | size_t |
---|
1070 | ttydisc_rint_simple(struct tty *tp, const void *buf, size_t len) |
---|
1071 | { |
---|
1072 | const char *cbuf; |
---|
1073 | |
---|
1074 | if (ttydisc_can_bypass(tp)) |
---|
1075 | return (ttydisc_rint_bypass(tp, buf, len)); |
---|
1076 | |
---|
1077 | for (cbuf = buf; len-- > 0; cbuf++) { |
---|
1078 | if (ttydisc_rint(tp, *cbuf, 0) != 0) |
---|
1079 | break; |
---|
1080 | } |
---|
1081 | |
---|
1082 | return (cbuf - (const char *)buf); |
---|
1083 | } |
---|
1084 | |
---|
1085 | size_t |
---|
1086 | ttydisc_rint_bypass(struct tty *tp, const void *buf, size_t len) |
---|
1087 | { |
---|
1088 | size_t ret; |
---|
1089 | |
---|
1090 | tty_lock_assert(tp, MA_OWNED); |
---|
1091 | |
---|
1092 | MPASS(tp->t_flags & TF_BYPASS); |
---|
1093 | |
---|
1094 | atomic_add_long(&tty_nin, len); |
---|
1095 | |
---|
1096 | if (ttyhook_hashook(tp, rint_bypass)) |
---|
1097 | return ttyhook_rint_bypass(tp, buf, len); |
---|
1098 | |
---|
1099 | ret = ttyinq_write(&tp->t_inq, buf, len, 0); |
---|
1100 | ttyinq_canonicalize(&tp->t_inq); |
---|
1101 | if (ret < len) |
---|
1102 | tty_hiwat_in_block(tp); |
---|
1103 | |
---|
1104 | return (ret); |
---|
1105 | } |
---|
1106 | |
---|
1107 | void |
---|
1108 | ttydisc_rint_done(struct tty *tp) |
---|
1109 | { |
---|
1110 | |
---|
1111 | tty_lock_assert(tp, MA_OWNED); |
---|
1112 | |
---|
1113 | if (ttyhook_hashook(tp, rint_done)) |
---|
1114 | ttyhook_rint_done(tp); |
---|
1115 | |
---|
1116 | /* Wake up readers. */ |
---|
1117 | tty_wakeup(tp, FREAD); |
---|
1118 | /* Wake up driver for echo. */ |
---|
1119 | ttydevsw_outwakeup(tp); |
---|
1120 | } |
---|
1121 | |
---|
1122 | size_t |
---|
1123 | ttydisc_rint_poll(struct tty *tp) |
---|
1124 | { |
---|
1125 | size_t l; |
---|
1126 | |
---|
1127 | tty_lock_assert(tp, MA_OWNED); |
---|
1128 | |
---|
1129 | if (ttyhook_hashook(tp, rint_poll)) |
---|
1130 | return ttyhook_rint_poll(tp); |
---|
1131 | |
---|
1132 | /* |
---|
1133 | * XXX: Still allow character input when there's no space in the |
---|
1134 | * buffers, but we haven't entered the high watermark. This is |
---|
1135 | * to allow backspace characters to be inserted when in |
---|
1136 | * canonical mode. |
---|
1137 | */ |
---|
1138 | l = ttyinq_bytesleft(&tp->t_inq); |
---|
1139 | if (l == 0 && (tp->t_flags & TF_HIWAT_IN) == 0) |
---|
1140 | return (1); |
---|
1141 | |
---|
1142 | return (l); |
---|
1143 | } |
---|
1144 | |
---|
1145 | static void |
---|
1146 | ttydisc_wakeup_watermark(struct tty *tp) |
---|
1147 | { |
---|
1148 | size_t c; |
---|
1149 | |
---|
1150 | c = ttyoutq_bytesleft(&tp->t_outq); |
---|
1151 | if (tp->t_flags & TF_HIWAT_OUT) { |
---|
1152 | /* Only allow us to run when we're below the watermark. */ |
---|
1153 | if (c < tp->t_outlow) |
---|
1154 | return; |
---|
1155 | |
---|
1156 | /* Reset the watermark. */ |
---|
1157 | tp->t_flags &= ~TF_HIWAT_OUT; |
---|
1158 | } else { |
---|
1159 | /* Only run when we have data at all. */ |
---|
1160 | if (c == 0) |
---|
1161 | return; |
---|
1162 | } |
---|
1163 | tty_wakeup(tp, FWRITE); |
---|
1164 | } |
---|
1165 | |
---|
1166 | size_t |
---|
1167 | ttydisc_getc(struct tty *tp, void *buf, size_t len) |
---|
1168 | { |
---|
1169 | |
---|
1170 | tty_lock_assert(tp, MA_OWNED); |
---|
1171 | |
---|
1172 | if (tp->t_flags & TF_STOPPED) |
---|
1173 | return (0); |
---|
1174 | |
---|
1175 | if (ttyhook_hashook(tp, getc_inject)) |
---|
1176 | return ttyhook_getc_inject(tp, buf, len); |
---|
1177 | |
---|
1178 | len = ttyoutq_read(&tp->t_outq, buf, len); |
---|
1179 | |
---|
1180 | if (ttyhook_hashook(tp, getc_capture)) |
---|
1181 | ttyhook_getc_capture(tp, buf, len); |
---|
1182 | |
---|
1183 | ttydisc_wakeup_watermark(tp); |
---|
1184 | atomic_add_long(&tty_nout, len); |
---|
1185 | |
---|
1186 | return (len); |
---|
1187 | } |
---|
1188 | |
---|
1189 | int |
---|
1190 | ttydisc_getc_uio(struct tty *tp, struct uio *uio) |
---|
1191 | { |
---|
1192 | int error = 0; |
---|
1193 | ssize_t obytes = uio->uio_resid; |
---|
1194 | size_t len; |
---|
1195 | char buf[TTY_STACKBUF]; |
---|
1196 | |
---|
1197 | tty_lock_assert(tp, MA_OWNED); |
---|
1198 | |
---|
1199 | if (tp->t_flags & TF_STOPPED) |
---|
1200 | return (0); |
---|
1201 | |
---|
1202 | /* |
---|
1203 | * When a TTY hook is attached, we cannot perform unbuffered |
---|
1204 | * copying to userspace. Just call ttydisc_getc() and |
---|
1205 | * temporarily store data in a shadow buffer. |
---|
1206 | */ |
---|
1207 | if (ttyhook_hashook(tp, getc_capture) || |
---|
1208 | ttyhook_hashook(tp, getc_inject)) { |
---|
1209 | while (uio->uio_resid > 0) { |
---|
1210 | /* Read to shadow buffer. */ |
---|
1211 | len = ttydisc_getc(tp, buf, |
---|
1212 | MIN(uio->uio_resid, sizeof buf)); |
---|
1213 | if (len == 0) |
---|
1214 | break; |
---|
1215 | |
---|
1216 | /* Copy to userspace. */ |
---|
1217 | tty_unlock(tp); |
---|
1218 | error = uiomove(buf, len, uio); |
---|
1219 | tty_lock(tp); |
---|
1220 | |
---|
1221 | if (error != 0) |
---|
1222 | break; |
---|
1223 | } |
---|
1224 | } else { |
---|
1225 | error = ttyoutq_read_uio(&tp->t_outq, tp, uio); |
---|
1226 | |
---|
1227 | ttydisc_wakeup_watermark(tp); |
---|
1228 | atomic_add_long(&tty_nout, obytes - uio->uio_resid); |
---|
1229 | } |
---|
1230 | |
---|
1231 | return (error); |
---|
1232 | } |
---|
1233 | |
---|
1234 | size_t |
---|
1235 | ttydisc_getc_poll(struct tty *tp) |
---|
1236 | { |
---|
1237 | |
---|
1238 | tty_lock_assert(tp, MA_OWNED); |
---|
1239 | |
---|
1240 | if (tp->t_flags & TF_STOPPED) |
---|
1241 | return (0); |
---|
1242 | |
---|
1243 | if (ttyhook_hashook(tp, getc_poll)) |
---|
1244 | return ttyhook_getc_poll(tp); |
---|
1245 | |
---|
1246 | return ttyoutq_bytesused(&tp->t_outq); |
---|
1247 | } |
---|
1248 | |
---|
1249 | /* |
---|
1250 | * XXX: not really related to the TTYDISC, but we'd better put |
---|
1251 | * tty_putchar() here, because we need to perform proper output |
---|
1252 | * processing. |
---|
1253 | */ |
---|
1254 | |
---|
1255 | int |
---|
1256 | tty_putchar(struct tty *tp, char c) |
---|
1257 | { |
---|
1258 | tty_lock_assert(tp, MA_OWNED); |
---|
1259 | |
---|
1260 | if (tty_gone(tp)) |
---|
1261 | return (-1); |
---|
1262 | |
---|
1263 | ttydisc_echo_force(tp, c, 0); |
---|
1264 | tp->t_writepos = tp->t_column; |
---|
1265 | ttyinq_reprintpos_set(&tp->t_inq); |
---|
1266 | |
---|
1267 | ttydevsw_outwakeup(tp); |
---|
1268 | return (0); |
---|
1269 | } |
---|