1 /* $NetBSD: ttycons.c,v 1.19 2012/03/03 21:15:15 reinoud 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.19 2012/03/03 21:15:15 reinoud 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 }; 99 100 static void ttycons_start(struct tty *); 101 static int ttycons_param(struct tty *, struct termios *); 102 103 static int ttycons_intr(void *); 104 static void ttycons_softintr(void *); 105 106 static sigfunc_t ttycons_ctrlc; 107 static void ttycons_softctrlc(void *); 108 static sigfunc_t ttycons_ctrlz; 109 static void ttycons_softctrlz(void *); 110 111 static int 112 ttycons_match(device_t parent, cfdata_t match, void *opaque) 113 { 114 struct thunkbus_attach_args *taa = opaque; 115 116 if (taa->taa_type != THUNKBUS_TYPE_TTYCONS) 117 return 0; 118 119 return 1; 120 } 121 122 static void 123 ttycons_attach(device_t parent, device_t self, void *opaque) 124 { 125 struct ttycons_softc *sc = device_private(self); 126 int maj; 127 128 aprint_naive("\n"); 129 aprint_normal(": console\n"); 130 131 sc->sc_dev = self; 132 sc->sc_tty = tty_alloc(); 133 tty_attach(sc->sc_tty); 134 sc->sc_tty->t_oproc = ttycons_start; 135 sc->sc_tty->t_param = ttycons_param; 136 sc->sc_tty->t_sc = sc; 137 138 maj = cdevsw_lookup_major(&ttycons_cdevsw); 139 cn_tab->cn_dev = makedev(maj, device_unit(self)); 140 sc->sc_tty->t_dev = cn_tab->cn_dev; 141 142 sc->sc_rd_sih = softint_establish(SOFTINT_SERIAL, 143 ttycons_softintr, sc); 144 if (sc->sc_rd_sih == NULL) 145 panic("couldn't establish ttycons intr handler\n"); 146 147 sc->sc_ctrlc_sih = softint_establish(SOFTINT_SERIAL, 148 ttycons_softctrlc, sc); 149 if (sc->sc_ctrlc_sih == NULL) 150 panic("couldn't establish ttycons ctrlc handler\n"); 151 sc->sc_ctrlz_sih = softint_establish(SOFTINT_SERIAL, 152 ttycons_softctrlz, sc); 153 if (sc->sc_ctrlz_sih == NULL) 154 panic("couldn't establish ttycons ctrlz handler\n"); 155 156 sigio_intr_establish(ttycons_intr, sc); 157 signal_intr_establish(SIGINT, ttycons_ctrlc); 158 signal_intr_establish(SIGTSTP, ttycons_ctrlz); 159 160 if (thunk_set_stdin_sigio(true) != 0) 161 panic("couldn't enable stdin async mode"); 162 } 163 164 void 165 ttycons_consinit(void) 166 { 167 struct thunk_termios t; 168 169 thunk_tcgetattr(0, &t); 170 t.c_lflag &= ~(ECHO|ICANON); 171 t.c_cc[VTIME] = 0; 172 t.c_cc[VMIN] = 1; 173 thunk_tcsetattr(0, TCSANOW, &t); 174 175 cn_tab = &ttycons_consdev; 176 cn_init_magic(&ttycons_cnm_state); 177 cn_set_magic("\047\001"); 178 } 179 180 int 181 ttycons_cngetc(dev_t dev) 182 { 183 return thunk_getchar(); 184 } 185 186 void 187 ttycons_cnputc(dev_t dev, int c) 188 { 189 thunk_putchar(c); 190 } 191 192 void 193 ttycons_cnpollc(dev_t dev, int on) 194 { 195 } 196 197 int 198 ttycons_open(dev_t dev, int flag, int mode, lwp_t *l) 199 { 200 struct ttycons_softc *sc; 201 struct tty *t; 202 203 sc = device_lookup_private(&ttycons_cd, minor(dev)); 204 if (sc == NULL) 205 return ENXIO; 206 t = sc->sc_tty; 207 208 if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, t)) 209 return EBUSY; 210 211 if ((t->t_state & TS_ISOPEN) == 0 && t->t_wopen == 0) { 212 t->t_dev = dev; 213 ttychars(t); 214 t->t_iflag = TTYDEF_IFLAG; 215 t->t_oflag = TTYDEF_OFLAG; 216 t->t_cflag = TTYDEF_CFLAG; 217 t->t_lflag = TTYDEF_LFLAG; 218 t->t_ispeed = t->t_ospeed = TTYDEF_SPEED; 219 ttycons_param(t, &t->t_termios); 220 ttsetwater(t); 221 } 222 t->t_state |= TS_CARR_ON; 223 224 return t->t_linesw->l_open(dev, t); 225 } 226 227 int 228 ttycons_close(dev_t dev, int flag, int mode, lwp_t *l) 229 { 230 struct ttycons_softc *sc; 231 struct tty *t; 232 233 sc = device_lookup_private(&ttycons_cd, minor(dev)); 234 t = sc->sc_tty; 235 236 if (t == NULL) 237 return 0; 238 239 t->t_linesw->l_close(t, flag); 240 ttyclose(t); 241 242 return 0; 243 } 244 245 int 246 ttycons_read(dev_t dev, struct uio *uio, int flag) 247 { 248 struct ttycons_softc *sc; 249 struct tty *t; 250 251 sc = device_lookup_private(&ttycons_cd, minor(dev)); 252 t = sc->sc_tty; 253 254 return t->t_linesw->l_read(t, uio, flag); 255 } 256 257 int 258 ttycons_write(dev_t dev, struct uio *uio, int flag) 259 { 260 struct ttycons_softc *sc; 261 struct tty *t; 262 263 sc = device_lookup_private(&ttycons_cd, minor(dev)); 264 t = sc->sc_tty; 265 266 return t->t_linesw->l_write(t, uio, flag); 267 } 268 269 int 270 ttycons_poll(dev_t dev, int events, lwp_t *l) 271 { 272 struct ttycons_softc *sc; 273 struct tty *t; 274 275 sc = device_lookup_private(&ttycons_cd, minor(dev)); 276 t = sc->sc_tty; 277 278 return t->t_linesw->l_poll(t, events, l); 279 } 280 281 struct tty * 282 ttycons_tty(dev_t dev) 283 { 284 struct ttycons_softc *sc; 285 286 sc = device_lookup_private(&ttycons_cd, minor(dev)); 287 288 return sc->sc_tty; 289 } 290 291 int 292 ttycons_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l) 293 { 294 struct ttycons_softc *sc; 295 struct tty *t; 296 int error; 297 298 sc = device_lookup_private(&ttycons_cd, minor(dev)); 299 t = sc->sc_tty; 300 301 error = t->t_linesw->l_ioctl(t, cmd, data, flag, l); 302 if (error != EPASSTHROUGH) 303 return error; 304 305 error = ttioctl(t, cmd, data, flag, l); 306 if (error != EPASSTHROUGH) 307 return error; 308 309 return EPASSTHROUGH; 310 } 311 312 static void 313 ttycons_start(struct tty *t) 314 { 315 struct ttycons_softc *sc = t->t_sc; 316 u_char *p = sc->sc_buf; 317 int s, len, brem; 318 319 s = spltty(); 320 if (t->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) { 321 splx(s); 322 return; 323 } 324 t->t_state |= TS_BUSY; 325 splx(s); 326 327 brem = q_to_b(&t->t_outq, sc->sc_buf, sizeof(sc->sc_buf)); 328 329 while (brem > 0) { 330 len = thunk_write(1, p, brem); 331 if (len > 0) { 332 p += len; 333 brem -= len; 334 } 335 } 336 337 s = spltty(); 338 t->t_state &= ~TS_BUSY; 339 if (ttypull(t)) { 340 t->t_state |= TS_TIMEOUT; 341 callout_schedule(&t->t_rstrt_ch, 1); 342 } 343 splx(s); 344 } 345 346 void 347 ttycons_stop(struct tty *t, int flag) 348 { 349 } 350 351 static int 352 ttycons_param(struct tty *t, struct termios *c) 353 { 354 t->t_ispeed = c->c_ispeed; 355 t->t_ospeed = c->c_ospeed; 356 t->t_cflag = c->c_cflag; 357 return 0; 358 } 359 360 static int 361 ttycons_intr(void *priv) 362 { 363 struct ttycons_softc *sc = priv; 364 365 softint_schedule(sc->sc_rd_sih); 366 367 return 0; 368 } 369 370 static void 371 ttycons_softintr(void *priv) 372 { 373 struct ttycons_softc *sc = priv; 374 struct tty *t = sc->sc_tty; 375 unsigned char ch; 376 int c; 377 378 while ((c = thunk_pollchar()) >= 0) { 379 ch = (unsigned char)c; 380 cn_check_magic(t->t_dev, ch, ttycons_cnm_state); 381 t->t_linesw->l_rint(ch, t); 382 } 383 } 384 385 386 /* 387 * handle SIGINT signal from trap.c 388 * 389 * argument 'pc' and 'va' are not used. 390 */ 391 static void 392 ttycons_ctrlc(siginfo_t *info, vaddr_t from_userland, vaddr_t pc, vaddr_t va) 393 { 394 struct ttycons_softc *sc; 395 396 sc = device_lookup_private(&ttycons_cd, minor(cn_tab->cn_dev)); 397 if (sc) 398 softint_schedule(sc->sc_ctrlc_sih); 399 400 } 401 402 static void 403 ttycons_softctrlc(void *priv) 404 { 405 struct ttycons_softc *sc = priv; 406 struct tty *t = sc->sc_tty; 407 unsigned char ch = 3; /* ETX */ 408 409 cn_check_magic(t->t_dev, ch, ttycons_cnm_state); 410 t->t_linesw->l_rint(ch, t); 411 } 412 413 /* 414 * handle SIGTSTP signal from trap.c 415 * 416 * argument 'pc' and 'va' are not used. 417 */ 418 static void 419 ttycons_ctrlz(siginfo_t *info, vaddr_t from_userland, vaddr_t pc, vaddr_t va) 420 { 421 struct ttycons_softc *sc; 422 423 sc = device_lookup_private(&ttycons_cd, minor(cn_tab->cn_dev)); 424 if (sc) 425 softint_schedule(sc->sc_ctrlz_sih); 426 } 427 428 static void 429 ttycons_softctrlz(void *priv) 430 { 431 struct ttycons_softc *sc = priv; 432 struct tty *t = sc->sc_tty; 433 unsigned char ch = 26; /* SUB */ 434 435 cn_check_magic(t->t_dev, ch, ttycons_cnm_state); 436 t->t_linesw->l_rint(ch, t); 437 } 438