1 /* $NetBSD: ttycons.c,v 1.17 2011/12/27 20:59:45 jmcneill 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.17 2011/12/27 20:59:45 jmcneill 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 void ttycons_ctrlc(int); 107 static void ttycons_softctrlc(void *); 108 static void ttycons_ctrlz(int); 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 thunk_signal(SIGINT, ttycons_ctrlc); 158 thunk_signal(SIGTSTP, ttycons_ctrlz); 159 if (thunk_set_stdin_sigio(true) != 0) 160 panic("couldn't enable stdin async mode"); 161 } 162 163 void 164 ttycons_consinit(void) 165 { 166 struct thunk_termios t; 167 168 thunk_tcgetattr(0, &t); 169 t.c_lflag &= ~(ECHO|ICANON); 170 t.c_cc[VTIME] = 0; 171 t.c_cc[VMIN] = 1; 172 thunk_tcsetattr(0, TCSANOW, &t); 173 174 cn_tab = &ttycons_consdev; 175 cn_init_magic(&ttycons_cnm_state); 176 cn_set_magic("\047\001"); 177 } 178 179 int 180 ttycons_cngetc(dev_t dev) 181 { 182 return thunk_getchar(); 183 } 184 185 void 186 ttycons_cnputc(dev_t dev, int c) 187 { 188 thunk_putchar(c); 189 } 190 191 void 192 ttycons_cnpollc(dev_t dev, int on) 193 { 194 } 195 196 int 197 ttycons_open(dev_t dev, int flag, int mode, lwp_t *l) 198 { 199 struct ttycons_softc *sc; 200 struct tty *t; 201 202 sc = device_lookup_private(&ttycons_cd, minor(dev)); 203 if (sc == NULL) 204 return ENXIO; 205 t = sc->sc_tty; 206 207 if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, t)) 208 return EBUSY; 209 210 if ((t->t_state & TS_ISOPEN) == 0 && t->t_wopen == 0) { 211 t->t_dev = dev; 212 ttychars(t); 213 t->t_iflag = TTYDEF_IFLAG; 214 t->t_oflag = TTYDEF_OFLAG; 215 t->t_cflag = TTYDEF_CFLAG; 216 t->t_lflag = TTYDEF_LFLAG; 217 t->t_ispeed = t->t_ospeed = TTYDEF_SPEED; 218 ttycons_param(t, &t->t_termios); 219 ttsetwater(t); 220 } 221 t->t_state |= TS_CARR_ON; 222 223 return t->t_linesw->l_open(dev, t); 224 } 225 226 int 227 ttycons_close(dev_t dev, int flag, int mode, lwp_t *l) 228 { 229 struct ttycons_softc *sc; 230 struct tty *t; 231 232 sc = device_lookup_private(&ttycons_cd, minor(dev)); 233 t = sc->sc_tty; 234 235 if (t == NULL) 236 return 0; 237 238 t->t_linesw->l_close(t, flag); 239 ttyclose(t); 240 241 return 0; 242 } 243 244 int 245 ttycons_read(dev_t dev, struct uio *uio, int flag) 246 { 247 struct ttycons_softc *sc; 248 struct tty *t; 249 250 sc = device_lookup_private(&ttycons_cd, minor(dev)); 251 t = sc->sc_tty; 252 253 return t->t_linesw->l_read(t, uio, flag); 254 } 255 256 int 257 ttycons_write(dev_t dev, struct uio *uio, int flag) 258 { 259 struct ttycons_softc *sc; 260 struct tty *t; 261 262 sc = device_lookup_private(&ttycons_cd, minor(dev)); 263 t = sc->sc_tty; 264 265 return t->t_linesw->l_write(t, uio, flag); 266 } 267 268 int 269 ttycons_poll(dev_t dev, int events, lwp_t *l) 270 { 271 struct ttycons_softc *sc; 272 struct tty *t; 273 274 sc = device_lookup_private(&ttycons_cd, minor(dev)); 275 t = sc->sc_tty; 276 277 return t->t_linesw->l_poll(t, events, l); 278 } 279 280 struct tty * 281 ttycons_tty(dev_t dev) 282 { 283 struct ttycons_softc *sc; 284 285 sc = device_lookup_private(&ttycons_cd, minor(dev)); 286 287 return sc->sc_tty; 288 } 289 290 int 291 ttycons_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l) 292 { 293 struct ttycons_softc *sc; 294 struct tty *t; 295 int error; 296 297 sc = device_lookup_private(&ttycons_cd, minor(dev)); 298 t = sc->sc_tty; 299 300 error = t->t_linesw->l_ioctl(t, cmd, data, flag, l); 301 if (error != EPASSTHROUGH) 302 return error; 303 304 error = ttioctl(t, cmd, data, flag, l); 305 if (error != EPASSTHROUGH) 306 return error; 307 308 return EPASSTHROUGH; 309 } 310 311 static void 312 ttycons_start(struct tty *t) 313 { 314 struct ttycons_softc *sc = t->t_sc; 315 u_char *p = sc->sc_buf; 316 int s, len, brem; 317 318 s = spltty(); 319 if (t->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) { 320 splx(s); 321 return; 322 } 323 t->t_state |= TS_BUSY; 324 splx(s); 325 326 brem = q_to_b(&t->t_outq, sc->sc_buf, sizeof(sc->sc_buf)); 327 328 while (brem > 0) { 329 len = thunk_write(1, p, brem); 330 if (len > 0) { 331 p += len; 332 brem -= len; 333 } 334 } 335 336 s = spltty(); 337 t->t_state &= ~TS_BUSY; 338 if (ttypull(t)) { 339 t->t_state |= TS_TIMEOUT; 340 callout_schedule(&t->t_rstrt_ch, 1); 341 } 342 splx(s); 343 } 344 345 void 346 ttycons_stop(struct tty *t, int flag) 347 { 348 } 349 350 static int 351 ttycons_param(struct tty *t, struct termios *c) 352 { 353 t->t_ispeed = c->c_ispeed; 354 t->t_ospeed = c->c_ospeed; 355 t->t_cflag = c->c_cflag; 356 return 0; 357 } 358 359 static int 360 ttycons_intr(void *priv) 361 { 362 struct ttycons_softc *sc = priv; 363 364 curcpu()->ci_idepth++; 365 spl_intr(IPL_SERIAL, softint_schedule, sc->sc_rd_sih); 366 curcpu()->ci_idepth--; 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 static void 387 ttycons_ctrlc(int sig) 388 { 389 struct ttycons_softc *sc; 390 391 curcpu()->ci_idepth++; 392 sc = device_lookup_private(&ttycons_cd, minor(cn_tab->cn_dev)); 393 if (sc) { 394 spl_intr(IPL_SERIAL, softint_schedule, sc->sc_ctrlc_sih); 395 } 396 curcpu()->ci_idepth--; 397 398 } 399 400 static void 401 ttycons_softctrlc(void *priv) 402 { 403 struct ttycons_softc *sc = priv; 404 struct tty *t = sc->sc_tty; 405 unsigned char ch = 3; /* ETX */ 406 407 cn_check_magic(t->t_dev, ch, ttycons_cnm_state); 408 t->t_linesw->l_rint(ch, t); 409 } 410 411 static void 412 ttycons_ctrlz(int sig) 413 { 414 struct ttycons_softc *sc; 415 416 curcpu()->ci_idepth++; 417 sc = device_lookup_private(&ttycons_cd, minor(cn_tab->cn_dev)); 418 if (sc) { 419 spl_intr(IPL_SERIAL, softint_schedule, sc->sc_ctrlz_sih); 420 } 421 curcpu()->ci_idepth--; 422 423 } 424 425 static void 426 ttycons_softctrlz(void *priv) 427 { 428 struct ttycons_softc *sc = priv; 429 struct tty *t = sc->sc_tty; 430 unsigned char ch = 26; /* SUB */ 431 432 cn_check_magic(t->t_dev, ch, ttycons_cnm_state); 433 t->t_linesw->l_rint(ch, t); 434 } 435