]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/sun4v/sun4v/hvcons.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.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
57 static d_open_t  hvcn_open;
58 static d_close_t hvcn_close;
59
60 static struct cdevsw hvcn_cdevsw = {
61         .d_version =    D_VERSION,
62         .d_open =       hvcn_open,
63         .d_close =      hvcn_close,
64         .d_name =       "hvcn",
65         .d_flags =      D_TTY | D_NEEDGIANT,
66 };
67
68 #define PCBURST 16
69 static struct tty               *hvcn_tp = NULL;
70 static struct resource          *hvcn_irq;
71 static void                     *hvcn_intrhand;
72
73 static int bufindex;
74 static int buflen;
75 static u_char buf[PCBURST];
76 static int                      polltime;
77 static struct callout_handle    hvcn_timeouthandle
78     = CALLOUT_HANDLE_INITIALIZER(&hvcn_timeouthandle);
79
80 #if defined(KDB)
81 static int                      alt_break_state;
82 #endif
83
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 *);
88
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;
94
95
96 CONSOLE_DRIVER(hvcn);
97
98 void
99 hv_cnputs(char *p)
100 {
101         int c, error;
102
103         while ((c = *p++) != '\0') {
104                 if (c == '\n') {
105                         do {
106                                 error = hv_cons_putchar('\r');
107                         } while (error == H_EWOULDBLOCK);
108                 }
109                 do {
110                         error = hv_cons_putchar(c);
111                 } while (error == H_EWOULDBLOCK);
112         }
113 }
114
115 static int
116 hvcn_open(struct cdev *dev, int flag, int mode, struct thread *td)
117 {
118         struct  tty *tp;
119         int     error, setuptimeout;
120
121         setuptimeout = 0;
122
123         if (dev->si_tty == NULL) {
124                 hvcn_tp = ttyalloc();
125                 dev->si_tty = hvcn_tp;
126                 hvcn_tp->t_dev = dev;
127         }
128         tp = dev->si_tty;
129
130         tp->t_oproc = hvcn_tty_start;
131         tp->t_param = hvcn_tty_param;
132         tp->t_stop = hvcn_tty_stop;
133
134         if ((tp->t_state & TS_ISOPEN) == 0) {
135                 tp->t_state |= TS_CARR_ON;
136                 ttyconsolemode(tp, 0);
137
138                 setuptimeout = 1;
139         } else if ((tp->t_state & TS_XCLUDE) && priv_check(td,
140              PRIV_TTY_EXCLUSIVE)) {
141                 return (EBUSY);
142         }
143
144         error = ttyld_open(tp, dev);
145 #if defined(SIMULATOR) || 1
146         if (error == 0 && setuptimeout) {
147                 int polltime;
148
149                 polltime = hz / HVCN_POLL_FREQ;
150                 if (polltime < 1) {
151                         polltime = 1;
152                 }
153
154                 hvcn_timeouthandle = timeout(hvcn_timeout, tp, polltime);
155         }
156 #endif
157         return (error);
158 }
159  
160 static int
161 hvcn_close(struct cdev *dev, int flag, int mode, struct thread *td)
162 {
163         int     unit;
164         struct  tty *tp;
165
166         unit = minor(dev);
167         tp = dev->si_tty;
168
169         if (unit != 0) 
170                 return (ENXIO);
171         
172         untimeout(hvcn_timeout, tp, hvcn_timeouthandle);
173         ttyld_close(tp, flag);
174         tty_close(tp);
175
176         return (0);
177 }
178
179 static void
180 hvcn_cnprobe(struct consdev *cp)
181 {
182
183 #if 0
184         char name[64];
185
186         node = OF_peer(0);
187         if (node == -1)
188                 panic("%s: OF_peer failed.", __func__);
189         
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"))
193                         break;
194         }
195         
196         if (node == 0)
197                 goto done;
198
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"))
202                         break;
203         }
204 done:
205 #endif  
206         cp->cn_pri = CN_NORMAL;
207
208 }
209
210 static void
211 hvcn_cninit(struct consdev *cp)
212 {
213         sprintf(cp->cn_name, "hvcn");
214 }
215
216 static int
217 hvcn_cngetc(struct consdev *cp)
218 {
219         unsigned char ch;
220         int l;
221
222         ch = '\0';
223
224         while ((l = hv_cons_getchar(&ch)) != H_EOK) {
225 #if defined(KDB)
226                 if (l == H_BREAK || l ==  H_HUP)
227                         kdb_enter_why(KDB_WHY_BREAK,
228                             "Break sequence on console");
229
230         if (kdb_alt_break(ch, &alt_break_state))
231                 kdb_enter_why(KDB_WHY_BREAK, "Break sequence on console");
232 #endif
233                 if (l != -2 && l != 0) {
234                         return (-1);
235                 }
236         }
237
238
239
240         return (ch);
241 }
242
243 static int
244 hvcn_cncheckc(struct consdev *cp)
245 {
246         unsigned char ch;
247         int l;
248
249         if ((l = hv_cons_getchar(&ch)) == H_EOK) {
250 #if defined(KDB)
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");
257 #endif
258                 return (ch);
259         }
260
261         return (-1);
262 }
263
264
265 static void
266 hvcn_cnterm(struct consdev *cp)
267 {
268         ;
269 }
270
271 static void
272 hvcn_cnputc(struct consdev *cp, int c)
273 {
274
275         int error;
276
277         error = 0;
278         do {
279                 if (c == '\n') 
280                         error = hv_cons_putchar('\r');
281         } while (error == H_EWOULDBLOCK);
282         do {
283                 error = hv_cons_putchar(c);
284         } while (error == H_EWOULDBLOCK);
285 }
286
287 static int
288 hvcn_tty_param(struct tty *tp, struct termios *t)
289 {
290         tp->t_ispeed = t->c_ispeed;
291         tp->t_ospeed = t->c_ospeed;
292         tp->t_cflag = t->c_cflag;
293
294         return (0);
295 }
296
297 static void
298 hvcn_tty_start(struct tty *tp)
299 {
300
301         if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) {
302                 tp->t_state |= TS_BUSY;
303
304                 do {
305                         if (buflen == 0) {
306                                 buflen = q_to_b(&tp->t_outq, buf, PCBURST);
307                                 bufindex = 0;
308                         }
309                         while (buflen) {
310                                 if (hv_cons_putchar(buf[bufindex]) == H_EWOULDBLOCK)
311                                         goto done;
312                                 bufindex++;
313                                 buflen--;
314                         }
315                 } while (tp->t_outq.c_cc != 0);
316         done:
317                 tp->t_state &= ~TS_BUSY;
318                 ttwwakeup(tp);
319         }
320 }
321
322 static void
323 hvcn_tty_stop(struct tty *tp, int flag)
324 {
325         if ((tp->t_state & TS_BUSY) && !(tp->t_state & TS_TTSTOP)) 
326                 tp->t_state |= TS_FLUSH;
327                 
328         
329 }
330
331 static void
332 hvcn_intr(void *v)
333 {
334         struct tty *tp;
335         int c;
336
337         tp = (struct tty *)v;
338         
339         while ((c = hvcn_cncheckc(NULL)) != -1) 
340                 if (tp->t_state & TS_ISOPEN) 
341                         ttyld_rint(tp, c);
342
343         if (tp->t_outq.c_cc != 0 || buflen != 0) 
344                 hvcn_tty_start(tp);
345 }
346
347 static void
348 hvcn_timeout(void *v)
349 {
350         hvcn_intr(v);
351
352         hvcn_timeouthandle = timeout(hvcn_timeout, v, polltime);
353 }
354
355
356 static int
357 hvcn_dev_probe(device_t dev)
358 {
359
360         if (strcmp(mdesc_bus_get_name(dev), "console"))
361                 return (ENXIO);
362         
363         device_set_desc(dev, "sun4v virtual console");  
364
365         return (0);
366 }
367
368
369 static int
370 hvcn_dev_attach(device_t dev)
371 {
372       
373         struct cdev *cdev;
374         int error, rid;
375
376         /* belongs in attach - but attach is getting called multiple times
377          * for reasons I have not delved into
378          */
379
380         if (hvcn_consdev.cn_pri == CN_DEAD || 
381             hvcn_consdev.cn_name[0] == '\0') 
382                 return (ENXIO);
383
384         cdev = make_dev(&hvcn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "ttyv%r", 1);
385         make_dev_alias(cdev, "hvcn");
386         
387         rid = 0;
388
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");
392                 error = ENXIO;
393                 goto fail;
394                 
395         }
396         error = bus_setup_intr(dev, hvcn_irq, INTR_TYPE_TTY, NULL, hvcn_intr, hvcn_tp, 
397                                hvcn_intrhand);
398         
399         if (error)
400                 device_printf(dev, "couldn't set up irq\n");
401         
402                 
403 fail:
404         return (error);
405       
406 }
407
408 static device_method_t hvcn_methods[] = {
409         DEVMETHOD(device_probe, hvcn_dev_probe),
410         DEVMETHOD(device_attach, hvcn_dev_attach),
411         {0, 0}
412 };
413
414
415 static driver_t hvcn_driver = {
416         "hvcn",
417         hvcn_methods,
418         0,
419 };
420
421
422 static devclass_t hvcn_devclass;
423
424 DRIVER_MODULE(hvcn, vnex, hvcn_driver, hvcn_devclass, 0, 0);