ofw_console.c revision 159065
1238106Sdes/*- 2238106Sdes * Copyright (C) 2001 Benno Rice. 3238106Sdes * All rights reserved. 4238106Sdes * 5238106Sdes * Redistribution and use in source and binary forms, with or without 6238106Sdes * modification, are permitted provided that the following conditions 7238106Sdes * are met: 8238106Sdes * 1. Redistributions of source code must retain the above copyright 9238106Sdes * notice, this list of conditions and the following disclaimer. 10238106Sdes * 2. Redistributions in binary form must reproduce the above copyright 11238106Sdes * notice, this list of conditions and the following disclaimer in the 12238106Sdes * documentation and/or other materials provided with the distribution. 13238106Sdes * 14238106Sdes * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR 15238106Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16238106Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17238106Sdes * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18238106Sdes * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19238106Sdes * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20238106Sdes * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21238106Sdes * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 22238106Sdes * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 23238106Sdes * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24266114Sdes */ 25266114Sdes 26266114Sdes#include <sys/cdefs.h> 27266114Sdes__FBSDID("$FreeBSD: head/sys/dev/ofw/ofw_console.c 159065 2006-05-30 07:56:57Z phk $"); 28266114Sdes 29266114Sdes#include "opt_comconsole.h" 30266114Sdes#include "opt_ofw.h" 31266114Sdes 32266114Sdes#include <sys/param.h> 33266114Sdes#include <sys/kdb.h> 34238106Sdes#include <sys/kernel.h> 35238106Sdes#include <sys/systm.h> 36238106Sdes#include <sys/types.h> 37238106Sdes#include <sys/conf.h> 38238106Sdes#include <sys/cons.h> 39238106Sdes#include <sys/consio.h> 40238106Sdes#include <sys/tty.h> 41238106Sdes 42238106Sdes#include <dev/ofw/openfirm.h> 43238106Sdes 44238106Sdes#include <ddb/ddb.h> 45238106Sdes 46238106Sdes#ifndef OFWCONS_POLL_HZ 47238106Sdes#define OFWCONS_POLL_HZ 4 /* 50-100 works best on Ultra2 */ 48238106Sdes#endif 49238106Sdes#define OFBURSTLEN 128 /* max number of bytes to write in one chunk */ 50238106Sdes 51238106Sdesstatic d_open_t ofw_dev_open; 52238106Sdesstatic d_close_t ofw_dev_close; 53238106Sdes 54238106Sdesstatic struct cdevsw ofw_cdevsw = { 55238106Sdes .d_version = D_VERSION, 56238106Sdes .d_open = ofw_dev_open, 57238106Sdes .d_close = ofw_dev_close, 58276605Sdes .d_name = "ofw", 59276605Sdes .d_flags = D_TTY | D_NEEDGIANT, 60276605Sdes}; 61238106Sdes 62238106Sdesstatic struct tty *ofw_tp = NULL; 63238106Sdesstatic int polltime; 64238106Sdesstatic struct callout_handle ofw_timeouthandle 65238106Sdes = CALLOUT_HANDLE_INITIALIZER(&ofw_timeouthandle); 66238106Sdes 67238106Sdes#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER) 68238106Sdesstatic int alt_break_state; 69238106Sdes#endif 70238106Sdes 71238106Sdesstatic void ofw_tty_start(struct tty *); 72238106Sdesstatic int ofw_tty_param(struct tty *, struct termios *); 73276605Sdesstatic void ofw_tty_stop(struct tty *, int); 74238106Sdesstatic void ofw_timeout(void *); 75238106Sdes 76238106Sdesstatic cn_probe_t ofw_cnprobe; 77238106Sdesstatic cn_init_t ofw_cninit; 78238106Sdesstatic cn_term_t ofw_cnterm; 79238106Sdesstatic cn_getc_t ofw_cngetc; 80238106Sdesstatic cn_putc_t ofw_cnputc; 81238106Sdes 82238106SdesCONSOLE_DRIVER(ofw); 83238106Sdes 84238106Sdesstatic void 85238106Sdescn_drvinit(void *unused) 86238106Sdes{ 87238106Sdes phandle_t options; 88238106Sdes char output[32]; 89238106Sdes struct cdev *dev; 90238106Sdes 91238106Sdes if (ofw_consdev.cn_pri != CN_DEAD && 92238106Sdes ofw_consdev.cn_name[0] != '\0') { 93238106Sdes if ((options = OF_finddevice("/options")) == -1 || 94238106Sdes OF_getprop(options, "output-device", output, 95238106Sdes sizeof(output)) == -1) 96238106Sdes return; 97238106Sdes /* 98238106Sdes * XXX: This is a hack and it may result in two /dev/ttya 99238106Sdes * XXX: devices on platforms where the sab driver works. 100238106Sdes */ 101238106Sdes dev = make_dev(&ofw_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "%s", 102238106Sdes output); 103238106Sdes make_dev_alias(dev, "ofwcons"); 104238106Sdes } 105238106Sdes} 106238106Sdes 107238106SdesSYSINIT(cndev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, cn_drvinit, NULL) 108238106Sdes 109238106Sdesstatic int stdin; 110238106Sdesstatic int stdout; 111238106Sdes 112238106Sdesstatic int 113238106Sdesofw_dev_open(struct cdev *dev, int flag, int mode, struct thread *td) 114238106Sdes{ 115238106Sdes struct tty *tp; 116238106Sdes int unit; 117238106Sdes int error, setuptimeout; 118238106Sdes 119238106Sdes error = 0; 120238106Sdes setuptimeout = 0; 121238106Sdes unit = minor(dev); 122238106Sdes 123238106Sdes /* 124238106Sdes * XXX: BAD, should happen at attach time 125238106Sdes */ 126238106Sdes if (dev->si_tty == NULL) { 127238106Sdes ofw_tp = ttyalloc(); 128238106Sdes dev->si_tty = ofw_tp; 129238106Sdes ofw_tp->t_dev = dev; 130238106Sdes } 131238106Sdes tp = dev->si_tty; 132238106Sdes 133238106Sdes tp->t_oproc = ofw_tty_start; 134238106Sdes tp->t_param = ofw_tty_param; 135238106Sdes tp->t_stop = ofw_tty_stop; 136238106Sdes tp->t_dev = dev; 137238106Sdes 138238106Sdes if ((tp->t_state & TS_ISOPEN) == 0) { 139238106Sdes tp->t_state |= TS_CARR_ON; 140238106Sdes ttyconsolemode(tp, 0); 141238106Sdes 142238106Sdes setuptimeout = 1; 143238106Sdes } else if ((tp->t_state & TS_XCLUDE) && suser(td)) { 144238106Sdes return (EBUSY); 145238106Sdes } 146238106Sdes 147238106Sdes error = ttyld_open(tp, dev); 148238106Sdes 149238106Sdes if (error == 0 && setuptimeout) { 150238106Sdes polltime = hz / OFWCONS_POLL_HZ; 151238106Sdes if (polltime < 1) { 152238106Sdes polltime = 1; 153238106Sdes } 154238106Sdes 155238106Sdes ofw_timeouthandle = timeout(ofw_timeout, tp, polltime); 156238106Sdes } 157238106Sdes 158238106Sdes return (error); 159238106Sdes} 160238106Sdes 161238106Sdesstatic int 162238106Sdesofw_dev_close(struct cdev *dev, int flag, int mode, struct thread *td) 163238106Sdes{ 164238106Sdes int unit; 165238106Sdes struct tty *tp; 166238106Sdes 167238106Sdes unit = minor(dev); 168238106Sdes tp = dev->si_tty; 169238106Sdes 170238106Sdes if (unit != 0) { 171238106Sdes return (ENXIO); 172238106Sdes } 173238106Sdes 174238106Sdes /* XXX Should be replaced with callout_stop(9) */ 175238106Sdes untimeout(ofw_timeout, tp, ofw_timeouthandle); 176238106Sdes ttyld_close(tp, flag); 177238106Sdes tty_close(tp); 178238106Sdes 179238106Sdes return (0); 180238106Sdes} 181238106Sdes 182238106Sdes 183238106Sdesstatic int 184238106Sdesofw_tty_param(struct tty *tp, struct termios *t) 185238106Sdes{ 186238106Sdes 187238106Sdes return (0); 188238106Sdes} 189238106Sdes 190238106Sdesstatic void 191238106Sdesofw_tty_start(struct tty *tp) 192238106Sdes{ 193238106Sdes struct clist *cl; 194238106Sdes int len; 195238106Sdes u_char buf[OFBURSTLEN]; 196238106Sdes 197238106Sdes 198238106Sdes if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) 199238106Sdes return; 200238106Sdes 201238106Sdes tp->t_state |= TS_BUSY; 202238106Sdes cl = &tp->t_outq; 203238106Sdes len = q_to_b(cl, buf, OFBURSTLEN); 204238106Sdes OF_write(stdout, buf, len); 205238106Sdes tp->t_state &= ~TS_BUSY; 206238106Sdes 207238106Sdes ttwwakeup(tp); 208238106Sdes} 209238106Sdes 210238106Sdesstatic void 211238106Sdesofw_tty_stop(struct tty *tp, int flag) 212238106Sdes{ 213238106Sdes 214238106Sdes if (tp->t_state & TS_BUSY) { 215238106Sdes if ((tp->t_state & TS_TTSTOP) == 0) { 216238106Sdes tp->t_state |= TS_FLUSH; 217238106Sdes } 218238106Sdes } 219238106Sdes} 220238106Sdes 221238106Sdesstatic void 222238106Sdesofw_timeout(void *v) 223238106Sdes{ 224238106Sdes struct tty *tp; 225238106Sdes int c; 226238106Sdes 227238106Sdes tp = (struct tty *)v; 228238106Sdes 229238106Sdes while ((c = ofw_cngetc(NULL)) != -1) { 230238106Sdes if (tp->t_state & TS_ISOPEN) { 231238106Sdes ttyld_rint(tp, c); 232238106Sdes } 233238106Sdes } 234238106Sdes 235238106Sdes ofw_timeouthandle = timeout(ofw_timeout, tp, polltime); 236238106Sdes} 237238106Sdes 238238106Sdesstatic void 239238106Sdesofw_cnprobe(struct consdev *cp) 240238106Sdes{ 241238106Sdes int chosen; 242238106Sdes 243238106Sdes if ((chosen = OF_finddevice("/chosen")) == -1) { 244238106Sdes cp->cn_pri = CN_DEAD; 245238106Sdes return; 246238106Sdes } 247238106Sdes 248238106Sdes if (OF_getprop(chosen, "stdin", &stdin, sizeof(stdin)) == -1) { 249238106Sdes cp->cn_pri = CN_DEAD; 250238106Sdes return; 251238106Sdes } 252238106Sdes 253238106Sdes if (OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)) == -1) { 254238106Sdes cp->cn_pri = CN_DEAD; 255238106Sdes return; 256238106Sdes } 257238106Sdes 258238106Sdes cp->cn_pri = CN_LOW; 259238106Sdes} 260238106Sdes 261238106Sdesstatic void 262238106Sdesofw_cninit(struct consdev *cp) 263238106Sdes{ 264238106Sdes 265238106Sdes /* XXX: This is the alias, but that should be good enough */ 266238106Sdes sprintf(cp->cn_name, "ofwcons"); 267238106Sdes cp->cn_tp = ofw_tp; 268238106Sdes} 269238106Sdes 270238106Sdesstatic void 271238106Sdesofw_cnterm(struct consdev *cp) 272238106Sdes{ 273238106Sdes} 274238106Sdes 275238106Sdesstatic int 276238106Sdesofw_cngetc(struct consdev *cp) 277238106Sdes{ 278238106Sdes unsigned char ch; 279238106Sdes 280238106Sdes if (OF_read(stdin, &ch, 1) > 0) { 281238106Sdes#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER) 282238106Sdes if (kdb_alt_break(ch, &alt_break_state)) 283238106Sdes kdb_enter("Break sequence on console"); 284238106Sdes#endif 285238106Sdes return (ch); 286238106Sdes } 287238106Sdes 288238106Sdes return (-1); 289238106Sdes} 290238106Sdes 291238106Sdesstatic void 292238106Sdesofw_cnputc(struct consdev *cp, int c) 293238106Sdes{ 294238106Sdes char cbuf; 295238106Sdes 296238106Sdes if (c == '\n') { 297 cbuf = '\r'; 298 OF_write(stdout, &cbuf, 1); 299 } 300 301 cbuf = c; 302 OF_write(stdout, &cbuf, 1); 303} 304