1 /* $OpenBSD: vcons.c,v 1.5 2008/11/24 16:19:33 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 maj; 83 84 sc->sc_si = softintr_establish(IPL_TTY, vcons_softintr, sc); 85 if (sc->sc_si == NULL) 86 panic(": can't establish soft interrupt"); 87 88 if (vbus_intr_map(va->va_node, va->va_intr[0], &sysino)) 89 printf(": can't map interrupt\n"); 90 printf(": ivec 0x%lx\n", sysino); 91 92 sc->sc_ih = bus_intr_establish(va->va_bustag, sysino, IPL_TTY, 0, vcons_intr, sc, sc->sc_dv.dv_xname); 93 if (sc->sc_ih == NULL) { 94 printf("%s: can't establish interrupt\n", sc->sc_dv.dv_xname); 95 return; 96 } 97 98 cn_tab->cn_pollc = nullcnpollc; 99 cn_tab->cn_getc = vcons_cngetc; 100 cn_tab->cn_putc = vcons_cnputc; 101 102 /* Locate the major number. */ 103 for (maj = 0; maj < nchrdev; maj++) 104 if (cdevsw[maj].d_open == vconsopen) 105 break; 106 cn_tab->cn_dev = makedev(maj, self->dv_unit); 107 } 108 109 int 110 vcons_intr(void *arg) 111 { 112 struct vcons_softc *sc = arg; 113 114 if (sc->sc_tty) 115 softintr_schedule(sc->sc_si); 116 return (1); 117 } 118 119 int 120 vcons_cnlookc(dev_t dev, int *cp) 121 { 122 int64_t ch; 123 124 if (hv_cons_getchar(&ch) == H_EOK) { 125 #ifdef DDB 126 if (ch == -1 && db_console) 127 Debugger(); 128 #endif 129 *cp = ch; 130 return (1); 131 } 132 133 return (0); 134 } 135 136 int 137 vcons_cngetc(dev_t dev) 138 { 139 int c; 140 141 while(!vcons_cnlookc(dev, &c)) 142 ; 143 144 return (c); 145 } 146 147 void 148 vcons_cnputc(dev_t dev, int c) 149 { 150 while (hv_cons_putchar(c) == H_EWOULDBLOCK); 151 } 152 153 int 154 vconsopen(dev_t dev, int flag, int mode, struct proc *p) 155 { 156 struct vcons_softc *sc; 157 struct tty *tp; 158 int unit = minor(dev); 159 160 if (unit > vcons_cd.cd_ndevs) 161 return (ENXIO); 162 sc = vcons_cd.cd_devs[unit]; 163 if (sc == NULL) 164 return (ENXIO); 165 166 if (sc->sc_tty) 167 tp = sc->sc_tty; 168 else 169 tp = sc->sc_tty = ttymalloc(); 170 171 tp->t_oproc = vconsstart; 172 tp->t_param = vconsparam; 173 tp->t_dev = dev; 174 if ((tp->t_state & TS_ISOPEN) == 0) { 175 ttychars(tp); 176 tp->t_iflag = TTYDEF_IFLAG; 177 tp->t_oflag = TTYDEF_OFLAG; 178 tp->t_cflag = TTYDEF_CFLAG; 179 tp->t_lflag = TTYDEF_LFLAG; 180 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 181 ttsetwater(tp); 182 } else if ((tp->t_state & TS_XCLUDE) && suser(p, 0)) 183 return (EBUSY); 184 tp->t_state |= TS_CARR_ON; 185 186 return ((*linesw[tp->t_line].l_open)(dev, tp)); 187 } 188 189 int 190 vconsclose(dev_t dev, int flag, int mode, struct proc *p) 191 { 192 struct vcons_softc *sc; 193 struct tty *tp; 194 int unit = minor(dev); 195 196 if (unit > vcons_cd.cd_ndevs) 197 return (ENXIO); 198 sc = vcons_cd.cd_devs[unit]; 199 if (sc == NULL) 200 return (ENXIO); 201 202 tp = sc->sc_tty; 203 (*linesw[tp->t_line].l_close)(tp, flag); 204 ttyclose(tp); 205 return (0); 206 } 207 208 int 209 vconsread(dev_t dev, struct uio *uio, int flag) 210 { 211 struct vcons_softc *sc; 212 struct tty *tp; 213 int unit = minor(dev); 214 215 if (unit > vcons_cd.cd_ndevs) 216 return (ENXIO); 217 sc = vcons_cd.cd_devs[unit]; 218 if (sc == NULL) 219 return (ENXIO); 220 221 tp = sc->sc_tty; 222 return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); 223 } 224 225 int 226 vconswrite(dev_t dev, struct uio *uio, int flag) 227 { 228 struct vcons_softc *sc; 229 struct tty *tp; 230 int unit = minor(dev); 231 232 if (unit > vcons_cd.cd_ndevs) 233 return (ENXIO); 234 sc = vcons_cd.cd_devs[unit]; 235 if (sc == NULL) 236 return (ENXIO); 237 238 tp = sc->sc_tty; 239 return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); 240 } 241 242 int 243 vconsioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 244 { 245 struct vcons_softc *sc; 246 struct tty *tp; 247 int unit = minor(dev); 248 int error; 249 250 if (unit > vcons_cd.cd_ndevs) 251 return (ENXIO); 252 sc = vcons_cd.cd_devs[unit]; 253 if (sc == NULL) 254 return (ENXIO); 255 256 tp = sc->sc_tty; 257 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 258 if (error >= 0) 259 return error; 260 error = ttioctl(tp, cmd, data, flag, p); 261 if (error >= 0) 262 return (error); 263 264 return (ENOTTY); 265 } 266 267 void 268 vconsstart(struct tty *tp) 269 { 270 int s; 271 272 s = spltty(); 273 if (tp->t_state & (TS_TTSTOP | TS_BUSY)) { 274 splx(s); 275 return; 276 } 277 if (tp->t_outq.c_cc <= tp->t_lowat) { 278 if (tp->t_state & TS_ASLEEP) { 279 tp->t_state &= ~TS_ASLEEP; 280 wakeup((caddr_t)&tp->t_outq); 281 } 282 selwakeup(&tp->t_wsel); 283 } 284 tp->t_state |= TS_BUSY; 285 while (tp->t_outq.c_cc != 0) 286 vcons_cnputc(tp->t_dev, getc(&tp->t_outq)); 287 tp->t_state &= ~TS_BUSY; 288 splx(s); 289 } 290 291 int 292 vconsstop(struct tty *tp, int flag) 293 { 294 int s; 295 296 s = spltty(); 297 if (tp->t_state & TS_BUSY) 298 if ((tp->t_state & TS_TTSTOP) == 0) 299 tp->t_state |= TS_FLUSH; 300 splx(s); 301 return (0); 302 } 303 304 struct tty * 305 vconstty(dev_t dev) 306 { 307 struct vcons_softc *sc; 308 int unit = minor(dev); 309 310 if (unit > vcons_cd.cd_ndevs) 311 return (NULL); 312 sc = vcons_cd.cd_devs[unit]; 313 if (sc == NULL) 314 return (NULL); 315 316 return sc->sc_tty; 317 } 318 319 int 320 vconsparam(struct tty *tp, struct termios *t) 321 { 322 tp->t_ispeed = t->c_ispeed; 323 tp->t_ospeed = t->c_ospeed; 324 tp->t_cflag = t->c_cflag; 325 return (0); 326 } 327 328 void 329 vcons_softintr(void *arg) 330 { 331 struct vcons_softc *sc = arg; 332 struct tty *tp = sc->sc_tty; 333 int c; 334 335 while (vcons_cnlookc(tp->t_dev, &c)) { 336 if (tp->t_state & TS_ISOPEN) 337 (*linesw[tp->t_line].l_rint)(c, tp); 338 } 339 } 340