1 /* $NetBSD: ofcons.c,v 1.43 2011/07/26 08:59:38 mrg Exp $ */ 2 3 /* 4 * Copyright (C) 1995, 1996 Wolfgang Solfrank. 5 * Copyright (C) 1995, 1996 TooLs GmbH. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by TooLs GmbH. 19 * 4. The name of TooLs GmbH may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: ofcons.c,v 1.43 2011/07/26 08:59:38 mrg Exp $"); 36 37 #include <sys/param.h> 38 #include <sys/conf.h> 39 #include <sys/device.h> 40 #include <sys/proc.h> 41 #include <sys/systm.h> 42 #include <sys/callout.h> 43 #include <sys/tty.h> 44 #include <sys/kauth.h> 45 46 #include <dev/cons.h> 47 48 #include <dev/ofw/openfirm.h> 49 50 struct ofcons_softc { 51 struct tty *of_tty; 52 struct callout sc_poll_ch; 53 int of_flags; 54 }; 55 /* flags: */ 56 #define OFPOLL 1 57 58 #define OFBURSTLEN 128 /* max number of bytes to write in one chunk */ 59 60 cons_decl(ofcons_); 61 62 static int stdin, stdout; 63 64 static int ofcons_match(device_t, cfdata_t, void *); 65 static void ofcons_attach(device_t, device_t, void *); 66 67 CFATTACH_DECL_NEW(ofcons, sizeof(struct ofcons_softc), 68 ofcons_match, ofcons_attach, NULL, NULL); 69 70 extern struct cfdriver ofcons_cd; 71 72 dev_type_open(ofcons_open); 73 dev_type_close(ofcons_close); 74 dev_type_read(ofcons_read); 75 dev_type_write(ofcons_write); 76 dev_type_ioctl(ofcons_ioctl); 77 dev_type_tty(ofcons_tty); 78 dev_type_poll(ofcons_poll); 79 80 const struct cdevsw ofcons_cdevsw = { 81 ofcons_open, ofcons_close, ofcons_read, ofcons_write, ofcons_ioctl, 82 nostop, ofcons_tty, ofcons_poll, nommap, ttykqfilter, D_TTY 83 }; 84 85 static int ofcons_probe(void); 86 87 static int 88 ofcons_match(device_t parent, cfdata_t match, void *aux) 89 { 90 struct ofbus_attach_args *oba = aux; 91 92 if (strcmp(oba->oba_busname, "ofw")) 93 return (0); 94 if (!ofcons_probe()) 95 return 0; 96 return OF_instance_to_package(stdin) == oba->oba_phandle 97 || OF_instance_to_package(stdout) == oba->oba_phandle; 98 } 99 100 static void 101 ofcons_attach(device_t parent, device_t self, void *aux) 102 { 103 struct ofcons_softc *sc = device_private(self); 104 105 printf("\n"); 106 107 callout_init(&sc->sc_poll_ch, 0); 108 } 109 110 static void ofcons_start(struct tty *); 111 static int ofcons_param(struct tty *, struct termios *); 112 static void ofcons_pollin(void *); 113 114 int 115 ofcons_open(dev_t dev, int flag, int mode, struct lwp *l) 116 { 117 struct ofcons_softc *sc; 118 struct tty *tp; 119 120 sc = device_lookup_private(&ofcons_cd, minor(dev)); 121 if (!sc) 122 return ENXIO; 123 if (!(tp = sc->of_tty)) 124 sc->of_tty = tp = tty_alloc(); 125 tp->t_oproc = ofcons_start; 126 tp->t_param = ofcons_param; 127 tp->t_dev = dev; 128 if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp)) 129 return (EBUSY); 130 if (!(tp->t_state & TS_ISOPEN)) { 131 ttychars(tp); 132 tp->t_iflag = TTYDEF_IFLAG; 133 tp->t_oflag = TTYDEF_OFLAG; 134 tp->t_cflag = TTYDEF_CFLAG; 135 tp->t_lflag = TTYDEF_LFLAG; 136 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 137 ofcons_param(tp, &tp->t_termios); 138 ttsetwater(tp); 139 } 140 tp->t_state |= TS_CARR_ON; 141 142 if (!(sc->of_flags & OFPOLL)) { 143 sc->of_flags |= OFPOLL; 144 callout_reset(&sc->sc_poll_ch, 1, ofcons_pollin, sc); 145 } 146 147 return (*tp->t_linesw->l_open)(dev, tp); 148 } 149 150 int 151 ofcons_close(dev_t dev, int flag, int mode, struct lwp *l) 152 { 153 struct ofcons_softc *sc = device_lookup_private(&ofcons_cd, minor(dev)); 154 struct tty *tp = sc->of_tty; 155 156 callout_stop(&sc->sc_poll_ch); 157 sc->of_flags &= ~OFPOLL; 158 (*tp->t_linesw->l_close)(tp, flag); 159 ttyclose(tp); 160 return 0; 161 } 162 163 int 164 ofcons_read(dev_t dev, struct uio *uio, int flag) 165 { 166 struct ofcons_softc *sc = device_lookup_private(&ofcons_cd, minor(dev)); 167 struct tty *tp = sc->of_tty; 168 169 return (*tp->t_linesw->l_read)(tp, uio, flag); 170 } 171 172 int 173 ofcons_write(dev_t dev, struct uio *uio, int flag) 174 { 175 struct ofcons_softc *sc = device_lookup_private(&ofcons_cd, minor(dev)); 176 struct tty *tp = sc->of_tty; 177 178 return (*tp->t_linesw->l_write)(tp, uio, flag); 179 } 180 181 int 182 ofcons_poll(dev_t dev, int events, struct lwp *l) 183 { 184 struct ofcons_softc *sc = device_lookup_private(&ofcons_cd, minor(dev)); 185 struct tty *tp = sc->of_tty; 186 187 return ((*tp->t_linesw->l_poll)(tp, events, l)); 188 } 189 int 190 ofcons_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 191 { 192 struct ofcons_softc *sc = device_lookup_private(&ofcons_cd, minor(dev)); 193 struct tty *tp = sc->of_tty; 194 int error; 195 196 if ((error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l)) != EPASSTHROUGH) 197 return error; 198 return ttioctl(tp, cmd, data, flag, l); 199 } 200 201 struct tty * 202 ofcons_tty(dev_t dev) 203 { 204 struct ofcons_softc *sc = device_lookup_private(&ofcons_cd, minor(dev)); 205 206 return sc->of_tty; 207 } 208 209 static void 210 ofcons_start(struct tty *tp) 211 { 212 int s, len; 213 u_char buf[OFBURSTLEN]; 214 215 s = spltty(); 216 if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) { 217 splx(s); 218 return; 219 } 220 tp->t_state |= TS_BUSY; 221 splx(s); 222 len = q_to_b(&tp->t_outq, buf, OFBURSTLEN); 223 OF_write(stdout, buf, len); 224 s = spltty(); 225 tp->t_state &= ~TS_BUSY; 226 if (ttypull(tp)) { 227 tp->t_state |= TS_TIMEOUT; 228 callout_schedule(&tp->t_rstrt_ch, 1); 229 } 230 splx(s); 231 } 232 233 static int 234 ofcons_param(struct tty *tp, struct termios *t) 235 { 236 tp->t_ispeed = t->c_ispeed; 237 tp->t_ospeed = t->c_ospeed; 238 tp->t_cflag = t->c_cflag; 239 return 0; 240 } 241 242 static void 243 ofcons_pollin(void *aux) 244 { 245 struct ofcons_softc *sc = aux; 246 struct tty *tp = sc->of_tty; 247 char ch; 248 249 while (OF_read(stdin, &ch, 1) > 0) { 250 if (tp && (tp->t_state & TS_ISOPEN)) 251 (*tp->t_linesw->l_rint)(ch, tp); 252 } 253 callout_reset(&sc->sc_poll_ch, 1, ofcons_pollin, sc); 254 } 255 256 static int 257 ofcons_probe(void) 258 { 259 int chosen; 260 char stdinbuf[4], stdoutbuf[4]; 261 262 if (stdin) 263 return 1; 264 if ((chosen = OF_finddevice("/chosen")) == -1) 265 return 0; 266 if (OF_getprop(chosen, "stdin", stdinbuf, sizeof stdinbuf) != 267 sizeof stdinbuf || 268 OF_getprop(chosen, "stdout", stdoutbuf, sizeof stdoutbuf) != 269 sizeof stdoutbuf) 270 return 0; 271 272 /* Decode properties. */ 273 stdin = of_decode_int(stdinbuf); 274 stdout = of_decode_int(stdoutbuf); 275 276 return 1; 277 } 278 279 void 280 ofcons_cnprobe(struct consdev *cd) 281 { 282 int maj; 283 284 if (!ofcons_probe()) 285 return; 286 287 maj = cdevsw_lookup_major(&ofcons_cdevsw); 288 cd->cn_dev = makedev(maj, 0); 289 cd->cn_pri = CN_INTERNAL; 290 } 291 292 void 293 ofcons_cninit(struct consdev *cd) 294 { 295 } 296 297 int 298 ofcons_cngetc(dev_t dev) 299 { 300 unsigned char ch = '\0'; 301 int l; 302 303 while ((l = OF_read(stdin, &ch, 1)) != 1) 304 if (l != -2 && l != 0) 305 return -1; 306 return ch; 307 } 308 309 void 310 ofcons_cnputc(dev_t dev, int c) 311 { 312 char ch = c; 313 314 OF_write(stdout, &ch, 1); 315 } 316 317 void 318 ofcons_cnpollc(dev_t dev, int on) 319 { 320 struct ofcons_softc *sc = device_lookup_private(&ofcons_cd, minor(dev)); 321 322 if (!sc) 323 return; 324 if (on) { 325 if (sc->of_flags & OFPOLL) 326 callout_stop(&sc->sc_poll_ch); 327 sc->of_flags &= ~OFPOLL; 328 } else { 329 if (!(sc->of_flags & OFPOLL)) { 330 sc->of_flags |= OFPOLL; 331 callout_reset(&sc->sc_poll_ch, 1, ofcons_pollin, sc); 332 } 333 } 334 } 335