1 /* $OpenBSD: vcons.c,v 1.14 2014/05/10 11:49:31 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2008 Mark Kettenis 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/conf.h> 20 #include <sys/device.h> 21 #include <sys/proc.h> 22 #include <sys/systm.h> 23 #include <sys/tty.h> 24 25 #ifdef DDB 26 #include <ddb/db_var.h> 27 #endif 28 29 #include <machine/autoconf.h> 30 #include <machine/conf.h> 31 #include <machine/hypervisor.h> 32 #include <machine/openfirm.h> 33 34 #include <dev/cons.h> 35 #include <sparc64/dev/vbusvar.h> 36 37 struct vcons_softc { 38 struct device sc_dv; 39 void *sc_ih; 40 struct tty *sc_tty; 41 void *sc_si; 42 }; 43 44 int vcons_match(struct device *, void *, void *); 45 void vcons_attach(struct device *, struct device *, void *); 46 47 struct cfattach vcons_ca = { 48 sizeof(struct vcons_softc), vcons_match, vcons_attach 49 }; 50 51 struct cfdriver vcons_cd = { 52 NULL, "vcons", DV_DULL 53 }; 54 55 int vcons_intr(void *); 56 57 int vcons_cnlookc(dev_t, int *); 58 int vcons_cngetc(dev_t); 59 void vcons_cnputc(dev_t, int); 60 61 void vconsstart(struct tty *); 62 int vconsparam(struct tty *, struct termios *); 63 void vcons_softintr(void *); 64 65 int 66 vcons_match(struct device *parent, void *match, void *aux) 67 { 68 struct vbus_attach_args *va = aux; 69 70 if (strcmp(va->va_name, "console") == 0) 71 return (1); 72 73 return (0); 74 } 75 76 void 77 vcons_attach(struct device *parent, struct device *self, void *aux) 78 { 79 struct vcons_softc *sc = (struct vcons_softc *)self; 80 struct vbus_attach_args *va = aux; 81 uint64_t sysino; 82 int vcons_is_input, vcons_is_output; 83 int node, maj; 84 85 sc->sc_si = softintr_establish(IPL_TTY, vcons_softintr, sc); 86 if (sc->sc_si == NULL) 87 panic(": can't establish soft interrupt"); 88 89 if (vbus_intr_map(va->va_node, va->va_intr[0], &sysino)) 90 printf(": can't map interrupt\n"); 91 printf(": ivec 0x%llx", sysino); 92 93 sc->sc_ih = bus_intr_establish(va->va_bustag, sysino, IPL_TTY, 94 BUS_INTR_ESTABLISH_MPSAFE, vcons_intr, sc, sc->sc_dv.dv_xname); 95 if (sc->sc_ih == NULL) { 96 printf(", can't establish interrupt\n"); 97 return; 98 } 99 100 node = OF_instance_to_package(OF_stdin()); 101 vcons_is_input = (va->va_node == node); 102 node = OF_instance_to_package(OF_stdout()); 103 vcons_is_output = (va->va_node == node); 104 105 if (vcons_is_input || vcons_is_output) { 106 if (vcons_is_input) { 107 cn_tab->cn_pollc = nullcnpollc; 108 cn_tab->cn_getc = vcons_cngetc; 109 110 /* Locate the major number. */ 111 for (maj = 0; maj < nchrdev; maj++) 112 if (cdevsw[maj].d_open == vconsopen) 113 break; 114 cn_tab->cn_dev = makedev(maj, self->dv_unit); 115 } 116 if (vcons_is_output) 117 cn_tab->cn_putc = vcons_cnputc; 118 119 printf(", console"); 120 } 121 122 printf("\n"); 123 } 124 125 int 126 vcons_intr(void *arg) 127 { 128 struct vcons_softc *sc = arg; 129 130 if (sc->sc_tty) 131 softintr_schedule(sc->sc_si); 132 return (1); 133 } 134 135 int 136 vcons_cnlookc(dev_t dev, int *cp) 137 { 138 int64_t ch; 139 140 if (hv_cons_getchar(&ch) == H_EOK) { 141 #ifdef DDB 142 if (ch == -1 && db_console) 143 Debugger(); 144 #endif 145 *cp = ch; 146 return (1); 147 } 148 149 return (0); 150 } 151 152 int 153 vcons_cngetc(dev_t dev) 154 { 155 int c; 156 157 while(!vcons_cnlookc(dev, &c)) 158 ; 159 160 return (c); 161 } 162 163 void 164 vcons_cnputc(dev_t dev, int c) 165 { 166 while (hv_cons_putchar(c) == H_EWOULDBLOCK); 167 } 168 169 int 170 vconsopen(dev_t dev, int flag, int mode, struct proc *p) 171 { 172 struct vcons_softc *sc; 173 struct tty *tp; 174 int unit = minor(dev); 175 176 if (unit >= vcons_cd.cd_ndevs) 177 return (ENXIO); 178 sc = vcons_cd.cd_devs[unit]; 179 if (sc == NULL) 180 return (ENXIO); 181 182 if (sc->sc_tty) 183 tp = sc->sc_tty; 184 else 185 tp = sc->sc_tty = ttymalloc(0); 186 187 tp->t_oproc = vconsstart; 188 tp->t_param = vconsparam; 189 tp->t_dev = dev; 190 if ((tp->t_state & TS_ISOPEN) == 0) { 191 ttychars(tp); 192 tp->t_iflag = TTYDEF_IFLAG; 193 tp->t_oflag = TTYDEF_OFLAG; 194 tp->t_cflag = TTYDEF_CFLAG; 195 tp->t_lflag = TTYDEF_LFLAG; 196 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 197 ttsetwater(tp); 198 } else if ((tp->t_state & TS_XCLUDE) && suser(p, 0)) 199 return (EBUSY); 200 tp->t_state |= TS_CARR_ON; 201 202 return ((*linesw[tp->t_line].l_open)(dev, tp, p)); 203 } 204 205 int 206 vconsclose(dev_t dev, int flag, int mode, struct proc *p) 207 { 208 struct vcons_softc *sc; 209 struct tty *tp; 210 int unit = minor(dev); 211 212 if (unit >= vcons_cd.cd_ndevs) 213 return (ENXIO); 214 sc = vcons_cd.cd_devs[unit]; 215 if (sc == NULL) 216 return (ENXIO); 217 218 tp = sc->sc_tty; 219 (*linesw[tp->t_line].l_close)(tp, flag, p); 220 ttyclose(tp); 221 return (0); 222 } 223 224 int 225 vconsread(dev_t dev, struct uio *uio, int flag) 226 { 227 struct vcons_softc *sc; 228 struct tty *tp; 229 int unit = minor(dev); 230 231 if (unit >= vcons_cd.cd_ndevs) 232 return (ENXIO); 233 sc = vcons_cd.cd_devs[unit]; 234 if (sc == NULL) 235 return (ENXIO); 236 237 tp = sc->sc_tty; 238 return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); 239 } 240 241 int 242 vconswrite(dev_t dev, struct uio *uio, int flag) 243 { 244 struct vcons_softc *sc; 245 struct tty *tp; 246 int unit = minor(dev); 247 248 if (unit >= vcons_cd.cd_ndevs) 249 return (ENXIO); 250 sc = vcons_cd.cd_devs[unit]; 251 if (sc == NULL) 252 return (ENXIO); 253 254 tp = sc->sc_tty; 255 return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); 256 } 257 258 int 259 vconsioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 260 { 261 struct vcons_softc *sc; 262 struct tty *tp; 263 int unit = minor(dev); 264 int error; 265 266 if (unit >= vcons_cd.cd_ndevs) 267 return (ENXIO); 268 sc = vcons_cd.cd_devs[unit]; 269 if (sc == NULL) 270 return (ENXIO); 271 272 tp = sc->sc_tty; 273 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 274 if (error >= 0) 275 return error; 276 error = ttioctl(tp, cmd, data, flag, p); 277 if (error >= 0) 278 return (error); 279 280 return (ENOTTY); 281 } 282 283 void 284 vconsstart(struct tty *tp) 285 { 286 int s; 287 288 s = spltty(); 289 if (tp->t_state & (TS_TTSTOP | TS_BUSY)) { 290 splx(s); 291 return; 292 } 293 ttwakeupwr(tp); 294 tp->t_state |= TS_BUSY; 295 while (tp->t_outq.c_cc != 0) 296 vcons_cnputc(tp->t_dev, getc(&tp->t_outq)); 297 tp->t_state &= ~TS_BUSY; 298 splx(s); 299 } 300 301 int 302 vconsstop(struct tty *tp, int flag) 303 { 304 int s; 305 306 s = spltty(); 307 if (tp->t_state & TS_BUSY) 308 if ((tp->t_state & TS_TTSTOP) == 0) 309 tp->t_state |= TS_FLUSH; 310 splx(s); 311 return (0); 312 } 313 314 struct tty * 315 vconstty(dev_t dev) 316 { 317 struct vcons_softc *sc; 318 int unit = minor(dev); 319 320 if (unit >= vcons_cd.cd_ndevs) 321 return (NULL); 322 sc = vcons_cd.cd_devs[unit]; 323 if (sc == NULL) 324 return (NULL); 325 326 return sc->sc_tty; 327 } 328 329 int 330 vconsparam(struct tty *tp, struct termios *t) 331 { 332 tp->t_ispeed = t->c_ispeed; 333 tp->t_ospeed = t->c_ospeed; 334 tp->t_cflag = t->c_cflag; 335 return (0); 336 } 337 338 void 339 vcons_softintr(void *arg) 340 { 341 struct vcons_softc *sc = arg; 342 struct tty *tp = sc->sc_tty; 343 int c; 344 345 while (vcons_cnlookc(tp->t_dev, &c)) { 346 if (tp->t_state & TS_ISOPEN) 347 (*linesw[tp->t_line].l_rint)(c, tp); 348 } 349 } 350