1 /* $NetBSD: ofcons.c,v 1.41 2009/05/29 20:10:39 rjs 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.41 2009/05/29 20:10:39 rjs 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 device of_dev; 52 struct tty *of_tty; 53 struct callout sc_poll_ch; 54 int of_flags; 55 }; 56 /* flags: */ 57 #define OFPOLL 1 58 59 #define OFBURSTLEN 128 /* max number of bytes to write in one chunk */ 60 61 cons_decl(ofcons_); 62 63 static int stdin, stdout; 64 65 static int ofcons_match(device_t, cfdata_t, void *); 66 static void ofcons_attach(device_t, device_t, void *); 67 68 CFATTACH_DECL(ofcons, sizeof(struct ofcons_softc), 69 ofcons_match, ofcons_attach, NULL, NULL); 70 71 extern struct cfdriver ofcons_cd; 72 73 dev_type_open(ofcons_open); 74 dev_type_close(ofcons_close); 75 dev_type_read(ofcons_read); 76 dev_type_write(ofcons_write); 77 dev_type_ioctl(ofcons_ioctl); 78 dev_type_tty(ofcons_tty); 79 dev_type_poll(ofcons_poll); 80 81 const struct cdevsw ofcons_cdevsw = { 82 ofcons_open, ofcons_close, ofcons_read, ofcons_write, ofcons_ioctl, 83 nostop, ofcons_tty, ofcons_poll, nommap, ttykqfilter, D_TTY 84 }; 85 86 static int ofcons_probe(void); 87 88 static int 89 ofcons_match(device_t parent, cfdata_t match, void *aux) 90 { 91 struct ofbus_attach_args *oba = aux; 92 93 if (strcmp(oba->oba_busname, "ofw")) 94 return (0); 95 if (!ofcons_probe()) 96 return 0; 97 return OF_instance_to_package(stdin) == oba->oba_phandle 98 || OF_instance_to_package(stdout) == oba->oba_phandle; 99 } 100 101 static void 102 ofcons_attach(device_t parent, device_t self, void *aux) 103 { 104 struct ofcons_softc *sc = device_private(self); 105 106 printf("\n"); 107 108 callout_init(&sc->sc_poll_ch, 0); 109 } 110 111 static void ofcons_start(struct tty *); 112 static int ofcons_param(struct tty *, struct termios *); 113 static void ofcons_pollin(void *); 114 115 int 116 ofcons_open(dev_t dev, int flag, int mode, struct lwp *l) 117 { 118 struct ofcons_softc *sc; 119 struct tty *tp; 120 121 sc = device_lookup_private(&ofcons_cd, minor(dev)); 122 if (!sc) 123 return ENXIO; 124 if (!(tp = sc->of_tty)) 125 sc->of_tty = tp = ttymalloc(); 126 tp->t_oproc = ofcons_start; 127 tp->t_param = ofcons_param; 128 tp->t_dev = dev; 129 if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp)) 130 return (EBUSY); 131 if (!(tp->t_state & TS_ISOPEN)) { 132 ttychars(tp); 133 tp->t_iflag = TTYDEF_IFLAG; 134 tp->t_oflag = TTYDEF_OFLAG; 135 tp->t_cflag = TTYDEF_CFLAG; 136 tp->t_lflag = TTYDEF_LFLAG; 137 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 138 ofcons_param(tp, &tp->t_termios); 139 ttsetwater(tp); 140 } 141 tp->t_state |= TS_CARR_ON; 142 143 if (!(sc->of_flags & OFPOLL)) { 144 sc->of_flags |= OFPOLL; 145 callout_reset(&sc->sc_poll_ch, 1, ofcons_pollin, sc); 146 } 147 148 return (*tp->t_linesw->l_open)(dev, tp); 149 } 150 151 int 152 ofcons_close(dev_t dev, int flag, int mode, struct lwp *l) 153 { 154 struct ofcons_softc *sc = device_lookup_private(&ofcons_cd, minor(dev)); 155 struct tty *tp = sc->of_tty; 156 157 callout_stop(&sc->sc_poll_ch); 158 sc->of_flags &= ~OFPOLL; 159 (*tp->t_linesw->l_close)(tp, flag); 160 ttyclose(tp); 161 return 0; 162 } 163 164 int 165 ofcons_read(dev_t dev, struct uio *uio, int flag) 166 { 167 struct ofcons_softc *sc = device_lookup_private(&ofcons_cd, minor(dev)); 168 struct tty *tp = sc->of_tty; 169 170 return (*tp->t_linesw->l_read)(tp, uio, flag); 171 } 172 173 int 174 ofcons_write(dev_t dev, struct uio *uio, int flag) 175 { 176 struct ofcons_softc *sc = device_lookup_private(&ofcons_cd, minor(dev)); 177 struct tty *tp = sc->of_tty; 178 179 return (*tp->t_linesw->l_write)(tp, uio, flag); 180 } 181 182 int 183 ofcons_poll(dev_t dev, int events, struct lwp *l) 184 { 185 struct ofcons_softc *sc = device_lookup_private(&ofcons_cd, minor(dev)); 186 struct tty *tp = sc->of_tty; 187 188 return ((*tp->t_linesw->l_poll)(tp, events, l)); 189 } 190 int 191 ofcons_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 192 { 193 struct ofcons_softc *sc = device_lookup_private(&ofcons_cd, minor(dev)); 194 struct tty *tp = sc->of_tty; 195 int error; 196 197 if ((error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l)) != EPASSTHROUGH) 198 return error; 199 return ttioctl(tp, cmd, data, flag, l); 200 } 201 202 struct tty * 203 ofcons_tty(dev_t dev) 204 { 205 struct ofcons_softc *sc = device_lookup_private(&ofcons_cd, minor(dev)); 206 207 return sc->of_tty; 208 } 209 210 static void 211 ofcons_start(struct tty *tp) 212 { 213 int s, len; 214 u_char buf[OFBURSTLEN]; 215 216 s = spltty(); 217 if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) { 218 splx(s); 219 return; 220 } 221 tp->t_state |= TS_BUSY; 222 splx(s); 223 len = q_to_b(&tp->t_outq, buf, OFBURSTLEN); 224 OF_write(stdout, buf, len); 225 s = spltty(); 226 tp->t_state &= ~TS_BUSY; 227 if (ttypull(tp)) { 228 tp->t_state |= TS_TIMEOUT; 229 callout_schedule(&tp->t_rstrt_ch, 1); 230 } 231 splx(s); 232 } 233 234 static int 235 ofcons_param(struct tty *tp, struct termios *t) 236 { 237 tp->t_ispeed = t->c_ispeed; 238 tp->t_ospeed = t->c_ospeed; 239 tp->t_cflag = t->c_cflag; 240 return 0; 241 } 242 243 static void 244 ofcons_pollin(void *aux) 245 { 246 struct ofcons_softc *sc = aux; 247 struct tty *tp = sc->of_tty; 248 char ch; 249 250 while (OF_read(stdin, &ch, 1) > 0) { 251 if (tp && (tp->t_state & TS_ISOPEN)) 252 (*tp->t_linesw->l_rint)(ch, tp); 253 } 254 callout_reset(&sc->sc_poll_ch, 1, ofcons_pollin, sc); 255 } 256 257 static int 258 ofcons_probe(void) 259 { 260 int chosen; 261 char stdinbuf[4], stdoutbuf[4]; 262 263 if (stdin) 264 return 1; 265 if ((chosen = OF_finddevice("/chosen")) == -1) 266 return 0; 267 if (OF_getprop(chosen, "stdin", stdinbuf, sizeof stdinbuf) != 268 sizeof stdinbuf || 269 OF_getprop(chosen, "stdout", stdoutbuf, sizeof stdoutbuf) != 270 sizeof stdoutbuf) 271 return 0; 272 273 /* Decode properties. */ 274 stdin = of_decode_int(stdinbuf); 275 stdout = of_decode_int(stdoutbuf); 276 277 return 1; 278 } 279 280 void 281 ofcons_cnprobe(struct consdev *cd) 282 { 283 int maj; 284 285 if (!ofcons_probe()) 286 return; 287 288 maj = cdevsw_lookup_major(&ofcons_cdevsw); 289 cd->cn_dev = makedev(maj, 0); 290 cd->cn_pri = CN_INTERNAL; 291 } 292 293 void 294 ofcons_cninit(struct consdev *cd) 295 { 296 } 297 298 int 299 ofcons_cngetc(dev_t dev) 300 { 301 unsigned char ch = '\0'; 302 int l; 303 304 while ((l = OF_read(stdin, &ch, 1)) != 1) 305 if (l != -2 && l != 0) 306 return -1; 307 return ch; 308 } 309 310 void 311 ofcons_cnputc(dev_t dev, int c) 312 { 313 char ch = c; 314 315 OF_write(stdout, &ch, 1); 316 } 317 318 void 319 ofcons_cnpollc(dev_t dev, int on) 320 { 321 struct ofcons_softc *sc = device_lookup_private(&ofcons_cd, minor(dev)); 322 323 if (!sc) 324 return; 325 if (on) { 326 if (sc->of_flags & OFPOLL) 327 callout_stop(&sc->sc_poll_ch); 328 sc->of_flags &= ~OFPOLL; 329 } else { 330 if (!(sc->of_flags & OFPOLL)) { 331 sc->of_flags |= OFPOLL; 332 callout_reset(&sc->sc_poll_ch, 1, ofcons_pollin, sc); 333 } 334 } 335 } 336