1 | /* $NetBSD: xencons.c,v 1.41 2014/07/25 08:10:35 dholland Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2006 Manuel Bouyer. |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions |
8 | * are met: |
9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. |
14 | * |
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | * |
26 | */ |
27 | |
28 | /* |
29 | * |
30 | * Copyright (c) 2004 Christian Limpach. |
31 | * All rights reserved. |
32 | * |
33 | * Redistribution and use in source and binary forms, with or without |
34 | * modification, are permitted provided that the following conditions |
35 | * are met: |
36 | * 1. Redistributions of source code must retain the above copyright |
37 | * notice, this list of conditions and the following disclaimer. |
38 | * 2. Redistributions in binary form must reproduce the above copyright |
39 | * notice, this list of conditions and the following disclaimer in the |
40 | * documentation and/or other materials provided with the distribution. |
41 | * |
42 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
43 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
44 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
45 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
46 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
47 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
48 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
49 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
50 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
51 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
52 | */ |
53 | |
54 | |
55 | #include <sys/cdefs.h> |
56 | __KERNEL_RCSID(0, "$NetBSD: xencons.c,v 1.41 2014/07/25 08:10:35 dholland Exp $" ); |
57 | |
58 | #include "opt_xen.h" |
59 | |
60 | #include <sys/param.h> |
61 | #include <sys/ioctl.h> |
62 | #include <sys/proc.h> |
63 | #include <sys/tty.h> |
64 | #include <sys/systm.h> |
65 | #include <sys/device.h> |
66 | #include <sys/conf.h> |
67 | #include <sys/kauth.h> |
68 | #include <sys/kernel.h> |
69 | |
70 | #include <xen/xen.h> |
71 | #include <xen/hypervisor.h> |
72 | #include <xen/evtchn.h> |
73 | #include <uvm/uvm.h> |
74 | #include <machine/pmap.h> |
75 | #include <xen/xen-public/io/console.h> |
76 | |
77 | #include <dev/cons.h> |
78 | |
79 | #ifdef DDB |
80 | #include <ddb/db_output.h> /* XXX for db_max_line */ |
81 | #endif |
82 | |
83 | #undef XENDEBUG |
84 | |
85 | #ifdef XENDEBUG |
86 | #define XENPRINTK(x) printk x |
87 | #else |
88 | #define XENPRINTK(x) |
89 | #endif |
90 | |
91 | static int xencons_isconsole = 0; |
92 | static struct xencons_softc *xencons_console_device = NULL; |
93 | |
94 | #define XENCONS_UNIT(x) (minor(x)) |
95 | #define XENCONS_BURST 128 |
96 | |
97 | int xencons_match(device_t, cfdata_t, void *); |
98 | void xencons_attach(device_t, device_t, void *); |
99 | int xencons_intr(void *); |
100 | void xencons_tty_input(struct xencons_softc *, char*, int); |
101 | |
102 | |
103 | struct xencons_softc { |
104 | device_t sc_dev; |
105 | struct tty *sc_tty; |
106 | int polling; |
107 | }; |
108 | volatile struct xencons_interface *xencons_interface; |
109 | |
110 | CFATTACH_DECL_NEW(xencons, sizeof(struct xencons_softc), |
111 | xencons_match, xencons_attach, NULL, NULL); |
112 | |
113 | extern struct cfdriver xencons_cd; |
114 | |
115 | dev_type_open(xencons_open); |
116 | dev_type_close(xencons_close); |
117 | dev_type_read(xencons_read); |
118 | dev_type_write(xencons_write); |
119 | dev_type_ioctl(xencons_ioctl); |
120 | dev_type_stop(xencons_stop); |
121 | dev_type_tty(xencons_tty); |
122 | dev_type_poll(xencons_poll); |
123 | |
124 | const struct cdevsw xencons_cdevsw = { |
125 | .d_open = xencons_open, |
126 | .d_close = xencons_close, |
127 | .d_read = xencons_read, |
128 | .d_write = xencons_write, |
129 | .d_ioctl = xencons_ioctl, |
130 | .d_stop = xencons_stop, |
131 | .d_tty = xencons_tty, |
132 | .d_poll = xencons_poll, |
133 | .d_mmap = NULL, /* XXX: is this safe? - dholland 20140315 */ |
134 | .d_kqfilter = ttykqfilter, |
135 | .d_discard = nodiscard, |
136 | .d_flag = D_TTY |
137 | }; |
138 | |
139 | |
140 | static int xencons_handler(void *); |
141 | int xenconscn_getc(dev_t); |
142 | void xenconscn_putc(dev_t, int); |
143 | void xenconscn_pollc(dev_t, int); |
144 | |
145 | static bool xencons_suspend(device_t, const pmf_qual_t *); |
146 | static bool xencons_resume(device_t, const pmf_qual_t *); |
147 | |
148 | static struct consdev xencons = { |
149 | NULL, NULL, xenconscn_getc, xenconscn_putc, xenconscn_pollc, |
150 | NULL, NULL, NULL, NODEV, CN_NORMAL |
151 | }; |
152 | |
153 | static struct cnm_state xencons_cnm_state; |
154 | |
155 | void xencons_start (struct tty *); |
156 | int xencons_param (struct tty *, struct termios *); |
157 | |
158 | int |
159 | xencons_match(device_t parent, cfdata_t match, void *aux) |
160 | { |
161 | struct xencons_attach_args *xa = (struct xencons_attach_args *)aux; |
162 | |
163 | if (strcmp(xa->xa_device, "xencons" ) == 0) |
164 | return 1; |
165 | return 0; |
166 | } |
167 | |
168 | void |
169 | xencons_attach(device_t parent, device_t self, void *aux) |
170 | { |
171 | struct xencons_softc *sc = device_private(self); |
172 | |
173 | aprint_normal(": Xen Virtual Console Driver\n" ); |
174 | |
175 | sc->sc_dev = self; |
176 | sc->sc_tty = tty_alloc(); |
177 | tty_attach(sc->sc_tty); |
178 | sc->sc_tty->t_oproc = xencons_start; |
179 | sc->sc_tty->t_param = xencons_param; |
180 | |
181 | if (xencons_isconsole) { |
182 | int maj; |
183 | |
184 | /* Locate the major number. */ |
185 | maj = cdevsw_lookup_major(&xencons_cdevsw); |
186 | |
187 | /* There can be only one, but it can have any unit number. */ |
188 | cn_tab->cn_dev = makedev(maj, device_unit(self)); |
189 | |
190 | aprint_verbose_dev(self, "console major %d, unit %d\n" , |
191 | maj, device_unit(self)); |
192 | |
193 | sc->sc_tty->t_dev = cn_tab->cn_dev; |
194 | |
195 | #ifdef DDB |
196 | /* Set db_max_line to avoid paging. */ |
197 | db_max_line = 0x7fffffff; |
198 | #endif |
199 | |
200 | xencons_console_device = sc; |
201 | |
202 | xencons_resume(self, PMF_Q_NONE); |
203 | } |
204 | sc->polling = 0; |
205 | |
206 | if (!pmf_device_register(self, xencons_suspend, xencons_resume)) |
207 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
208 | } |
209 | |
210 | static bool |
211 | xencons_suspend(device_t dev, const pmf_qual_t *qual) { |
212 | |
213 | int evtch; |
214 | |
215 | /* dom0 console should not be suspended */ |
216 | if (!xendomain_is_dom0()) { |
217 | evtch = xen_start_info.console_evtchn; |
218 | hypervisor_mask_event(evtch); |
219 | if (event_remove_handler(evtch, xencons_handler, |
220 | xencons_console_device) != 0) { |
221 | aprint_error_dev(dev, |
222 | "can't remove handler: xencons_handler\n" ); |
223 | } |
224 | |
225 | aprint_verbose_dev(dev, "removed event channel %d\n" , evtch); |
226 | } |
227 | |
228 | return true; |
229 | } |
230 | |
231 | static bool |
232 | xencons_resume(device_t dev, const pmf_qual_t *qual) { |
233 | |
234 | int evtch = -1; |
235 | |
236 | if (xendomain_is_dom0()) { |
237 | /* dom0 console resume is required only during first start-up */ |
238 | if (cold) { |
239 | evtch = bind_virq_to_evtch(VIRQ_CONSOLE); |
240 | event_set_handler(evtch, xencons_intr, |
241 | xencons_console_device, IPL_TTY, "xencons" ); |
242 | } |
243 | } else { |
244 | evtch = xen_start_info.console_evtchn; |
245 | event_set_handler(evtch, xencons_handler, |
246 | xencons_console_device, IPL_TTY, "xencons" ); |
247 | } |
248 | |
249 | if (evtch != -1) { |
250 | aprint_verbose_dev(dev, "using event channel %d\n" , evtch); |
251 | hypervisor_enable_event(evtch); |
252 | } |
253 | |
254 | return true; |
255 | } |
256 | |
257 | int |
258 | xencons_open(dev_t dev, int flag, int mode, struct lwp *l) |
259 | { |
260 | struct xencons_softc *sc; |
261 | struct tty *tp; |
262 | |
263 | sc = device_lookup_private(&xencons_cd, XENCONS_UNIT(dev)); |
264 | if (sc == NULL) |
265 | return (ENXIO); |
266 | |
267 | tp = sc->sc_tty; |
268 | |
269 | if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp)) |
270 | return (EBUSY); |
271 | |
272 | if ((tp->t_state & TS_ISOPEN) == 0 && tp->t_wopen == 0) { |
273 | tp->t_dev = dev; |
274 | ttychars(tp); |
275 | tp->t_iflag = TTYDEF_IFLAG; |
276 | tp->t_oflag = TTYDEF_OFLAG; |
277 | tp->t_cflag = TTYDEF_CFLAG; |
278 | tp->t_lflag = TTYDEF_LFLAG; |
279 | tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; |
280 | xencons_param(tp, &tp->t_termios); |
281 | ttsetwater(tp); |
282 | } |
283 | tp->t_state |= TS_CARR_ON; |
284 | |
285 | return ((*tp->t_linesw->l_open)(dev, tp)); |
286 | } |
287 | |
288 | int |
289 | xencons_close(dev_t dev, int flag, int mode, struct lwp *l) |
290 | { |
291 | struct xencons_softc *sc = device_lookup_private(&xencons_cd, |
292 | XENCONS_UNIT(dev)); |
293 | struct tty *tp = sc->sc_tty; |
294 | |
295 | if (tp == NULL) |
296 | return (0); |
297 | (*tp->t_linesw->l_close)(tp, flag); |
298 | ttyclose(tp); |
299 | #ifdef notyet /* XXX */ |
300 | tty_free(tp); |
301 | #endif |
302 | return (0); |
303 | } |
304 | |
305 | int |
306 | xencons_read(dev_t dev, struct uio *uio, int flag) |
307 | { |
308 | struct xencons_softc *sc = device_lookup_private(&xencons_cd, |
309 | XENCONS_UNIT(dev)); |
310 | struct tty *tp = sc->sc_tty; |
311 | |
312 | return ((*tp->t_linesw->l_read)(tp, uio, flag)); |
313 | } |
314 | |
315 | int |
316 | xencons_write(dev_t dev, struct uio *uio, int flag) |
317 | { |
318 | struct xencons_softc *sc = device_lookup_private(&xencons_cd, |
319 | XENCONS_UNIT(dev)); |
320 | struct tty *tp = sc->sc_tty; |
321 | |
322 | return ((*tp->t_linesw->l_write)(tp, uio, flag)); |
323 | } |
324 | |
325 | int |
326 | xencons_poll(dev_t dev, int events, struct lwp *l) |
327 | { |
328 | struct xencons_softc *sc = device_lookup_private(&xencons_cd, |
329 | XENCONS_UNIT(dev)); |
330 | struct tty *tp = sc->sc_tty; |
331 | |
332 | return ((*tp->t_linesw->l_poll)(tp, events, l)); |
333 | } |
334 | |
335 | struct tty * |
336 | xencons_tty(dev_t dev) |
337 | { |
338 | struct xencons_softc *sc = device_lookup_private(&xencons_cd, |
339 | XENCONS_UNIT(dev)); |
340 | struct tty *tp = sc->sc_tty; |
341 | |
342 | return (tp); |
343 | } |
344 | |
345 | int |
346 | xencons_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) |
347 | { |
348 | struct xencons_softc *sc = device_lookup_private(&xencons_cd, |
349 | XENCONS_UNIT(dev)); |
350 | struct tty *tp = sc->sc_tty; |
351 | int error; |
352 | |
353 | error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l); |
354 | if (error != EPASSTHROUGH) |
355 | return (error); |
356 | |
357 | error = ttioctl(tp, cmd, data, flag, l); |
358 | if (error != EPASSTHROUGH) |
359 | return (error); |
360 | |
361 | switch (cmd) { |
362 | default: |
363 | return (EPASSTHROUGH); |
364 | } |
365 | |
366 | #ifdef DIAGNOSTIC |
367 | panic("xencons_ioctl: impossible" ); |
368 | #endif |
369 | } |
370 | |
371 | void |
372 | xencons_start(struct tty *tp) |
373 | { |
374 | struct clist *cl; |
375 | int s; |
376 | |
377 | s = spltty(); |
378 | if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) |
379 | goto out; |
380 | tp->t_state |= TS_BUSY; |
381 | splx(s); |
382 | |
383 | /* |
384 | * We need to do this outside spl since it could be fairly |
385 | * expensive and we don't want our serial ports to overflow. |
386 | */ |
387 | cl = &tp->t_outq; |
388 | if (xendomain_is_dom0()) { |
389 | int len, r; |
390 | u_char buf[XENCONS_BURST+1]; |
391 | |
392 | len = q_to_b(cl, buf, XENCONS_BURST); |
393 | while (len > 0) { |
394 | r = HYPERVISOR_console_io(CONSOLEIO_write, len, buf); |
395 | if (r <= 0) |
396 | break; |
397 | len -= r; |
398 | } |
399 | } else { |
400 | XENCONS_RING_IDX cons, prod, len; |
401 | |
402 | #define XNC_OUT (xencons_interface->out) |
403 | cons = xencons_interface->out_cons; |
404 | prod = xencons_interface->out_prod; |
405 | xen_rmb(); |
406 | while (prod != cons + sizeof(xencons_interface->out)) { |
407 | if (MASK_XENCONS_IDX(prod, XNC_OUT) < |
408 | MASK_XENCONS_IDX(cons, XNC_OUT)) { |
409 | len = MASK_XENCONS_IDX(cons, XNC_OUT) - |
410 | MASK_XENCONS_IDX(prod, XNC_OUT); |
411 | } else { |
412 | len = sizeof(XNC_OUT) - |
413 | MASK_XENCONS_IDX(prod, XNC_OUT); |
414 | } |
415 | len = q_to_b(cl, __UNVOLATILE( |
416 | &XNC_OUT[MASK_XENCONS_IDX(prod, XNC_OUT)]), len); |
417 | if (len == 0) |
418 | break; |
419 | prod = prod + len; |
420 | } |
421 | xen_wmb(); |
422 | xencons_interface->out_prod = prod; |
423 | xen_wmb(); |
424 | hypervisor_notify_via_evtchn(xen_start_info.console.domU.evtchn); |
425 | #undef XNC_OUT |
426 | } |
427 | |
428 | s = spltty(); |
429 | tp->t_state &= ~TS_BUSY; |
430 | if (ttypull(tp)) { |
431 | tp->t_state |= TS_TIMEOUT; |
432 | callout_schedule(&tp->t_rstrt_ch, 1); |
433 | } |
434 | out: |
435 | splx(s); |
436 | } |
437 | |
438 | void |
439 | xencons_stop(struct tty *tp, int flag) |
440 | { |
441 | |
442 | } |
443 | |
444 | |
445 | /* Non-privileged console interrupt routine */ |
446 | static int |
447 | xencons_handler(void *arg) |
448 | { |
449 | struct xencons_softc *sc = arg; |
450 | XENCONS_RING_IDX cons, prod, len; |
451 | int s = spltty(); |
452 | |
453 | if (sc->polling) { |
454 | splx(s); |
455 | return 1; |
456 | } |
457 | |
458 | |
459 | #define XNC_IN (xencons_interface->in) |
460 | |
461 | cons = xencons_interface->in_cons; |
462 | prod = xencons_interface->in_prod; |
463 | xen_rmb(); |
464 | while (cons != prod) { |
465 | if (MASK_XENCONS_IDX(cons, XNC_IN) < |
466 | MASK_XENCONS_IDX(prod, XNC_IN)) |
467 | len = MASK_XENCONS_IDX(prod, XNC_IN) - |
468 | MASK_XENCONS_IDX(cons, XNC_IN); |
469 | else |
470 | len = sizeof(XNC_IN) - MASK_XENCONS_IDX(cons, XNC_IN); |
471 | |
472 | xencons_tty_input(sc, __UNVOLATILE( |
473 | &XNC_IN[MASK_XENCONS_IDX(cons, XNC_IN)]), len); |
474 | if (__predict_false(xencons_interface->in_cons != cons)) { |
475 | /* catch up with xenconscn_getc() */ |
476 | cons = xencons_interface->in_cons; |
477 | prod = xencons_interface->in_prod; |
478 | xen_rmb(); |
479 | } else { |
480 | cons += len; |
481 | xen_wmb(); |
482 | xencons_interface->in_cons = cons; |
483 | xen_wmb(); |
484 | } |
485 | } |
486 | hypervisor_notify_via_evtchn(xen_start_info.console.domU.evtchn); |
487 | splx(s); |
488 | return 1; |
489 | #undef XNC_IN |
490 | } |
491 | |
492 | |
493 | void |
494 | xencons_tty_input(struct xencons_softc *sc, char* buf, int len) |
495 | { |
496 | struct tty *tp; |
497 | int i; |
498 | |
499 | tp = sc->sc_tty; |
500 | if (tp == NULL) |
501 | return; |
502 | |
503 | for (i = 0; i < len; i++) { |
504 | cn_check_magic(sc->sc_tty->t_dev, buf[i], xencons_cnm_state); |
505 | (*tp->t_linesw->l_rint)(buf[i], tp); |
506 | } |
507 | } |
508 | |
509 | /* privileged receive callback */ |
510 | int |
511 | xencons_intr(void *p) |
512 | { |
513 | static char rbuf[16]; |
514 | int len; |
515 | struct xencons_softc *sc = p; |
516 | |
517 | if (sc == NULL) |
518 | /* Interrupt may happen during resume */ |
519 | return 1; |
520 | |
521 | if (sc->polling) |
522 | return 1; |
523 | |
524 | while ((len = |
525 | HYPERVISOR_console_io(CONSOLEIO_read, sizeof(rbuf), rbuf)) > 0) { |
526 | xencons_tty_input(sc, rbuf, len); |
527 | } |
528 | return 1; |
529 | } |
530 | |
531 | void |
532 | xenconscn_attach(void) |
533 | { |
534 | |
535 | cn_tab = &xencons; |
536 | |
537 | /* console ring mapped in locore.S */ |
538 | |
539 | cn_init_magic(&xencons_cnm_state); |
540 | cn_set_magic("+++++" ); |
541 | |
542 | xencons_isconsole = 1; |
543 | } |
544 | |
545 | int |
546 | xenconscn_getc(dev_t dev) |
547 | { |
548 | char c; |
549 | int s = spltty(); |
550 | XENCONS_RING_IDX cons, prod; |
551 | |
552 | if (xencons_console_device && xencons_console_device->polling == 0) { |
553 | printf("xenconscn_getc() but not polling\n" ); |
554 | splx(s); |
555 | return 0; |
556 | } |
557 | if (xendomain_is_dom0()) { |
558 | while (HYPERVISOR_console_io(CONSOLEIO_read, 1, &c) == 0) |
559 | ; |
560 | cn_check_magic(dev, c, xencons_cnm_state); |
561 | splx(s); |
562 | return c; |
563 | } |
564 | if (xencons_console_device == NULL) { |
565 | printf("xenconscn_getc(): not console\n" ); |
566 | while (1) |
567 | ; /* loop here instead of in ddb */ |
568 | splx(s); |
569 | return 0; |
570 | } |
571 | |
572 | if (xencons_console_device->polling == 0) { |
573 | printf("xenconscn_getc() but not polling\n" ); |
574 | splx(s); |
575 | return 0; |
576 | } |
577 | |
578 | cons = xencons_interface->in_cons; |
579 | prod = xencons_interface->in_prod; |
580 | xen_rmb(); |
581 | while (cons == prod) { |
582 | HYPERVISOR_yield(); |
583 | prod = xencons_interface->in_prod; |
584 | } |
585 | xen_rmb(); |
586 | c = xencons_interface->in[MASK_XENCONS_IDX(xencons_interface->in_cons, |
587 | xencons_interface->in)]; |
588 | xen_rmb(); |
589 | xencons_interface->in_cons = cons + 1; |
590 | cn_check_magic(dev, c, xencons_cnm_state); |
591 | splx(s); |
592 | return c; |
593 | } |
594 | |
595 | void |
596 | xenconscn_putc(dev_t dev, int c) |
597 | { |
598 | int s = spltty(); |
599 | XENCONS_RING_IDX cons, prod; |
600 | |
601 | if (xendomain_is_dom0()) { |
602 | u_char buf[1]; |
603 | |
604 | buf[0] = c; |
605 | (void)HYPERVISOR_console_io(CONSOLEIO_write, 1, buf); |
606 | } else { |
607 | XENPRINTK(("xenconscn_putc(%c)\n" , c)); |
608 | |
609 | cons = xencons_interface->out_cons; |
610 | prod = xencons_interface->out_prod; |
611 | xen_rmb(); |
612 | while (prod == cons + sizeof(xencons_interface->out)) { |
613 | cons = xencons_interface->out_cons; |
614 | prod = xencons_interface->out_prod; |
615 | xen_rmb(); |
616 | } |
617 | xencons_interface->out[MASK_XENCONS_IDX(xencons_interface->out_prod, |
618 | xencons_interface->out)] = c; |
619 | xen_rmb(); |
620 | xencons_interface->out_prod++; |
621 | xen_rmb(); |
622 | hypervisor_notify_via_evtchn(xen_start_info.console.domU.evtchn); |
623 | splx(s); |
624 | } |
625 | } |
626 | |
627 | void |
628 | xenconscn_pollc(dev_t dev, int on) |
629 | { |
630 | if (xencons_console_device) |
631 | xencons_console_device->polling = on; |
632 | } |
633 | |
634 | /* |
635 | * Set line parameters. |
636 | */ |
637 | int |
638 | xencons_param(struct tty *tp, struct termios *t) |
639 | { |
640 | |
641 | tp->t_ispeed = t->c_ispeed; |
642 | tp->t_ospeed = t->c_ospeed; |
643 | tp->t_cflag = t->c_cflag; |
644 | return (0); |
645 | } |
646 | |