1181905Sed/*- 2181905Sed * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> 3181905Sed * All rights reserved. 4181905Sed * 5181905Sed * Portions of this software were developed under sponsorship from Snow 6181905Sed * B.V., the Netherlands. 7181905Sed * 8181905Sed * Redistribution and use in source and binary forms, with or without 9181905Sed * modification, are permitted provided that the following conditions 10181905Sed * are met: 11181905Sed * 1. Redistributions of source code must retain the above copyright 12181905Sed * notice, this list of conditions and the following disclaimer. 13181905Sed * 2. Redistributions in binary form must reproduce the above copyright 14181905Sed * notice, this list of conditions and the following disclaimer in the 15181905Sed * documentation and/or other materials provided with the distribution. 16181905Sed * 17181905Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18181905Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19181905Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20181905Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21181905Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22181905Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23181905Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24181905Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25181905Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26181905Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27181905Sed * SUCH DAMAGE. 28181905Sed */ 29181905Sed 30181905Sed#include <sys/cdefs.h> 31181905Sed__FBSDID("$FreeBSD$"); 32181905Sed 33181905Sed#include <sys/param.h> 34181905Sed#include <sys/fcntl.h> 35181905Sed#include <sys/filio.h> 36181905Sed#include <sys/kernel.h> 37181905Sed#include <sys/signal.h> 38181905Sed#include <sys/sysctl.h> 39181905Sed#include <sys/systm.h> 40181905Sed#include <sys/tty.h> 41181905Sed#include <sys/ttycom.h> 42181905Sed#include <sys/ttydefaults.h> 43181905Sed#include <sys/uio.h> 44181905Sed#include <sys/vnode.h> 45181905Sed 46181905Sed/* 47181905Sed * Standard TTYDISC `termios' line discipline. 48181905Sed */ 49181905Sed 50181905Sed/* Statistics. */ 51189061Sedstatic unsigned long tty_nin = 0; 52189061SedSYSCTL_ULONG(_kern, OID_AUTO, tty_nin, CTLFLAG_RD, 53181905Sed &tty_nin, 0, "Total amount of bytes received"); 54189061Sedstatic unsigned long tty_nout = 0; 55189061SedSYSCTL_ULONG(_kern, OID_AUTO, tty_nout, CTLFLAG_RD, 56181905Sed &tty_nout, 0, "Total amount of bytes transmitted"); 57181905Sed 58181905Sed/* termios comparison macro's. */ 59181905Sed#define CMP_CC(v,c) (tp->t_termios.c_cc[v] != _POSIX_VDISABLE && \ 60181905Sed tp->t_termios.c_cc[v] == (c)) 61181905Sed#define CMP_FLAG(field,opt) (tp->t_termios.c_ ## field ## flag & (opt)) 62181905Sed 63181905Sed/* Characters that cannot be modified through c_cc. */ 64181905Sed#define CTAB '\t' 65181905Sed#define CNL '\n' 66181905Sed#define CCR '\r' 67181905Sed 68181905Sed/* Character is a control character. */ 69181905Sed#define CTL_VALID(c) ((c) == 0x7f || (unsigned char)(c) < 0x20) 70181905Sed/* Control character should be processed on echo. */ 71181905Sed#define CTL_ECHO(c,q) (!(q) && ((c) == CERASE2 || (c) == CTAB || \ 72181905Sed (c) == CNL || (c) == CCR)) 73181905Sed/* Control character should be printed using ^X notation. */ 74181905Sed#define CTL_PRINT(c,q) ((c) == 0x7f || ((unsigned char)(c) < 0x20 && \ 75181905Sed ((q) || ((c) != CTAB && (c) != CNL)))) 76181905Sed/* Character is whitespace. */ 77181905Sed#define CTL_WHITE(c) ((c) == ' ' || (c) == CTAB) 78181905Sed/* Character is alphanumeric. */ 79181905Sed#define CTL_ALNUM(c) (((c) >= '0' && (c) <= '9') || \ 80181905Sed ((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) 81181905Sed 82183276Sed#define TTY_STACKBUF 256 83183276Sed 84181905Sedvoid 85181905Sedttydisc_open(struct tty *tp) 86181905Sed{ 87181905Sed ttydisc_optimize(tp); 88181905Sed} 89181905Sed 90181905Sedvoid 91181905Sedttydisc_close(struct tty *tp) 92181905Sed{ 93181905Sed 94181905Sed /* Clean up our flags when leaving the discipline. */ 95181905Sed tp->t_flags &= ~(TF_STOPPED|TF_HIWAT|TF_ZOMBIE); 96181905Sed 97181905Sed /* POSIX states we should flush when close() is called. */ 98181905Sed ttyinq_flush(&tp->t_inq); 99181905Sed ttyoutq_flush(&tp->t_outq); 100181905Sed 101181905Sed if (!tty_gone(tp)) { 102181905Sed ttydevsw_inwakeup(tp); 103181905Sed ttydevsw_outwakeup(tp); 104181905Sed } 105183276Sed 106183276Sed if (ttyhook_hashook(tp, close)) 107183276Sed ttyhook_close(tp); 108181905Sed} 109181905Sed 110181905Sedstatic int 111181905Sedttydisc_read_canonical(struct tty *tp, struct uio *uio, int ioflag) 112181905Sed{ 113181905Sed char breakc[4] = { CNL }; /* enough to hold \n, VEOF and VEOL. */ 114181905Sed int error; 115181905Sed size_t clen, flen = 0, n = 1; 116181905Sed unsigned char lastc = _POSIX_VDISABLE; 117181905Sed 118181905Sed#define BREAK_ADD(c) do { \ 119181905Sed if (tp->t_termios.c_cc[c] != _POSIX_VDISABLE) \ 120181905Sed breakc[n++] = tp->t_termios.c_cc[c]; \ 121181905Sed} while (0) 122181905Sed /* Determine which characters we should trigger on. */ 123181905Sed BREAK_ADD(VEOF); 124181905Sed BREAK_ADD(VEOL); 125181905Sed#undef BREAK_ADD 126181905Sed breakc[n] = '\0'; 127181905Sed 128181905Sed do { 129242892Sed error = tty_wait_background(tp, curthread, SIGTTIN); 130242892Sed if (error) 131242892Sed return (error); 132242892Sed 133181905Sed /* 134181905Sed * Quite a tricky case: unlike the old TTY 135181905Sed * implementation, this implementation copies data back 136181905Sed * to userspace in large chunks. Unfortunately, we can't 137181905Sed * calculate the line length on beforehand if it crosses 138181905Sed * ttyinq_block boundaries, because multiple reads could 139181905Sed * then make this code read beyond the newline. 140181905Sed * 141181905Sed * This is why we limit the read to: 142181905Sed * - The size the user has requested 143181905Sed * - The blocksize (done in tty_inq.c) 144181905Sed * - The amount of bytes until the newline 145181905Sed * 146181905Sed * This causes the line length to be recalculated after 147181905Sed * each block has been copied to userspace. This will 148181905Sed * cause the TTY layer to return data in chunks using 149181905Sed * the blocksize (except the first and last blocks). 150181905Sed */ 151181905Sed clen = ttyinq_findchar(&tp->t_inq, breakc, uio->uio_resid, 152181905Sed &lastc); 153181905Sed 154181905Sed /* No more data. */ 155181905Sed if (clen == 0) { 156242892Sed if (tp->t_flags & TF_ZOMBIE) 157242892Sed return (0); 158242892Sed else if (ioflag & IO_NDELAY) 159181905Sed return (EWOULDBLOCK); 160181905Sed 161181905Sed error = tty_wait(tp, &tp->t_inwait); 162181905Sed if (error) 163181905Sed return (error); 164181905Sed continue; 165181905Sed } 166181905Sed 167181905Sed /* Don't send the EOF char back to userspace. */ 168181905Sed if (CMP_CC(VEOF, lastc)) 169181905Sed flen = 1; 170181905Sed 171181905Sed MPASS(flen <= clen); 172181905Sed 173181905Sed /* Read and throw away the EOF character. */ 174181905Sed error = ttyinq_read_uio(&tp->t_inq, tp, uio, clen, flen); 175181905Sed if (error) 176181905Sed return (error); 177181905Sed 178181905Sed } while (uio->uio_resid > 0 && lastc == _POSIX_VDISABLE); 179181905Sed 180181905Sed return (0); 181181905Sed} 182181905Sed 183181905Sedstatic int 184181905Sedttydisc_read_raw_no_timer(struct tty *tp, struct uio *uio, int ioflag) 185181905Sed{ 186181905Sed size_t vmin = tp->t_termios.c_cc[VMIN]; 187233353Skib ssize_t oresid = uio->uio_resid; 188181905Sed int error; 189181905Sed 190181905Sed MPASS(tp->t_termios.c_cc[VTIME] == 0); 191181905Sed 192181905Sed /* 193181905Sed * This routine implements the easy cases of read()s while in 194181905Sed * non-canonical mode, namely case B and D, where we don't have 195181905Sed * any timers at all. 196181905Sed */ 197181905Sed 198181905Sed for (;;) { 199242892Sed error = tty_wait_background(tp, curthread, SIGTTIN); 200242892Sed if (error) 201242892Sed return (error); 202242892Sed 203181905Sed error = ttyinq_read_uio(&tp->t_inq, tp, uio, 204181905Sed uio->uio_resid, 0); 205181905Sed if (error) 206181905Sed return (error); 207181905Sed if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin) 208181905Sed return (0); 209181905Sed 210181905Sed /* We have to wait for more. */ 211242892Sed if (tp->t_flags & TF_ZOMBIE) 212242892Sed return (0); 213242892Sed else if (ioflag & IO_NDELAY) 214181905Sed return (EWOULDBLOCK); 215181905Sed 216181905Sed error = tty_wait(tp, &tp->t_inwait); 217181905Sed if (error) 218181905Sed return (error); 219181905Sed } 220181905Sed} 221181905Sed 222181905Sedstatic int 223181905Sedttydisc_read_raw_read_timer(struct tty *tp, struct uio *uio, int ioflag, 224181905Sed int oresid) 225181905Sed{ 226181905Sed size_t vmin = MAX(tp->t_termios.c_cc[VMIN], 1); 227181905Sed unsigned int vtime = tp->t_termios.c_cc[VTIME]; 228181905Sed struct timeval end, now, left; 229181905Sed int error, hz; 230181905Sed 231181905Sed MPASS(tp->t_termios.c_cc[VTIME] != 0); 232181905Sed 233181905Sed /* Determine when the read should be expired. */ 234181905Sed end.tv_sec = vtime / 10; 235181905Sed end.tv_usec = (vtime % 10) * 100000; 236181905Sed getmicrotime(&now); 237181905Sed timevaladd(&end, &now); 238181905Sed 239181905Sed for (;;) { 240242892Sed error = tty_wait_background(tp, curthread, SIGTTIN); 241242892Sed if (error) 242242892Sed return (error); 243242892Sed 244181905Sed error = ttyinq_read_uio(&tp->t_inq, tp, uio, 245181905Sed uio->uio_resid, 0); 246181905Sed if (error) 247181905Sed return (error); 248181905Sed if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin) 249181905Sed return (0); 250181905Sed 251181905Sed /* Calculate how long we should wait. */ 252181905Sed getmicrotime(&now); 253181905Sed if (timevalcmp(&now, &end, >)) 254181905Sed return (0); 255181905Sed left = end; 256181905Sed timevalsub(&left, &now); 257181905Sed hz = tvtohz(&left); 258181905Sed 259181905Sed /* 260181905Sed * We have to wait for more. If the timer expires, we 261181905Sed * should return a 0-byte read. 262181905Sed */ 263242892Sed if (tp->t_flags & TF_ZOMBIE) 264242892Sed return (0); 265242892Sed else if (ioflag & IO_NDELAY) 266181905Sed return (EWOULDBLOCK); 267181905Sed 268181905Sed error = tty_timedwait(tp, &tp->t_inwait, hz); 269181905Sed if (error) 270181905Sed return (error == EWOULDBLOCK ? 0 : error); 271181905Sed } 272181905Sed 273181905Sed return (0); 274181905Sed} 275181905Sed 276181905Sedstatic int 277181905Sedttydisc_read_raw_interbyte_timer(struct tty *tp, struct uio *uio, int ioflag) 278181905Sed{ 279181905Sed size_t vmin = tp->t_termios.c_cc[VMIN]; 280233353Skib ssize_t oresid = uio->uio_resid; 281181905Sed int error; 282181905Sed 283181905Sed MPASS(tp->t_termios.c_cc[VMIN] != 0); 284181905Sed MPASS(tp->t_termios.c_cc[VTIME] != 0); 285223575Sed 286181905Sed /* 287181905Sed * When using the interbyte timer, the timer should be started 288181905Sed * after the first byte has been received. We just call into the 289181905Sed * generic read timer code after we've received the first byte. 290181905Sed */ 291223575Sed 292181905Sed for (;;) { 293242892Sed error = tty_wait_background(tp, curthread, SIGTTIN); 294242892Sed if (error) 295242892Sed return (error); 296242892Sed 297181905Sed error = ttyinq_read_uio(&tp->t_inq, tp, uio, 298181905Sed uio->uio_resid, 0); 299181905Sed if (error) 300181905Sed return (error); 301181905Sed if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin) 302181905Sed return (0); 303181905Sed 304181905Sed /* 305181905Sed * Not enough data, but we did receive some, which means 306181905Sed * we'll now start using the interbyte timer. 307181905Sed */ 308181905Sed if (oresid != uio->uio_resid) 309181905Sed break; 310181905Sed 311181905Sed /* We have to wait for more. */ 312242892Sed if (tp->t_flags & TF_ZOMBIE) 313242892Sed return (0); 314242892Sed else if (ioflag & IO_NDELAY) 315181905Sed return (EWOULDBLOCK); 316181905Sed 317181905Sed error = tty_wait(tp, &tp->t_inwait); 318181905Sed if (error) 319181905Sed return (error); 320181905Sed } 321181905Sed 322181905Sed return ttydisc_read_raw_read_timer(tp, uio, ioflag, oresid); 323181905Sed} 324181905Sed 325181905Sedint 326181905Sedttydisc_read(struct tty *tp, struct uio *uio, int ioflag) 327181905Sed{ 328181905Sed int error; 329181905Sed 330181905Sed tty_lock_assert(tp, MA_OWNED); 331181905Sed 332181905Sed if (uio->uio_resid == 0) 333181905Sed return (0); 334181905Sed 335181905Sed if (CMP_FLAG(l, ICANON)) 336181905Sed error = ttydisc_read_canonical(tp, uio, ioflag); 337181905Sed else if (tp->t_termios.c_cc[VTIME] == 0) 338181905Sed error = ttydisc_read_raw_no_timer(tp, uio, ioflag); 339181905Sed else if (tp->t_termios.c_cc[VMIN] == 0) 340181905Sed error = ttydisc_read_raw_read_timer(tp, uio, ioflag, 341181905Sed uio->uio_resid); 342181905Sed else 343181905Sed error = ttydisc_read_raw_interbyte_timer(tp, uio, ioflag); 344181905Sed 345182444Sed if (ttyinq_bytesleft(&tp->t_inq) >= tp->t_inlow || 346182444Sed ttyinq_bytescanonicalized(&tp->t_inq) == 0) { 347181905Sed /* Unset the input watermark when we've got enough space. */ 348181905Sed tty_hiwat_in_unblock(tp); 349181905Sed } 350223575Sed 351181905Sed return (error); 352181905Sed} 353181905Sed 354181905Sedstatic __inline unsigned int 355181905Sedttydisc_findchar(const char *obstart, unsigned int oblen) 356181905Sed{ 357181905Sed const char *c = obstart; 358181905Sed 359181905Sed while (oblen--) { 360181905Sed if (CTL_VALID(*c)) 361181905Sed break; 362181905Sed c++; 363181905Sed } 364181905Sed 365181905Sed return (c - obstart); 366181905Sed} 367181905Sed 368181905Sedstatic int 369181905Sedttydisc_write_oproc(struct tty *tp, char c) 370181905Sed{ 371181905Sed unsigned int scnt, error; 372181905Sed 373181905Sed MPASS(CMP_FLAG(o, OPOST)); 374181905Sed MPASS(CTL_VALID(c)); 375181905Sed 376181905Sed#define PRINT_NORMAL() ttyoutq_write_nofrag(&tp->t_outq, &c, 1) 377181905Sed switch (c) { 378181905Sed case CEOF: 379181905Sed /* End-of-text dropping. */ 380181905Sed if (CMP_FLAG(o, ONOEOT)) 381181905Sed return (0); 382181905Sed return PRINT_NORMAL(); 383181905Sed 384181905Sed case CERASE2: 385181905Sed /* Handle backspace to fix tab expansion. */ 386181905Sed if (PRINT_NORMAL() != 0) 387181905Sed return (-1); 388181905Sed if (tp->t_column > 0) 389181905Sed tp->t_column--; 390181905Sed return (0); 391181905Sed 392181905Sed case CTAB: 393181905Sed /* Tab expansion. */ 394181905Sed scnt = 8 - (tp->t_column & 7); 395181905Sed if (CMP_FLAG(o, TAB3)) { 396181905Sed error = ttyoutq_write_nofrag(&tp->t_outq, 397181905Sed " ", scnt); 398181905Sed } else { 399181905Sed error = PRINT_NORMAL(); 400181905Sed } 401181905Sed if (error) 402181905Sed return (-1); 403181905Sed 404181905Sed tp->t_column += scnt; 405181905Sed MPASS((tp->t_column % 8) == 0); 406181905Sed return (0); 407181905Sed 408181905Sed case CNL: 409181905Sed /* Newline conversion. */ 410181905Sed if (CMP_FLAG(o, ONLCR)) { 411181905Sed /* Convert \n to \r\n. */ 412181905Sed error = ttyoutq_write_nofrag(&tp->t_outq, "\r\n", 2); 413181905Sed } else { 414181905Sed error = PRINT_NORMAL(); 415181905Sed } 416181905Sed if (error) 417181905Sed return (-1); 418181905Sed 419181905Sed if (CMP_FLAG(o, ONLCR|ONLRET)) { 420181905Sed tp->t_column = tp->t_writepos = 0; 421181905Sed ttyinq_reprintpos_set(&tp->t_inq); 422181905Sed } 423181905Sed return (0); 424181905Sed 425181905Sed case CCR: 426181905Sed /* Carriage return to newline conversion. */ 427181905Sed if (CMP_FLAG(o, OCRNL)) 428181905Sed c = CNL; 429181905Sed /* Omit carriage returns on column 0. */ 430181905Sed if (CMP_FLAG(o, ONOCR) && tp->t_column == 0) 431181905Sed return (0); 432181905Sed if (PRINT_NORMAL() != 0) 433181905Sed return (-1); 434181905Sed 435181905Sed tp->t_column = tp->t_writepos = 0; 436181905Sed ttyinq_reprintpos_set(&tp->t_inq); 437181905Sed return (0); 438181905Sed } 439181905Sed 440181905Sed /* 441181905Sed * Invisible control character. Print it, but don't 442181905Sed * increase the column count. 443181905Sed */ 444181905Sed return PRINT_NORMAL(); 445181905Sed#undef PRINT_NORMAL 446181905Sed} 447181905Sed 448181905Sed/* 449181905Sed * Just like the old TTY implementation, we need to copy data in chunks 450181905Sed * into a temporary buffer. One of the reasons why we need to do this, 451181905Sed * is because output processing (only TAB3 though) may allow the buffer 452181905Sed * to grow eight times. 453181905Sed */ 454181905Sedint 455181905Sedttydisc_write(struct tty *tp, struct uio *uio, int ioflag) 456181905Sed{ 457183276Sed char ob[TTY_STACKBUF]; 458181905Sed char *obstart; 459181905Sed int error = 0; 460181905Sed unsigned int oblen = 0; 461181905Sed 462181905Sed tty_lock_assert(tp, MA_OWNED); 463181905Sed 464181905Sed if (tp->t_flags & TF_ZOMBIE) 465181905Sed return (EIO); 466181905Sed 467181905Sed /* 468181905Sed * We don't need to check whether the process is the foreground 469181905Sed * process group or if we have a carrier. This is already done 470181905Sed * in ttydev_write(). 471181905Sed */ 472181905Sed 473181905Sed while (uio->uio_resid > 0) { 474181905Sed unsigned int nlen; 475181905Sed 476181905Sed MPASS(oblen == 0); 477181905Sed 478181905Sed /* Step 1: read data. */ 479181905Sed obstart = ob; 480181905Sed nlen = MIN(uio->uio_resid, sizeof ob); 481184866Sed tty_unlock(tp); 482181905Sed error = uiomove(ob, nlen, uio); 483184866Sed tty_lock(tp); 484181905Sed if (error != 0) 485181905Sed break; 486181905Sed oblen = nlen; 487181905Sed 488181905Sed if (tty_gone(tp)) { 489181905Sed error = ENXIO; 490181905Sed break; 491181905Sed } 492181905Sed 493181905Sed MPASS(oblen > 0); 494181905Sed 495181905Sed /* Step 2: process data. */ 496181905Sed do { 497181905Sed unsigned int plen, wlen; 498181905Sed 499181905Sed /* Search for special characters for post processing. */ 500181905Sed if (CMP_FLAG(o, OPOST)) { 501181905Sed plen = ttydisc_findchar(obstart, oblen); 502181905Sed } else { 503181905Sed plen = oblen; 504181905Sed } 505181905Sed 506181905Sed if (plen == 0) { 507181905Sed /* 508181905Sed * We're going to process a character 509181905Sed * that needs processing 510181905Sed */ 511181905Sed if (ttydisc_write_oproc(tp, *obstart) == 0) { 512181905Sed obstart++; 513181905Sed oblen--; 514181905Sed 515181905Sed tp->t_writepos = tp->t_column; 516181905Sed ttyinq_reprintpos_set(&tp->t_inq); 517181905Sed continue; 518181905Sed } 519181905Sed } else { 520181905Sed /* We're going to write regular data. */ 521181905Sed wlen = ttyoutq_write(&tp->t_outq, obstart, plen); 522181905Sed obstart += wlen; 523181905Sed oblen -= wlen; 524181905Sed tp->t_column += wlen; 525181905Sed 526181905Sed tp->t_writepos = tp->t_column; 527181905Sed ttyinq_reprintpos_set(&tp->t_inq); 528181905Sed 529181905Sed if (wlen == plen) 530181905Sed continue; 531181905Sed } 532181905Sed 533181905Sed /* Watermark reached. Try to sleep. */ 534181905Sed tp->t_flags |= TF_HIWAT_OUT; 535181905Sed 536181905Sed if (ioflag & IO_NDELAY) { 537181905Sed error = EWOULDBLOCK; 538181905Sed goto done; 539181905Sed } 540223575Sed 541181905Sed /* 542181905Sed * The driver may write back the data 543181905Sed * synchronously. Be sure to check the high 544181905Sed * water mark before going to sleep. 545181905Sed */ 546181905Sed ttydevsw_outwakeup(tp); 547181905Sed if ((tp->t_flags & TF_HIWAT_OUT) == 0) 548181905Sed continue; 549181905Sed 550181905Sed error = tty_wait(tp, &tp->t_outwait); 551181905Sed if (error) 552181905Sed goto done; 553181905Sed 554181905Sed if (tp->t_flags & TF_ZOMBIE) { 555181905Sed error = EIO; 556181905Sed goto done; 557181905Sed } 558181905Sed } while (oblen > 0); 559181905Sed } 560181905Sed 561182917Scsjpdone: 562182917Scsjp if (!tty_gone(tp)) 563182917Scsjp ttydevsw_outwakeup(tp); 564181905Sed 565181905Sed /* 566181905Sed * Add the amount of bytes that we didn't process back to the 567181905Sed * uio counters. We need to do this to make sure write() doesn't 568181905Sed * count the bytes we didn't store in the queue. 569181905Sed */ 570181905Sed uio->uio_resid += oblen; 571181905Sed return (error); 572181905Sed} 573181905Sed 574181905Sedvoid 575181905Sedttydisc_optimize(struct tty *tp) 576181905Sed{ 577181905Sed tty_lock_assert(tp, MA_OWNED); 578181905Sed 579191893Sed if (ttyhook_hashook(tp, rint_bypass)) { 580191893Sed tp->t_flags |= TF_BYPASS; 581191893Sed } else if (ttyhook_hashook(tp, rint)) { 582191893Sed tp->t_flags &= ~TF_BYPASS; 583191893Sed } else if (!CMP_FLAG(i, ICRNL|IGNCR|IMAXBEL|INLCR|ISTRIP|IXON) && 584181905Sed (!CMP_FLAG(i, BRKINT) || CMP_FLAG(i, IGNBRK)) && 585181905Sed (!CMP_FLAG(i, PARMRK) || 586223575Sed CMP_FLAG(i, IGNPAR|IGNBRK) == (IGNPAR|IGNBRK)) && 587191893Sed !CMP_FLAG(l, ECHO|ICANON|IEXTEN|ISIG|PENDIN)) { 588181905Sed tp->t_flags |= TF_BYPASS; 589181905Sed } else { 590181905Sed tp->t_flags &= ~TF_BYPASS; 591181905Sed } 592181905Sed} 593181905Sed 594181905Sedvoid 595181905Sedttydisc_modem(struct tty *tp, int open) 596181905Sed{ 597181905Sed 598181905Sed tty_lock_assert(tp, MA_OWNED); 599181905Sed 600181905Sed if (open) 601181905Sed cv_broadcast(&tp->t_dcdwait); 602223575Sed 603181905Sed /* 604181905Sed * Ignore modem status lines when CLOCAL is turned on, but don't 605181905Sed * enter the zombie state when the TTY isn't opened, because 606181905Sed * that would cause the TTY to be in zombie state after being 607181905Sed * opened. 608181905Sed */ 609181905Sed if (!tty_opened(tp) || CMP_FLAG(c, CLOCAL)) 610181905Sed return; 611181905Sed 612181905Sed if (open == 0) { 613181905Sed /* 614181905Sed * Lost carrier. 615181905Sed */ 616181905Sed tp->t_flags |= TF_ZOMBIE; 617181905Sed 618181905Sed tty_signal_sessleader(tp, SIGHUP); 619181905Sed tty_flush(tp, FREAD|FWRITE); 620181905Sed } else { 621181905Sed /* 622181905Sed * Carrier is back again. 623181905Sed */ 624181905Sed 625181905Sed /* XXX: what should we do here? */ 626181905Sed } 627181905Sed} 628181905Sed 629181905Sedstatic int 630181905Sedttydisc_echo_force(struct tty *tp, char c, int quote) 631181905Sed{ 632181905Sed 633181905Sed if (CMP_FLAG(o, OPOST) && CTL_ECHO(c, quote)) { 634181905Sed /* 635181905Sed * Only perform postprocessing when OPOST is turned on 636181905Sed * and the character is an unquoted BS/TB/NL/CR. 637181905Sed */ 638181905Sed return ttydisc_write_oproc(tp, c); 639181905Sed } else if (CMP_FLAG(l, ECHOCTL) && CTL_PRINT(c, quote)) { 640181905Sed /* 641181905Sed * Only use ^X notation when ECHOCTL is turned on and 642181905Sed * we've got an quoted control character. 643198185Sed * 644198185Sed * Print backspaces when echoing an end-of-file. 645181905Sed */ 646198185Sed char ob[4] = "^?\b\b"; 647181905Sed 648181905Sed /* Print ^X notation. */ 649181905Sed if (c != 0x7f) 650181905Sed ob[1] = c + 'A' - 1; 651181905Sed 652198185Sed if (!quote && CMP_CC(VEOF, c)) { 653198185Sed return ttyoutq_write_nofrag(&tp->t_outq, ob, 4); 654198185Sed } else { 655198185Sed tp->t_column += 2; 656198185Sed return ttyoutq_write_nofrag(&tp->t_outq, ob, 2); 657198185Sed } 658181905Sed } else { 659181905Sed /* Can just be printed. */ 660181905Sed tp->t_column++; 661181905Sed return ttyoutq_write_nofrag(&tp->t_outq, &c, 1); 662181905Sed } 663181905Sed} 664181905Sed 665181905Sedstatic int 666181905Sedttydisc_echo(struct tty *tp, char c, int quote) 667181905Sed{ 668181905Sed 669181905Sed /* 670181905Sed * Only echo characters when ECHO is turned on, or ECHONL when 671181905Sed * the character is an unquoted newline. 672181905Sed */ 673181905Sed if (!CMP_FLAG(l, ECHO) && 674181905Sed (!CMP_FLAG(l, ECHONL) || c != CNL || quote)) 675181905Sed return (0); 676181905Sed 677181905Sed return ttydisc_echo_force(tp, c, quote); 678181905Sed} 679181905Sed 680181905Sed 681181905Sedstatic void 682181905Sedttydisc_reprint_char(void *d, char c, int quote) 683181905Sed{ 684181905Sed struct tty *tp = d; 685181905Sed 686181905Sed ttydisc_echo(tp, c, quote); 687181905Sed} 688181905Sed 689181905Sedstatic void 690181905Sedttydisc_reprint(struct tty *tp) 691181905Sed{ 692181905Sed cc_t c; 693181905Sed 694181905Sed /* Print ^R\n, followed by the line. */ 695181905Sed c = tp->t_termios.c_cc[VREPRINT]; 696181905Sed if (c != _POSIX_VDISABLE) 697181905Sed ttydisc_echo(tp, c, 0); 698181905Sed ttydisc_echo(tp, CNL, 0); 699181905Sed ttyinq_reprintpos_reset(&tp->t_inq); 700181905Sed 701181905Sed ttyinq_line_iterate_from_linestart(&tp->t_inq, ttydisc_reprint_char, tp); 702181905Sed} 703181905Sed 704181905Sedstruct ttydisc_recalc_length { 705181905Sed struct tty *tp; 706181905Sed unsigned int curlen; 707181905Sed}; 708181905Sed 709181905Sedstatic void 710181905Sedttydisc_recalc_charlength(void *d, char c, int quote) 711181905Sed{ 712181905Sed struct ttydisc_recalc_length *data = d; 713181905Sed struct tty *tp = data->tp; 714181905Sed 715181905Sed if (CTL_PRINT(c, quote)) { 716181905Sed if (CMP_FLAG(l, ECHOCTL)) 717181905Sed data->curlen += 2; 718181905Sed } else if (c == CTAB) { 719181905Sed data->curlen += 8 - (data->curlen & 7); 720181905Sed } else { 721181905Sed data->curlen++; 722181905Sed } 723181905Sed} 724181905Sed 725181905Sedstatic unsigned int 726181905Sedttydisc_recalc_linelength(struct tty *tp) 727181905Sed{ 728181905Sed struct ttydisc_recalc_length data = { tp, tp->t_writepos }; 729181905Sed 730181905Sed ttyinq_line_iterate_from_reprintpos(&tp->t_inq, 731181905Sed ttydisc_recalc_charlength, &data); 732181905Sed return (data.curlen); 733181905Sed} 734181905Sed 735181905Sedstatic int 736181905Sedttydisc_rubchar(struct tty *tp) 737181905Sed{ 738181905Sed char c; 739181905Sed int quote; 740181905Sed unsigned int prevpos, tablen; 741181905Sed 742181905Sed if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0) 743181905Sed return (-1); 744181905Sed ttyinq_unputchar(&tp->t_inq); 745181905Sed 746181905Sed if (CMP_FLAG(l, ECHO)) { 747181905Sed /* 748181905Sed * Remove the character from the screen. This is even 749181905Sed * safe for characters that span multiple characters 750181905Sed * (tabs, quoted, etc). 751181905Sed */ 752181905Sed if (tp->t_writepos >= tp->t_column) { 753181905Sed /* Retype the sentence. */ 754181905Sed ttydisc_reprint(tp); 755181905Sed } else if (CMP_FLAG(l, ECHOE)) { 756181905Sed if (CTL_PRINT(c, quote)) { 757181905Sed /* Remove ^X formatted chars. */ 758181905Sed if (CMP_FLAG(l, ECHOCTL)) { 759181905Sed tp->t_column -= 2; 760181905Sed ttyoutq_write_nofrag(&tp->t_outq, 761181905Sed "\b\b \b\b", 6); 762181905Sed } 763181905Sed } else if (c == ' ') { 764181905Sed /* Space character needs no rubbing. */ 765181905Sed tp->t_column -= 1; 766181905Sed ttyoutq_write_nofrag(&tp->t_outq, "\b", 1); 767181905Sed } else if (c == CTAB) { 768181905Sed /* 769181905Sed * Making backspace work with tabs is 770181905Sed * quite hard. Recalculate the length of 771181905Sed * this character and remove it. 772181905Sed * 773181905Sed * Because terminal settings could be 774181905Sed * changed while the line is being 775181905Sed * inserted, the calculations don't have 776181905Sed * to be correct. Make sure we keep the 777181905Sed * tab length within proper bounds. 778181905Sed */ 779181905Sed prevpos = ttydisc_recalc_linelength(tp); 780181905Sed if (prevpos >= tp->t_column) 781181905Sed tablen = 1; 782181905Sed else 783181905Sed tablen = tp->t_column - prevpos; 784181905Sed if (tablen > 8) 785181905Sed tablen = 8; 786181905Sed 787181905Sed tp->t_column = prevpos; 788181905Sed ttyoutq_write_nofrag(&tp->t_outq, 789181905Sed "\b\b\b\b\b\b\b\b", tablen); 790181905Sed return (0); 791181905Sed } else { 792181905Sed /* 793181905Sed * Remove a regular character by 794181905Sed * punching a space over it. 795181905Sed */ 796181905Sed tp->t_column -= 1; 797181905Sed ttyoutq_write_nofrag(&tp->t_outq, "\b \b", 3); 798181905Sed } 799181905Sed } else { 800181905Sed /* Don't print spaces. */ 801181905Sed ttydisc_echo(tp, tp->t_termios.c_cc[VERASE], 0); 802181905Sed } 803181905Sed } 804181905Sed 805181905Sed return (0); 806181905Sed} 807181905Sed 808181905Sedstatic void 809181905Sedttydisc_rubword(struct tty *tp) 810181905Sed{ 811181905Sed char c; 812181905Sed int quote, alnum; 813181905Sed 814181905Sed /* Strip whitespace first. */ 815181905Sed for (;;) { 816181905Sed if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0) 817181905Sed return; 818181905Sed if (!CTL_WHITE(c)) 819181905Sed break; 820181905Sed ttydisc_rubchar(tp); 821181905Sed } 822181905Sed 823181905Sed /* 824181905Sed * Record whether the last character from the previous iteration 825181905Sed * was alphanumeric or not. We need this to implement ALTWERASE. 826181905Sed */ 827181905Sed alnum = CTL_ALNUM(c); 828181905Sed for (;;) { 829181905Sed ttydisc_rubchar(tp); 830181905Sed 831181905Sed if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0) 832181905Sed return; 833181905Sed if (CTL_WHITE(c)) 834181905Sed return; 835181905Sed if (CMP_FLAG(l, ALTWERASE) && CTL_ALNUM(c) != alnum) 836181905Sed return; 837181905Sed } 838181905Sed} 839181905Sed 840181905Sedint 841181905Sedttydisc_rint(struct tty *tp, char c, int flags) 842181905Sed{ 843181905Sed int signal, quote = 0; 844181905Sed char ob[3] = { 0xff, 0x00 }; 845181905Sed size_t ol; 846181905Sed 847181905Sed tty_lock_assert(tp, MA_OWNED); 848181905Sed 849181905Sed atomic_add_long(&tty_nin, 1); 850183276Sed 851183276Sed if (ttyhook_hashook(tp, rint)) 852183276Sed return ttyhook_rint(tp, c, flags); 853223575Sed 854181905Sed if (tp->t_flags & TF_BYPASS) 855181905Sed goto processed; 856181905Sed 857181905Sed if (flags) { 858181905Sed if (flags & TRE_BREAK) { 859181905Sed if (CMP_FLAG(i, IGNBRK)) { 860181905Sed /* Ignore break characters. */ 861181905Sed return (0); 862181905Sed } else if (CMP_FLAG(i, BRKINT)) { 863181905Sed /* Generate SIGINT on break. */ 864181905Sed tty_flush(tp, FREAD|FWRITE); 865181905Sed tty_signal_pgrp(tp, SIGINT); 866181905Sed return (0); 867181905Sed } else { 868181905Sed /* Just print it. */ 869181905Sed goto parmrk; 870181905Sed } 871181905Sed } else if (flags & TRE_FRAMING || 872181905Sed (flags & TRE_PARITY && CMP_FLAG(i, INPCK))) { 873181905Sed if (CMP_FLAG(i, IGNPAR)) { 874181905Sed /* Ignore bad characters. */ 875181905Sed return (0); 876181905Sed } else { 877181905Sed /* Just print it. */ 878181905Sed goto parmrk; 879181905Sed } 880181905Sed } 881181905Sed } 882181905Sed 883181905Sed /* Allow any character to perform a wakeup. */ 884181905Sed if (CMP_FLAG(i, IXANY)) 885181905Sed tp->t_flags &= ~TF_STOPPED; 886181905Sed 887181905Sed /* Remove the top bit. */ 888181905Sed if (CMP_FLAG(i, ISTRIP)) 889181905Sed c &= ~0x80; 890181905Sed 891181905Sed /* Skip input processing when we want to print it literally. */ 892181905Sed if (tp->t_flags & TF_LITERAL) { 893181905Sed tp->t_flags &= ~TF_LITERAL; 894181905Sed quote = 1; 895181905Sed goto processed; 896181905Sed } 897181905Sed 898181905Sed /* Special control characters that are implementation dependent. */ 899181905Sed if (CMP_FLAG(l, IEXTEN)) { 900181905Sed /* Accept the next character as literal. */ 901181905Sed if (CMP_CC(VLNEXT, c)) { 902181905Sed if (CMP_FLAG(l, ECHO)) { 903181905Sed if (CMP_FLAG(l, ECHOE)) 904181905Sed ttyoutq_write_nofrag(&tp->t_outq, "^\b", 2); 905181905Sed else 906181905Sed ttydisc_echo(tp, c, 0); 907181905Sed } 908181905Sed tp->t_flags |= TF_LITERAL; 909181905Sed return (0); 910181905Sed } 911181905Sed } 912181905Sed 913181905Sed /* 914181905Sed * Handle signal processing. 915181905Sed */ 916181905Sed if (CMP_FLAG(l, ISIG)) { 917181905Sed if (CMP_FLAG(l, ICANON|IEXTEN) == (ICANON|IEXTEN)) { 918181905Sed if (CMP_CC(VSTATUS, c)) { 919181905Sed tty_signal_pgrp(tp, SIGINFO); 920181905Sed return (0); 921181905Sed } 922181905Sed } 923181905Sed 924181905Sed /* 925181905Sed * When compared to the old implementation, this 926181905Sed * implementation also flushes the output queue. POSIX 927181905Sed * is really brief about this, but does makes us assume 928181905Sed * we have to do so. 929181905Sed */ 930181905Sed signal = 0; 931181905Sed if (CMP_CC(VINTR, c)) { 932181905Sed signal = SIGINT; 933181905Sed } else if (CMP_CC(VQUIT, c)) { 934181905Sed signal = SIGQUIT; 935181905Sed } else if (CMP_CC(VSUSP, c)) { 936181905Sed signal = SIGTSTP; 937181905Sed } 938181905Sed 939181905Sed if (signal != 0) { 940181905Sed /* 941181905Sed * Echo the character before signalling the 942181905Sed * processes. 943181905Sed */ 944181905Sed if (!CMP_FLAG(l, NOFLSH)) 945181905Sed tty_flush(tp, FREAD|FWRITE); 946181905Sed ttydisc_echo(tp, c, 0); 947181905Sed tty_signal_pgrp(tp, signal); 948181905Sed return (0); 949181905Sed } 950181905Sed } 951181905Sed 952181905Sed /* 953181905Sed * Handle start/stop characters. 954181905Sed */ 955181905Sed if (CMP_FLAG(i, IXON)) { 956181905Sed if (CMP_CC(VSTOP, c)) { 957181905Sed /* Stop it if we aren't stopped yet. */ 958181905Sed if ((tp->t_flags & TF_STOPPED) == 0) { 959181905Sed tp->t_flags |= TF_STOPPED; 960181905Sed return (0); 961181905Sed } 962181905Sed /* 963181905Sed * Fallthrough: 964181905Sed * When VSTART == VSTOP, we should make this key 965181905Sed * toggle it. 966181905Sed */ 967181905Sed if (!CMP_CC(VSTART, c)) 968181905Sed return (0); 969181905Sed } 970181905Sed if (CMP_CC(VSTART, c)) { 971181905Sed tp->t_flags &= ~TF_STOPPED; 972181905Sed return (0); 973181905Sed } 974181905Sed } 975181905Sed 976181905Sed /* Conversion of CR and NL. */ 977181905Sed switch (c) { 978181905Sed case CCR: 979181905Sed if (CMP_FLAG(i, IGNCR)) 980181905Sed return (0); 981181905Sed if (CMP_FLAG(i, ICRNL)) 982181905Sed c = CNL; 983181905Sed break; 984181905Sed case CNL: 985181905Sed if (CMP_FLAG(i, INLCR)) 986181905Sed c = CCR; 987181905Sed break; 988181905Sed } 989181905Sed 990181905Sed /* Canonical line editing. */ 991181905Sed if (CMP_FLAG(l, ICANON)) { 992181905Sed if (CMP_CC(VERASE, c) || CMP_CC(VERASE2, c)) { 993181905Sed ttydisc_rubchar(tp); 994181905Sed return (0); 995181905Sed } else if (CMP_CC(VKILL, c)) { 996181905Sed while (ttydisc_rubchar(tp) == 0); 997181905Sed return (0); 998181905Sed } else if (CMP_FLAG(l, IEXTEN)) { 999181905Sed if (CMP_CC(VWERASE, c)) { 1000181905Sed ttydisc_rubword(tp); 1001181905Sed return (0); 1002181905Sed } else if (CMP_CC(VREPRINT, c)) { 1003181905Sed ttydisc_reprint(tp); 1004181905Sed return (0); 1005181905Sed } 1006181905Sed } 1007181905Sed } 1008181905Sed 1009181905Sedprocessed: 1010181905Sed if (CMP_FLAG(i, PARMRK) && (unsigned char)c == 0xff) { 1011181905Sed /* Print 0xff 0xff. */ 1012181905Sed ob[1] = 0xff; 1013181905Sed ol = 2; 1014181905Sed quote = 1; 1015181905Sed } else { 1016181905Sed ob[0] = c; 1017181905Sed ol = 1; 1018181905Sed } 1019181905Sed 1020181905Sed goto print; 1021181905Sed 1022181905Sedparmrk: 1023181905Sed if (CMP_FLAG(i, PARMRK)) { 1024181905Sed /* Prepend 0xff 0x00 0x.. */ 1025181905Sed ob[2] = c; 1026181905Sed ol = 3; 1027181905Sed quote = 1; 1028181905Sed } else { 1029181905Sed ob[0] = c; 1030181905Sed ol = 1; 1031181905Sed } 1032181905Sed 1033181905Sedprint: 1034181905Sed /* See if we can store this on the input queue. */ 1035181905Sed if (ttyinq_write_nofrag(&tp->t_inq, ob, ol, quote) != 0) { 1036182444Sed if (CMP_FLAG(i, IMAXBEL)) 1037182444Sed ttyoutq_write_nofrag(&tp->t_outq, "\a", 1); 1038182444Sed 1039182444Sed /* 1040182444Sed * Prevent a deadlock here. It may be possible that a 1041182444Sed * user has entered so much data, there is no data 1042182444Sed * available to read(), but the buffers are full anyway. 1043182444Sed * 1044182444Sed * Only enter the high watermark if the device driver 1045182444Sed * can actually transmit something. 1046182444Sed */ 1047182444Sed if (ttyinq_bytescanonicalized(&tp->t_inq) == 0) 1048182444Sed return (0); 1049182444Sed 1050181905Sed tty_hiwat_in_block(tp); 1051181905Sed return (-1); 1052181905Sed } 1053181905Sed 1054181905Sed /* 1055181905Sed * In raw mode, we canonicalize after receiving a single 1056181905Sed * character. Otherwise, we canonicalize when we receive a 1057181905Sed * newline, VEOL or VEOF, but only when it isn't quoted. 1058181905Sed */ 1059181905Sed if (!CMP_FLAG(l, ICANON) || 1060181905Sed (!quote && (c == CNL || CMP_CC(VEOL, c) || CMP_CC(VEOF, c)))) { 1061181905Sed ttyinq_canonicalize(&tp->t_inq); 1062181905Sed } 1063181905Sed 1064181905Sed ttydisc_echo(tp, c, quote); 1065181905Sed 1066181905Sed return (0); 1067181905Sed} 1068181905Sed 1069181905Sedsize_t 1070196452Sedttydisc_rint_simple(struct tty *tp, const void *buf, size_t len) 1071196452Sed{ 1072196452Sed const char *cbuf; 1073196452Sed 1074196452Sed if (ttydisc_can_bypass(tp)) 1075196452Sed return (ttydisc_rint_bypass(tp, buf, len)); 1076196452Sed 1077196452Sed for (cbuf = buf; len-- > 0; cbuf++) { 1078196452Sed if (ttydisc_rint(tp, *cbuf, 0) != 0) 1079196452Sed break; 1080196452Sed } 1081196452Sed 1082196452Sed return (cbuf - (const char *)buf); 1083196452Sed} 1084196452Sed 1085196452Sedsize_t 1086183274Sedttydisc_rint_bypass(struct tty *tp, const void *buf, size_t len) 1087181905Sed{ 1088181905Sed size_t ret; 1089181905Sed 1090181905Sed tty_lock_assert(tp, MA_OWNED); 1091223575Sed 1092181905Sed MPASS(tp->t_flags & TF_BYPASS); 1093181905Sed 1094181905Sed atomic_add_long(&tty_nin, len); 1095181905Sed 1096183276Sed if (ttyhook_hashook(tp, rint_bypass)) 1097183276Sed return ttyhook_rint_bypass(tp, buf, len); 1098183276Sed 1099181905Sed ret = ttyinq_write(&tp->t_inq, buf, len, 0); 1100181905Sed ttyinq_canonicalize(&tp->t_inq); 1101196036Sed if (ret < len) 1102196036Sed tty_hiwat_in_block(tp); 1103181905Sed 1104181905Sed return (ret); 1105181905Sed} 1106181905Sed 1107181905Sedvoid 1108181905Sedttydisc_rint_done(struct tty *tp) 1109181905Sed{ 1110181905Sed 1111181905Sed tty_lock_assert(tp, MA_OWNED); 1112181905Sed 1113183276Sed if (ttyhook_hashook(tp, rint_done)) 1114183276Sed ttyhook_rint_done(tp); 1115183276Sed 1116181905Sed /* Wake up readers. */ 1117181905Sed tty_wakeup(tp, FREAD); 1118181905Sed /* Wake up driver for echo. */ 1119181905Sed ttydevsw_outwakeup(tp); 1120181905Sed} 1121181905Sed 1122183276Sedsize_t 1123183276Sedttydisc_rint_poll(struct tty *tp) 1124183276Sed{ 1125183276Sed size_t l; 1126183276Sed 1127183276Sed tty_lock_assert(tp, MA_OWNED); 1128183276Sed 1129183276Sed if (ttyhook_hashook(tp, rint_poll)) 1130183276Sed return ttyhook_rint_poll(tp); 1131183276Sed 1132183276Sed /* 1133183276Sed * XXX: Still allow character input when there's no space in the 1134183276Sed * buffers, but we haven't entered the high watermark. This is 1135183276Sed * to allow backspace characters to be inserted when in 1136183276Sed * canonical mode. 1137183276Sed */ 1138183276Sed l = ttyinq_bytesleft(&tp->t_inq); 1139183276Sed if (l == 0 && (tp->t_flags & TF_HIWAT_IN) == 0) 1140183276Sed return (1); 1141223575Sed 1142183276Sed return (l); 1143183276Sed} 1144183276Sed 1145181905Sedstatic void 1146181905Sedttydisc_wakeup_watermark(struct tty *tp) 1147181905Sed{ 1148181905Sed size_t c; 1149181905Sed 1150181905Sed c = ttyoutq_bytesleft(&tp->t_outq); 1151181905Sed if (tp->t_flags & TF_HIWAT_OUT) { 1152181905Sed /* Only allow us to run when we're below the watermark. */ 1153181905Sed if (c < tp->t_outlow) 1154181905Sed return; 1155181905Sed 1156181905Sed /* Reset the watermark. */ 1157181905Sed tp->t_flags &= ~TF_HIWAT_OUT; 1158181905Sed } else { 1159181905Sed /* Only run when we have data at all. */ 1160181905Sed if (c == 0) 1161181905Sed return; 1162181905Sed } 1163181905Sed tty_wakeup(tp, FWRITE); 1164181905Sed} 1165181905Sed 1166181905Sedsize_t 1167181905Sedttydisc_getc(struct tty *tp, void *buf, size_t len) 1168181905Sed{ 1169181905Sed 1170181905Sed tty_lock_assert(tp, MA_OWNED); 1171181905Sed 1172181905Sed if (tp->t_flags & TF_STOPPED) 1173181905Sed return (0); 1174181905Sed 1175183276Sed if (ttyhook_hashook(tp, getc_inject)) 1176183276Sed return ttyhook_getc_inject(tp, buf, len); 1177183276Sed 1178182683Sed len = ttyoutq_read(&tp->t_outq, buf, len); 1179183276Sed 1180183276Sed if (ttyhook_hashook(tp, getc_capture)) 1181183276Sed ttyhook_getc_capture(tp, buf, len); 1182183276Sed 1183181905Sed ttydisc_wakeup_watermark(tp); 1184182683Sed atomic_add_long(&tty_nout, len); 1185181905Sed 1186182683Sed return (len); 1187181905Sed} 1188181905Sed 1189181905Sedint 1190181905Sedttydisc_getc_uio(struct tty *tp, struct uio *uio) 1191181905Sed{ 1192183276Sed int error = 0; 1193233353Skib ssize_t obytes = uio->uio_resid; 1194183276Sed size_t len; 1195183276Sed char buf[TTY_STACKBUF]; 1196181905Sed 1197181905Sed tty_lock_assert(tp, MA_OWNED); 1198181905Sed 1199181905Sed if (tp->t_flags & TF_STOPPED) 1200181905Sed return (0); 1201181905Sed 1202183276Sed /* 1203183276Sed * When a TTY hook is attached, we cannot perform unbuffered 1204183276Sed * copying to userspace. Just call ttydisc_getc() and 1205183276Sed * temporarily store data in a shadow buffer. 1206183276Sed */ 1207183276Sed if (ttyhook_hashook(tp, getc_capture) || 1208183276Sed ttyhook_hashook(tp, getc_inject)) { 1209183276Sed while (uio->uio_resid > 0) { 1210183276Sed /* Read to shadow buffer. */ 1211183276Sed len = ttydisc_getc(tp, buf, 1212183276Sed MIN(uio->uio_resid, sizeof buf)); 1213183276Sed if (len == 0) 1214183276Sed break; 1215181905Sed 1216183276Sed /* Copy to userspace. */ 1217183276Sed tty_unlock(tp); 1218183276Sed error = uiomove(buf, len, uio); 1219183276Sed tty_lock(tp); 1220223575Sed 1221183276Sed if (error != 0) 1222183276Sed break; 1223183276Sed } 1224183276Sed } else { 1225183276Sed error = ttyoutq_read_uio(&tp->t_outq, tp, uio); 1226181905Sed 1227183276Sed ttydisc_wakeup_watermark(tp); 1228183276Sed atomic_add_long(&tty_nout, obytes - uio->uio_resid); 1229183276Sed } 1230183276Sed 1231181905Sed return (error); 1232181905Sed} 1233181905Sed 1234183276Sedsize_t 1235183276Sedttydisc_getc_poll(struct tty *tp) 1236183276Sed{ 1237183276Sed 1238183276Sed tty_lock_assert(tp, MA_OWNED); 1239183276Sed 1240183276Sed if (tp->t_flags & TF_STOPPED) 1241183276Sed return (0); 1242183276Sed 1243183276Sed if (ttyhook_hashook(tp, getc_poll)) 1244183276Sed return ttyhook_getc_poll(tp); 1245183276Sed 1246183276Sed return ttyoutq_bytesused(&tp->t_outq); 1247183276Sed} 1248183276Sed 1249181905Sed/* 1250181905Sed * XXX: not really related to the TTYDISC, but we'd better put 1251181905Sed * tty_putchar() here, because we need to perform proper output 1252181905Sed * processing. 1253181905Sed */ 1254181905Sed 1255181905Sedint 1256181905Sedtty_putchar(struct tty *tp, char c) 1257181905Sed{ 1258181905Sed tty_lock_assert(tp, MA_OWNED); 1259181905Sed 1260181905Sed if (tty_gone(tp)) 1261181905Sed return (-1); 1262181905Sed 1263181905Sed ttydisc_echo_force(tp, c, 0); 1264181905Sed tp->t_writepos = tp->t_column; 1265181905Sed ttyinq_reprintpos_set(&tp->t_inq); 1266181905Sed 1267181905Sed ttydevsw_outwakeup(tp); 1268181905Sed return (0); 1269181905Sed} 1270