Re: git: 2ed9833791f2 - main - thunderbolt: Import USB4 code

From: Nuno Teixeira <eduardo_at_freebsd.org>
Date: Thu, 02 Oct 2025 21:17:40 UTC
yep, seing that symlink too.

Wolfram Schneider <wosch@freebsd.org> escreveu (terça, 30/09/2025 à(s)
07:44):

> I'm now getting a stale symlink:
>
> ./tools/build/stale-symlink-buildworld.sh
> stale symlink detected: lrwxrwxr-x  1 wosch wheel 91 Sep 29 22:14
>
> /var/tmp/freebsd-obj-wosch/home/projects/freebsd-src/amd64.amd64/sys/GENERIC/modules/home/projects/freebsd-src/sys/modules/thunderbolt/opt_acpi_wmi.h
> ->
> /var/tmp/freebsd-obj-wosch/home/projects/freebsd-src/amd64.amd64/sys/GENERIC/opt_acpi_wmi.h
>
> the only reference to the file "opt_acpi_wmi.h" is in the Makefile
>
> sys/modules/thunderbolt/Makefile:SRCS+= opt_acpi.h opt_acpi_wmi.h
> acpi_if.h acpi_wmi_if.h
>
> What is this file supposed to do, where does it come from?
>
> -Wolfram
>
> On Sat, 27 Sept 2025 at 19:13, Aymeric Wibo <obiwac@freebsd.org> wrote:
> >
> > The branch main has been updated by obiwac:
> >
> > URL:
> https://cgit.FreeBSD.org/src/commit/?id=2ed9833791f28e14843ac813f90cb030e45948dc
> >
> > commit 2ed9833791f28e14843ac813f90cb030e45948dc
> > Author:     Aymeric Wibo <obiwac@FreeBSD.org>
> > AuthorDate: 2025-09-27 11:50:43 +0000
> > Commit:     Aymeric Wibo <obiwac@FreeBSD.org>
> > CommitDate: 2025-09-27 17:13:13 +0000
> >
> >     thunderbolt: Import USB4 code
> >
> >     Add initial USB4 code written by Scott Long and originally passed on
> to
> >     HPS (source: https://github.com/hselasky/usb4), minus the ICM code
> and
> >     with some small fixes.
> >
> >     For context, older TB chips implemented the connection manager in
> >     firmware (ICM) instead of in the OS (HCM), but maintaining the ICM
> code
> >     would be a huge burden for not many chips.
> >
> >     Mostly completed work:
> >
> >     - Debug/trace framework.
> >     - NHI controller driver.
> >     - PCIe bridge driver.
> >     - Router and config space layer handling (just reading in this
> commit).
> >
> >     Link to the email where Scott shared details about the initial USB4
> >     work:
> >
> >
> https://lists.freebsd.org/archives/freebsd-hackers/2024-July/003411.html
> >
> >     Glanced at by:  emaste, imp
> >     Sponsored by:   The FreeBSD Foundation
> >     Differential Revision:  https://reviews.freebsd.org/D49450
> >     Event:          EuroBSDcon 2025
> > ---
> >  sys/dev/thunderbolt/hcm.c          |  223 +++++++
> >  sys/dev/thunderbolt/hcm_var.h      |   47 ++
> >  sys/dev/thunderbolt/nhi.c          | 1170
> ++++++++++++++++++++++++++++++++++++
> >  sys/dev/thunderbolt/nhi_pci.c      |  529 ++++++++++++++++
> >  sys/dev/thunderbolt/nhi_reg.h      |  332 ++++++++++
> >  sys/dev/thunderbolt/nhi_var.h      |  277 +++++++++
> >  sys/dev/thunderbolt/nhi_wmi.c      |  198 ++++++
> >  sys/dev/thunderbolt/router.c       |  939 +++++++++++++++++++++++++++++
> >  sys/dev/thunderbolt/router_var.h   |  242 ++++++++
> >  sys/dev/thunderbolt/tb_acpi_pcib.c |  181 ++++++
> >  sys/dev/thunderbolt/tb_debug.c     |  334 ++++++++++
> >  sys/dev/thunderbolt/tb_debug.h     |   93 +++
> >  sys/dev/thunderbolt/tb_dev.c       |  331 ++++++++++
> >  sys/dev/thunderbolt/tb_dev.h       |   41 ++
> >  sys/dev/thunderbolt/tb_if.m        |  121 ++++
> >  sys/dev/thunderbolt/tb_ioctl.h     |   52 ++
> >  sys/dev/thunderbolt/tb_pcib.c      |  614 +++++++++++++++++++
> >  sys/dev/thunderbolt/tb_pcib.h      |   93 +++
> >  sys/dev/thunderbolt/tb_reg.h       |   52 ++
> >  sys/dev/thunderbolt/tb_var.h       |   54 ++
> >  sys/dev/thunderbolt/tbcfg_reg.h    |  363 +++++++++++
> >  sys/modules/Makefile               |    5 +
> >  sys/modules/thunderbolt/Makefile   |   13 +
> >  23 files changed, 6304 insertions(+)
> >
> > diff --git a/sys/dev/thunderbolt/hcm.c b/sys/dev/thunderbolt/hcm.c
> > new file mode 100644
> > index 000000000000..b8f703fc3b52
> > --- /dev/null
> > +++ b/sys/dev/thunderbolt/hcm.c
> > @@ -0,0 +1,223 @@
> > +/*-
> > + * SPDX-License-Identifier: BSD-2-Clause
> > + *
> > + * Copyright (c) 2022 Scott Long
> > + * All rights reserved.
> > + *
> > + * Redistribution and use in source and binary forms, with or without
> > + * modification, are permitted provided that the following conditions
> > + * are met:
> > + * 1. Redistributions of source code must retain the above copyright
> > + *    notice, this list of conditions and the following disclaimer.
> > + * 2. Redistributions in binary form must reproduce the above copyright
> > + *    notice, this list of conditions and the following disclaimer in
> the
> > + *    documentation and/or other materials provided with the
> distribution.
> > + *
> > + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
> AND
> > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
> PURPOSE
> > + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
> LIABLE
> > + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> CONSEQUENTIAL
> > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
> GOODS
> > + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> > + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> STRICT
> > + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
> ANY WAY
> > + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
> OF
> > + * SUCH DAMAGE.
> > + */
> > +
> > +#include "opt_thunderbolt.h"
> > +
> > +/* Host Configuration Manager (HCM) for USB4 and later TB3 */
> > +#include <sys/types.h>
> > +#include <sys/param.h>
> > +#include <sys/systm.h>
> > +#include <sys/kernel.h>
> > +#include <sys/module.h>
> > +#include <sys/bus.h>
> > +#include <sys/conf.h>
> > +#include <sys/malloc.h>
> > +#include <sys/queue.h>
> > +#include <sys/sysctl.h>
> > +#include <sys/lock.h>
> > +#include <sys/mutex.h>
> > +#include <sys/taskqueue.h>
> > +#include <sys/gsb_crc32.h>
> > +#include <sys/endian.h>
> > +#include <vm/vm.h>
> > +#include <vm/pmap.h>
> > +
> > +#include <machine/bus.h>
> > +#include <machine/stdarg.h>
> > +
> > +#include <dev/thunderbolt/nhi_reg.h>
> > +#include <dev/thunderbolt/nhi_var.h>
> > +#include <dev/thunderbolt/tb_reg.h>
> > +#include <dev/thunderbolt/tb_var.h>
> > +#include <dev/thunderbolt/tb_debug.h>
> > +#include <dev/thunderbolt/tbcfg_reg.h>
> > +#include <dev/thunderbolt/router_var.h>
> > +#include <dev/thunderbolt/hcm_var.h>
> > +
> > +static void hcm_cfg_task(void *, int);
> > +
> > +int
> > +hcm_attach(struct nhi_softc *nsc)
> > +{
> > +       struct hcm_softc *hcm;
> > +
> > +       tb_debug(nsc, DBG_HCM|DBG_EXTRA, "hcm_attach called\n");
> > +
> > +       hcm = malloc(sizeof(struct hcm_softc), M_THUNDERBOLT,
> M_NOWAIT|M_ZERO);
> > +       if (hcm == NULL) {
> > +               tb_debug(nsc, DBG_HCM, "Cannot allocate hcm object\n");
> > +               return (ENOMEM);
> > +       }
> > +
> > +       hcm->dev = nsc->dev;
> > +       hcm->nsc = nsc;
> > +       nsc->hcm = hcm;
> > +
> > +       hcm->taskqueue = taskqueue_create("hcm_event", M_NOWAIT,
> > +           taskqueue_thread_enqueue, &hcm->taskqueue);
> > +       if (hcm->taskqueue == NULL)
> > +               return (ENOMEM);
> > +       taskqueue_start_threads(&hcm->taskqueue, 1, PI_DISK,
> "tbhcm%d_tq",
> > +           device_get_unit(nsc->dev));
> > +       TASK_INIT(&hcm->cfg_task, 0, hcm_cfg_task, hcm);
> > +
> > +       return (0);
> > +}
> > +
> > +int
> > +hcm_detach(struct nhi_softc *nsc)
> > +{
> > +       struct hcm_softc *hcm;
> > +
> > +       hcm = nsc->hcm;
> > +       if (hcm->taskqueue)
> > +               taskqueue_free(hcm->taskqueue);
> > +
> > +       return (0);
> > +}
> > +
> > +int
> > +hcm_router_discover(struct hcm_softc *hcm)
> > +{
> > +
> > +       taskqueue_enqueue(hcm->taskqueue, &hcm->cfg_task);
> > +
> > +       return (0);
> > +}
> > +
> > +static void
> > +hcm_cfg_task(void *arg, int pending)
> > +{
> > +       struct hcm_softc *hcm;
> > +       struct router_softc *rsc;
> > +       struct router_cfg_cap cap;
> > +       struct tb_cfg_router *cfg;
> > +       struct tb_cfg_adapter *adp;
> > +       struct tb_cfg_cap_lane *lane;
> > +       uint32_t *buf;
> > +       uint8_t *u;
> > +       u_int error, i, offset;
> > +
> > +       hcm = (struct hcm_softc *)arg;
> > +
> > +       tb_debug(hcm, DBG_HCM|DBG_EXTRA, "hcm_cfg_task called\n");
> > +
> > +       buf = malloc(8 * 4, M_THUNDERBOLT, M_NOWAIT|M_ZERO);
> > +       if (buf == NULL) {
> > +               tb_debug(hcm, DBG_HCM, "Cannot alloc memory for
> discovery\n");
> > +               return;
> > +       }
> > +
> > +       rsc = hcm->nsc->root_rsc;
> > +       error = tb_config_router_read(rsc, 0, 5, buf);
> > +       if (error != 0) {
> > +               free(buf, M_NHI);
> > +               return;
> > +       }
> > +
> > +       cfg = (struct tb_cfg_router *)buf;
> > +
> > +       cap.space = TB_CFG_CS_ROUTER;
> > +       cap.adap = 0;
> > +       cap.next_cap = GET_ROUTER_CS_NEXT_CAP(cfg);
> > +       while (cap.next_cap != 0) {
> > +               error = tb_config_next_cap(rsc, &cap);
> > +               if (error != 0)
> > +                       break;
> > +
> > +               if ((cap.cap_id == TB_CFG_CAP_VSEC) && (cap.vsc_len ==
> 0)) {
> > +                       tb_debug(hcm, DBG_HCM, "Router Cap= %d, vsec=
> %d, "
> > +                           "len= %d, next_cap= %d\n", cap.cap_id,
> > +                           cap.vsc_id, cap.vsec_len, cap.next_cap);
> > +               } else if (cap.cap_id == TB_CFG_CAP_VSC) {
> > +                       tb_debug(hcm, DBG_HCM, "Router cap= %d, vsc= %d,
> "
> > +                           "len= %d, next_cap= %d\n", cap.cap_id,
> > +                           cap.vsc_id, cap.vsc_len, cap.next_cap);
> > +               } else
> > +                       tb_debug(hcm, DBG_HCM, "Router cap= %d, "
> > +                           "next_cap= %d\n", cap.cap_id, cap.next_cap);
> > +               if (cap.next_cap > TB_CFG_CAP_OFFSET_MAX)
> > +                       cap.next_cap = 0;
> > +       }
> > +
> > +       u = (uint8_t *)buf;
> > +       error = tb_config_get_lc_uuid(rsc, u);
> > +       if (error == 0) {
> > +               tb_debug(hcm, DBG_HCM, "Router LC UUID:
> %02x%02x%02x%02x-"
> > +
>  "%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
> > +                   u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8],
> > +                   u[9], u[10], u[11], u[12], u[13], u[14], u[15]);
> > +       } else
> > +               tb_printf(hcm, "Error finding LC registers: %d\n",
> error);
> > +
> > +       for (i = 1; i <= rsc->max_adap; i++) {
> > +               error = tb_config_adapter_read(rsc, i, 0, 8, buf);
> > +               if (error != 0) {
> > +                       tb_debug(hcm, DBG_HCM, "Adapter %d: no
> adapter\n", i);
> > +                       continue;
> > +               }
> > +               adp = (struct tb_cfg_adapter *)buf;
> > +               tb_debug(hcm, DBG_HCM, "Adapter %d: %s, max_counters=
> 0x%08x,"
> > +                   " adapter_num= %d\n", i,
> > +                   tb_get_string(GET_ADP_CS_TYPE(adp), tb_adapter_type),
> > +                   GET_ADP_CS_MAX_COUNTERS(adp),
> GET_ADP_CS_ADP_NUM(adp));
> > +
> > +               if (GET_ADP_CS_TYPE(adp) != ADP_CS2_LANE)
> > +                       continue;
> > +
> > +               error = tb_config_find_adapter_cap(rsc, i,
> TB_CFG_CAP_LANE,
> > +                   &offset);
> > +               if (error)
> > +                       continue;
> > +
> > +               error = tb_config_adapter_read(rsc, i, offset, 3, buf);
> > +               if (error)
> > +                       continue;
> > +
> > +               lane = (struct tb_cfg_cap_lane *)buf;
> > +               tb_debug(hcm, DBG_HCM, "Lane Adapter State= %s %s\n",
> > +                   tb_get_string((lane->current_lws &
> CAP_LANE_STATE_MASK),
> > +                   tb_adapter_state), (lane->targ_lwp &
> CAP_LANE_DISABLE) ?
> > +                   "disabled" : "enabled");
> > +
> > +               if ((lane->current_lws & CAP_LANE_STATE_MASK) ==
> > +                   CAP_LANE_STATE_CL0) {
> > +                       tb_route_t newr;
> > +
> > +                       newr.hi = rsc->route.hi;
> > +                       newr.lo = rsc->route.lo | (i << rsc->depth * 8);
> > +
> > +                       tb_printf(hcm, "want to add router at
> 0x%08x%08x\n",
> > +                           newr.hi, newr.lo);
> > +                       error = tb_router_attach(rsc, newr);
> > +                       tb_printf(rsc, "tb_router_attach returned %d\n",
> error);
> > +               }
> > +       }
> > +
> > +       free(buf, M_THUNDERBOLT);
> > +}
> > diff --git a/sys/dev/thunderbolt/hcm_var.h
> b/sys/dev/thunderbolt/hcm_var.h
> > new file mode 100644
> > index 000000000000..a11c8e9b6a92
> > --- /dev/null
> > +++ b/sys/dev/thunderbolt/hcm_var.h
> > @@ -0,0 +1,47 @@
> > +/*-
> > + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
> > + *
> > + * Copyright (c) 2022 Scott Long
> > + * All rights reserved.
> > + *
> > + * Redistribution and use in source and binary forms, with or without
> > + * modification, are permitted provided that the following conditions
> > + * are met:
> > + * 1. Redistributions of source code must retain the above copyright
> > + *    notice, this list of conditions and the following disclaimer.
> > + * 2. Redistributions in binary form must reproduce the above copyright
> > + *    notice, this list of conditions and the following disclaimer in
> the
> > + *    documentation and/or other materials provided with the
> distribution.
> > + *
> > + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
> AND
> > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
> PURPOSE
> > + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
> LIABLE
> > + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> CONSEQUENTIAL
> > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
> GOODS
> > + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> > + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> STRICT
> > + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
> ANY WAY
> > + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
> OF
> > + * SUCH DAMAGE.
> > + *
> > + * $FreeBSD$
> > + */
> > +
> > +#ifndef _HCM_VAR_H
> > +#define _HCM_VAR_H
> > +
> > +struct hcm_softc {
> > +       u_int                   debug;
> > +       device_t                dev;
> > +       struct nhi_softc        *nsc;
> > +
> > +       struct task             cfg_task;
> > +       struct taskqueue        *taskqueue;
> > +};
> > +
> > +int hcm_attach(struct nhi_softc *);
> > +int hcm_detach(struct nhi_softc *);
> > +int hcm_router_discover(struct hcm_softc *);
> > +
> > +#endif /* _HCM_VAR_H */
> > diff --git a/sys/dev/thunderbolt/nhi.c b/sys/dev/thunderbolt/nhi.c
> > new file mode 100644
> > index 000000000000..205e69c16253
> > --- /dev/null
> > +++ b/sys/dev/thunderbolt/nhi.c
> > @@ -0,0 +1,1170 @@
> > +/*-
> > + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
> > + *
> > + * Copyright (c) 2022 Scott Long
> > + * All rights reserved.
> > + *
> > + * Redistribution and use in source and binary forms, with or without
> > + * modification, are permitted provided that the following conditions
> > + * are met:
> > + * 1. Redistributions of source code must retain the above copyright
> > + *    notice, this list of conditions and the following disclaimer.
> > + * 2. Redistributions in binary form must reproduce the above copyright
> > + *    notice, this list of conditions and the following disclaimer in
> the
> > + *    documentation and/or other materials provided with the
> distribution.
> > + *
> > + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
> AND
> > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
> PURPOSE
> > + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
> LIABLE
> > + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> CONSEQUENTIAL
> > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
> GOODS
> > + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> > + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> STRICT
> > + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
> ANY WAY
> > + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
> OF
> > + * SUCH DAMAGE.
> > + */
> > +
> > +#include "opt_thunderbolt.h"
> > +
> > +/* PCIe interface for Thunderbolt Native Host Interface (nhi) */
> > +#include <sys/types.h>
> > +#include <sys/param.h>
> > +#include <sys/systm.h>
> > +#include <sys/kernel.h>
> > +#include <sys/module.h>
> > +#include <sys/bus.h>
> > +#include <sys/conf.h>
> > +#include <sys/malloc.h>
> > +#include <sys/queue.h>
> > +#include <sys/sysctl.h>
> > +#include <sys/lock.h>
> > +#include <sys/mutex.h>
> > +#include <sys/taskqueue.h>
> > +#include <sys/gsb_crc32.h>
> > +#include <sys/endian.h>
> > +#include <vm/vm.h>
> > +#include <vm/pmap.h>
> > +
> > +#include <machine/bus.h>
> > +#include <machine/stdarg.h>
> > +
> > +#include <dev/thunderbolt/nhi_reg.h>
> > +#include <dev/thunderbolt/nhi_var.h>
> > +#include <dev/thunderbolt/tb_reg.h>
> > +#include <dev/thunderbolt/tb_var.h>
> > +#include <dev/thunderbolt/tb_debug.h>
> > +#include <dev/thunderbolt/hcm_var.h>
> > +#include <dev/thunderbolt/tbcfg_reg.h>
> > +#include <dev/thunderbolt/router_var.h>
> > +#include <dev/thunderbolt/tb_dev.h>
> > +#include "tb_if.h"
> > +
> > +static int nhi_alloc_ring(struct nhi_softc *, int, int, int,
> > +    struct nhi_ring_pair **);
> > +static void nhi_free_ring(struct nhi_ring_pair *);
> > +static void nhi_free_rings(struct nhi_softc *);
> > +static int nhi_configure_ring(struct nhi_softc *, struct nhi_ring_pair
> *);
> > +static int nhi_activate_ring(struct nhi_ring_pair *);
> > +static int nhi_deactivate_ring(struct nhi_ring_pair *);
> > +static int nhi_alloc_ring0(struct nhi_softc *);
> > +static void nhi_free_ring0(struct nhi_softc *);
> > +static void nhi_fill_rx_ring(struct nhi_softc *, struct nhi_ring_pair
> *);
> > +static int nhi_init(struct nhi_softc *);
> > +static void nhi_post_init(void *);
> > +static int nhi_tx_enqueue(struct nhi_ring_pair *, struct nhi_cmd_frame
> *);
> > +static int nhi_setup_sysctl(struct nhi_softc *);
> > +
> > +SYSCTL_NODE(_hw, OID_AUTO, nhi, CTLFLAG_RD, 0, "NHI Driver Parameters");
> > +
> > +MALLOC_DEFINE(M_NHI, "nhi", "nhi driver memory");
> > +
> > +#ifndef NHI_DEBUG_LEVEL
> > +#define NHI_DEBUG_LEVEL 0
> > +#endif
> > +
> > +/* 0 = default, 1 = force-on, 2 = force-off */
> > +#ifndef NHI_FORCE_HCM
> > +#define NHI_FORCE_HCM 0
> > +#endif
> > +
> > +void
> > +nhi_get_tunables(struct nhi_softc *sc)
> > +{
> > +       devclass_t dc;
> > +       device_t ufp;
> > +       char    tmpstr[80], oid[80];
> > +       u_int   val;
> > +
> > +       /* Set local defaults */
> > +       sc->debug = NHI_DEBUG_LEVEL;
> > +       sc->max_ring_count = NHI_DEFAULT_NUM_RINGS;
> > +       sc->force_hcm = NHI_FORCE_HCM;
> > +
> > +       /* Inherit setting from the upstream thunderbolt switch node */
> > +       val = TB_GET_DEBUG(sc->dev, &sc->debug);
> > +       if (val != 0) {
> > +               dc = devclass_find("tbolt");
> > +               if (dc != NULL) {
> > +                       ufp = devclass_get_device(dc,
> device_get_unit(sc->dev));
> > +                       if (ufp != NULL)
> > +                               TB_GET_DEBUG(ufp, &sc->debug);
> > +               } else {
> > +                       if (TUNABLE_STR_FETCH("hw.tbolt.debug_level",
> oid,
> > +                           80) != 0)
> > +                               tb_parse_debug(&sc->debug, oid);
> > +               }
> > +       }
> > +
> > +       /*
> > +        * Grab global variables.  Allow nhi debug flags to override
> > +        * thunderbolt debug flags, if present.
> > +        */
> > +       bzero(oid, 80);
> > +       if (TUNABLE_STR_FETCH("hw.nhi.debug_level", oid, 80) != 0)
> > +               tb_parse_debug(&sc->debug, oid);
> > +       if (TUNABLE_INT_FETCH("hw.nhi.max_rings", &val) != 0) {
> > +               val = min(val, NHI_MAX_NUM_RINGS);
> > +               sc->max_ring_count = max(val, 1);
> > +       }
> > +       if (TUNABLE_INT_FETCH("hw.nhi.force_hcm", &val) != 0)
> > +               sc->force_hcm = val;
> > +
> > +       /* Grab instance variables */
> > +       bzero(oid, 80);
> > +       snprintf(tmpstr, sizeof(tmpstr), "dev.nhi.%d.debug_level",
> > +           device_get_unit(sc->dev));
> > +       if (TUNABLE_STR_FETCH(tmpstr, oid, 80) != 0)
> > +               tb_parse_debug(&sc->debug, oid);
> > +       snprintf(tmpstr, sizeof(tmpstr), "dev.nhi.%d.max_rings",
> > +           device_get_unit(sc->dev));
> > +       if (TUNABLE_INT_FETCH(tmpstr, &val) != 0) {
> > +               val = min(val, NHI_MAX_NUM_RINGS);
> > +               sc->max_ring_count = max(val, 1);
> > +       }
> > +       snprintf(tmpstr, sizeof(tmpstr), "dev, nhi.%d.force_hcm",
> > +           device_get_unit(sc->dev));
> > +       if (TUNABLE_INT_FETCH(tmpstr, &val) != 0)
> > +               sc->force_hcm = val;
> > +
> > +       return;
> > +}
> > +
> > +static void
> > +nhi_configure_caps(struct nhi_softc *sc)
> > +{
> > +
> > +       if (NHI_IS_USB4(sc) || (sc->force_hcm == NHI_FORCE_HCM_ON))
> > +               sc->caps |= NHI_CAP_HCM;
> > +       if (sc->force_hcm == NHI_FORCE_HCM_OFF)
> > +               sc->caps &= ~NHI_CAP_HCM;
> > +}
> > +
> > +struct nhi_cmd_frame *
> > +nhi_alloc_tx_frame(struct nhi_ring_pair *r)
> > +{
> > +       struct nhi_cmd_frame *cmd;
> > +
> > +       mtx_lock(&r->mtx);
> > +       cmd = nhi_alloc_tx_frame_locked(r);
> > +       mtx_unlock(&r->mtx);
> > +
> > +       return (cmd);
> > +}
> > +
> > +void
> > +nhi_free_tx_frame(struct nhi_ring_pair *r, struct nhi_cmd_frame *cmd)
> > +{
> > +       mtx_lock(&r->mtx);
> > +       nhi_free_tx_frame_locked(r, cmd);
> > +       mtx_unlock(&r->mtx);
> > +}
> > +
> > +/*
> > + * Push a command and data dword through the mailbox to the firmware.
> > + * Response is either good, error, or timeout.  Commands that return
> data
> > + * do so by reading OUTMAILDATA.
> > + */
> > +int
> > +nhi_inmail_cmd(struct nhi_softc *sc, uint32_t cmd, uint32_t data)
> > +{
> > +       uint32_t val;
> > +       u_int error, timeout;
> > +
> > +       mtx_lock(&sc->nhi_mtx);
> > +       /*
> > +        * XXX Should a defer/reschedule happen here, or is it not worth
> > +        * worrying about?
> > +        */
> > +       if (sc->hwflags & NHI_MBOX_BUSY) {
> > +               mtx_unlock(&sc->nhi_mtx);
> > +               tb_debug(sc, DBG_MBOX, "Driver busy with mailbox\n");
> > +               return (EBUSY);
> > +       }
> > +       sc->hwflags |= NHI_MBOX_BUSY;
> > +
> > +       val = nhi_read_reg(sc, TBT_INMAILCMD);
> > +       tb_debug(sc, DBG_MBOX|DBG_FULL, "Reading INMAILCMD= 0x%08x\n",
> val);
> > +       if (val & INMAILCMD_ERROR)
> > +               tb_debug(sc, DBG_MBOX, "Error already set in
> INMAILCMD\n");
> > +       if (val & INMAILCMD_OPREQ) {
> > +               mtx_unlock(&sc->nhi_mtx);
> > +               tb_debug(sc, DBG_MBOX,
> > +                   "INMAILCMD request already in progress\n");
> > +               return (EBUSY);
> > +       }
> > +
> > +       nhi_write_reg(sc, TBT_INMAILDATA, data);
> > +       nhi_write_reg(sc, TBT_INMAILCMD, cmd | INMAILCMD_OPREQ);
> > +
> > +       /* Poll at 1s intervals */
> > +       timeout = NHI_MAILBOX_TIMEOUT;
> > +       while (timeout--) {
> > +               DELAY(1000000);
> > +               val = nhi_read_reg(sc, TBT_INMAILCMD);
> > +               tb_debug(sc, DBG_MBOX|DBG_EXTRA,
> > +                   "Polling INMAILCMD= 0x%08x\n", val);
> > +               if ((val & INMAILCMD_OPREQ) == 0)
> > +                       break;
> > +       }
> > +       sc->hwflags &= ~NHI_MBOX_BUSY;
> > +       mtx_unlock(&sc->nhi_mtx);
> > +
> > +       error = 0;
> > +       if (val & INMAILCMD_OPREQ) {
> > +               tb_printf(sc, "Timeout waiting for mailbox\n");
> > +               error = ETIMEDOUT;
> > +       }
> > +       if (val & INMAILCMD_ERROR) {
> > +               tb_printf(sc, "Firmware reports error in mailbox\n");
> > +               error = EINVAL;
> > +       }
> > +
> > +       return (error);
> > +}
> > +
> > +/*
> > + * Pull command status and data from the firmware mailbox.
> > + */
> > +int
> > +nhi_outmail_cmd(struct nhi_softc *sc, uint32_t *val)
> > +{
> > +
> > +       if (val == NULL)
> > +               return (EINVAL);
> > +       *val = nhi_read_reg(sc, TBT_OUTMAILCMD);
> > +       return (0);
> > +}
> > +
> > +int
> > +nhi_attach(struct nhi_softc *sc)
> > +{
> > +       uint32_t val;
> > +       int error = 0;
> > +
> > +       if ((error = nhi_setup_sysctl(sc)) != 0)
> > +               return (error);
> > +
> > +       mtx_init(&sc->nhi_mtx, "nhimtx", "NHI Control Mutex", MTX_DEF);
> > +
> > +       nhi_configure_caps(sc);
> > +
> > +       /*
> > +        * Get the number of TX/RX paths.  This sizes some of the
> register
> > +        * arrays during allocation and initialization.  USB4 spec says
> that
> > +        * the max is 21.  Alpine Ridge appears to default to 12.
> > +        */
> > +       val = GET_HOST_CAPS_PATHS(nhi_read_reg(sc, NHI_HOST_CAPS));
> > +       tb_debug(sc, DBG_INIT|DBG_NOISY, "Total Paths= %d\n", val);
> > +       if ((val == 0) || (val > 21) || ((NHI_IS_AR(sc) && val != 12))) {
> > +               tb_printf(sc, "WARN: unexpected number of paths: %d\n",
> val);
> > +               /* return (ENXIO); */
> > +       }
> > +       sc->path_count = val;
> > +
> > +       SLIST_INIT(&sc->ring_list);
> > +
> > +       error = nhi_pci_configure_interrupts(sc);
> > +       if (error == 0)
> > +               error = nhi_alloc_ring0(sc);
> > +       if (error == 0) {
> > +               nhi_configure_ring(sc, sc->ring0);
> > +               nhi_activate_ring(sc->ring0);
> > +               nhi_fill_rx_ring(sc, sc->ring0);
> > +       }
> > +
> > +       if (error == 0)
> > +               error = tbdev_add_interface(sc);
> > +
> > +       if ((error == 0) && (NHI_USE_ICM(sc)))
> > +               tb_printf(sc, "WARN: device uses an internal connection
> manager\n");
> > +       if ((error == 0) && (NHI_USE_HCM(sc)))
> > +               ;
> > +       error = hcm_attach(sc);
> > +
> > +       if (error == 0)
> > +               error = nhi_init(sc);
> > +
> > +       return (error);
> > +}
> > +
> > +int
> > +nhi_detach(struct nhi_softc *sc)
> > +{
> > +
> > +       if (NHI_USE_HCM(sc))
> > +               hcm_detach(sc);
> > +
> > +       if (sc->root_rsc != NULL)
> > +               tb_router_detach(sc->root_rsc);
> > +
> > +       tbdev_remove_interface(sc);
> > +
> > +       nhi_pci_disable_interrupts(sc);
> > +
> > +       nhi_free_ring0(sc);
> > +
> > +       /* XXX Should the rings be marked as !VALID in the descriptors?
> */
> > +       nhi_free_rings(sc);
> > +
> > +       mtx_destroy(&sc->nhi_mtx);
> > +
> > +       return (0);
> > +}
> > +
> > +static void
> > +nhi_memaddr_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
> > +{
> > +       bus_addr_t *addr;
> > +
> > +       addr = arg;
> > +       if (error == 0 && nsegs == 1) {
> > +               *addr = segs[0].ds_addr;
> > +       } else
> > +               *addr = 0;
> > +}
> > +
> > +static int
> > +nhi_alloc_ring(struct nhi_softc *sc, int ringnum, int tx_depth, int
> rx_depth,
> > +    struct nhi_ring_pair **rp)
> > +{
> > +       bus_dma_template_t t;
> > +       bus_addr_t ring_busaddr;
> > +       struct nhi_ring_pair *r;
> > +       int ring_size, error;
> > +       u_int rxring_len, txring_len;
> > +       char *ring;
> > +
> > +       if (ringnum >= sc->max_ring_count) {
> > +               tb_debug(sc, DBG_INIT, "Tried to allocate ring number
> %d\n",
> > +                   ringnum);
> > +               return (EINVAL);
> > +       }
> > +
> > +       /* Allocate the ring structure and the RX ring tacker together.
> */
> > +       rxring_len = rx_depth * sizeof(void *);
> > +       txring_len = tx_depth * sizeof(void *);
> > +       r = malloc(sizeof(struct nhi_ring_pair) + rxring_len +
> txring_len,
> > +           M_NHI, M_NOWAIT|M_ZERO);
> > +       if (r == NULL) {
> > +               tb_printf(sc, "ERROR: Cannot allocate ring memory\n");
> > +               return (ENOMEM);
> > +       }
> > +
> > +       r->sc = sc;
> > +       TAILQ_INIT(&r->tx_head);
> > +       TAILQ_INIT(&r->rx_head);
> > +       r->ring_num = ringnum;
> > +       r->tx_ring_depth = tx_depth;
> > +       r->tx_ring_mask = tx_depth - 1;
> > +       r->rx_ring_depth = rx_depth;
> > +       r->rx_ring_mask = rx_depth - 1;
> > +       r->rx_pici_reg = NHI_RX_RING_PICI + ringnum * 16;
> > +       r->tx_pici_reg = NHI_TX_RING_PICI + ringnum * 16;
> > +       r->rx_cmd_ring = (struct nhi_cmd_frame **)((uint8_t *)r + sizeof
> (*r));
> > +       r->tx_cmd_ring = (struct nhi_cmd_frame **)((uint8_t
> *)r->rx_cmd_ring +
> > +           rxring_len);
> > +
> > +       snprintf(r->name, NHI_RING_NAMELEN, "nhiring%d\n", ringnum);
> > +       mtx_init(&r->mtx, r->name, "NHI Ring Lock", MTX_DEF);
> > +       tb_debug(sc, DBG_INIT | DBG_FULL, "Allocated ring context at %p,
> "
> > +           "mutex %p\n", r, &r->mtx);
> > +
> > +       /* Allocate the RX and TX buffer descriptor rings */
> > +       ring_size = sizeof(struct nhi_tx_buffer_desc) * r->tx_ring_depth;
> > +       ring_size += sizeof(struct nhi_rx_buffer_desc) *
> r->rx_ring_depth;
> > +       tb_debug(sc, DBG_INIT | DBG_FULL, "Ring %d ring_size= %d\n",
> > +           ringnum, ring_size);
> > +
> > +       bus_dma_template_init(&t, sc->parent_dmat);
> > +       t.alignment = 4;
> > +       t.maxsize = t.maxsegsize = ring_size;
> > +       t.nsegments = 1;
> > +       if ((error = bus_dma_template_tag(&t, &r->ring_dmat)) != 0) {
> > +               tb_printf(sc, "Cannot allocate ring %d DMA tag: %d\n",
> > +                   ringnum, error);
> > +               return (ENOMEM);
> > +       }
> > +       if (bus_dmamem_alloc(r->ring_dmat, (void **)&ring,
> BUS_DMA_NOWAIT,
> > +           &r->ring_map)) {
> > +               tb_printf(sc, "Cannot allocate ring memory\n");
> > +               return (ENOMEM);
> > +       }
> > +       bzero(ring, ring_size);
> > +       bus_dmamap_load(r->ring_dmat, r->ring_map, ring, ring_size,
> > +           nhi_memaddr_cb, &ring_busaddr, 0);
> > +
> > +       r->ring = ring;
> > +
> > +       r->tx_ring = (union nhi_ring_desc *)(ring);
> > +       r->tx_ring_busaddr = ring_busaddr;
> > +       ring += sizeof(struct nhi_tx_buffer_desc) * r->tx_ring_depth;
> > +       ring_busaddr += sizeof(struct nhi_tx_buffer_desc) *
> r->tx_ring_depth;
> > +
> > +       r->rx_ring = (union nhi_ring_desc *)(ring);
> > +       r->rx_ring_busaddr = ring_busaddr;
> > +
> > +       tb_debug(sc, DBG_INIT | DBG_EXTRA, "Ring %d: RX %p [0x%jx] "
> > +           "TX %p [0x%jx]\n", ringnum, r->tx_ring, r->tx_ring_busaddr,
> > +           r->rx_ring, r->rx_ring_busaddr);
> > +
> > +       *rp = r;
> > +       return (0);
> > +}
> > +
> > +static void
> > +nhi_free_ring(struct nhi_ring_pair *r)
> > +{
> > +
> > +       tb_debug(r->sc, DBG_INIT, "Freeing ring %d resources\n",
> r->ring_num);
> > +       nhi_deactivate_ring(r);
> > +
> > +       if (r->tx_ring_busaddr != 0) {
> > +               bus_dmamap_unload(r->ring_dmat, r->ring_map);
> > +               r->tx_ring_busaddr = 0;
> > +       }
> > +       if (r->ring != NULL) {
> > +               bus_dmamem_free(r->ring_dmat, r->ring, r->ring_map);
> > +               r->ring = NULL;
> > +       }
> > +       if (r->ring_dmat != NULL) {
> > +               bus_dma_tag_destroy(r->ring_dmat);
> > +               r->ring_dmat = NULL;
> > +       }
> > +       mtx_destroy(&r->mtx);
> > +}
> > +
> > +static void
> > +nhi_free_rings(struct nhi_softc *sc)
> > +{
> > +       struct nhi_ring_pair *r;
> > +
> > +       while ((r = SLIST_FIRST(&sc->ring_list)) != NULL) {
> > +               nhi_free_ring(r);
> > +               mtx_lock(&sc->nhi_mtx);
> > +               SLIST_REMOVE_HEAD(&sc->ring_list, ring_link);
> > +               mtx_unlock(&sc->nhi_mtx);
> > +               free(r, M_NHI);
> > +       }
> > +
> > +       return;
> > +}
> > +
> > +static int
> > +nhi_configure_ring(struct nhi_softc *sc, struct nhi_ring_pair *ring)
> > +{
> > +       bus_addr_t busaddr;
> > +       uint32_t val;
> > +       int idx;
> > +
> > +       idx = ring->ring_num * 16;
> > +
> > +       /* Program the TX ring address and size */
> > +       busaddr = ring->tx_ring_busaddr;
> > +       nhi_write_reg(sc, NHI_TX_RING_ADDR_LO + idx, busaddr &
> 0xffffffff);
> > +       nhi_write_reg(sc, NHI_TX_RING_ADDR_HI + idx, busaddr >> 32);
> > +       nhi_write_reg(sc, NHI_TX_RING_SIZE + idx, ring->tx_ring_depth);
> > +       nhi_write_reg(sc, NHI_TX_RING_TABLE_TIMESTAMP + idx, 0x0);
> > +       tb_debug(sc, DBG_INIT, "TX Ring %d TX_RING_SIZE= 0x%x\n",
> > +           ring->ring_num, ring->tx_ring_depth);
> > +
> > +       /* Program the RX ring address and size */
> > +       busaddr = ring->rx_ring_busaddr;
> > +       val = (ring->rx_buffer_size << 16) | ring->rx_ring_depth;
> > +       nhi_write_reg(sc, NHI_RX_RING_ADDR_LO + idx, busaddr &
> 0xffffffff);
> > +       nhi_write_reg(sc, NHI_RX_RING_ADDR_HI + idx, busaddr >> 32);
> > +       nhi_write_reg(sc, NHI_RX_RING_SIZE + idx, val);
> > +       nhi_write_reg(sc, NHI_RX_RING_TABLE_BASE1 + idx, 0xffffffff);
> > +       tb_debug(sc, DBG_INIT, "RX Ring %d RX_RING_SIZE= 0x%x\n",
> > +           ring->ring_num, val);
> > +
> > +       return (0);
> > +}
> > +
> > +static int
> > +nhi_activate_ring(struct nhi_ring_pair *ring)
> > +{
> > +       struct nhi_softc *sc = ring->sc;
> > +       int idx;
> > +
> > +       nhi_pci_enable_interrupt(ring);
> > +
> > +       idx = ring->ring_num * 32;
> > +       tb_debug(sc, DBG_INIT, "Activating ring %d at idx %d\n",
> > +           ring->ring_num, idx);
> > +       nhi_write_reg(sc, NHI_TX_RING_TABLE_BASE0 + idx,
> > +           TX_TABLE_RAW | TX_TABLE_VALID);
> > +       nhi_write_reg(sc, NHI_RX_RING_TABLE_BASE0 + idx,
> > +           RX_TABLE_RAW | RX_TABLE_VALID);
> > +
> > +       return (0);
> > +}
> > +
> > +static int
> > +nhi_deactivate_ring(struct nhi_ring_pair *r)
> > +{
> > +       struct nhi_softc *sc = r->sc;
> > +       int idx;
> > +
> > +       idx = r->ring_num * 32;
> > +       tb_debug(sc, DBG_INIT, "Deactiving ring %d at idx %d\n",
> > +           r->ring_num, idx);
> > +       nhi_write_reg(sc, NHI_TX_RING_TABLE_BASE0 + idx, 0);
> > +       nhi_write_reg(sc, NHI_RX_RING_TABLE_BASE0 + idx, 0);
> > +
> > +       idx = r->ring_num * 16;
> > +       tb_debug(sc, DBG_INIT, "Setting ring %d sizes to 0\n",
> r->ring_num);
> > +       nhi_write_reg(sc, NHI_TX_RING_SIZE + idx, 0);
> > +       nhi_write_reg(sc, NHI_RX_RING_SIZE + idx, 0);
> > +
> > +       return (0);
> > +}
> > +
> > +static int
> > +nhi_alloc_ring0(struct nhi_softc *sc)
> > +{
> > +       bus_addr_t frames_busaddr;
> > +       bus_dma_template_t t;
> > +       struct nhi_intr_tracker *trkr;
> > +       struct nhi_ring_pair *r;
> > +       struct nhi_cmd_frame *cmd;
> > +       char *frames;
> > +       int error, size, i;
> > +
> > +       if ((error = nhi_alloc_ring(sc, 0, NHI_RING0_TX_DEPTH,
> > +           NHI_RING0_RX_DEPTH, &r)) != 0) {
> > +               tb_printf(sc, "Error allocating control ring\n");
> > +               return (error);
> > +       }
> > +
> > +       r->rx_buffer_size = NHI_RING0_FRAME_SIZE;/* Control packets are
> small */
> > +
> > +       /* Allocate the RX and TX buffers that are used for Ring0 comms
> */
> > +       size = r->tx_ring_depth * NHI_RING0_FRAME_SIZE;
> > +       size += r->rx_ring_depth * NHI_RING0_FRAME_SIZE;
> > +
> > +       bus_dma_template_init(&t, sc->parent_dmat);
> > +       t.maxsize = t.maxsegsize = size;
> > +       t.nsegments = 1;
> > +       if (bus_dma_template_tag(&t, &sc->ring0_dmat)) {
> > +               tb_printf(sc, "Error allocating control ring buffer
> tag\n");
> > +               return (ENOMEM);
> > +       }
> > +
> > +       if (bus_dmamem_alloc(sc->ring0_dmat, (void **)&frames,
> BUS_DMA_NOWAIT,
> > +           &sc->ring0_map) != 0) {
> > +               tb_printf(sc, "Error allocating control ring memory\n");
> > +               return (ENOMEM);
> > +       }
> > +       bzero(frames, size);
> > +       bus_dmamap_load(sc->ring0_dmat, sc->ring0_map, frames, size,
> > +           nhi_memaddr_cb, &frames_busaddr, 0);
> > +       sc->ring0_frames_busaddr = frames_busaddr;
> > +       sc->ring0_frames = frames;
> > +
> > +       /* Allocate the driver command trackers */
> > +       sc->ring0_cmds = malloc(sizeof(struct nhi_cmd_frame) *
> > +           (r->tx_ring_depth + r->rx_ring_depth), M_NHI, M_NOWAIT |
> M_ZERO);
> > +       if (sc->ring0_cmds == NULL)
> > +               return (ENOMEM);
> > +
> > +       /* Initialize the RX frames so they can be used */
> > +       mtx_lock(&r->mtx);
> > +       for (i = 0; i < r->rx_ring_depth; i++) {
> > +               cmd = &sc->ring0_cmds[i];
> > +               cmd->data = (uint32_t *)(frames + NHI_RING0_FRAME_SIZE *
> i);
> > +               cmd->data_busaddr = frames_busaddr +
> NHI_RING0_FRAME_SIZE * i;
> > +               cmd->flags = CMD_MAPPED;
> > +               cmd->idx = i;
> > +               TAILQ_INSERT_TAIL(&r->rx_head, cmd, cm_link);
> > +       }
> > +
> > +       /* Inititalize the TX frames */
> > +       for ( ; i < r->tx_ring_depth + r->rx_ring_depth - 1; i++) {
> > +               cmd = &sc->ring0_cmds[i];
> > +               cmd->data = (uint32_t *)(frames + NHI_RING0_FRAME_SIZE *
> i);
> > +               cmd->data_busaddr = frames_busaddr +
> NHI_RING0_FRAME_SIZE * i;
> > +               cmd->flags = CMD_MAPPED;
> > +               cmd->idx = i;
> > +               nhi_free_tx_frame_locked(r, cmd);
> > +       }
> > +       mtx_unlock(&r->mtx);
> > +
> > +       /* Do a 1:1 mapping of rings to interrupt vectors. */
> > +       /* XXX Should be abstracted */
> > +       trkr = &sc->intr_trackers[0];
> > +       trkr->ring = r;
> > +       r->tracker = trkr;
> > +
> > +       /* XXX Should be an array */
> > +       sc->ring0 = r;
> > +       SLIST_INSERT_HEAD(&sc->ring_list, r, ring_link);
> > +
> > +       return (0);
> > +}
> > +
> > +static void
> > +nhi_free_ring0(struct nhi_softc *sc)
> > +{
> > +       if (sc->ring0_cmds != NULL) {
> > +               free(sc->ring0_cmds, M_NHI);
> > +               sc->ring0_cmds = NULL;
> > +       }
> > +
> > +       if (sc->ring0_frames_busaddr != 0) {
> > +               bus_dmamap_unload(sc->ring0_dmat, sc->ring0_map);
> > +               sc->ring0_frames_busaddr = 0;
> > *** 5529 LINES SKIPPED ***
>
>
>
> --
> Wolfram Schneider <wosch@FreeBSD.org> https://wolfram.schneider.org
>
>

-- 
Nuno Teixeira
FreeBSD UNIX:  <eduardo@FreeBSD.org>   Web:  https://FreeBSD.org