ofw_console.c revision 164033
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: head/sys/dev/ofw/ofw_console.c 164033 2006-11-06 13:42:10Z rwatson $"); 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/systm.h> 36#include <sys/types.h> 37#include <sys/conf.h> 38#include <sys/cons.h> 39#include <sys/consio.h> 40#include <sys/tty.h> 41 42#include <dev/ofw/openfirm.h> 43 44#include <ddb/ddb.h> 45 46#ifndef OFWCONS_POLL_HZ 47#define OFWCONS_POLL_HZ 4 /* 50-100 works best on Ultra2 */ 48#endif 49#define OFBURSTLEN 128 /* max number of bytes to write in one chunk */ 50 51static d_open_t ofw_dev_open; 52static d_close_t ofw_dev_close; 53 54static struct cdevsw ofw_cdevsw = { 55 .d_version = D_VERSION, 56 .d_open = ofw_dev_open, 57 .d_close = ofw_dev_close, 58 .d_name = "ofw", 59 .d_flags = D_TTY | D_NEEDGIANT, 60}; 61 62static struct tty *ofw_tp = NULL; 63static int polltime; 64static struct callout_handle ofw_timeouthandle 65 = CALLOUT_HANDLE_INITIALIZER(&ofw_timeouthandle); 66 67#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER) 68static int alt_break_state; 69#endif 70 71static void ofw_tty_start(struct tty *); 72static int ofw_tty_param(struct tty *, struct termios *); 73static void ofw_tty_stop(struct tty *, int); 74static void ofw_timeout(void *); 75 76static cn_probe_t ofw_cnprobe; 77static cn_init_t ofw_cninit; 78static cn_term_t ofw_cnterm; 79static cn_getc_t ofw_cngetc; 80static cn_putc_t ofw_cnputc; 81 82CONSOLE_DRIVER(ofw); 83 84static void 85cn_drvinit(void *unused) 86{ 87 phandle_t options; 88 char output[32]; 89 struct cdev *dev; 90 91 if (ofw_consdev.cn_pri != CN_DEAD && 92 ofw_consdev.cn_name[0] != '\0') { 93 if ((options = OF_finddevice("/options")) == -1 || 94 OF_getprop(options, "output-device", output, 95 sizeof(output)) == -1) 96 return; 97 /* 98 * XXX: This is a hack and it may result in two /dev/ttya 99 * XXX: devices on platforms where the sab driver works. 100 */ 101 dev = make_dev(&ofw_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "%s", 102 output); 103 make_dev_alias(dev, "ofwcons"); 104 } 105} 106 107SYSINIT(cndev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, cn_drvinit, NULL) 108 109static int stdin; 110static int stdout; 111 112static int 113ofw_dev_open(struct cdev *dev, int flag, int mode, struct thread *td) 114{ 115 struct tty *tp; 116 int unit; 117 int error, setuptimeout; 118 119 error = 0; 120 setuptimeout = 0; 121 unit = minor(dev); 122 123 /* 124 * XXX: BAD, should happen at attach time 125 */ 126 if (dev->si_tty == NULL) { 127 ofw_tp = ttyalloc(); 128 dev->si_tty = ofw_tp; 129 ofw_tp->t_dev = dev; 130 } 131 tp = dev->si_tty; 132 133 tp->t_oproc = ofw_tty_start; 134 tp->t_param = ofw_tty_param; 135 tp->t_stop = ofw_tty_stop; 136 tp->t_dev = dev; 137 138 if ((tp->t_state & TS_ISOPEN) == 0) { 139 tp->t_state |= TS_CARR_ON; 140 ttyconsolemode(tp, 0); 141 142 setuptimeout = 1; 143 } else if ((tp->t_state & TS_XCLUDE) && 144 priv_check(td, PRIV_TTY_EXCLUSIVE)) { 145 return (EBUSY); 146 } 147 148 error = ttyld_open(tp, dev); 149 150 if (error == 0 && setuptimeout) { 151 polltime = hz / OFWCONS_POLL_HZ; 152 if (polltime < 1) { 153 polltime = 1; 154 } 155 156 ofw_timeouthandle = timeout(ofw_timeout, tp, polltime); 157 } 158 159 return (error); 160} 161 162static int 163ofw_dev_close(struct cdev *dev, int flag, int mode, struct thread *td) 164{ 165 int unit; 166 struct tty *tp; 167 168 unit = minor(dev); 169 tp = dev->si_tty; 170 171 if (unit != 0) { 172 return (ENXIO); 173 } 174 175 /* XXX Should be replaced with callout_stop(9) */ 176 untimeout(ofw_timeout, tp, ofw_timeouthandle); 177 ttyld_close(tp, flag); 178 tty_close(tp); 179 180 return (0); 181} 182 183 184static int 185ofw_tty_param(struct tty *tp, struct termios *t) 186{ 187 188 return (0); 189} 190 191static void 192ofw_tty_start(struct tty *tp) 193{ 194 struct clist *cl; 195 int len; 196 u_char buf[OFBURSTLEN]; 197 198 199 if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) 200 return; 201 202 tp->t_state |= TS_BUSY; 203 cl = &tp->t_outq; 204 len = q_to_b(cl, buf, OFBURSTLEN); 205 OF_write(stdout, buf, len); 206 tp->t_state &= ~TS_BUSY; 207 208 ttwwakeup(tp); 209} 210 211static void 212ofw_tty_stop(struct tty *tp, int flag) 213{ 214 215 if (tp->t_state & TS_BUSY) { 216 if ((tp->t_state & TS_TTSTOP) == 0) { 217 tp->t_state |= TS_FLUSH; 218 } 219 } 220} 221 222static void 223ofw_timeout(void *v) 224{ 225 struct tty *tp; 226 int c; 227 228 tp = (struct tty *)v; 229 230 while ((c = ofw_cngetc(NULL)) != -1) { 231 if (tp->t_state & TS_ISOPEN) { 232 ttyld_rint(tp, c); 233 } 234 } 235 236 ofw_timeouthandle = timeout(ofw_timeout, tp, polltime); 237} 238 239static void 240ofw_cnprobe(struct consdev *cp) 241{ 242 int chosen; 243 244 if ((chosen = OF_finddevice("/chosen")) == -1) { 245 cp->cn_pri = CN_DEAD; 246 return; 247 } 248 249 if (OF_getprop(chosen, "stdin", &stdin, sizeof(stdin)) == -1) { 250 cp->cn_pri = CN_DEAD; 251 return; 252 } 253 254 if (OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)) == -1) { 255 cp->cn_pri = CN_DEAD; 256 return; 257 } 258 259 cp->cn_pri = CN_LOW; 260} 261 262static void 263ofw_cninit(struct consdev *cp) 264{ 265 266 /* XXX: This is the alias, but that should be good enough */ 267 sprintf(cp->cn_name, "ofwcons"); 268 cp->cn_tp = ofw_tp; 269} 270 271static void 272ofw_cnterm(struct consdev *cp) 273{ 274} 275 276static int 277ofw_cngetc(struct consdev *cp) 278{ 279 unsigned char ch; 280 281 if (OF_read(stdin, &ch, 1) > 0) { 282#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER) 283 if (kdb_alt_break(ch, &alt_break_state)) 284 kdb_enter("Break sequence on console"); 285#endif 286 return (ch); 287 } 288 289 return (-1); 290} 291 292static void 293ofw_cnputc(struct consdev *cp, int c) 294{ 295 char cbuf; 296 297 if (c == '\n') { 298 cbuf = '\r'; 299 OF_write(stdout, &cbuf, 1); 300 } 301 302 cbuf = c; 303 OF_write(stdout, &cbuf, 1); 304} 305