]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/sun4v/sun4v/hvcons.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / sun4v / sun4v / hvcons.c
1 /*-
2  * Copyright (C) 2006 Kip Macy
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 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.
24  */
25
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/module.h>
33 #include <sys/bus.h>
34 #include <sys/kdb.h>
35 #include <sys/kernel.h>
36 #include <sys/conf.h>
37 #include <sys/cons.h>
38 #include <sys/consio.h>
39 #include <sys/priv.h>
40 #include <sys/rman.h>
41 #include <sys/tty.h>
42
43 #include <machine/mdesc_bus.h>
44 #include <machine/cddl/mdesc.h>
45
46 #include "mdesc_bus_if.h"
47
48 #include "opt_simulator.h"
49
50 #include <machine/resource.h>
51 #include <machine/hypervisorvar.h>
52 #include <machine/hv_api.h>
53
54 #define HVCN_POLL_FREQ 10
55
56 static tsw_open_t       hvcn_open;
57 static tsw_outwakeup_t  hvcn_outwakeup;
58 static tsw_close_t      hvcn_close;
59
60 static struct ttydevsw hvcn_class = {
61         .tsw_open       = hvcn_open,
62         .tsw_outwakeup  = hvcn_outwakeup,
63         .tsw_close      = hvcn_close,
64 };
65
66 #define PCBURST 16
67 static struct tty               *hvcn_tp = NULL;
68 static struct resource          *hvcn_irq;
69 static void                     *hvcn_intrhand;
70
71 static int bufindex;
72 static int buflen;
73 static u_char buf[PCBURST];
74 static int                      polltime;
75 static struct callout_handle    hvcn_timeouthandle
76     = CALLOUT_HANDLE_INITIALIZER(&hvcn_timeouthandle);
77
78 #if defined(KDB)
79 static int                      alt_break_state;
80 #endif
81
82 static void     hvcn_timeout(void *);
83
84 static cn_probe_t       hvcn_cnprobe;
85 static cn_init_t        hvcn_cninit;
86 static cn_getc_t        hvcn_cngetc;
87 static cn_putc_t        hvcn_cnputc;
88 static cn_term_t        hvcn_cnterm;
89
90
91 CONSOLE_DRIVER(hvcn);
92
93 void
94 hv_cnputs(char *p)
95 {
96         int c, error;
97
98         while ((c = *p++) != '\0') {
99                 if (c == '\n') {
100                         do {
101                                 error = hv_cons_putchar('\r');
102                         } while (error == H_EWOULDBLOCK);
103                 }
104                 do {
105                         error = hv_cons_putchar(c);
106                 } while (error == H_EWOULDBLOCK);
107         }
108 }
109
110 static int
111 hvcn_open(struct tty *tp)
112 {
113
114         /*
115          * Set up timeout to trigger fake interrupts to transmit
116          * trailing data.
117          */
118         polltime = hz / HVCN_POLL_FREQ;
119         if (polltime < 1)
120                 polltime = 1;
121         hvcn_timeouthandle = timeout(hvcn_timeout, tp, polltime);
122
123         buflen = 0;
124
125         return (0);
126 }
127  
128 static void
129 hvcn_close(struct tty *tp)
130 {
131         untimeout(hvcn_timeout, tp, hvcn_timeouthandle);
132 }
133
134 static void
135 hvcn_cnprobe(struct consdev *cp)
136 {
137
138 #if 0
139         char name[64];
140
141         node = OF_peer(0);
142         if (node == -1)
143                 panic("%s: OF_peer failed.", __func__);
144         
145         for (node = OF_child(node); node > 0; node = OF_peer(node)) {
146                 OF_getprop(node, "name", name, sizeof(name));
147                 if (!strcmp(name, "virtual-devices"))
148                         break;
149         }
150         
151         if (node == 0)
152                 goto done;
153
154         for (node = OF_child(node); node > 0; node = OF_peer(node)) {
155                 OF_getprop(node, "name", name, sizeof(name));
156                 if (!strcmp(name, "console"))
157                         break;
158         }
159 done:
160 #endif  
161         cp->cn_pri = CN_NORMAL;
162
163 }
164
165 static void
166 hvcn_cninit(struct consdev *cp)
167 {
168
169         strcpy(cp->cn_name, "hvcn");
170 }
171
172 static int
173 hvcn_cngetc(struct consdev *cp)
174 {
175         unsigned char ch;
176         int l;
177
178         ch = '\0';
179
180         while ((l = hv_cons_getchar(&ch)) != H_EOK) {
181 #if defined(KDB)
182                 int kdb_brk;
183
184                 if (l == H_BREAK || l ==  H_HUP)
185                         kdb_enter(KDB_WHY_BREAK, "Break sequence on console");
186
187                 if ((kdb_brk = kdb_alt_break(ch, &alt_break_state)) != 0) {
188                         switch (kdb_brk) {
189                         case KDB_REQ_DEBUGGER:
190                                 kdb_enter(KDB_WHY_BREAK,
191                                     "Break sequence on console");
192                                 break;
193                         case KDB_REQ_PANIC:
194                                 kdb_panic("Panic sequence on console");
195                                 break;
196                         case KDB_REQ_REBOOT:
197                                 kdb_reboot();
198                                 break;
199                         }
200                 }
201 #endif
202                 if (l != -2 && l != 0) {
203                         return (-1);
204                 }
205         }
206
207
208
209         return (ch);
210 }
211
212 static int
213 hvcn_cncheckc(struct consdev *cp)
214 {
215         unsigned char ch;
216         int l;
217
218         if ((l = hv_cons_getchar(&ch)) == H_EOK) {
219 #if defined(KDB)
220                 if (l == H_BREAK || l ==  H_HUP)
221                         kdb_enter(KDB_WHY_BREAK, "Break sequence on console");
222                 if (kdb_alt_break(ch, &alt_break_state))
223                         kdb_enter(KDB_WHY_BREAK, "Break sequence on console");
224 #endif
225                 return (ch);
226         }
227
228         return (-1);
229 }
230
231
232 static void
233 hvcn_cnterm(struct consdev *cp)
234 {
235         ;
236 }
237
238 static void
239 hvcn_cnputc(struct consdev *cp, int c)
240 {
241
242         int error;
243
244         error = 0;
245         do {
246                 if (c == '\n') 
247                         error = hv_cons_putchar('\r');
248         } while (error == H_EWOULDBLOCK);
249         do {
250                 error = hv_cons_putchar(c);
251         } while (error == H_EWOULDBLOCK);
252 }
253
254 static void
255 hvcn_outwakeup(struct tty *tp)
256 {
257
258         for (;;) {
259                 /* Refill the input buffer. */
260                 if (buflen == 0) {
261                         buflen = ttydisc_getc(tp, buf, PCBURST);
262                         bufindex = 0;
263                 }
264
265                 /* Transmit the input buffer. */
266                 while (buflen) {
267                         if (hv_cons_putchar(buf[bufindex]) == H_EWOULDBLOCK)
268                                 return;
269                         bufindex++;
270                         buflen--;
271                 }
272         }
273 }
274
275 static void
276 hvcn_intr(void *v)
277 {
278         struct tty *tp = v;
279         int c;
280
281         tty_lock(tp);
282         
283         /* Receive data. */
284         while ((c = hvcn_cncheckc(NULL)) != -1) 
285                 ttydisc_rint(tp, c, 0);
286         ttydisc_rint_done(tp);
287
288         /* Transmit trailing data. */
289         hvcn_outwakeup(tp);
290         tty_unlock(tp);
291 }
292
293 static void
294 hvcn_timeout(void *v)
295 {
296         hvcn_intr(v);
297
298         hvcn_timeouthandle = timeout(hvcn_timeout, v, polltime);
299 }
300
301
302 static int
303 hvcn_dev_probe(device_t dev)
304 {
305
306         if (strcmp(mdesc_bus_get_name(dev), "console"))
307                 return (ENXIO);
308         
309         device_set_desc(dev, "sun4v virtual console");  
310
311         return (0);
312 }
313
314
315 static int
316 hvcn_dev_attach(device_t dev)
317 {
318       
319         struct tty *tp;
320         int error, rid;
321
322         /* belongs in attach - but attach is getting called multiple times
323          * for reasons I have not delved into
324          */
325
326         if (hvcn_consdev.cn_pri == CN_DEAD || 
327             hvcn_consdev.cn_name[0] == '\0') 
328                 return (ENXIO);
329
330         tp = tty_alloc(&hvcn_class, NULL);
331         tty_makedev(tp, NULL, "v%r", 1);
332         tty_makealias(tp, "hvcn");
333         
334         rid = 0;
335
336         if ((hvcn_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
337                                         RF_SHAREABLE | RF_ACTIVE)) == NULL) {
338                 device_printf(dev, "couldn't map interrupt\n");
339                 error = ENXIO;
340                 goto fail;
341                 
342         }
343         error = bus_setup_intr(dev, hvcn_irq, INTR_TYPE_TTY, NULL, hvcn_intr, hvcn_tp, 
344                                hvcn_intrhand);
345         
346         if (error)
347                 device_printf(dev, "couldn't set up irq\n");
348         
349                 
350 fail:
351         return (error);
352       
353 }
354
355 static device_method_t hvcn_methods[] = {
356         DEVMETHOD(device_probe, hvcn_dev_probe),
357         DEVMETHOD(device_attach, hvcn_dev_attach),
358         {0, 0}
359 };
360
361
362 static driver_t hvcn_driver = {
363         "hvcn",
364         hvcn_methods,
365         0,
366 };
367
368
369 static devclass_t hvcn_devclass;
370
371 DRIVER_MODULE(hvcn, vnex, hvcn_driver, hvcn_devclass, 0, 0);