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