1 1.20 dholland /* $NetBSD: ttycons.c,v 1.20 2014/07/25 08:10:35 dholland Exp $ */ 2 1.1 jmcneill 3 1.1 jmcneill /*- 4 1.1 jmcneill * Copyright (c) 2007 Jared D. McNeill <jmcneill (at) invisible.ca> 5 1.1 jmcneill * All rights reserved. 6 1.1 jmcneill * 7 1.1 jmcneill * Redistribution and use in source and binary forms, with or without 8 1.1 jmcneill * modification, are permitted provided that the following conditions 9 1.1 jmcneill * are met: 10 1.1 jmcneill * 1. Redistributions of source code must retain the above copyright 11 1.1 jmcneill * notice, this list of conditions and the following disclaimer. 12 1.1 jmcneill * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 jmcneill * notice, this list of conditions and the following disclaimer in the 14 1.1 jmcneill * documentation and/or other materials provided with the distribution. 15 1.1 jmcneill * 16 1.1 jmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 1.1 jmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 1.1 jmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 1.1 jmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 1.1 jmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 1.1 jmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 1.1 jmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 1.1 jmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 1.1 jmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 1.1 jmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 1.1 jmcneill * POSSIBILITY OF SUCH DAMAGE. 27 1.1 jmcneill */ 28 1.1 jmcneill 29 1.1 jmcneill #include <sys/cdefs.h> 30 1.20 dholland __KERNEL_RCSID(0, "$NetBSD: ttycons.c,v 1.20 2014/07/25 08:10:35 dholland Exp $"); 31 1.1 jmcneill 32 1.1 jmcneill #include <sys/param.h> 33 1.8 jmcneill #include <sys/conf.h> 34 1.1 jmcneill #include <sys/proc.h> 35 1.1 jmcneill #include <sys/systm.h> 36 1.1 jmcneill #include <sys/device.h> 37 1.8 jmcneill #include <sys/kauth.h> 38 1.5 jmcneill #include <sys/termios.h> 39 1.8 jmcneill #include <sys/tty.h> 40 1.1 jmcneill 41 1.1 jmcneill #include <dev/cons.h> 42 1.1 jmcneill 43 1.1 jmcneill #include <machine/mainbus.h> 44 1.4 jmcneill #include <machine/thunk.h> 45 1.1 jmcneill 46 1.1 jmcneill static int ttycons_match(device_t, cfdata_t, void *); 47 1.1 jmcneill static void ttycons_attach(device_t, device_t, void *); 48 1.1 jmcneill 49 1.1 jmcneill void ttycons_consinit(void); 50 1.1 jmcneill 51 1.8 jmcneill struct ttycons_softc { 52 1.1 jmcneill device_t sc_dev; 53 1.8 jmcneill struct tty *sc_tty; 54 1.8 jmcneill void *sc_rd_sih; 55 1.11 jmcneill void *sc_ctrlc_sih; 56 1.17 jmcneill void *sc_ctrlz_sih; 57 1.15 jmcneill u_char sc_buf[1024]; 58 1.8 jmcneill }; 59 1.1 jmcneill 60 1.1 jmcneill dev_type_cngetc(ttycons_cngetc); 61 1.1 jmcneill dev_type_cnputc(ttycons_cnputc); 62 1.1 jmcneill dev_type_cnpollc(ttycons_cnpollc); 63 1.1 jmcneill 64 1.1 jmcneill static struct cnm_state ttycons_cnm_state; 65 1.1 jmcneill struct consdev ttycons_consdev = { 66 1.1 jmcneill .cn_getc = ttycons_cngetc, 67 1.1 jmcneill .cn_putc = ttycons_cnputc, 68 1.1 jmcneill .cn_pollc = ttycons_cnpollc, 69 1.7 jmcneill .cn_dev = NODEV, 70 1.7 jmcneill .cn_pri = CN_NORMAL, 71 1.1 jmcneill }; 72 1.1 jmcneill 73 1.8 jmcneill CFATTACH_DECL_NEW(ttycons, sizeof(struct ttycons_softc), 74 1.1 jmcneill ttycons_match, ttycons_attach, NULL, NULL); 75 1.1 jmcneill 76 1.8 jmcneill extern struct cfdriver ttycons_cd; 77 1.8 jmcneill 78 1.8 jmcneill dev_type_open(ttycons_open); 79 1.8 jmcneill dev_type_close(ttycons_close); 80 1.8 jmcneill dev_type_read(ttycons_read); 81 1.8 jmcneill dev_type_write(ttycons_write); 82 1.8 jmcneill dev_type_ioctl(ttycons_ioctl); 83 1.8 jmcneill dev_type_stop(ttycons_stop); 84 1.8 jmcneill dev_type_tty(ttycons_tty); 85 1.8 jmcneill dev_type_poll(ttycons_poll); 86 1.8 jmcneill 87 1.8 jmcneill const struct cdevsw ttycons_cdevsw = { 88 1.8 jmcneill .d_open = ttycons_open, 89 1.8 jmcneill .d_close = ttycons_close, 90 1.8 jmcneill .d_read = ttycons_read, 91 1.8 jmcneill .d_write = ttycons_write, 92 1.8 jmcneill .d_ioctl = ttycons_ioctl, 93 1.8 jmcneill .d_stop = ttycons_stop, 94 1.8 jmcneill .d_tty = ttycons_tty, 95 1.8 jmcneill .d_poll = ttycons_poll, 96 1.8 jmcneill .d_kqfilter = ttykqfilter, 97 1.8 jmcneill .d_flag = D_TTY, 98 1.20 dholland .d_discard = nodiscard, 99 1.8 jmcneill }; 100 1.8 jmcneill 101 1.8 jmcneill static void ttycons_start(struct tty *); 102 1.8 jmcneill static int ttycons_param(struct tty *, struct termios *); 103 1.8 jmcneill 104 1.16 jmcneill static int ttycons_intr(void *); 105 1.8 jmcneill static void ttycons_softintr(void *); 106 1.8 jmcneill 107 1.18 reinoud static sigfunc_t ttycons_ctrlc; 108 1.11 jmcneill static void ttycons_softctrlc(void *); 109 1.18 reinoud static sigfunc_t ttycons_ctrlz; 110 1.17 jmcneill static void ttycons_softctrlz(void *); 111 1.11 jmcneill 112 1.1 jmcneill static int 113 1.1 jmcneill ttycons_match(device_t parent, cfdata_t match, void *opaque) 114 1.1 jmcneill { 115 1.1 jmcneill struct thunkbus_attach_args *taa = opaque; 116 1.1 jmcneill 117 1.1 jmcneill if (taa->taa_type != THUNKBUS_TYPE_TTYCONS) 118 1.1 jmcneill return 0; 119 1.1 jmcneill 120 1.1 jmcneill return 1; 121 1.1 jmcneill } 122 1.1 jmcneill 123 1.1 jmcneill static void 124 1.1 jmcneill ttycons_attach(device_t parent, device_t self, void *opaque) 125 1.1 jmcneill { 126 1.8 jmcneill struct ttycons_softc *sc = device_private(self); 127 1.8 jmcneill int maj; 128 1.1 jmcneill 129 1.1 jmcneill aprint_naive("\n"); 130 1.1 jmcneill aprint_normal(": console\n"); 131 1.1 jmcneill 132 1.1 jmcneill sc->sc_dev = self; 133 1.8 jmcneill sc->sc_tty = tty_alloc(); 134 1.8 jmcneill tty_attach(sc->sc_tty); 135 1.8 jmcneill sc->sc_tty->t_oproc = ttycons_start; 136 1.8 jmcneill sc->sc_tty->t_param = ttycons_param; 137 1.15 jmcneill sc->sc_tty->t_sc = sc; 138 1.8 jmcneill 139 1.8 jmcneill maj = cdevsw_lookup_major(&ttycons_cdevsw); 140 1.8 jmcneill cn_tab->cn_dev = makedev(maj, device_unit(self)); 141 1.8 jmcneill sc->sc_tty->t_dev = cn_tab->cn_dev; 142 1.8 jmcneill 143 1.8 jmcneill sc->sc_rd_sih = softint_establish(SOFTINT_SERIAL, 144 1.8 jmcneill ttycons_softintr, sc); 145 1.8 jmcneill if (sc->sc_rd_sih == NULL) 146 1.11 jmcneill panic("couldn't establish ttycons intr handler\n"); 147 1.11 jmcneill 148 1.11 jmcneill sc->sc_ctrlc_sih = softint_establish(SOFTINT_SERIAL, 149 1.11 jmcneill ttycons_softctrlc, sc); 150 1.11 jmcneill if (sc->sc_ctrlc_sih == NULL) 151 1.11 jmcneill panic("couldn't establish ttycons ctrlc handler\n"); 152 1.17 jmcneill sc->sc_ctrlz_sih = softint_establish(SOFTINT_SERIAL, 153 1.17 jmcneill ttycons_softctrlz, sc); 154 1.17 jmcneill if (sc->sc_ctrlz_sih == NULL) 155 1.17 jmcneill panic("couldn't establish ttycons ctrlz handler\n"); 156 1.8 jmcneill 157 1.16 jmcneill sigio_intr_establish(ttycons_intr, sc); 158 1.18 reinoud signal_intr_establish(SIGINT, ttycons_ctrlc); 159 1.18 reinoud signal_intr_establish(SIGTSTP, ttycons_ctrlz); 160 1.18 reinoud 161 1.10 jmcneill if (thunk_set_stdin_sigio(true) != 0) 162 1.10 jmcneill panic("couldn't enable stdin async mode"); 163 1.1 jmcneill } 164 1.1 jmcneill 165 1.1 jmcneill void 166 1.1 jmcneill ttycons_consinit(void) 167 1.1 jmcneill { 168 1.5 jmcneill struct thunk_termios t; 169 1.5 jmcneill 170 1.5 jmcneill thunk_tcgetattr(0, &t); 171 1.5 jmcneill t.c_lflag &= ~(ECHO|ICANON); 172 1.5 jmcneill t.c_cc[VTIME] = 0; 173 1.5 jmcneill t.c_cc[VMIN] = 1; 174 1.5 jmcneill thunk_tcsetattr(0, TCSANOW, &t); 175 1.5 jmcneill 176 1.1 jmcneill cn_tab = &ttycons_consdev; 177 1.1 jmcneill cn_init_magic(&ttycons_cnm_state); 178 1.1 jmcneill cn_set_magic("\047\001"); 179 1.1 jmcneill } 180 1.1 jmcneill 181 1.1 jmcneill int 182 1.1 jmcneill ttycons_cngetc(dev_t dev) 183 1.1 jmcneill { 184 1.4 jmcneill return thunk_getchar(); 185 1.1 jmcneill } 186 1.1 jmcneill 187 1.1 jmcneill void 188 1.1 jmcneill ttycons_cnputc(dev_t dev, int c) 189 1.1 jmcneill { 190 1.4 jmcneill thunk_putchar(c); 191 1.1 jmcneill } 192 1.1 jmcneill 193 1.1 jmcneill void 194 1.1 jmcneill ttycons_cnpollc(dev_t dev, int on) 195 1.1 jmcneill { 196 1.1 jmcneill } 197 1.8 jmcneill 198 1.8 jmcneill int 199 1.8 jmcneill ttycons_open(dev_t dev, int flag, int mode, lwp_t *l) 200 1.8 jmcneill { 201 1.8 jmcneill struct ttycons_softc *sc; 202 1.8 jmcneill struct tty *t; 203 1.8 jmcneill 204 1.8 jmcneill sc = device_lookup_private(&ttycons_cd, minor(dev)); 205 1.8 jmcneill if (sc == NULL) 206 1.8 jmcneill return ENXIO; 207 1.8 jmcneill t = sc->sc_tty; 208 1.8 jmcneill 209 1.8 jmcneill if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, t)) 210 1.8 jmcneill return EBUSY; 211 1.8 jmcneill 212 1.8 jmcneill if ((t->t_state & TS_ISOPEN) == 0 && t->t_wopen == 0) { 213 1.8 jmcneill t->t_dev = dev; 214 1.8 jmcneill ttychars(t); 215 1.8 jmcneill t->t_iflag = TTYDEF_IFLAG; 216 1.8 jmcneill t->t_oflag = TTYDEF_OFLAG; 217 1.8 jmcneill t->t_cflag = TTYDEF_CFLAG; 218 1.8 jmcneill t->t_lflag = TTYDEF_LFLAG; 219 1.8 jmcneill t->t_ispeed = t->t_ospeed = TTYDEF_SPEED; 220 1.8 jmcneill ttycons_param(t, &t->t_termios); 221 1.8 jmcneill ttsetwater(t); 222 1.8 jmcneill } 223 1.8 jmcneill t->t_state |= TS_CARR_ON; 224 1.8 jmcneill 225 1.8 jmcneill return t->t_linesw->l_open(dev, t); 226 1.8 jmcneill } 227 1.8 jmcneill 228 1.8 jmcneill int 229 1.8 jmcneill ttycons_close(dev_t dev, int flag, int mode, lwp_t *l) 230 1.8 jmcneill { 231 1.8 jmcneill struct ttycons_softc *sc; 232 1.8 jmcneill struct tty *t; 233 1.8 jmcneill 234 1.8 jmcneill sc = device_lookup_private(&ttycons_cd, minor(dev)); 235 1.8 jmcneill t = sc->sc_tty; 236 1.8 jmcneill 237 1.8 jmcneill if (t == NULL) 238 1.8 jmcneill return 0; 239 1.8 jmcneill 240 1.8 jmcneill t->t_linesw->l_close(t, flag); 241 1.8 jmcneill ttyclose(t); 242 1.8 jmcneill 243 1.8 jmcneill return 0; 244 1.8 jmcneill } 245 1.8 jmcneill 246 1.8 jmcneill int 247 1.8 jmcneill ttycons_read(dev_t dev, struct uio *uio, int flag) 248 1.8 jmcneill { 249 1.8 jmcneill struct ttycons_softc *sc; 250 1.8 jmcneill struct tty *t; 251 1.8 jmcneill 252 1.8 jmcneill sc = device_lookup_private(&ttycons_cd, minor(dev)); 253 1.8 jmcneill t = sc->sc_tty; 254 1.8 jmcneill 255 1.8 jmcneill return t->t_linesw->l_read(t, uio, flag); 256 1.8 jmcneill } 257 1.8 jmcneill 258 1.8 jmcneill int 259 1.8 jmcneill ttycons_write(dev_t dev, struct uio *uio, int flag) 260 1.8 jmcneill { 261 1.8 jmcneill struct ttycons_softc *sc; 262 1.8 jmcneill struct tty *t; 263 1.8 jmcneill 264 1.8 jmcneill sc = device_lookup_private(&ttycons_cd, minor(dev)); 265 1.8 jmcneill t = sc->sc_tty; 266 1.8 jmcneill 267 1.8 jmcneill return t->t_linesw->l_write(t, uio, flag); 268 1.8 jmcneill } 269 1.8 jmcneill 270 1.8 jmcneill int 271 1.8 jmcneill ttycons_poll(dev_t dev, int events, lwp_t *l) 272 1.8 jmcneill { 273 1.8 jmcneill struct ttycons_softc *sc; 274 1.8 jmcneill struct tty *t; 275 1.8 jmcneill 276 1.8 jmcneill sc = device_lookup_private(&ttycons_cd, minor(dev)); 277 1.8 jmcneill t = sc->sc_tty; 278 1.8 jmcneill 279 1.8 jmcneill return t->t_linesw->l_poll(t, events, l); 280 1.8 jmcneill } 281 1.8 jmcneill 282 1.8 jmcneill struct tty * 283 1.8 jmcneill ttycons_tty(dev_t dev) 284 1.8 jmcneill { 285 1.8 jmcneill struct ttycons_softc *sc; 286 1.8 jmcneill 287 1.8 jmcneill sc = device_lookup_private(&ttycons_cd, minor(dev)); 288 1.8 jmcneill 289 1.8 jmcneill return sc->sc_tty; 290 1.8 jmcneill } 291 1.8 jmcneill 292 1.8 jmcneill int 293 1.8 jmcneill ttycons_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l) 294 1.8 jmcneill { 295 1.8 jmcneill struct ttycons_softc *sc; 296 1.8 jmcneill struct tty *t; 297 1.8 jmcneill int error; 298 1.8 jmcneill 299 1.8 jmcneill sc = device_lookup_private(&ttycons_cd, minor(dev)); 300 1.8 jmcneill t = sc->sc_tty; 301 1.8 jmcneill 302 1.8 jmcneill error = t->t_linesw->l_ioctl(t, cmd, data, flag, l); 303 1.8 jmcneill if (error != EPASSTHROUGH) 304 1.8 jmcneill return error; 305 1.8 jmcneill 306 1.8 jmcneill error = ttioctl(t, cmd, data, flag, l); 307 1.8 jmcneill if (error != EPASSTHROUGH) 308 1.8 jmcneill return error; 309 1.8 jmcneill 310 1.8 jmcneill return EPASSTHROUGH; 311 1.8 jmcneill } 312 1.8 jmcneill 313 1.8 jmcneill static void 314 1.8 jmcneill ttycons_start(struct tty *t) 315 1.8 jmcneill { 316 1.15 jmcneill struct ttycons_softc *sc = t->t_sc; 317 1.15 jmcneill u_char *p = sc->sc_buf; 318 1.13 jmcneill int s, len, brem; 319 1.8 jmcneill 320 1.8 jmcneill s = spltty(); 321 1.8 jmcneill if (t->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) { 322 1.8 jmcneill splx(s); 323 1.8 jmcneill return; 324 1.8 jmcneill } 325 1.8 jmcneill t->t_state |= TS_BUSY; 326 1.8 jmcneill splx(s); 327 1.8 jmcneill 328 1.15 jmcneill brem = q_to_b(&t->t_outq, sc->sc_buf, sizeof(sc->sc_buf)); 329 1.13 jmcneill 330 1.13 jmcneill while (brem > 0) { 331 1.13 jmcneill len = thunk_write(1, p, brem); 332 1.13 jmcneill if (len > 0) { 333 1.13 jmcneill p += len; 334 1.13 jmcneill brem -= len; 335 1.13 jmcneill } 336 1.13 jmcneill } 337 1.8 jmcneill 338 1.8 jmcneill s = spltty(); 339 1.8 jmcneill t->t_state &= ~TS_BUSY; 340 1.8 jmcneill if (ttypull(t)) { 341 1.8 jmcneill t->t_state |= TS_TIMEOUT; 342 1.8 jmcneill callout_schedule(&t->t_rstrt_ch, 1); 343 1.8 jmcneill } 344 1.8 jmcneill splx(s); 345 1.8 jmcneill } 346 1.8 jmcneill 347 1.8 jmcneill void 348 1.8 jmcneill ttycons_stop(struct tty *t, int flag) 349 1.8 jmcneill { 350 1.8 jmcneill } 351 1.8 jmcneill 352 1.8 jmcneill static int 353 1.8 jmcneill ttycons_param(struct tty *t, struct termios *c) 354 1.8 jmcneill { 355 1.8 jmcneill t->t_ispeed = c->c_ispeed; 356 1.8 jmcneill t->t_ospeed = c->c_ospeed; 357 1.8 jmcneill t->t_cflag = c->c_cflag; 358 1.8 jmcneill return 0; 359 1.8 jmcneill } 360 1.8 jmcneill 361 1.16 jmcneill static int 362 1.16 jmcneill ttycons_intr(void *priv) 363 1.8 jmcneill { 364 1.16 jmcneill struct ttycons_softc *sc = priv; 365 1.8 jmcneill 366 1.18 reinoud softint_schedule(sc->sc_rd_sih); 367 1.8 jmcneill 368 1.16 jmcneill return 0; 369 1.8 jmcneill } 370 1.8 jmcneill 371 1.8 jmcneill static void 372 1.8 jmcneill ttycons_softintr(void *priv) 373 1.8 jmcneill { 374 1.8 jmcneill struct ttycons_softc *sc = priv; 375 1.8 jmcneill struct tty *t = sc->sc_tty; 376 1.8 jmcneill unsigned char ch; 377 1.8 jmcneill int c; 378 1.8 jmcneill 379 1.8 jmcneill while ((c = thunk_pollchar()) >= 0) { 380 1.8 jmcneill ch = (unsigned char)c; 381 1.8 jmcneill cn_check_magic(t->t_dev, ch, ttycons_cnm_state); 382 1.8 jmcneill t->t_linesw->l_rint(ch, t); 383 1.8 jmcneill } 384 1.8 jmcneill } 385 1.11 jmcneill 386 1.18 reinoud 387 1.18 reinoud /* 388 1.18 reinoud * handle SIGINT signal from trap.c 389 1.18 reinoud * 390 1.18 reinoud * argument 'pc' and 'va' are not used. 391 1.18 reinoud */ 392 1.11 jmcneill static void 393 1.19 reinoud ttycons_ctrlc(siginfo_t *info, vaddr_t from_userland, vaddr_t pc, vaddr_t va) 394 1.11 jmcneill { 395 1.11 jmcneill struct ttycons_softc *sc; 396 1.11 jmcneill 397 1.11 jmcneill sc = device_lookup_private(&ttycons_cd, minor(cn_tab->cn_dev)); 398 1.18 reinoud if (sc) 399 1.18 reinoud softint_schedule(sc->sc_ctrlc_sih); 400 1.11 jmcneill 401 1.11 jmcneill } 402 1.11 jmcneill 403 1.11 jmcneill static void 404 1.11 jmcneill ttycons_softctrlc(void *priv) 405 1.11 jmcneill { 406 1.11 jmcneill struct ttycons_softc *sc = priv; 407 1.11 jmcneill struct tty *t = sc->sc_tty; 408 1.11 jmcneill unsigned char ch = 3; /* ETX */ 409 1.11 jmcneill 410 1.11 jmcneill cn_check_magic(t->t_dev, ch, ttycons_cnm_state); 411 1.11 jmcneill t->t_linesw->l_rint(ch, t); 412 1.11 jmcneill } 413 1.17 jmcneill 414 1.18 reinoud /* 415 1.18 reinoud * handle SIGTSTP signal from trap.c 416 1.18 reinoud * 417 1.18 reinoud * argument 'pc' and 'va' are not used. 418 1.18 reinoud */ 419 1.17 jmcneill static void 420 1.19 reinoud ttycons_ctrlz(siginfo_t *info, vaddr_t from_userland, vaddr_t pc, vaddr_t va) 421 1.17 jmcneill { 422 1.17 jmcneill struct ttycons_softc *sc; 423 1.17 jmcneill 424 1.17 jmcneill sc = device_lookup_private(&ttycons_cd, minor(cn_tab->cn_dev)); 425 1.18 reinoud if (sc) 426 1.18 reinoud softint_schedule(sc->sc_ctrlz_sih); 427 1.17 jmcneill } 428 1.17 jmcneill 429 1.17 jmcneill static void 430 1.17 jmcneill ttycons_softctrlz(void *priv) 431 1.17 jmcneill { 432 1.17 jmcneill struct ttycons_softc *sc = priv; 433 1.17 jmcneill struct tty *t = sc->sc_tty; 434 1.17 jmcneill unsigned char ch = 26; /* SUB */ 435 1.17 jmcneill 436 1.17 jmcneill cn_check_magic(t->t_dev, ch, ttycons_cnm_state); 437 1.17 jmcneill t->t_linesw->l_rint(ch, t); 438 1.17 jmcneill } 439