1 /* $NetBSD: pcons.c,v 1.7 2003/07/15 03:36:12 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 2000 Eduardo E. Horvath 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 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * Default console driver. Uses the PROM or whatever 33 * driver(s) are appropriate. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: pcons.c,v 1.7 2003/07/15 03:36:12 lukem Exp $"); 38 39 #include "opt_ddb.h" 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/conf.h> 44 #include <sys/device.h> 45 #include <sys/file.h> 46 #include <sys/ioctl.h> 47 #include <sys/kernel.h> 48 #include <sys/proc.h> 49 #include <sys/tty.h> 50 #include <sys/time.h> 51 #include <sys/syslog.h> 52 53 #include <machine/autoconf.h> 54 #include <machine/promlib.h> 55 #include <machine/cpu.h> 56 #include <machine/psl.h> 57 58 #include <dev/cons.h> 59 60 #include <sun2/dev/cons.h> 61 62 static int pconsmatch __P((struct device *, struct cfdata *, void *)); 63 static void pconsattach __P((struct device *, struct device *, void *)); 64 65 CFATTACH_DECL(pcons, sizeof(struct pconssoftc), 66 pconsmatch, pconsattach, NULL, NULL); 67 68 extern struct cfdriver pcons_cd; 69 static struct cnm_state pcons_cnm_state; 70 71 static int pconsprobe __P((void)); 72 extern struct consdev *cn_tab; 73 74 dev_type_open(pconsopen); 75 dev_type_close(pconsclose); 76 dev_type_read(pconsread); 77 dev_type_write(pconswrite); 78 dev_type_ioctl(pconsioctl); 79 dev_type_tty(pconstty); 80 dev_type_poll(pconspoll); 81 82 const struct cdevsw pcons_cdevsw = { 83 pconsopen, pconsclose, pconsread, pconswrite, pconsioctl, 84 nostop, pconstty, pconspoll, nommap, ttykqfilter, D_TTY 85 }; 86 87 static int 88 pconsmatch(parent, match, aux) 89 struct device *parent; 90 struct cfdata *match; 91 void *aux; 92 { 93 struct mainbus_attach_args *ma = aux; 94 extern int prom_cngetc __P((dev_t)); 95 96 /* Only attach if no other console has attached. */ 97 return ((ma->ma_name != NULL) && 98 (strcmp("pcons", ma->ma_name) == 0) && 99 (cn_tab->cn_getc == prom_cngetc)); 100 101 } 102 103 static void 104 pconsattach(parent, self, aux) 105 struct device *parent, *self; 106 void *aux; 107 { 108 struct pconssoftc *sc = (struct pconssoftc *) self; 109 110 printf("\n"); 111 if (!pconsprobe()) 112 return; 113 114 cn_init_magic(&pcons_cnm_state); 115 cn_set_magic("+++++"); 116 callout_init(&sc->sc_poll_ch); 117 } 118 119 static void pconsstart __P((struct tty *)); 120 static int pconsparam __P((struct tty *, struct termios *)); 121 static void pcons_poll __P((void *)); 122 123 int 124 pconsopen(dev, flag, mode, p) 125 dev_t dev; 126 int flag, mode; 127 struct proc *p; 128 { 129 struct pconssoftc *sc; 130 int unit = minor(dev); 131 struct tty *tp; 132 133 if (unit >= pcons_cd.cd_ndevs) 134 return ENXIO; 135 sc = pcons_cd.cd_devs[unit]; 136 if (!sc) 137 return ENXIO; 138 if (!(tp = sc->of_tty)) 139 sc->of_tty = tp = ttymalloc(); 140 tp->t_oproc = pconsstart; 141 tp->t_param = pconsparam; 142 tp->t_dev = dev; 143 cn_tab->cn_dev = dev; 144 if (!(tp->t_state & TS_ISOPEN)) { 145 ttychars(tp); 146 tp->t_iflag = TTYDEF_IFLAG; 147 tp->t_oflag = TTYDEF_OFLAG; 148 tp->t_cflag = TTYDEF_CFLAG; 149 tp->t_lflag = TTYDEF_LFLAG; 150 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 151 pconsparam(tp, &tp->t_termios); 152 ttsetwater(tp); 153 } else if ((tp->t_state&TS_XCLUDE) && suser(p->p_ucred, &p->p_acflag)) 154 return EBUSY; 155 tp->t_state |= TS_CARR_ON; 156 157 if (!(sc->of_flags & OFPOLL)) { 158 sc->of_flags |= OFPOLL; 159 callout_reset(&sc->sc_poll_ch, 1, pcons_poll, sc); 160 } 161 162 return (*tp->t_linesw->l_open)(dev, tp); 163 } 164 165 int 166 pconsclose(dev, flag, mode, p) 167 dev_t dev; 168 int flag, mode; 169 struct proc *p; 170 { 171 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)]; 172 struct tty *tp = sc->of_tty; 173 174 callout_stop(&sc->sc_poll_ch); 175 sc->of_flags &= ~OFPOLL; 176 (*tp->t_linesw->l_close)(tp, flag); 177 ttyclose(tp); 178 return 0; 179 } 180 181 int 182 pconsread(dev, uio, flag) 183 dev_t dev; 184 struct uio *uio; 185 int flag; 186 { 187 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)]; 188 struct tty *tp = sc->of_tty; 189 190 return (*tp->t_linesw->l_read)(tp, uio, flag); 191 } 192 193 int 194 pconswrite(dev, uio, flag) 195 dev_t dev; 196 struct uio *uio; 197 int flag; 198 { 199 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)]; 200 struct tty *tp = sc->of_tty; 201 202 return (*tp->t_linesw->l_write)(tp, uio, flag); 203 } 204 205 int 206 pconspoll(dev, events, p) 207 dev_t dev; 208 int events; 209 struct proc *p; 210 { 211 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)]; 212 struct tty *tp = sc->of_tty; 213 214 return ((*tp->t_linesw->l_poll)(tp, events, p)); 215 } 216 217 int 218 pconsioctl(dev, cmd, data, flag, p) 219 dev_t dev; 220 u_long cmd; 221 caddr_t data; 222 int flag; 223 struct proc *p; 224 { 225 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)]; 226 struct tty *tp = sc->of_tty; 227 int error; 228 229 if ((error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, p)) >= 0) 230 return error; 231 if ((error = ttioctl(tp, cmd, data, flag, p)) >= 0) 232 return error; 233 return ENOTTY; 234 } 235 236 struct tty * 237 pconstty(dev) 238 dev_t dev; 239 { 240 struct pconssoftc *sc = pcons_cd.cd_devs[minor(dev)]; 241 242 return sc->of_tty; 243 } 244 245 static void 246 pconsstart(tp) 247 struct tty *tp; 248 { 249 struct clist *cl; 250 int s, len; 251 u_char buf[OFBURSTLEN]; 252 253 s = spltty(); 254 if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) { 255 splx(s); 256 return; 257 } 258 tp->t_state |= TS_BUSY; 259 splx(s); 260 cl = &tp->t_outq; 261 len = q_to_b(cl, buf, OFBURSTLEN); 262 prom_putstr(buf, len); 263 s = spltty(); 264 tp->t_state &= ~TS_BUSY; 265 if (cl->c_cc) { 266 tp->t_state |= TS_TIMEOUT; 267 callout_reset(&tp->t_rstrt_ch, 1, ttrstrt, (void *)tp); 268 } 269 if (cl->c_cc <= tp->t_lowat) { 270 if (tp->t_state & TS_ASLEEP) { 271 tp->t_state &= ~TS_ASLEEP; 272 wakeup(cl); 273 } 274 selwakeup(&tp->t_wsel); 275 } 276 splx(s); 277 } 278 279 static int 280 pconsparam(tp, t) 281 struct tty *tp; 282 struct termios *t; 283 { 284 tp->t_ispeed = t->c_ispeed; 285 tp->t_ospeed = t->c_ospeed; 286 tp->t_cflag = t->c_cflag; 287 return 0; 288 } 289 290 static void 291 pcons_poll(aux) 292 void *aux; 293 { 294 struct pconssoftc *sc = aux; 295 struct tty *tp = sc->of_tty; 296 int c; 297 char ch; 298 299 while ((c = prom_peekchar()) >= 0) { 300 ch = c; 301 cn_check_magic(tp->t_dev, ch, pcons_cnm_state); 302 if (tp && (tp->t_state & TS_ISOPEN)) 303 (*tp->t_linesw->l_rint)(ch, tp); 304 } 305 callout_reset(&sc->sc_poll_ch, 1, pcons_poll, sc); 306 } 307 308 int 309 pconsprobe() 310 { 311 switch (prom_version()) { 312 #ifdef PROM_OLDMON 313 case PROM_OLDMON: 314 case PROM_OBP_V0: 315 return (1); 316 #endif /* PROM_OLDMON */ 317 #ifdef PROM_OBP_V2 318 case PROM_OBP_V2: 319 case PROM_OBP_V3: 320 case PROM_OPENFIRM: 321 return (prom_stdin() && prom_stdout()); 322 #endif /* PROM_OBP_V2 */ 323 default: break; 324 } 325 return (0); 326 } 327 328 void 329 pcons_cnpollc(dev, on) 330 dev_t dev; 331 int on; 332 { 333 struct pconssoftc *sc = NULL; 334 335 if (pcons_cd.cd_devs) 336 sc = pcons_cd.cd_devs[minor(dev)]; 337 338 if (on) { 339 if (!sc) return; 340 if (sc->of_flags & OFPOLL) 341 callout_stop(&sc->sc_poll_ch); 342 sc->of_flags &= ~OFPOLL; 343 } else { 344 /* Resuming kernel. */ 345 if (sc && !(sc->of_flags & OFPOLL)) { 346 sc->of_flags |= OFPOLL; 347 callout_reset(&sc->sc_poll_ch, 1, pcons_poll, sc); 348 } 349 } 350 } 351 352 void pcons_dopoll __P((void)); 353 void 354 pcons_dopoll() { 355 pcons_poll((void*)pcons_cd.cd_devs[0]); 356 } 357