]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/uart_emul.c
Import device-tree files from Linux 5.11
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / uart_emul.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2012 NetApp, Inc.
5  * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/types.h>
36 #include <dev/ic/ns16550.h>
37 #ifndef WITHOUT_CAPSICUM
38 #include <sys/capsicum.h>
39 #include <capsicum_helpers.h>
40 #endif
41
42 #include <machine/vmm_snapshot.h>
43
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <assert.h>
47 #include <err.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <termios.h>
51 #include <unistd.h>
52 #include <stdbool.h>
53 #include <string.h>
54 #include <pthread.h>
55 #include <sysexits.h>
56
57 #include "mevent.h"
58 #include "uart_emul.h"
59 #include "debug.h"
60
61 #define COM1_BASE       0x3F8
62 #define COM1_IRQ        4
63 #define COM2_BASE       0x2F8
64 #define COM2_IRQ        3
65 #define COM3_BASE       0x3E8
66 #define COM3_IRQ        4
67 #define COM4_BASE       0x2E8
68 #define COM4_IRQ        3
69
70 #define DEFAULT_RCLK    1843200
71 #define DEFAULT_BAUD    9600
72
73 #define FCR_RX_MASK     0xC0
74
75 #define MCR_OUT1        0x04
76 #define MCR_OUT2        0x08
77
78 #define MSR_DELTA_MASK  0x0f
79
80 #ifndef REG_SCR
81 #define REG_SCR         com_scr
82 #endif
83
84 #define FIFOSZ  16
85
86 static bool uart_stdio;         /* stdio in use for i/o */
87 static struct termios tio_stdio_orig;
88
89 static struct {
90         int     baseaddr;
91         int     irq;
92         bool    inuse;
93 } uart_lres[] = {
94         { COM1_BASE, COM1_IRQ, false},
95         { COM2_BASE, COM2_IRQ, false},
96         { COM3_BASE, COM3_IRQ, false},
97         { COM4_BASE, COM4_IRQ, false},
98 };
99
100 #define UART_NLDEVS     (sizeof(uart_lres) / sizeof(uart_lres[0]))
101
102 struct fifo {
103         uint8_t buf[FIFOSZ];
104         int     rindex;         /* index to read from */
105         int     windex;         /* index to write to */
106         int     num;            /* number of characters in the fifo */
107         int     size;           /* size of the fifo */
108 };
109
110 struct ttyfd {
111         bool    opened;
112         int     rfd;            /* fd for reading */
113         int     wfd;            /* fd for writing, may be == rfd */
114 };
115
116 struct uart_softc {
117         pthread_mutex_t mtx;    /* protects all softc elements */
118         uint8_t data;           /* Data register (R/W) */
119         uint8_t ier;            /* Interrupt enable register (R/W) */
120         uint8_t lcr;            /* Line control register (R/W) */
121         uint8_t mcr;            /* Modem control register (R/W) */
122         uint8_t lsr;            /* Line status register (R/W) */
123         uint8_t msr;            /* Modem status register (R/W) */
124         uint8_t fcr;            /* FIFO control register (W) */
125         uint8_t scr;            /* Scratch register (R/W) */
126
127         uint8_t dll;            /* Baudrate divisor latch LSB */
128         uint8_t dlh;            /* Baudrate divisor latch MSB */
129
130         struct fifo rxfifo;
131         struct mevent *mev;
132
133         struct ttyfd tty;
134         bool    thre_int_pending;       /* THRE interrupt pending */
135
136         void    *arg;
137         uart_intr_func_t intr_assert;
138         uart_intr_func_t intr_deassert;
139 };
140
141 static void uart_drain(int fd, enum ev_type ev, void *arg);
142
143 static void
144 ttyclose(void)
145 {
146
147         tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig);
148 }
149
150 static void
151 ttyopen(struct ttyfd *tf)
152 {
153         struct termios orig, new;
154
155         tcgetattr(tf->rfd, &orig);
156         new = orig;
157         cfmakeraw(&new);
158         new.c_cflag |= CLOCAL;
159         tcsetattr(tf->rfd, TCSANOW, &new);
160         if (uart_stdio) {
161                 tio_stdio_orig = orig;
162                 atexit(ttyclose);
163         }
164         raw_stdio = 1;
165 }
166
167 static int
168 ttyread(struct ttyfd *tf)
169 {
170         unsigned char rb;
171
172         if (read(tf->rfd, &rb, 1) == 1)
173                 return (rb);
174         else
175                 return (-1);
176 }
177
178 static void
179 ttywrite(struct ttyfd *tf, unsigned char wb)
180 {
181
182         (void)write(tf->wfd, &wb, 1);
183 }
184
185 static void
186 rxfifo_reset(struct uart_softc *sc, int size)
187 {
188         char flushbuf[32];
189         struct fifo *fifo;
190         ssize_t nread;
191         int error;
192
193         fifo = &sc->rxfifo;
194         bzero(fifo, sizeof(struct fifo));
195         fifo->size = size;
196
197         if (sc->tty.opened) {
198                 /*
199                  * Flush any unread input from the tty buffer.
200                  */
201                 while (1) {
202                         nread = read(sc->tty.rfd, flushbuf, sizeof(flushbuf));
203                         if (nread != sizeof(flushbuf))
204                                 break;
205                 }
206
207                 /*
208                  * Enable mevent to trigger when new characters are available
209                  * on the tty fd.
210                  */
211                 error = mevent_enable(sc->mev);
212                 assert(error == 0);
213         }
214 }
215
216 static int
217 rxfifo_available(struct uart_softc *sc)
218 {
219         struct fifo *fifo;
220
221         fifo = &sc->rxfifo;
222         return (fifo->num < fifo->size);
223 }
224
225 static int
226 rxfifo_putchar(struct uart_softc *sc, uint8_t ch)
227 {
228         struct fifo *fifo;
229         int error;
230
231         fifo = &sc->rxfifo;
232
233         if (fifo->num < fifo->size) {
234                 fifo->buf[fifo->windex] = ch;
235                 fifo->windex = (fifo->windex + 1) % fifo->size;
236                 fifo->num++;
237                 if (!rxfifo_available(sc)) {
238                         if (sc->tty.opened) {
239                                 /*
240                                  * Disable mevent callback if the FIFO is full.
241                                  */
242                                 error = mevent_disable(sc->mev);
243                                 assert(error == 0);
244                         }
245                 }
246                 return (0);
247         } else
248                 return (-1);
249 }
250
251 static int
252 rxfifo_getchar(struct uart_softc *sc)
253 {
254         struct fifo *fifo;
255         int c, error, wasfull;
256
257         wasfull = 0;
258         fifo = &sc->rxfifo;
259         if (fifo->num > 0) {
260                 if (!rxfifo_available(sc))
261                         wasfull = 1;
262                 c = fifo->buf[fifo->rindex];
263                 fifo->rindex = (fifo->rindex + 1) % fifo->size;
264                 fifo->num--;
265                 if (wasfull) {
266                         if (sc->tty.opened) {
267                                 error = mevent_enable(sc->mev);
268                                 assert(error == 0);
269                         }
270                 }
271                 return (c);
272         } else
273                 return (-1);
274 }
275
276 static int
277 rxfifo_numchars(struct uart_softc *sc)
278 {
279         struct fifo *fifo = &sc->rxfifo;
280
281         return (fifo->num);
282 }
283
284 static void
285 uart_opentty(struct uart_softc *sc)
286 {
287
288         ttyopen(&sc->tty);
289         sc->mev = mevent_add(sc->tty.rfd, EVF_READ, uart_drain, sc);
290         assert(sc->mev != NULL);
291 }
292
293 static uint8_t
294 modem_status(uint8_t mcr)
295 {
296         uint8_t msr;
297
298         if (mcr & MCR_LOOPBACK) {
299                 /*
300                  * In the loopback mode certain bits from the MCR are
301                  * reflected back into MSR.
302                  */
303                 msr = 0;
304                 if (mcr & MCR_RTS)
305                         msr |= MSR_CTS;
306                 if (mcr & MCR_DTR)
307                         msr |= MSR_DSR;
308                 if (mcr & MCR_OUT1)
309                         msr |= MSR_RI;
310                 if (mcr & MCR_OUT2)
311                         msr |= MSR_DCD;
312         } else {
313                 /*
314                  * Always assert DCD and DSR so tty open doesn't block
315                  * even if CLOCAL is turned off.
316                  */
317                 msr = MSR_DCD | MSR_DSR;
318         }
319         assert((msr & MSR_DELTA_MASK) == 0);
320
321         return (msr);
322 }
323
324 /*
325  * The IIR returns a prioritized interrupt reason:
326  * - receive data available
327  * - transmit holding register empty
328  * - modem status change
329  *
330  * Return an interrupt reason if one is available.
331  */
332 static int
333 uart_intr_reason(struct uart_softc *sc)
334 {
335
336         if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0)
337                 return (IIR_RLS);
338         else if (rxfifo_numchars(sc) > 0 && (sc->ier & IER_ERXRDY) != 0)
339                 return (IIR_RXTOUT);
340         else if (sc->thre_int_pending && (sc->ier & IER_ETXRDY) != 0)
341                 return (IIR_TXRDY);
342         else if ((sc->msr & MSR_DELTA_MASK) != 0 && (sc->ier & IER_EMSC) != 0)
343                 return (IIR_MLSC);
344         else
345                 return (IIR_NOPEND);
346 }
347
348 static void
349 uart_reset(struct uart_softc *sc)
350 {
351         uint16_t divisor;
352
353         divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16;
354         sc->dll = divisor;
355         sc->dlh = divisor >> 16;
356         sc->msr = modem_status(sc->mcr);
357
358         rxfifo_reset(sc, 1);    /* no fifo until enabled by software */
359 }
360
361 /*
362  * Toggle the COM port's intr pin depending on whether or not we have an
363  * interrupt condition to report to the processor.
364  */
365 static void
366 uart_toggle_intr(struct uart_softc *sc)
367 {
368         uint8_t intr_reason;
369
370         intr_reason = uart_intr_reason(sc);
371
372         if (intr_reason == IIR_NOPEND)
373                 (*sc->intr_deassert)(sc->arg);
374         else
375                 (*sc->intr_assert)(sc->arg);
376 }
377
378 static void
379 uart_drain(int fd, enum ev_type ev, void *arg)
380 {
381         struct uart_softc *sc;
382         int ch;
383
384         sc = arg;       
385
386         assert(fd == sc->tty.rfd);
387         assert(ev == EVF_READ);
388         
389         /*
390          * This routine is called in the context of the mevent thread
391          * to take out the softc lock to protect against concurrent
392          * access from a vCPU i/o exit
393          */
394         pthread_mutex_lock(&sc->mtx);
395
396         if ((sc->mcr & MCR_LOOPBACK) != 0) {
397                 (void) ttyread(&sc->tty);
398         } else {
399                 while (rxfifo_available(sc) &&
400                        ((ch = ttyread(&sc->tty)) != -1)) {
401                         rxfifo_putchar(sc, ch);
402                 }
403                 uart_toggle_intr(sc);
404         }
405
406         pthread_mutex_unlock(&sc->mtx);
407 }
408
409 void
410 uart_write(struct uart_softc *sc, int offset, uint8_t value)
411 {
412         int fifosz;
413         uint8_t msr;
414
415         pthread_mutex_lock(&sc->mtx);
416
417         /*
418          * Take care of the special case DLAB accesses first
419          */
420         if ((sc->lcr & LCR_DLAB) != 0) {
421                 if (offset == REG_DLL) {
422                         sc->dll = value;
423                         goto done;
424                 }
425                 
426                 if (offset == REG_DLH) {
427                         sc->dlh = value;
428                         goto done;
429                 }
430         }
431
432         switch (offset) {
433         case REG_DATA:
434                 if (sc->mcr & MCR_LOOPBACK) {
435                         if (rxfifo_putchar(sc, value) != 0)
436                                 sc->lsr |= LSR_OE;
437                 } else if (sc->tty.opened) {
438                         ttywrite(&sc->tty, value);
439                 } /* else drop on floor */
440                 sc->thre_int_pending = true;
441                 break;
442         case REG_IER:
443                 /* Set pending when IER_ETXRDY is raised (edge-triggered). */
444                 if ((sc->ier & IER_ETXRDY) == 0 && (value & IER_ETXRDY) != 0)
445                         sc->thre_int_pending = true;
446                 /*
447                  * Apply mask so that bits 4-7 are 0
448                  * Also enables bits 0-3 only if they're 1
449                  */
450                 sc->ier = value & 0x0F;
451                 break;
452         case REG_FCR:
453                 /*
454                  * When moving from FIFO and 16450 mode and vice versa,
455                  * the FIFO contents are reset.
456                  */
457                 if ((sc->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) {
458                         fifosz = (value & FCR_ENABLE) ? FIFOSZ : 1;
459                         rxfifo_reset(sc, fifosz);
460                 }
461
462                 /*
463                  * The FCR_ENABLE bit must be '1' for the programming
464                  * of other FCR bits to be effective.
465                  */
466                 if ((value & FCR_ENABLE) == 0) {
467                         sc->fcr = 0;
468                 } else {
469                         if ((value & FCR_RCV_RST) != 0)
470                                 rxfifo_reset(sc, FIFOSZ);
471
472                         sc->fcr = value &
473                                  (FCR_ENABLE | FCR_DMA | FCR_RX_MASK);
474                 }
475                 break;
476         case REG_LCR:
477                 sc->lcr = value;
478                 break;
479         case REG_MCR:
480                 /* Apply mask so that bits 5-7 are 0 */
481                 sc->mcr = value & 0x1F;
482                 msr = modem_status(sc->mcr);
483
484                 /*
485                  * Detect if there has been any change between the
486                  * previous and the new value of MSR. If there is
487                  * then assert the appropriate MSR delta bit.
488                  */
489                 if ((msr & MSR_CTS) ^ (sc->msr & MSR_CTS))
490                         sc->msr |= MSR_DCTS;
491                 if ((msr & MSR_DSR) ^ (sc->msr & MSR_DSR))
492                         sc->msr |= MSR_DDSR;
493                 if ((msr & MSR_DCD) ^ (sc->msr & MSR_DCD))
494                         sc->msr |= MSR_DDCD;
495                 if ((sc->msr & MSR_RI) != 0 && (msr & MSR_RI) == 0)
496                         sc->msr |= MSR_TERI;
497
498                 /*
499                  * Update the value of MSR while retaining the delta
500                  * bits.
501                  */
502                 sc->msr &= MSR_DELTA_MASK;
503                 sc->msr |= msr;
504                 break;
505         case REG_LSR:
506                 /*
507                  * Line status register is not meant to be written to
508                  * during normal operation.
509                  */
510                 break;
511         case REG_MSR:
512                 /*
513                  * As far as I can tell MSR is a read-only register.
514                  */
515                 break;
516         case REG_SCR:
517                 sc->scr = value;
518                 break;
519         default:
520                 break;
521         }
522
523 done:
524         uart_toggle_intr(sc);
525         pthread_mutex_unlock(&sc->mtx);
526 }
527
528 uint8_t
529 uart_read(struct uart_softc *sc, int offset)
530 {
531         uint8_t iir, intr_reason, reg;
532
533         pthread_mutex_lock(&sc->mtx);
534
535         /*
536          * Take care of the special case DLAB accesses first
537          */
538         if ((sc->lcr & LCR_DLAB) != 0) {
539                 if (offset == REG_DLL) {
540                         reg = sc->dll;
541                         goto done;
542                 }
543                 
544                 if (offset == REG_DLH) {
545                         reg = sc->dlh;
546                         goto done;
547                 }
548         }
549
550         switch (offset) {
551         case REG_DATA:
552                 reg = rxfifo_getchar(sc);
553                 break;
554         case REG_IER:
555                 reg = sc->ier;
556                 break;
557         case REG_IIR:
558                 iir = (sc->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0;
559
560                 intr_reason = uart_intr_reason(sc);
561                         
562                 /*
563                  * Deal with side effects of reading the IIR register
564                  */
565                 if (intr_reason == IIR_TXRDY)
566                         sc->thre_int_pending = false;
567
568                 iir |= intr_reason;
569
570                 reg = iir;
571                 break;
572         case REG_LCR:
573                 reg = sc->lcr;
574                 break;
575         case REG_MCR:
576                 reg = sc->mcr;
577                 break;
578         case REG_LSR:
579                 /* Transmitter is always ready for more data */
580                 sc->lsr |= LSR_TEMT | LSR_THRE;
581
582                 /* Check for new receive data */
583                 if (rxfifo_numchars(sc) > 0)
584                         sc->lsr |= LSR_RXRDY;
585                 else
586                         sc->lsr &= ~LSR_RXRDY;
587
588                 reg = sc->lsr;
589
590                 /* The LSR_OE bit is cleared on LSR read */
591                 sc->lsr &= ~LSR_OE;
592                 break;
593         case REG_MSR:
594                 /*
595                  * MSR delta bits are cleared on read
596                  */
597                 reg = sc->msr;
598                 sc->msr &= ~MSR_DELTA_MASK;
599                 break;
600         case REG_SCR:
601                 reg = sc->scr;
602                 break;
603         default:
604                 reg = 0xFF;
605                 break;
606         }
607
608 done:
609         uart_toggle_intr(sc);
610         pthread_mutex_unlock(&sc->mtx);
611
612         return (reg);
613 }
614
615 int
616 uart_legacy_alloc(int which, int *baseaddr, int *irq)
617 {
618
619         if (which < 0 || which >= UART_NLDEVS || uart_lres[which].inuse)
620                 return (-1);
621
622         uart_lres[which].inuse = true;
623         *baseaddr = uart_lres[which].baseaddr;
624         *irq = uart_lres[which].irq;
625
626         return (0);
627 }
628
629 struct uart_softc *
630 uart_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert,
631     void *arg)
632 {
633         struct uart_softc *sc;
634
635         sc = calloc(1, sizeof(struct uart_softc));
636
637         sc->arg = arg;
638         sc->intr_assert = intr_assert;
639         sc->intr_deassert = intr_deassert;
640
641         pthread_mutex_init(&sc->mtx, NULL);
642
643         uart_reset(sc);
644
645         return (sc);
646 }
647
648 static int
649 uart_stdio_backend(struct uart_softc *sc)
650 {
651 #ifndef WITHOUT_CAPSICUM
652         cap_rights_t rights;
653         cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ };
654 #endif
655
656         if (uart_stdio)
657                 return (-1);
658
659         sc->tty.rfd = STDIN_FILENO;
660         sc->tty.wfd = STDOUT_FILENO;
661         sc->tty.opened = true;
662
663         if (fcntl(sc->tty.rfd, F_SETFL, O_NONBLOCK) != 0)
664                 return (-1);
665         if (fcntl(sc->tty.wfd, F_SETFL, O_NONBLOCK) != 0)
666                 return (-1);
667
668 #ifndef WITHOUT_CAPSICUM
669         cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ);
670         if (caph_rights_limit(sc->tty.rfd, &rights) == -1)
671                 errx(EX_OSERR, "Unable to apply rights for sandbox");
672         if (caph_ioctls_limit(sc->tty.rfd, cmds, nitems(cmds)) == -1)
673                 errx(EX_OSERR, "Unable to apply rights for sandbox");
674 #endif
675
676         uart_stdio = true;
677
678         return (0);
679 }
680
681 static int
682 uart_tty_backend(struct uart_softc *sc, const char *path)
683 {
684 #ifndef WITHOUT_CAPSICUM
685         cap_rights_t rights;
686         cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ };
687 #endif
688         int fd;
689
690         fd = open(path, O_RDWR | O_NONBLOCK);
691         if (fd < 0)
692                 return (-1);
693
694         if (!isatty(fd)) {
695                 close(fd);
696                 return (-1);
697         }
698
699         sc->tty.rfd = sc->tty.wfd = fd;
700         sc->tty.opened = true;
701
702 #ifndef WITHOUT_CAPSICUM
703         cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE);
704         if (caph_rights_limit(fd, &rights) == -1)
705                 errx(EX_OSERR, "Unable to apply rights for sandbox");
706         if (caph_ioctls_limit(fd, cmds, nitems(cmds)) == -1)
707                 errx(EX_OSERR, "Unable to apply rights for sandbox");
708 #endif
709
710         return (0);
711 }
712
713 int
714 uart_set_backend(struct uart_softc *sc, const char *device)
715 {
716         int retval;
717
718         if (device == NULL)
719                 return (0);
720
721         if (strcmp("stdio", device) == 0)
722                 retval = uart_stdio_backend(sc);
723         else
724                 retval = uart_tty_backend(sc, device);
725         if (retval == 0)
726                 uart_opentty(sc);
727
728         return (retval);
729 }
730
731 #ifdef BHYVE_SNAPSHOT
732 int
733 uart_snapshot(struct uart_softc *sc, struct vm_snapshot_meta *meta)
734 {
735         int ret;
736
737         SNAPSHOT_VAR_OR_LEAVE(sc->data, meta, ret, done);
738         SNAPSHOT_VAR_OR_LEAVE(sc->ier, meta, ret, done);
739         SNAPSHOT_VAR_OR_LEAVE(sc->lcr, meta, ret, done);
740         SNAPSHOT_VAR_OR_LEAVE(sc->mcr, meta, ret, done);
741         SNAPSHOT_VAR_OR_LEAVE(sc->lsr, meta, ret, done);
742         SNAPSHOT_VAR_OR_LEAVE(sc->msr, meta, ret, done);
743         SNAPSHOT_VAR_OR_LEAVE(sc->fcr, meta, ret, done);
744         SNAPSHOT_VAR_OR_LEAVE(sc->scr, meta, ret, done);
745
746         SNAPSHOT_VAR_OR_LEAVE(sc->dll, meta, ret, done);
747         SNAPSHOT_VAR_OR_LEAVE(sc->dlh, meta, ret, done);
748
749         SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.rindex, meta, ret, done);
750         SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.windex, meta, ret, done);
751         SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.num, meta, ret, done);
752         SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.size, meta, ret, done);
753         SNAPSHOT_BUF_OR_LEAVE(sc->rxfifo.buf, sizeof(sc->rxfifo.buf),
754                               meta, ret, done);
755
756         sc->thre_int_pending = 1;
757
758 done:
759         return (ret);
760 }
761 #endif