2 * Copyright (C) 2006 Kip Macy
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
14 * THIS SOFTWARE IS PROVIDED BY Kip Macy ``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 Kip Macy 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.
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/module.h>
35 #include <sys/kernel.h>
38 #include <sys/consio.h>
43 #include <machine/mdesc_bus.h>
44 #include <machine/cddl/mdesc.h>
46 #include "mdesc_bus_if.h"
48 #include "opt_simulator.h"
50 #include <machine/resource.h>
51 #include <machine/hypervisorvar.h>
52 #include <machine/hv_api.h>
54 #define HVCN_POLL_FREQ 10
57 static d_open_t hvcn_open;
58 static d_close_t hvcn_close;
60 static struct cdevsw hvcn_cdevsw = {
61 .d_version = D_VERSION,
63 .d_close = hvcn_close,
65 .d_flags = D_TTY | D_NEEDGIANT,
69 static struct tty *hvcn_tp = NULL;
70 static struct resource *hvcn_irq;
71 static void *hvcn_intrhand;
75 static u_char buf[PCBURST];
77 static struct callout_handle hvcn_timeouthandle
78 = CALLOUT_HANDLE_INITIALIZER(&hvcn_timeouthandle);
81 static int alt_break_state;
84 static void hvcn_tty_start(struct tty *);
85 static int hvcn_tty_param(struct tty *, struct termios *);
86 static void hvcn_tty_stop(struct tty *, int);
87 static void hvcn_timeout(void *);
89 static cn_probe_t hvcn_cnprobe;
90 static cn_init_t hvcn_cninit;
91 static cn_getc_t hvcn_cngetc;
92 static cn_putc_t hvcn_cnputc;
93 static cn_term_t hvcn_cnterm;
103 while ((c = *p++) != '\0') {
106 error = hv_cons_putchar('\r');
107 } while (error == H_EWOULDBLOCK);
110 error = hv_cons_putchar(c);
111 } while (error == H_EWOULDBLOCK);
116 hvcn_open(struct cdev *dev, int flag, int mode, struct thread *td)
119 int error, setuptimeout;
123 if (dev->si_tty == NULL) {
124 hvcn_tp = ttyalloc();
125 dev->si_tty = hvcn_tp;
126 hvcn_tp->t_dev = dev;
130 tp->t_oproc = hvcn_tty_start;
131 tp->t_param = hvcn_tty_param;
132 tp->t_stop = hvcn_tty_stop;
134 if ((tp->t_state & TS_ISOPEN) == 0) {
135 tp->t_state |= TS_CARR_ON;
136 ttyconsolemode(tp, 0);
139 } else if ((tp->t_state & TS_XCLUDE) && priv_check(td,
140 PRIV_TTY_EXCLUSIVE)) {
144 error = ttyld_open(tp, dev);
145 #if defined(SIMULATOR) || 1
146 if (error == 0 && setuptimeout) {
149 polltime = hz / HVCN_POLL_FREQ;
154 hvcn_timeouthandle = timeout(hvcn_timeout, tp, polltime);
161 hvcn_close(struct cdev *dev, int flag, int mode, struct thread *td)
172 untimeout(hvcn_timeout, tp, hvcn_timeouthandle);
173 ttyld_close(tp, flag);
180 hvcn_cnprobe(struct consdev *cp)
188 panic("%s: OF_peer failed.", __func__);
190 for (node = OF_child(node); node > 0; node = OF_peer(node)) {
191 OF_getprop(node, "name", name, sizeof(name));
192 if (!strcmp(name, "virtual-devices"))
199 for (node = OF_child(node); node > 0; node = OF_peer(node)) {
200 OF_getprop(node, "name", name, sizeof(name));
201 if (!strcmp(name, "console"))
206 cp->cn_pri = CN_NORMAL;
211 hvcn_cninit(struct consdev *cp)
213 sprintf(cp->cn_name, "hvcn");
217 hvcn_cngetc(struct consdev *cp)
224 while ((l = hv_cons_getchar(&ch)) != H_EOK) {
226 if (l == H_BREAK || l == H_HUP)
227 kdb_enter_why(KDB_WHY_BREAK,
228 "Break sequence on console");
230 if (kdb_alt_break(ch, &alt_break_state))
231 kdb_enter_why(KDB_WHY_BREAK, "Break sequence on console");
233 if (l != -2 && l != 0) {
244 hvcn_cncheckc(struct consdev *cp)
249 if ((l = hv_cons_getchar(&ch)) == H_EOK) {
251 if (l == H_BREAK || l == H_HUP)
252 kdb_enter_why(KDB_WHY_BREAK,
253 "Break sequence on console");
254 if (kdb_alt_break(ch, &alt_break_state))
255 kdb_enter_why(KDB_WHY_BREAK,
256 "Break sequence on console");
266 hvcn_cnterm(struct consdev *cp)
272 hvcn_cnputc(struct consdev *cp, int c)
280 error = hv_cons_putchar('\r');
281 } while (error == H_EWOULDBLOCK);
283 error = hv_cons_putchar(c);
284 } while (error == H_EWOULDBLOCK);
288 hvcn_tty_param(struct tty *tp, struct termios *t)
290 tp->t_ispeed = t->c_ispeed;
291 tp->t_ospeed = t->c_ospeed;
292 tp->t_cflag = t->c_cflag;
298 hvcn_tty_start(struct tty *tp)
301 if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) {
302 tp->t_state |= TS_BUSY;
306 buflen = q_to_b(&tp->t_outq, buf, PCBURST);
310 if (hv_cons_putchar(buf[bufindex]) == H_EWOULDBLOCK)
315 } while (tp->t_outq.c_cc != 0);
317 tp->t_state &= ~TS_BUSY;
323 hvcn_tty_stop(struct tty *tp, int flag)
325 if ((tp->t_state & TS_BUSY) && !(tp->t_state & TS_TTSTOP))
326 tp->t_state |= TS_FLUSH;
337 tp = (struct tty *)v;
339 while ((c = hvcn_cncheckc(NULL)) != -1)
340 if (tp->t_state & TS_ISOPEN)
343 if (tp->t_outq.c_cc != 0 || buflen != 0)
348 hvcn_timeout(void *v)
352 hvcn_timeouthandle = timeout(hvcn_timeout, v, polltime);
357 hvcn_dev_probe(device_t dev)
360 if (strcmp(mdesc_bus_get_name(dev), "console"))
363 device_set_desc(dev, "sun4v virtual console");
370 hvcn_dev_attach(device_t dev)
376 /* belongs in attach - but attach is getting called multiple times
377 * for reasons I have not delved into
380 if (hvcn_consdev.cn_pri == CN_DEAD ||
381 hvcn_consdev.cn_name[0] == '\0')
384 cdev = make_dev(&hvcn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "ttyv%r", 1);
385 make_dev_alias(cdev, "hvcn");
389 if ((hvcn_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
390 RF_SHAREABLE | RF_ACTIVE)) == NULL) {
391 device_printf(dev, "couldn't map interrupt\n");
396 error = bus_setup_intr(dev, hvcn_irq, INTR_TYPE_TTY, NULL, hvcn_intr, hvcn_tp,
400 device_printf(dev, "couldn't set up irq\n");
408 static device_method_t hvcn_methods[] = {
409 DEVMETHOD(device_probe, hvcn_dev_probe),
410 DEVMETHOD(device_attach, hvcn_dev_attach),
415 static driver_t hvcn_driver = {
422 static devclass_t hvcn_devclass;
424 DRIVER_MODULE(hvcn, vnex, hvcn_driver, hvcn_devclass, 0, 0);