1 /*-
2 * Copyright (c) 2014, Bryan Venteicher <bryanv@FreeBSD.org>
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 unmodified, this list of conditions, and the following
10 * 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 /* Driver for VirtIO console devices. */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/ctype.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/malloc.h>
37 #include <sys/module.h>
38 #include <sys/kdb.h>
39 #include <sys/lock.h>
40 #include <sys/mutex.h>
41 #include <sys/sglist.h>
42 #include <sys/sysctl.h>
43 #include <sys/taskqueue.h>
44 #include <sys/queue.h>
45
46 #include <sys/conf.h>
47 #include <sys/cons.h>
48 #include <sys/tty.h>
49
50 #include <machine/bus.h>
51 #include <machine/resource.h>
52 #include <sys/bus.h>
53
54 #include <dev/virtio/virtio.h>
55 #include <dev/virtio/virtqueue.h>
56 #include <dev/virtio/console/virtio_console.h>
57
58 #include "virtio_if.h"
59
60 #define VTCON_MAX_PORTS 32
61 #define VTCON_TTY_PREFIX "V"
62 #define VTCON_TTY_ALIAS_PREFIX "vtcon"
63 #define VTCON_BULK_BUFSZ 128
64 #define VTCON_CTRL_BUFSZ 128
65
66 /*
67 * The buffers cannot cross more than one page boundary due to the
68 * size of the sglist segment array used.
69 */
70 CTASSERT(VTCON_BULK_BUFSZ <= PAGE_SIZE);
71 CTASSERT(VTCON_CTRL_BUFSZ <= PAGE_SIZE);
72
73 CTASSERT(sizeof(struct virtio_console_config) <= VTCON_CTRL_BUFSZ);
74
75 struct vtcon_softc;
76 struct vtcon_softc_port;
77
78 struct vtcon_port {
79 struct mtx vtcport_mtx;
80 struct vtcon_softc *vtcport_sc;
81 struct vtcon_softc_port *vtcport_scport;
82 struct tty *vtcport_tty;
83 struct virtqueue *vtcport_invq;
84 struct virtqueue *vtcport_outvq;
85 int vtcport_id;
86 int vtcport_flags;
87 #define VTCON_PORT_FLAG_GONE 0x01
88 #define VTCON_PORT_FLAG_CONSOLE 0x02
89 #define VTCON_PORT_FLAG_ALIAS 0x04
90
91 #if defined(KDB)
92 int vtcport_alt_break_state;
93 #endif
94 };
95
96 #define VTCON_PORT_LOCK(_port) mtx_lock(&(_port)->vtcport_mtx)
97 #define VTCON_PORT_UNLOCK(_port) mtx_unlock(&(_port)->vtcport_mtx)
98
99 struct vtcon_softc_port {
100 struct vtcon_softc *vcsp_sc;
101 struct vtcon_port *vcsp_port;
102 struct virtqueue *vcsp_invq;
103 struct virtqueue *vcsp_outvq;
104 };
105
106 struct vtcon_softc {
107 device_t vtcon_dev;
108 struct mtx vtcon_mtx;
109 uint64_t vtcon_features;
110 uint32_t vtcon_max_ports;
111 uint32_t vtcon_flags;
112 #define VTCON_FLAG_DETACHED 0x01
113 #define VTCON_FLAG_SIZE 0x02
114 #define VTCON_FLAG_MULTIPORT 0x04
115
116 /*
117 * Ports can be added and removed during runtime, but we have
118 * to allocate all the virtqueues during attach. This array is
119 * indexed by the port ID.
120 */
121 struct vtcon_softc_port *vtcon_ports;
122
123 struct task vtcon_ctrl_task;
124 struct virtqueue *vtcon_ctrl_rxvq;
125 struct virtqueue *vtcon_ctrl_txvq;
126 struct mtx vtcon_ctrl_tx_mtx;
127 };
128
129 #define VTCON_LOCK(_sc) mtx_lock(&(_sc)->vtcon_mtx)
130 #define VTCON_UNLOCK(_sc) mtx_unlock(&(_sc)->vtcon_mtx)
131 #define VTCON_LOCK_ASSERT(_sc) \
132 mtx_assert(&(_sc)->vtcon_mtx, MA_OWNED)
133 #define VTCON_LOCK_ASSERT_NOTOWNED(_sc) \
134 mtx_assert(&(_sc)->vtcon_mtx, MA_NOTOWNED)
135
136 #define VTCON_CTRL_TX_LOCK(_sc) mtx_lock(&(_sc)->vtcon_ctrl_tx_mtx)
137 #define VTCON_CTRL_TX_UNLOCK(_sc) mtx_unlock(&(_sc)->vtcon_ctrl_tx_mtx)
138
139 #define VTCON_ASSERT_VALID_PORTID(_sc, _id) \
140 KASSERT((_id) >= 0 && (_id) < (_sc)->vtcon_max_ports, \
141 ("%s: port ID %d out of range", __func__, _id))
142
143 #define VTCON_FEATURES VIRTIO_CONSOLE_F_MULTIPORT
144
145 static struct virtio_feature_desc vtcon_feature_desc[] = {
146 { VIRTIO_CONSOLE_F_SIZE, "ConsoleSize" },
147 { VIRTIO_CONSOLE_F_MULTIPORT, "MultiplePorts" },
148 { VIRTIO_CONSOLE_F_EMERG_WRITE, "EmergencyWrite" },
149 { 0, NULL }
150 };
151
152 static int vtcon_modevent(module_t, int, void *);
153 static void vtcon_drain_all(void);
154
155 static int vtcon_probe(device_t);
156 static int vtcon_attach(device_t);
157 static int vtcon_detach(device_t);
158 static int vtcon_config_change(device_t);
159
160 static int vtcon_setup_features(struct vtcon_softc *);
161 static int vtcon_negotiate_features(struct vtcon_softc *);
162 static int vtcon_alloc_scports(struct vtcon_softc *);
163 static int vtcon_alloc_virtqueues(struct vtcon_softc *);
164 static void vtcon_read_config(struct vtcon_softc *,
165 struct virtio_console_config *);
166
167 static void vtcon_determine_max_ports(struct vtcon_softc *,
168 struct virtio_console_config *);
169 static void vtcon_destroy_ports(struct vtcon_softc *);
170 static void vtcon_stop(struct vtcon_softc *);
171
172 static int vtcon_ctrl_event_enqueue(struct vtcon_softc *,
173 struct virtio_console_control *);
174 static int vtcon_ctrl_event_create(struct vtcon_softc *);
175 static void vtcon_ctrl_event_requeue(struct vtcon_softc *,
176 struct virtio_console_control *);
177 static int vtcon_ctrl_event_populate(struct vtcon_softc *);
178 static void vtcon_ctrl_event_drain(struct vtcon_softc *);
179 static int vtcon_ctrl_init(struct vtcon_softc *);
180 static void vtcon_ctrl_deinit(struct vtcon_softc *);
181 static void vtcon_ctrl_port_add_event(struct vtcon_softc *, int);
182 static void vtcon_ctrl_port_remove_event(struct vtcon_softc *, int);
183 static void vtcon_ctrl_port_console_event(struct vtcon_softc *, int);
184 static void vtcon_ctrl_port_open_event(struct vtcon_softc *, int);
185 static void vtcon_ctrl_port_name_event(struct vtcon_softc *, int,
186 const char *, size_t);
187 static void vtcon_ctrl_process_event(struct vtcon_softc *,
188 struct virtio_console_control *, void *, size_t);
189 static void vtcon_ctrl_task_cb(void *, int);
190 static void vtcon_ctrl_event_intr(void *);
191 static void vtcon_ctrl_poll(struct vtcon_softc *,
192 struct virtio_console_control *control);
193 static void vtcon_ctrl_send_control(struct vtcon_softc *, uint32_t,
194 uint16_t, uint16_t);
195
196 static int vtcon_port_enqueue_buf(struct vtcon_port *, void *, size_t);
197 static int vtcon_port_create_buf(struct vtcon_port *);
198 static void vtcon_port_requeue_buf(struct vtcon_port *, void *);
199 static int vtcon_port_populate(struct vtcon_port *);
200 static void vtcon_port_destroy(struct vtcon_port *);
201 static int vtcon_port_create(struct vtcon_softc *, int);
202 static void vtcon_port_dev_alias(struct vtcon_port *, const char *,
203 size_t);
204 static void vtcon_port_drain_bufs(struct virtqueue *);
205 static void vtcon_port_drain(struct vtcon_port *);
206 static void vtcon_port_teardown(struct vtcon_port *);
207 static void vtcon_port_change_size(struct vtcon_port *, uint16_t,
208 uint16_t);
209 static void vtcon_port_update_console_size(struct vtcon_softc *);
210 static void vtcon_port_enable_intr(struct vtcon_port *);
211 static void vtcon_port_disable_intr(struct vtcon_port *);
212 static void vtcon_port_in(struct vtcon_port *);
213 static void vtcon_port_intr(void *);
214 static void vtcon_port_out(struct vtcon_port *, void *, int);
215 static void vtcon_port_submit_event(struct vtcon_port *, uint16_t,
216 uint16_t);
217
218 static int vtcon_tty_open(struct tty *);
219 static void vtcon_tty_close(struct tty *);
220 static void vtcon_tty_outwakeup(struct tty *);
221 static void vtcon_tty_free(void *);
222
223 static void vtcon_get_console_size(struct vtcon_softc *, uint16_t *,
224 uint16_t *);
225
226 static void vtcon_enable_interrupts(struct vtcon_softc *);
227 static void vtcon_disable_interrupts(struct vtcon_softc *);
228
229 #define vtcon_modern(_sc) (((_sc)->vtcon_features & VIRTIO_F_VERSION_1) != 0)
230 #define vtcon_htog16(_sc, _val) virtio_htog16(vtcon_modern(_sc), _val)
231 #define vtcon_htog32(_sc, _val) virtio_htog32(vtcon_modern(_sc), _val)
232 #define vtcon_htog64(_sc, _val) virtio_htog64(vtcon_modern(_sc), _val)
233 #define vtcon_gtoh16(_sc, _val) virtio_gtoh16(vtcon_modern(_sc), _val)
234 #define vtcon_gtoh32(_sc, _val) virtio_gtoh32(vtcon_modern(_sc), _val)
235 #define vtcon_gtoh64(_sc, _val) virtio_gtoh64(vtcon_modern(_sc), _val)
236
237 static int vtcon_pending_free;
238
239 static struct ttydevsw vtcon_tty_class = {
240 .tsw_flags = 0,
241 .tsw_open = vtcon_tty_open,
242 .tsw_close = vtcon_tty_close,
243 .tsw_outwakeup = vtcon_tty_outwakeup,
244 .tsw_free = vtcon_tty_free,
245 };
246
247 static device_method_t vtcon_methods[] = {
248 /* Device methods. */
249 DEVMETHOD(device_probe, vtcon_probe),
250 DEVMETHOD(device_attach, vtcon_attach),
251 DEVMETHOD(device_detach, vtcon_detach),
252
253 /* VirtIO methods. */
254 DEVMETHOD(virtio_config_change, vtcon_config_change),
255
256 DEVMETHOD_END
257 };
258
259 static driver_t vtcon_driver = {
260 "vtcon",
261 vtcon_methods,
262 sizeof(struct vtcon_softc)
263 };
264
265 VIRTIO_DRIVER_MODULE(virtio_console, vtcon_driver, vtcon_modevent, NULL);
266 MODULE_VERSION(virtio_console, 1);
267 MODULE_DEPEND(virtio_console, virtio, 1, 1, 1);
268
269 VIRTIO_SIMPLE_PNPINFO(virtio_console, VIRTIO_ID_CONSOLE,
270 "VirtIO Console Adapter");
271
272 static int
273 vtcon_modevent(module_t mod, int type, void *unused)
274 {
275 int error;
276
277 switch (type) {
278 case MOD_LOAD:
279 error = 0;
280 break;
281 case MOD_QUIESCE:
282 error = 0;
283 break;
284 case MOD_UNLOAD:
285 vtcon_drain_all();
286 error = 0;
287 break;
288 case MOD_SHUTDOWN:
289 error = 0;
290 break;
291 default:
292 error = EOPNOTSUPP;
293 break;
294 }
295
296 return (error);
297 }
298
299 static void
300 vtcon_drain_all(void)
301 {
302 int first;
303
304 for (first = 1; vtcon_pending_free != 0; first = 0) {
305 if (first != 0) {
306 printf("virtio_console: Waiting for all detached TTY "
307 "devices to have open fds closed.\n");
308 }
309 pause("vtcondra", hz);
310 }
311 }
312
313 static int
314 vtcon_probe(device_t dev)
315 {
316 return (VIRTIO_SIMPLE_PROBE(dev, virtio_console));
317 }
318
319 static int
320 vtcon_attach(device_t dev)
321 {
322 struct vtcon_softc *sc;
323 struct virtio_console_config concfg;
324 int error;
325
326 sc = device_get_softc(dev);
327 sc->vtcon_dev = dev;
328 virtio_set_feature_desc(dev, vtcon_feature_desc);
329
330 mtx_init(&sc->vtcon_mtx, "vtconmtx", NULL, MTX_DEF);
331 mtx_init(&sc->vtcon_ctrl_tx_mtx, "vtconctrlmtx", NULL, MTX_DEF);
332
333 error = vtcon_setup_features(sc);
334 if (error) {
335 device_printf(dev, "cannot setup features\n");
336 goto fail;
337 }
338
339 vtcon_read_config(sc, &concfg);
340 vtcon_determine_max_ports(sc, &concfg);
341
342 error = vtcon_alloc_scports(sc);
343 if (error) {
344 device_printf(dev, "cannot allocate softc port structures\n");
345 goto fail;
346 }
347
348 error = vtcon_alloc_virtqueues(sc);
349 if (error) {
350 device_printf(dev, "cannot allocate virtqueues\n");
351 goto fail;
352 }
353
354 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) {
355 TASK_INIT(&sc->vtcon_ctrl_task, 0, vtcon_ctrl_task_cb, sc);
356 error = vtcon_ctrl_init(sc);
357 if (error)
358 goto fail;
359 } else {
360 error = vtcon_port_create(sc, 0);
361 if (error)
362 goto fail;
363 if (sc->vtcon_flags & VTCON_FLAG_SIZE)
364 vtcon_port_update_console_size(sc);
365 }
366
367 error = virtio_setup_intr(dev, INTR_TYPE_TTY);
368 if (error) {
369 device_printf(dev, "cannot setup virtqueue interrupts\n");
370 goto fail;
371 }
372
373 vtcon_enable_interrupts(sc);
374
375 vtcon_ctrl_send_control(sc, VIRTIO_CONSOLE_BAD_ID,
376 VIRTIO_CONSOLE_DEVICE_READY, 1);
377
378 fail:
379 if (error)
380 vtcon_detach(dev);
381
382 return (error);
383 }
384
385 static int
386 vtcon_detach(device_t dev)
387 {
388 struct vtcon_softc *sc;
389
390 sc = device_get_softc(dev);
391
392 VTCON_LOCK(sc);
393 sc->vtcon_flags |= VTCON_FLAG_DETACHED;
394 if (device_is_attached(dev))
395 vtcon_stop(sc);
396 VTCON_UNLOCK(sc);
397
398 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) {
399 taskqueue_drain(taskqueue_thread, &sc->vtcon_ctrl_task);
400 vtcon_ctrl_deinit(sc);
401 }
402
403 vtcon_destroy_ports(sc);
404 mtx_destroy(&sc->vtcon_mtx);
405 mtx_destroy(&sc->vtcon_ctrl_tx_mtx);
406
407 return (0);
408 }
409
410 static int
411 vtcon_config_change(device_t dev)
412 {
413 struct vtcon_softc *sc;
414
415 sc = device_get_softc(dev);
416
417 /*
418 * When the multiport feature is negotiated, all configuration
419 * changes are done through control virtqueue events.
420 */
421 if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) {
422 if (sc->vtcon_flags & VTCON_FLAG_SIZE)
423 vtcon_port_update_console_size(sc);
424 }
425
426 return (0);
427 }
428
429 static int
430 vtcon_negotiate_features(struct vtcon_softc *sc)
431 {
432 device_t dev;
433 uint64_t features;
434
435 dev = sc->vtcon_dev;
436 features = VTCON_FEATURES;
437
438 sc->vtcon_features = virtio_negotiate_features(dev, features);
439 return (virtio_finalize_features(dev));
440 }
441
442 static int
443 vtcon_setup_features(struct vtcon_softc *sc)
444 {
445 device_t dev;
446 int error;
447
448 dev = sc->vtcon_dev;
449
450 error = vtcon_negotiate_features(sc);
451 if (error)
452 return (error);
453
454 if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_SIZE))
455 sc->vtcon_flags |= VTCON_FLAG_SIZE;
456 if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_MULTIPORT))
457 sc->vtcon_flags |= VTCON_FLAG_MULTIPORT;
458
459 return (0);
460 }
461
462 #define VTCON_GET_CONFIG(_dev, _feature, _field, _cfg) \
463 if (virtio_with_feature(_dev, _feature)) { \
464 virtio_read_device_config(_dev, \
465 offsetof(struct virtio_console_config, _field), \
466 &(_cfg)->_field, sizeof((_cfg)->_field)); \
467 }
468
469 static void
470 vtcon_read_config(struct vtcon_softc *sc, struct virtio_console_config *concfg)
471 {
472 device_t dev;
473
474 dev = sc->vtcon_dev;
475
476 bzero(concfg, sizeof(struct virtio_console_config));
477
478 VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, cols, concfg);
479 VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, rows, concfg);
480 VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_MULTIPORT, max_nr_ports, concfg);
481 }
482
483 #undef VTCON_GET_CONFIG
484
485 static int
486 vtcon_alloc_scports(struct vtcon_softc *sc)
487 {
488 struct vtcon_softc_port *scport;
489 int max, i;
490
491 max = sc->vtcon_max_ports;
492
493 sc->vtcon_ports = malloc(sizeof(struct vtcon_softc_port) * max,
494 M_DEVBUF, M_NOWAIT | M_ZERO);
495 if (sc->vtcon_ports == NULL)
496 return (ENOMEM);
497
498 for (i = 0; i < max; i++) {
499 scport = &sc->vtcon_ports[i];
500 scport->vcsp_sc = sc;
501 }
502
503 return (0);
504 }
505
506 static int
507 vtcon_alloc_virtqueues(struct vtcon_softc *sc)
508 {
509 device_t dev;
510 struct vq_alloc_info *info;
511 struct vtcon_softc_port *scport;
512 int i, idx, portidx, nvqs, error;
513
514 dev = sc->vtcon_dev;
515
516 nvqs = sc->vtcon_max_ports * 2;
517 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
518 nvqs += 2;
519
520 info = malloc(sizeof(struct vq_alloc_info) * nvqs, M_TEMP, M_NOWAIT);
521 if (info == NULL)
522 return (ENOMEM);
523
524 for (i = 0, idx = 0, portidx = 0; i < nvqs / 2; i++, idx += 2) {
525 if (i == 1) {
526 /* The control virtqueues are after the first port. */
527 VQ_ALLOC_INFO_INIT(&info[idx], 0,
528 vtcon_ctrl_event_intr, sc, &sc->vtcon_ctrl_rxvq,
529 "%s-control rx", device_get_nameunit(dev));
530 VQ_ALLOC_INFO_INIT(&info[idx+1], 0,
531 NULL, sc, &sc->vtcon_ctrl_txvq,
532 "%s-control tx", device_get_nameunit(dev));
533 continue;
534 }
535
536 scport = &sc->vtcon_ports[portidx];
537
538 VQ_ALLOC_INFO_INIT(&info[idx], 0, vtcon_port_intr,
539 scport, &scport->vcsp_invq, "%s-port%d in",
540 device_get_nameunit(dev), i);
541 VQ_ALLOC_INFO_INIT(&info[idx+1], 0, NULL,
542 NULL, &scport->vcsp_outvq, "%s-port%d out",
543 device_get_nameunit(dev), i);
544
545 portidx++;
546 }
547
548 error = virtio_alloc_virtqueues(dev, 0, nvqs, info);
549 free(info, M_TEMP);
550
551 return (error);
552 }
553
554 static void
555 vtcon_determine_max_ports(struct vtcon_softc *sc,
556 struct virtio_console_config *concfg)
557 {
558
559 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) {
560 sc->vtcon_max_ports =
561 min(concfg->max_nr_ports, VTCON_MAX_PORTS);
562 if (sc->vtcon_max_ports == 0)
563 sc->vtcon_max_ports = 1;
564 } else
565 sc->vtcon_max_ports = 1;
566 }
567
568 static void
569 vtcon_destroy_ports(struct vtcon_softc *sc)
570 {
571 struct vtcon_softc_port *scport;
572 struct vtcon_port *port;
573 struct virtqueue *vq;
574 int i;
575
576 if (sc->vtcon_ports == NULL)
577 return;
578
579 VTCON_LOCK(sc);
580 for (i = 0; i < sc->vtcon_max_ports; i++) {
581 scport = &sc->vtcon_ports[i];
582
583 port = scport->vcsp_port;
584 if (port != NULL) {
585 scport->vcsp_port = NULL;
586 VTCON_PORT_LOCK(port);
587 VTCON_UNLOCK(sc);
588 vtcon_port_teardown(port);
589 VTCON_LOCK(sc);
590 }
591
592 vq = scport->vcsp_invq;
593 if (vq != NULL)
594 vtcon_port_drain_bufs(vq);
595 }
596 VTCON_UNLOCK(sc);
597
598 free(sc->vtcon_ports, M_DEVBUF);
599 sc->vtcon_ports = NULL;
600 }
601
602 static void
603 vtcon_stop(struct vtcon_softc *sc)
604 {
605
606 vtcon_disable_interrupts(sc);
607 virtio_stop(sc->vtcon_dev);
608 }
609
610 static int
611 vtcon_ctrl_event_enqueue(struct vtcon_softc *sc,
612 struct virtio_console_control *control)
613 {
614 struct sglist_seg segs[2];
615 struct sglist sg;
616 struct virtqueue *vq;
617 int error __diagused;
618
619 vq = sc->vtcon_ctrl_rxvq;
620
621 sglist_init(&sg, 2, segs);
622 error = sglist_append(&sg, control, VTCON_CTRL_BUFSZ);
623 KASSERT(error == 0, ("%s: error %d adding control to sglist",
624 __func__, error));
625
626 return (virtqueue_enqueue(vq, control, &sg, 0, sg.sg_nseg));
627 }
628
629 static int
630 vtcon_ctrl_event_create(struct vtcon_softc *sc)
631 {
632 struct virtio_console_control *control;
633 int error;
634
635 control = malloc(VTCON_CTRL_BUFSZ, M_DEVBUF, M_ZERO | M_NOWAIT);
636 if (control == NULL)
637 return (ENOMEM);
638
639 error = vtcon_ctrl_event_enqueue(sc, control);
640 if (error)
641 free(control, M_DEVBUF);
642
643 return (error);
644 }
645
646 static void
647 vtcon_ctrl_event_requeue(struct vtcon_softc *sc,
648 struct virtio_console_control *control)
649 {
650 int error __diagused;
651
652 bzero(control, VTCON_CTRL_BUFSZ);
653
654 error = vtcon_ctrl_event_enqueue(sc, control);
655 KASSERT(error == 0,
656 ("%s: cannot requeue control buffer %d", __func__, error));
657 }
658
659 static int
660 vtcon_ctrl_event_populate(struct vtcon_softc *sc)
661 {
662 struct virtqueue *vq;
663 int nbufs, error;
664
665 vq = sc->vtcon_ctrl_rxvq;
666 error = ENOSPC;
667
668 for (nbufs = 0; !virtqueue_full(vq); nbufs++) {
669 error = vtcon_ctrl_event_create(sc);
670 if (error)
671 break;
672 }
673
674 if (nbufs > 0) {
675 virtqueue_notify(vq);
676 error = 0;
677 }
678
679 return (error);
680 }
681
682 static void
683 vtcon_ctrl_event_drain(struct vtcon_softc *sc)
684 {
685 struct virtio_console_control *control;
686 struct virtqueue *vq;
687 int last;
688
689 vq = sc->vtcon_ctrl_rxvq;
690 last = 0;
691
692 if (vq == NULL)
693 return;
694
695 VTCON_LOCK(sc);
696 while ((control = virtqueue_drain(vq, &last)) != NULL)
697 free(control, M_DEVBUF);
698 VTCON_UNLOCK(sc);
699 }
700
701 static int
702 vtcon_ctrl_init(struct vtcon_softc *sc)
703 {
704 int error;
705
706 error = vtcon_ctrl_event_populate(sc);
707
708 return (error);
709 }
710
711 static void
712 vtcon_ctrl_deinit(struct vtcon_softc *sc)
713 {
714
715 vtcon_ctrl_event_drain(sc);
716 }
717
718 static void
719 vtcon_ctrl_port_add_event(struct vtcon_softc *sc, int id)
720 {
721 device_t dev;
722 int error;
723
724 dev = sc->vtcon_dev;
725
726 /* This single thread only way for ports to be created. */
727 if (sc->vtcon_ports[id].vcsp_port != NULL) {
728 device_printf(dev, "%s: adding port %d, but already exists\n",
729 __func__, id);
730 return;
731 }
732
733 error = vtcon_port_create(sc, id);
734 if (error) {
735 device_printf(dev, "%s: cannot create port %d: %d\n",
736 __func__, id, error);
737 vtcon_ctrl_send_control(sc, id, VIRTIO_CONSOLE_PORT_READY, 0);
738 return;
739 }
740 }
741
742 static void
743 vtcon_ctrl_port_remove_event(struct vtcon_softc *sc, int id)
744 {
745 device_t dev;
746 struct vtcon_softc_port *scport;
747 struct vtcon_port *port;
748
749 dev = sc->vtcon_dev;
750 scport = &sc->vtcon_ports[id];
751
752 VTCON_LOCK(sc);
753 port = scport->vcsp_port;
754 if (port == NULL) {
755 VTCON_UNLOCK(sc);
756 device_printf(dev, "%s: remove port %d, but does not exist\n",
757 __func__, id);
758 return;
759 }
760
761 scport->vcsp_port = NULL;
762 VTCON_PORT_LOCK(port);
763 VTCON_UNLOCK(sc);
764 vtcon_port_teardown(port);
765 }
766
767 static void
768 vtcon_ctrl_port_console_event(struct vtcon_softc *sc, int id)
769 {
770 device_t dev;
771 struct vtcon_softc_port *scport;
772 struct vtcon_port *port;
773
774 dev = sc->vtcon_dev;
775 scport = &sc->vtcon_ports[id];
776
777 VTCON_LOCK(sc);
778 port = scport->vcsp_port;
779 if (port == NULL) {
780 VTCON_UNLOCK(sc);
781 device_printf(dev, "%s: console port %d, but does not exist\n",
782 __func__, id);
783 return;
784 }
785
786 VTCON_PORT_LOCK(port);
787 VTCON_UNLOCK(sc);
788 port->vtcport_flags |= VTCON_PORT_FLAG_CONSOLE;
789 vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
790 VTCON_PORT_UNLOCK(port);
791 }
792
793 static void
794 vtcon_ctrl_port_open_event(struct vtcon_softc *sc, int id)
795 {
796 device_t dev;
797 struct vtcon_softc_port *scport;
798 struct vtcon_port *port;
799
800 dev = sc->vtcon_dev;
801 scport = &sc->vtcon_ports[id];
802
803 VTCON_LOCK(sc);
804 port = scport->vcsp_port;
805 if (port == NULL) {
806 VTCON_UNLOCK(sc);
807 device_printf(dev, "%s: open port %d, but does not exist\n",
808 __func__, id);
809 return;
810 }
811
812 VTCON_PORT_LOCK(port);
813 VTCON_UNLOCK(sc);
814 vtcon_port_enable_intr(port);
815 VTCON_PORT_UNLOCK(port);
816 }
817
818 static void
819 vtcon_ctrl_port_name_event(struct vtcon_softc *sc, int id, const char *name,
820 size_t len)
821 {
822 device_t dev;
823 struct vtcon_softc_port *scport;
824 struct vtcon_port *port;
825
826 dev = sc->vtcon_dev;
827 scport = &sc->vtcon_ports[id];
828
829 /*
830 * The VirtIO specification says the NUL terminator is not included in
831 * the length, but QEMU includes it. Adjust the length if needed.
832 */
833 if (name == NULL || len == 0)
834 return;
835 if (name[len - 1] == '\0') {
836 len--;
837 if (len == 0)
838 return;
839 }
840
841 VTCON_LOCK(sc);
842 port = scport->vcsp_port;
843 if (port == NULL) {
844 VTCON_UNLOCK(sc);
845 device_printf(dev, "%s: name port %d, but does not exist\n",
846 __func__, id);
847 return;
848 }
849
850 VTCON_PORT_LOCK(port);
851 VTCON_UNLOCK(sc);
852 vtcon_port_dev_alias(port, name, len);
853 VTCON_PORT_UNLOCK(port);
854 }
855
856 static void
857 vtcon_ctrl_process_event(struct vtcon_softc *sc,
858 struct virtio_console_control *control, void *data, size_t data_len)
859 {
860 device_t dev;
861 uint32_t id;
862 uint16_t event;
863
864 dev = sc->vtcon_dev;
865 id = vtcon_htog32(sc, control->id);
866 event = vtcon_htog16(sc, control->event);
867
868 if (id >= sc->vtcon_max_ports) {
869 device_printf(dev, "%s: event %d invalid port ID %d\n",
870 __func__, event, id);
871 return;
872 }
873
874 switch (event) {
875 case VIRTIO_CONSOLE_PORT_ADD:
876 vtcon_ctrl_port_add_event(sc, id);
877 break;
878
879 case VIRTIO_CONSOLE_PORT_REMOVE:
880 vtcon_ctrl_port_remove_event(sc, id);
881 break;
882
883 case VIRTIO_CONSOLE_CONSOLE_PORT:
884 vtcon_ctrl_port_console_event(sc, id);
885 break;
886
887 case VIRTIO_CONSOLE_RESIZE:
888 break;
889
890 case VIRTIO_CONSOLE_PORT_OPEN:
891 vtcon_ctrl_port_open_event(sc, id);
892 break;
893
894 case VIRTIO_CONSOLE_PORT_NAME:
895 vtcon_ctrl_port_name_event(sc, id, (const char *)data, data_len);
896 break;
897 }
898 }
899
900 static void
901 vtcon_ctrl_task_cb(void *xsc, int pending)
902 {
903 struct vtcon_softc *sc;
904 struct virtqueue *vq;
905 struct virtio_console_control *control;
906 void *data;
907 size_t data_len;
908 int detached;
909 uint32_t len;
910
911 sc = xsc;
912 vq = sc->vtcon_ctrl_rxvq;
913
914 VTCON_LOCK(sc);
915
916 while ((detached = (sc->vtcon_flags & VTCON_FLAG_DETACHED)) == 0) {
917 control = virtqueue_dequeue(vq, &len);
918 if (control == NULL)
919 break;
920
921 if (len > sizeof(struct virtio_console_control)) {
922 data = (void *) &control[1];
923 data_len = len - sizeof(struct virtio_console_control);
924 } else {
925 data = NULL;
926 data_len = 0;
927 }
928
929 VTCON_UNLOCK(sc);
930 vtcon_ctrl_process_event(sc, control, data, data_len);
931 VTCON_LOCK(sc);
932 vtcon_ctrl_event_requeue(sc, control);
933 }
934
935 if (!detached) {
936 virtqueue_notify(vq);
937 if (virtqueue_enable_intr(vq) != 0)
938 taskqueue_enqueue(taskqueue_thread,
939 &sc->vtcon_ctrl_task);
940 }
941
942 VTCON_UNLOCK(sc);
943 }
944
945 static void
946 vtcon_ctrl_event_intr(void *xsc)
947 {
948 struct vtcon_softc *sc;
949
950 sc = xsc;
951
952 /*
953 * Only some events require us to potentially block, but it
954 * easier to just defer all event handling to the taskqueue.
955 */
956 taskqueue_enqueue(taskqueue_thread, &sc->vtcon_ctrl_task);
957 }
958
959 static void
960 vtcon_ctrl_poll(struct vtcon_softc *sc,
961 struct virtio_console_control *control)
962 {
963 struct sglist_seg segs[2];
964 struct sglist sg;
965 struct virtqueue *vq;
966 int error;
967
968 vq = sc->vtcon_ctrl_txvq;
969
970 sglist_init(&sg, 2, segs);
971 error = sglist_append(&sg, control,
972 sizeof(struct virtio_console_control));
973 KASSERT(error == 0, ("%s: error %d adding control to sglist",
974 __func__, error));
975
976 /*
977 * We cannot use the softc lock to serialize access to this
978 * virtqueue since this is called from the tty layer with the
979 * port lock held. Acquiring the softc would violate our lock
980 * ordering.
981 */
982 VTCON_CTRL_TX_LOCK(sc);
983 KASSERT(virtqueue_empty(vq),
984 ("%s: virtqueue is not emtpy", __func__));
985 error = virtqueue_enqueue(vq, control, &sg, sg.sg_nseg, 0);
986 if (error == 0) {
987 virtqueue_notify(vq);
988 virtqueue_poll(vq, NULL);
989 }
990 VTCON_CTRL_TX_UNLOCK(sc);
991 }
992
993 static void
994 vtcon_ctrl_send_control(struct vtcon_softc *sc, uint32_t portid,
995 uint16_t event, uint16_t value)
996 {
997 struct virtio_console_control control;
998
999 if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0)
1000 return;
1001
1002 control.id = vtcon_gtoh32(sc, portid);
1003 control.event = vtcon_gtoh16(sc, event);
1004 control.value = vtcon_gtoh16(sc, value);
1005
1006 vtcon_ctrl_poll(sc, &control);
1007 }
1008
1009 static int
1010 vtcon_port_enqueue_buf(struct vtcon_port *port, void *buf, size_t len)
1011 {
1012 struct sglist_seg segs[2];
1013 struct sglist sg;
1014 struct virtqueue *vq;
1015 int error;
1016
1017 vq = port->vtcport_invq;
1018
1019 sglist_init(&sg, 2, segs);
1020 error = sglist_append(&sg, buf, len);
1021 KASSERT(error == 0,
1022 ("%s: error %d adding buffer to sglist", __func__, error));
1023
1024 error = virtqueue_enqueue(vq, buf, &sg, 0, sg.sg_nseg);
1025
1026 return (error);
1027 }
1028
1029 static int
1030 vtcon_port_create_buf(struct vtcon_port *port)
1031 {
1032 void *buf;
1033 int error;
1034
1035 buf = malloc(VTCON_BULK_BUFSZ, M_DEVBUF, M_ZERO | M_NOWAIT);
1036 if (buf == NULL)
1037 return (ENOMEM);
1038
1039 error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ);
1040 if (error)
1041 free(buf, M_DEVBUF);
1042
1043 return (error);
1044 }
1045
1046 static void
1047 vtcon_port_requeue_buf(struct vtcon_port *port, void *buf)
1048 {
1049 int error __diagused;
1050
1051 error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ);
1052 KASSERT(error == 0,
1053 ("%s: cannot requeue input buffer %d", __func__, error));
1054 }
1055
1056 static int
1057 vtcon_port_populate(struct vtcon_port *port)
1058 {
1059 struct virtqueue *vq;
1060 int nbufs, error;
1061
1062 vq = port->vtcport_invq;
1063 error = ENOSPC;
1064
1065 for (nbufs = 0; !virtqueue_full(vq); nbufs++) {
1066 error = vtcon_port_create_buf(port);
1067 if (error)
1068 break;
1069 }
1070
1071 if (nbufs > 0) {
1072 virtqueue_notify(vq);
1073 error = 0;
1074 }
1075
1076 return (error);
1077 }
1078
1079 static void
1080 vtcon_port_destroy(struct vtcon_port *port)
1081 {
1082
1083 port->vtcport_sc = NULL;
1084 port->vtcport_scport = NULL;
1085 port->vtcport_invq = NULL;
1086 port->vtcport_outvq = NULL;
1087 port->vtcport_id = -1;
1088 mtx_destroy(&port->vtcport_mtx);
1089 free(port, M_DEVBUF);
1090 }
1091
1092 static int
1093 vtcon_port_init_vqs(struct vtcon_port *port)
1094 {
1095 struct vtcon_softc_port *scport;
1096 int error;
1097
1098 scport = port->vtcport_scport;
1099
1100 port->vtcport_invq = scport->vcsp_invq;
1101 port->vtcport_outvq = scport->vcsp_outvq;
1102
1103 /*
1104 * Free any data left over from when this virtqueue was in use by a
1105 * prior port. We have not yet notified the host that the port is
1106 * ready, so assume nothing in the virtqueue can be for us.
1107 */
1108 vtcon_port_drain(port);
1109
1110 KASSERT(virtqueue_empty(port->vtcport_invq),
1111 ("%s: in virtqueue is not empty", __func__));
1112 KASSERT(virtqueue_empty(port->vtcport_outvq),
1113 ("%s: out virtqueue is not empty", __func__));
1114
1115 error = vtcon_port_populate(port);
1116 if (error)
1117 return (error);
1118
1119 return (0);
1120 }
1121
1122 static int
1123 vtcon_port_create(struct vtcon_softc *sc, int id)
1124 {
1125 device_t dev;
1126 struct vtcon_softc_port *scport;
1127 struct vtcon_port *port;
1128 int error;
1129
1130 dev = sc->vtcon_dev;
1131 scport = &sc->vtcon_ports[id];
1132
1133 VTCON_ASSERT_VALID_PORTID(sc, id);
1134 MPASS(scport->vcsp_port == NULL);
1135
1136 port = malloc(sizeof(struct vtcon_port), M_DEVBUF, M_NOWAIT | M_ZERO);
1137 if (port == NULL)
1138 return (ENOMEM);
1139
1140 port->vtcport_sc = sc;
1141 port->vtcport_scport = scport;
1142 port->vtcport_id = id;
1143 mtx_init(&port->vtcport_mtx, "vtcpmtx", NULL, MTX_DEF);
1144 port->vtcport_tty = tty_alloc_mutex(&vtcon_tty_class, port,
1145 &port->vtcport_mtx);
1146
1147 error = vtcon_port_init_vqs(port);
1148 if (error) {
1149 VTCON_PORT_LOCK(port);
1150 vtcon_port_teardown(port);
1151 return (error);
1152 }
1153
1154 VTCON_LOCK(sc);
1155 VTCON_PORT_LOCK(port);
1156 scport->vcsp_port = port;
1157 vtcon_port_enable_intr(port);
1158 vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_READY, 1);
1159 VTCON_PORT_UNLOCK(port);
1160 VTCON_UNLOCK(sc);
1161
1162 tty_makedev(port->vtcport_tty, NULL, "%s%r.%r", VTCON_TTY_PREFIX,
1163 device_get_unit(dev), id);
1164
1165 return (0);
1166 }
1167
1168 static void
1169 vtcon_port_dev_alias(struct vtcon_port *port, const char *name, size_t len)
1170 {
1171 struct vtcon_softc *sc;
1172 struct cdev *pdev;
1173 struct tty *tp;
1174 int i, error;
1175
1176 sc = port->vtcport_sc;
1177 tp = port->vtcport_tty;
1178
1179 if (port->vtcport_flags & VTCON_PORT_FLAG_ALIAS)
1180 return;
1181
1182 /* Port name is UTF-8, but we can only handle ASCII. */
1183 for (i = 0; i < len; i++) {
1184 if (!isascii(name[i]))
1185 return;
1186 }
1187
1188 /*
1189 * Port name may not conform to the devfs requirements so we cannot use
1190 * tty_makealias() because the MAKEDEV_CHECKNAME flag must be specified.
1191 */
1192 error = make_dev_alias_p(MAKEDEV_NOWAIT | MAKEDEV_CHECKNAME, &pdev,
1193 tp->t_dev, "%s/%*s", VTCON_TTY_ALIAS_PREFIX, (int)len, name);
1194 if (error) {
1195 device_printf(sc->vtcon_dev,
1196 "%s: cannot make dev alias (%s/%*s) error %d\n", __func__,
1197 VTCON_TTY_ALIAS_PREFIX, (int)len, name, error);
1198 } else
1199 port->vtcport_flags |= VTCON_PORT_FLAG_ALIAS;
1200 }
1201
1202 static void
1203 vtcon_port_drain_bufs(struct virtqueue *vq)
1204 {
1205 void *buf;
1206 int last;
1207
1208 last = 0;
1209
1210 while ((buf = virtqueue_drain(vq, &last)) != NULL)
1211 free(buf, M_DEVBUF);
1212 }
1213
1214 static void
1215 vtcon_port_drain(struct vtcon_port *port)
1216 {
1217
1218 vtcon_port_drain_bufs(port->vtcport_invq);
1219 }
1220
1221 static void
1222 vtcon_port_teardown(struct vtcon_port *port)
1223 {
1224 struct tty *tp;
1225
1226 tp = port->vtcport_tty;
1227
1228 port->vtcport_flags |= VTCON_PORT_FLAG_GONE;
1229
1230 if (tp != NULL) {
1231 atomic_add_int(&vtcon_pending_free, 1);
1232 tty_rel_gone(tp);
1233 } else
1234 vtcon_port_destroy(port);
1235 }
1236
1237 static void
1238 vtcon_port_change_size(struct vtcon_port *port, uint16_t cols, uint16_t rows)
1239 {
1240 struct tty *tp;
1241 struct winsize sz;
1242
1243 tp = port->vtcport_tty;
1244
1245 if (tp == NULL)
1246 return;
1247
1248 bzero(&sz, sizeof(struct winsize));
1249 sz.ws_col = cols;
1250 sz.ws_row = rows;
1251
1252 tty_set_winsize(tp, &sz);
1253 }
1254
1255 static void
1256 vtcon_port_update_console_size(struct vtcon_softc *sc)
1257 {
1258 struct vtcon_port *port;
1259 struct vtcon_softc_port *scport;
1260 uint16_t cols, rows;
1261
1262 vtcon_get_console_size(sc, &cols, &rows);
1263
1264 /*
1265 * For now, assume the first (only) port is the console. Note
1266 * QEMU does not implement this feature yet.
1267 */
1268 scport = &sc->vtcon_ports[0];
1269
1270 VTCON_LOCK(sc);
1271 port = scport->vcsp_port;
1272
1273 if (port != NULL) {
1274 VTCON_PORT_LOCK(port);
1275 VTCON_UNLOCK(sc);
1276 vtcon_port_change_size(port, cols, rows);
1277 VTCON_PORT_UNLOCK(port);
1278 } else
1279 VTCON_UNLOCK(sc);
1280 }
1281
1282 static void
1283 vtcon_port_enable_intr(struct vtcon_port *port)
1284 {
1285
1286 /*
1287 * NOTE: The out virtqueue is always polled, so its interrupt
1288 * kept disabled.
1289 */
1290 virtqueue_enable_intr(port->vtcport_invq);
1291 }
1292
1293 static void
1294 vtcon_port_disable_intr(struct vtcon_port *port)
1295 {
1296
1297 if (port->vtcport_invq != NULL)
1298 virtqueue_disable_intr(port->vtcport_invq);
1299 if (port->vtcport_outvq != NULL)
1300 virtqueue_disable_intr(port->vtcport_outvq);
1301 }
1302
1303 static void
1304 vtcon_port_in(struct vtcon_port *port)
1305 {
1306 struct virtqueue *vq;
1307 struct tty *tp;
1308 char *buf;
1309 uint32_t len;
1310 int i, deq;
1311
1312 tp = port->vtcport_tty;
1313 vq = port->vtcport_invq;
1314
1315 again:
1316 deq = 0;
1317
1318 while ((buf = virtqueue_dequeue(vq, &len)) != NULL) {
1319 for (i = 0; i < len; i++) {
1320 #if defined(KDB)
1321 if (port->vtcport_flags & VTCON_PORT_FLAG_CONSOLE)
1322 kdb_alt_break(buf[i],
1323 &port->vtcport_alt_break_state);
1324 #endif
1325 ttydisc_rint(tp, buf[i], 0);
1326 }
1327 vtcon_port_requeue_buf(port, buf);
1328 deq++;
1329 }
1330 ttydisc_rint_done(tp);
1331
1332 if (deq > 0)
1333 virtqueue_notify(vq);
1334
1335 if (virtqueue_enable_intr(vq) != 0)
1336 goto again;
1337 }
1338
1339 static void
1340 vtcon_port_intr(void *scportx)
1341 {
1342 struct vtcon_softc_port *scport;
1343 struct vtcon_softc *sc;
1344 struct vtcon_port *port;
1345
1346 scport = scportx;
1347 sc = scport->vcsp_sc;
1348
1349 VTCON_LOCK(sc);
1350 port = scport->vcsp_port;
1351 if (port == NULL) {
1352 VTCON_UNLOCK(sc);
1353 return;
1354 }
1355 VTCON_PORT_LOCK(port);
1356 VTCON_UNLOCK(sc);
1357 if ((port->vtcport_flags & VTCON_PORT_FLAG_GONE) == 0)
1358 vtcon_port_in(port);
1359 VTCON_PORT_UNLOCK(port);
1360 }
1361
1362 static void
1363 vtcon_port_out(struct vtcon_port *port, void *buf, int bufsize)
1364 {
1365 struct sglist_seg segs[2];
1366 struct sglist sg;
1367 struct virtqueue *vq;
1368 int error;
1369
1370 vq = port->vtcport_outvq;
1371 KASSERT(virtqueue_empty(vq),
1372 ("%s: port %p out virtqueue not emtpy", __func__, port));
1373
1374 sglist_init(&sg, 2, segs);
1375 error = sglist_append(&sg, buf, bufsize);
1376 KASSERT(error == 0, ("%s: error %d adding buffer to sglist",
1377 __func__, error));
1378
1379 error = virtqueue_enqueue(vq, buf, &sg, sg.sg_nseg, 0);
1380 if (error == 0) {
1381 virtqueue_notify(vq);
1382 virtqueue_poll(vq, NULL);
1383 }
1384 }
1385
1386 static void
1387 vtcon_port_submit_event(struct vtcon_port *port, uint16_t event,
1388 uint16_t value)
1389 {
1390 struct vtcon_softc *sc;
1391
1392 sc = port->vtcport_sc;
1393
1394 vtcon_ctrl_send_control(sc, port->vtcport_id, event, value);
1395 }
1396
1397 static int
1398 vtcon_tty_open(struct tty *tp)
1399 {
1400 struct vtcon_port *port;
1401
1402 port = tty_softc(tp);
1403
1404 if (port->vtcport_flags & VTCON_PORT_FLAG_GONE)
1405 return (ENXIO);
1406
1407 vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
1408
1409 return (0);
1410 }
1411
1412 static void
1413 vtcon_tty_close(struct tty *tp)
1414 {
1415 struct vtcon_port *port;
1416
1417 port = tty_softc(tp);
1418
1419 if (port->vtcport_flags & VTCON_PORT_FLAG_GONE)
1420 return;
1421
1422 vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
1423 }
1424
1425 static void
1426 vtcon_tty_outwakeup(struct tty *tp)
1427 {
1428 struct vtcon_port *port;
1429 char buf[VTCON_BULK_BUFSZ];
1430 int len;
1431
1432 port = tty_softc(tp);
1433
1434 if (port->vtcport_flags & VTCON_PORT_FLAG_GONE)
1435 return;
1436
1437 while ((len = ttydisc_getc(tp, buf, sizeof(buf))) != 0)
1438 vtcon_port_out(port, buf, len);
1439 }
1440
1441 static void
1442 vtcon_tty_free(void *xport)
1443 {
1444 struct vtcon_port *port;
1445
1446 port = xport;
1447
1448 vtcon_port_destroy(port);
1449 atomic_subtract_int(&vtcon_pending_free, 1);
1450 }
1451
1452 static void
1453 vtcon_get_console_size(struct vtcon_softc *sc, uint16_t *cols, uint16_t *rows)
1454 {
1455 struct virtio_console_config concfg;
1456
1457 KASSERT(sc->vtcon_flags & VTCON_FLAG_SIZE,
1458 ("%s: size feature not negotiated", __func__));
1459
1460 vtcon_read_config(sc, &concfg);
1461
1462 *cols = concfg.cols;
1463 *rows = concfg.rows;
1464 }
1465
1466 static void
1467 vtcon_enable_interrupts(struct vtcon_softc *sc)
1468 {
1469 struct vtcon_softc_port *scport;
1470 struct vtcon_port *port;
1471 int i;
1472
1473 VTCON_LOCK(sc);
1474
1475 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
1476 virtqueue_enable_intr(sc->vtcon_ctrl_rxvq);
1477
1478 for (i = 0; i < sc->vtcon_max_ports; i++) {
1479 scport = &sc->vtcon_ports[i];
1480
1481 port = scport->vcsp_port;
1482 if (port == NULL)
1483 continue;
1484
1485 VTCON_PORT_LOCK(port);
1486 vtcon_port_enable_intr(port);
1487 VTCON_PORT_UNLOCK(port);
1488 }
1489
1490 VTCON_UNLOCK(sc);
1491 }
1492
1493 static void
1494 vtcon_disable_interrupts(struct vtcon_softc *sc)
1495 {
1496 struct vtcon_softc_port *scport;
1497 struct vtcon_port *port;
1498 int i;
1499
1500 VTCON_LOCK_ASSERT(sc);
1501
1502 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
1503 virtqueue_disable_intr(sc->vtcon_ctrl_rxvq);
1504
1505 for (i = 0; i < sc->vtcon_max_ports; i++) {
1506 scport = &sc->vtcon_ports[i];
1507
1508 port = scport->vcsp_port;
1509 if (port == NULL)
1510 continue;
1511
1512 VTCON_PORT_LOCK(port);
1513 vtcon_port_disable_intr(port);
1514 VTCON_PORT_UNLOCK(port);
1515 }
1516 }
Cache object: 7844536cabeabae7cf51535b8a332f7c
|