1 /*- 2 * Copyright (C) 2001 Benno Rice. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 22 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 23 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 __FBSDID("$FreeBSD$"); 28 29 #include "opt_comconsole.h" 30 #include "opt_ofw.h" 31 32 #include <sys/param.h> 33 #include <sys/kdb.h> 34 #include <sys/kernel.h> 35 #include <sys/priv.h> 36 #include <sys/systm.h> 37 #include <sys/types.h> 38 #include <sys/conf.h> 39 #include <sys/cons.h> 40 #include <sys/consio.h> 41 #include <sys/tty.h> 42 43 #include <dev/ofw/openfirm.h> 44 45 #include <ddb/ddb.h> 46 47 #ifndef OFWCONS_POLL_HZ 48 #define OFWCONS_POLL_HZ 4 /* 50-100 works best on Ultra2 */ 49 #endif 50 #define OFBURSTLEN 128 /* max number of bytes to write in one chunk */ 51 52 static d_open_t ofw_dev_open; 53 static d_close_t ofw_dev_close; 54 55 static struct cdevsw ofw_cdevsw = { 56 .d_version = D_VERSION, 57 .d_open = ofw_dev_open, 58 .d_close = ofw_dev_close, 59 .d_name = "ofw", 60 .d_flags = D_TTY | D_NEEDGIANT, 61 }; 62 63 static struct tty *ofw_tp = NULL; 64 static int polltime; 65 static struct callout_handle ofw_timeouthandle 66 = CALLOUT_HANDLE_INITIALIZER(&ofw_timeouthandle); 67 68 #if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER) 69 static int alt_break_state; 70 #endif 71 72 static void ofw_tty_start(struct tty *); 73 static int ofw_tty_param(struct tty *, struct termios *); 74 static void ofw_tty_stop(struct tty *, int); 75 static void ofw_timeout(void *); 76 77 static cn_probe_t ofw_cnprobe; 78 static cn_init_t ofw_cninit; 79 static cn_term_t ofw_cnterm; 80 static cn_getc_t ofw_cngetc; 81 static cn_putc_t ofw_cnputc; 82 83 CONSOLE_DRIVER(ofw); 84 85 static void 86 cn_drvinit(void *unused) 87 { 88 phandle_t options; 89 char output[32]; 90 struct cdev *dev; 91 92 if (ofw_consdev.cn_pri != CN_DEAD && 93 ofw_consdev.cn_name[0] != '\0') { 94 if ((options = OF_finddevice("/options")) == -1 || 95 OF_getprop(options, "output-device", output, 96 sizeof(output)) == -1) 97 return; 98 /* 99 * XXX: This is a hack and it may result in two /dev/ttya 100 * XXX: devices on platforms where the sab driver works. 101 */ 102 dev = make_dev(&ofw_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "%s", 103 output); 104 make_dev_alias(dev, "ofwcons"); 105 } 106 } 107 108 SYSINIT(cndev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, cn_drvinit, NULL) 109 110 static int stdin; 111 static int stdout; 112 113 static int 114 ofw_dev_open(struct cdev *dev, int flag, int mode, struct thread *td) 115 { 116 struct tty *tp; 117 int unit; 118 int error, setuptimeout; 119 120 error = 0; 121 setuptimeout = 0; 122 unit = minor(dev); 123 124 /* 125 * XXX: BAD, should happen at attach time 126 */ 127 if (dev->si_tty == NULL) { 128 ofw_tp = ttyalloc(); 129 dev->si_tty = ofw_tp; 130 ofw_tp->t_dev = dev; 131 } 132 tp = dev->si_tty; 133 134 tp->t_oproc = ofw_tty_start; 135 tp->t_param = ofw_tty_param; 136 tp->t_stop = ofw_tty_stop; 137 tp->t_dev = dev; 138 139 if ((tp->t_state & TS_ISOPEN) == 0) { 140 tp->t_state |= TS_CARR_ON; 141 ttyconsolemode(tp, 0); 142 143 setuptimeout = 1; 144 } else if ((tp->t_state & TS_XCLUDE) && 145 priv_check(td, PRIV_TTY_EXCLUSIVE)) { 146 return (EBUSY); 147 } 148 149 error = ttyld_open(tp, dev); 150 151 if (error == 0 && setuptimeout) { 152 polltime = hz / OFWCONS_POLL_HZ; 153 if (polltime < 1) { 154 polltime = 1; 155 } 156 157 ofw_timeouthandle = timeout(ofw_timeout, tp, polltime); 158 } 159 160 return (error); 161 } 162 163 static int 164 ofw_dev_close(struct cdev *dev, int flag, int mode, struct thread *td) 165 { 166 int unit; 167 struct tty *tp; 168 169 unit = minor(dev); 170 tp = dev->si_tty; 171 172 if (unit != 0) { 173 return (ENXIO); 174 } 175 176 /* XXX Should be replaced with callout_stop(9) */ 177 untimeout(ofw_timeout, tp, ofw_timeouthandle); 178 ttyld_close(tp, flag); 179 tty_close(tp); 180 181 return (0); 182 } 183 184 185 static int 186 ofw_tty_param(struct tty *tp, struct termios *t) 187 { 188 189 return (0); 190 } 191 192 static void 193 ofw_tty_start(struct tty *tp) 194 { 195 struct clist *cl; 196 int len; 197 u_char buf[OFBURSTLEN]; 198 199 200 if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) 201 return; 202 203 tp->t_state |= TS_BUSY; 204 cl = &tp->t_outq; 205 len = q_to_b(cl, buf, OFBURSTLEN); 206 OF_write(stdout, buf, len); 207 tp->t_state &= ~TS_BUSY; 208 209 ttwwakeup(tp); 210 } 211 212 static void 213 ofw_tty_stop(struct tty *tp, int flag) 214 { 215 216 if (tp->t_state & TS_BUSY) { 217 if ((tp->t_state & TS_TTSTOP) == 0) { 218 tp->t_state |= TS_FLUSH; 219 } 220 } 221 } 222 223 static void 224 ofw_timeout(void *v) 225 { 226 struct tty *tp; 227 int c; 228 229 tp = (struct tty *)v; 230 231 while ((c = ofw_cngetc(NULL)) != -1) { 232 if (tp->t_state & TS_ISOPEN) { 233 ttyld_rint(tp, c); 234 } 235 } 236 237 ofw_timeouthandle = timeout(ofw_timeout, tp, polltime); 238 } 239 240 static void 241 ofw_cnprobe(struct consdev *cp) 242 { 243 int chosen; 244 245 if ((chosen = OF_finddevice("/chosen")) == -1) { 246 cp->cn_pri = CN_DEAD; 247 return; 248 } 249 250 if (OF_getprop(chosen, "stdin", &stdin, sizeof(stdin)) == -1) { 251 cp->cn_pri = CN_DEAD; 252 return; 253 } 254 255 if (OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)) == -1) { 256 cp->cn_pri = CN_DEAD; 257 return; 258 } 259 260 cp->cn_pri = CN_LOW; 261 } 262 263 static void 264 ofw_cninit(struct consdev *cp) 265 { 266 267 /* XXX: This is the alias, but that should be good enough */ 268 sprintf(cp->cn_name, "ofwcons"); 269 cp->cn_tp = ofw_tp; 270 } 271 272 static void 273 ofw_cnterm(struct consdev *cp) 274 { 275 } 276 277 static int 278 ofw_cngetc(struct consdev *cp) 279 { 280 unsigned char ch; 281 282 if (OF_read(stdin, &ch, 1) > 0) { 283 #if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER) 284 if (kdb_alt_break(ch, &alt_break_state)) 285 kdb_enter(KDB_WHY_BREAK, "Break sequence on console"); 286 #endif 287 return (ch); 288 } 289 290 return (-1); 291 } 292 293 static void 294 ofw_cnputc(struct consdev *cp, int c) 295 { 296 char cbuf; 297 298 if (c == '\n') { 299 cbuf = '\r'; 300 OF_write(stdout, &cbuf, 1); 301 } 302 303 cbuf = c; 304 OF_write(stdout, &cbuf, 1); 305 } 306