1/* $NetBSD$ */ 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$"); 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 46static int ttycons_match(device_t, cfdata_t, void *); 47static void ttycons_attach(device_t, device_t, void *); 48 49void ttycons_consinit(void); 50 51struct 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 60dev_type_cngetc(ttycons_cngetc); 61dev_type_cnputc(ttycons_cnputc); 62dev_type_cnpollc(ttycons_cnpollc); 63 64static struct cnm_state ttycons_cnm_state; 65struct 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 73CFATTACH_DECL_NEW(ttycons, sizeof(struct ttycons_softc), 74 ttycons_match, ttycons_attach, NULL, NULL); 75 76extern struct cfdriver ttycons_cd; 77 78dev_type_open(ttycons_open); 79dev_type_close(ttycons_close); 80dev_type_read(ttycons_read); 81dev_type_write(ttycons_write); 82dev_type_ioctl(ttycons_ioctl); 83dev_type_stop(ttycons_stop); 84dev_type_tty(ttycons_tty); 85dev_type_poll(ttycons_poll); 86 87const 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 100static void ttycons_start(struct tty *); 101static int ttycons_param(struct tty *, struct termios *); 102 103static int ttycons_intr(void *); 104static void ttycons_softintr(void *); 105 106static sigfunc_t ttycons_ctrlc; 107static void ttycons_softctrlc(void *); 108static sigfunc_t ttycons_ctrlz; 109static void ttycons_softctrlz(void *); 110 111static int 112ttycons_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 122static void 123ttycons_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 164void 165ttycons_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 180int 181ttycons_cngetc(dev_t dev) 182{ 183 return thunk_getchar(); 184} 185 186void 187ttycons_cnputc(dev_t dev, int c) 188{ 189 thunk_putchar(c); 190} 191 192void 193ttycons_cnpollc(dev_t dev, int on) 194{ 195} 196 197int 198ttycons_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 227int 228ttycons_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 245int 246ttycons_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 257int 258ttycons_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 269int 270ttycons_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 281struct tty * 282ttycons_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 291int 292ttycons_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 312static void 313ttycons_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 346void 347ttycons_stop(struct tty *t, int flag) 348{ 349} 350 351static int 352ttycons_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 360static int 361ttycons_intr(void *priv) 362{ 363 struct ttycons_softc *sc = priv; 364 365 softint_schedule(sc->sc_rd_sih); 366 367 return 0; 368} 369 370static void 371ttycons_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 */ 391static void 392ttycons_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 402static void 403ttycons_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 */ 418static void 419ttycons_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 428static void 429ttycons_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