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