/*      $NetBSD: acpi_verbose.c,v 1.22 2022/06/02 00:12:20 rin Exp $ */

/*-
* Copyright (c) 2003, 2007, 2010 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Charles M. Hannum of By Noon Software, Inc, and Jukka Ruohonen.
*
* 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
*/

/*
* Copyright 2001, 2003 Wasabi Systems, Inc.
* All rights reserved.
*
* Written by Jason R. Thorpe for Wasabi Systems, Inc.
*
* 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.
* 3. All advertising materials mentioning features or use of this software
*    must display the following acknowledgement:
*      This product includes software developed for the NetBSD Project by
*      Wasabi Systems, Inc.
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
*    or promote products derived from this software without specific prior
*    written permission.
*
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
* 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 <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: acpi_verbose.c,v 1.22 2022/06/02 00:12:20 rin Exp $");

#include "pci.h"

#include <sys/param.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/module.h>

#include <dev/acpi/acpireg.h>
#include <dev/acpi/acpivar.h>
#include <dev/acpi/acpidevs_data.h>
#include <dev/acpi/acpi_pci.h>

#include <prop/proplib.h>

#define _COMPONENT ACPI_UTILITIES
ACPI_MODULE_NAME   ("acpi_verbose")

static bool        acpiverbose_modcmd_prop(prop_dictionary_t);

void               acpi_print_verbose_real(struct acpi_softc *);
void               acpi_print_dev_real(const char *);
static void        acpi_print_madt(struct acpi_softc *);
static ACPI_STATUS acpi_print_madt_callback(ACPI_SUBTABLE_HEADER *, void *);
static void        acpi_print_fadt(struct acpi_softc *);
static void        acpi_print_devnodes(struct acpi_softc *);
static void        acpi_print_tree(struct acpi_devnode *, uint32_t);

extern ACPI_TABLE_HEADER *madt_header;

MODULE(MODULE_CLASS_MISC, acpiverbose, NULL);

static int
acpiverbose_modcmd(modcmd_t cmd, void *arg)
{
       static void (*saved_print_verbose)(struct acpi_softc *);
       static void (*saved_print_dev)(const char *);
       bool dump;

       dump = false;

       switch (cmd) {

       case MODULE_CMD_INIT:
               saved_print_verbose = acpi_print_verbose;
               saved_print_dev = acpi_print_dev;
               acpi_print_verbose = acpi_print_verbose_real;
               acpi_print_dev = acpi_print_dev_real;
               acpi_verbose_loaded = 1;

               if (arg != NULL)
                       dump = acpiverbose_modcmd_prop(arg);

               if (dump != false)
                       acpi_print_verbose_real(acpi_softc);

               return 0;

       case MODULE_CMD_FINI:
               acpi_print_verbose = saved_print_verbose;
               acpi_print_dev = saved_print_dev;
               acpi_verbose_loaded = 0;
               return 0;

       default:
               return ENOTTY;
       }
}

static bool
acpiverbose_modcmd_prop(prop_dictionary_t dict)
{
       prop_object_t obj;

       obj = prop_dictionary_get(dict, "dump");

       if (obj == NULL || prop_object_type(obj) != PROP_TYPE_BOOL)
               return false;

       return prop_bool_true(obj);
}

void
acpi_print_verbose_real(struct acpi_softc *sc)
{

       acpi_print_madt(sc);
       acpi_print_fadt(sc);
       acpi_print_devnodes(sc);
       acpi_print_tree(sc->sc_root, 0);
}

void
acpi_print_dev_real(const char *pnpstr)
{
       int i;

       for (i = 0; i < __arraycount(acpi_knowndevs); i++) {

               if (strcmp(acpi_knowndevs[i].pnp, pnpstr) == 0)
                       aprint_normal("[%s] ", acpi_knowndevs[i].str);
       }
}

static void
acpi_print_madt(struct acpi_softc *sc)
{
       ACPI_STATUS rv;

       rv = acpi_madt_map();

       if (ACPI_FAILURE(rv) && rv != AE_ALREADY_EXISTS)
               return;

       if (madt_header == NULL)
               return;

       acpi_madt_walk(acpi_print_madt_callback, sc);
}

static ACPI_STATUS
acpi_print_madt_callback(ACPI_SUBTABLE_HEADER *hdr, void *aux)
{
       struct acpi_softc *sc = aux;
       device_t self = sc->sc_dev;

       /*
        * See ACPI 4.0, section 5.2.12.
        */
       switch (hdr->Type) {

       case ACPI_MADT_TYPE_LOCAL_APIC:

               aprint_normal_dev(self, "[MADT] %-15s: "
                   "CPU ID %u, LAPIC ID %u, FLAGS 0x%02X", "LAPIC",
                   ((ACPI_MADT_LOCAL_APIC *)hdr)->ProcessorId,
                   ((ACPI_MADT_LOCAL_APIC *)hdr)->Id,
                   ((ACPI_MADT_LOCAL_APIC *)hdr)->LapicFlags);

               break;

       case ACPI_MADT_TYPE_IO_APIC:

               aprint_normal_dev(self, "[MADT] %-15s: "
                   "ID %u, GSI %u, ADDR 0x%04X", "I/O APIC",
                   ((ACPI_MADT_IO_APIC *)hdr)->Id,
                   ((ACPI_MADT_IO_APIC *)hdr)->GlobalIrqBase,
                   ((ACPI_MADT_IO_APIC *)hdr)->Address);

               break;

       case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE:

               aprint_normal_dev(self, "[MADT] %-15s: "
                   "BUS %u, IRQ %u, GSI %u, FLAGS 0x%02X", "INTR OVERRIDE",
                   ((ACPI_MADT_INTERRUPT_OVERRIDE *)hdr)->Bus,
                   ((ACPI_MADT_INTERRUPT_OVERRIDE *)hdr)->SourceIrq,
                   ((ACPI_MADT_INTERRUPT_OVERRIDE *)hdr)->GlobalIrq,
                   ((ACPI_MADT_INTERRUPT_OVERRIDE *)hdr)->IntiFlags);

               break;

       case ACPI_MADT_TYPE_NMI_SOURCE:

               aprint_normal_dev(self, "[MADT] %-15s: "
                   "GSI %u, FLAGS 0x%02X", "NMI SOURCE",
                   ((ACPI_MADT_NMI_SOURCE *)hdr)->GlobalIrq,
                   ((ACPI_MADT_NMI_SOURCE *)hdr)->IntiFlags);

               break;

       case ACPI_MADT_TYPE_LOCAL_APIC_NMI:

               aprint_normal_dev(self, "[MADT] %-15s: "
                   "CPU ID %u, LINT %u, FLAGS 0x%02X", "LAPIC NMI",
                   ((ACPI_MADT_LOCAL_APIC_NMI *)hdr)->ProcessorId,
                   ((ACPI_MADT_LOCAL_APIC_NMI *)hdr)->Lint,
                   ((ACPI_MADT_LOCAL_APIC_NMI *)hdr)->IntiFlags);

               break;

       case ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE:

               aprint_normal_dev(self, "[MADT] %-15s: "
                   "ADDR 0x%016" PRIX64"", "APIC OVERRIDE",
                   ((ACPI_MADT_LOCAL_APIC_OVERRIDE *)hdr)->Address);

               break;

       case ACPI_MADT_TYPE_IO_SAPIC:

               aprint_normal_dev(self, "[MADT] %-15s: "
                   "ID %u, GSI %u, ADDR 0x%016" PRIX64"", "I/O SAPIC",
                   ((ACPI_MADT_IO_SAPIC *)hdr)->Id,
                   ((ACPI_MADT_IO_SAPIC *)hdr)->GlobalIrqBase,
                   ((ACPI_MADT_IO_SAPIC *)hdr)->Address);

               break;

       case ACPI_MADT_TYPE_LOCAL_SAPIC:

               aprint_normal_dev(self, "[MADT] %-15s: "
                   "CPU ID %u, ID %u, EID %u, UID %u, FLAGS 0x%02X", "LSAPIC",
                   ((ACPI_MADT_LOCAL_SAPIC*)hdr)->ProcessorId,
                   ((ACPI_MADT_LOCAL_SAPIC*)hdr)->Id,
                   ((ACPI_MADT_LOCAL_SAPIC*)hdr)->Eid,
                   ((ACPI_MADT_LOCAL_SAPIC*)hdr)->Uid,
                   ((ACPI_MADT_LOCAL_SAPIC*)hdr)->LapicFlags);

               break;

       case ACPI_MADT_TYPE_INTERRUPT_SOURCE:

               aprint_normal_dev(self, "[MADT] %-15s: ID %u, EID %u, "
                   "TYPE %u, PMI %u, GSI %u, FLAGS 0x%02X", "INTR SOURCE",
                   ((ACPI_MADT_INTERRUPT_SOURCE *)hdr)->Id,
                   ((ACPI_MADT_INTERRUPT_SOURCE *)hdr)->Eid,
                   ((ACPI_MADT_INTERRUPT_SOURCE *)hdr)->Type,
                   ((ACPI_MADT_INTERRUPT_SOURCE *)hdr)->IoSapicVector,
                   ((ACPI_MADT_INTERRUPT_SOURCE *)hdr)->GlobalIrq,
                   ((ACPI_MADT_INTERRUPT_SOURCE *)hdr)->Flags);

               break;

       case ACPI_MADT_TYPE_LOCAL_X2APIC:

               aprint_normal_dev(self, "[MADT] %-15s: "
                   "ID %u, UID %u, FLAGS 0x%02X", "X2APIC",
                   ((ACPI_MADT_LOCAL_X2APIC *)hdr)->LocalApicId,
                   ((ACPI_MADT_LOCAL_X2APIC *)hdr)->Uid,
                   ((ACPI_MADT_LOCAL_X2APIC *)hdr)->LapicFlags);

               break;

       case ACPI_MADT_TYPE_LOCAL_X2APIC_NMI:

               aprint_normal_dev(self, "[MADT] %-15s: "
                   "UID %u, LINT %u, FLAGS 0x%02X", "X2APIC NMI",
                   ((ACPI_MADT_LOCAL_X2APIC_NMI *)hdr)->Uid,
                   ((ACPI_MADT_LOCAL_X2APIC_NMI *)hdr)->Lint,
                   ((ACPI_MADT_LOCAL_X2APIC_NMI *)hdr)->IntiFlags);

               break;

       default:
               aprint_normal_dev(self, "[MADT] %-15s", "UNKNOWN");
               break;
       }

       aprint_normal("\n");

       return AE_OK;
}

static void
acpi_print_fadt(struct acpi_softc *sc)
{
       uint32_t i;

       /*
        * See ACPI 4.0, section 5.2.9.
        */
       struct acpi_fadt {
               uint32_t         fadt_offset;
               const char      *fadt_name;
               uint64_t         fadt_value;
       };

       struct acpi_fadt_genaddr {
               uint32_t                 fadt_offset;
               const char              *fadt_name;
               ACPI_GENERIC_ADDRESS     fadt_value;
       };

       const struct acpi_fadt acpi_fadt_table[] = {

               { 36,   "FACS",          AcpiGbl_FADT.Facs              },
               { 40,   "DSDT",          AcpiGbl_FADT.Dsdt              },
               { 44,   "INT_MODEL",     AcpiGbl_FADT.Model             },
               { 45,   "PM_PROFILE",    AcpiGbl_FADT.PreferredProfile  },
               { 46,   "SCI_INT",       AcpiGbl_FADT.SciInterrupt      },
               { 48,   "SMI_CMD",       AcpiGbl_FADT.SmiCommand        },
               { 52,   "ACPI_ENABLE",   AcpiGbl_FADT.AcpiEnable        },
               { 53,   "ACPI_DISABLE",  AcpiGbl_FADT.AcpiDisable       },
               { 54,   "S4BIOS_REQ",    AcpiGbl_FADT.S4BiosRequest     },
               { 55,   "PSTATE_CNT",    AcpiGbl_FADT.PstateControl     },
               { 56,   "PM1a_EVT_BLK",  AcpiGbl_FADT.Pm1aEventBlock    },
               { 60,   "PM1b_EVT_BLK",  AcpiGbl_FADT.Pm1bEventBlock    },
               { 64,   "PM1a_CNT_BLK",  AcpiGbl_FADT.Pm1aControlBlock  },
               { 68,   "PM1b_CNT_BLK",  AcpiGbl_FADT.Pm1bControlBlock  },
               { 72,   "PM2_CNT_BLK",   AcpiGbl_FADT.Pm2ControlBlock   },
               { 76,   "PM_TMR_BLK",    AcpiGbl_FADT.PmTimerBlock      },
               { 80,   "GPE0_BLK",      AcpiGbl_FADT.Gpe0Block         },
               { 84,   "GPE1_BLK",      AcpiGbl_FADT.Gpe1Block         },
               { 88,   "PM1_EVT_LEN",   AcpiGbl_FADT.Pm1EventLength    },
               { 89,   "PM1_CNT_LEN",   AcpiGbl_FADT.Pm1ControlLength  },
               { 90,   "PM2_CNT_LEN",   AcpiGbl_FADT.Pm2ControlLength  },
               { 91,   "PM_TMR_LEN",    AcpiGbl_FADT.PmTimerLength     },
               { 92,   "GPE0_BLK_LEN",  AcpiGbl_FADT.Gpe0BlockLength   },
               { 93,   "GPE1_BLK_LEN",  AcpiGbl_FADT.Gpe1BlockLength   },
               { 94,   "GPE1_BASE",     AcpiGbl_FADT.Gpe1Base          },
               { 95,   "CST_CNT",       AcpiGbl_FADT.CstControl        },
               { 96,   "P_LVL2_LAT",    AcpiGbl_FADT.C2Latency         },
               { 98,   "P_LVL3_LAT",    AcpiGbl_FADT.C3Latency         },
               { 100,  "FLUSH_SIZE",    AcpiGbl_FADT.FlushSize         },
               { 102,  "FLUSH_STRIDE",  AcpiGbl_FADT.FlushStride       },
               { 104,  "DUTY_OFFSET",   AcpiGbl_FADT.DutyOffset        },
               { 105,  "DUTY_WIDTH",    AcpiGbl_FADT.DutyWidth         },
               { 106,  "DAY_ALRM",      AcpiGbl_FADT.DayAlarm          },
               { 107,  "MON_ALRM",      AcpiGbl_FADT.MonthAlarm        },
               { 108,  "CENTURY",       AcpiGbl_FADT.Century           },
               { 109,  "IAPC_BOOT_ARCH",AcpiGbl_FADT.BootFlags         },
               { 128,  "RESET_VALUE",   AcpiGbl_FADT.ResetValue        },
               { 129,  "ARM_BOOT_ARCH", AcpiGbl_FADT.ArmBootFlags      },
               { 132,  "X_FACS",        AcpiGbl_FADT.XFacs             },
               { 140,  "X_DSDT",        AcpiGbl_FADT.XDsdt             },
       };

       const struct acpi_fadt_genaddr acpi_fadt_genaddr_table[] = {

               { 116,  "RESET_REG",     AcpiGbl_FADT.ResetRegister     },
               { 148,  "X_PM1a_EVT_BLK",AcpiGbl_FADT.XPm1aEventBlock   },
               { 160,  "X_PM1b_EVT_BLK",AcpiGbl_FADT.XPm1bEventBlock   },
               { 172,  "X_PM1a_CNT_BLK",AcpiGbl_FADT.XPm1aControlBlock },
               { 184,  "X_PM1b_CNT_BLK",AcpiGbl_FADT.XPm1bControlBlock },
               { 196,  "X_PM2_CNT_BLK", AcpiGbl_FADT.XPm2ControlBlock  },
               { 208,  "X_PM_TMR_BLK",  AcpiGbl_FADT.XPmTimerBlock     },
               { 220,  "X_GPE0_BLK",    AcpiGbl_FADT.XGpe0Block        },
               { 232,  "X_GPE1_BLK",    AcpiGbl_FADT.XGpe1Block        },
               { 244,  "SLEEP_CTRL_REG",AcpiGbl_FADT.SleepControl      },
               { 256,  "SLEEP_STAT_REG",AcpiGbl_FADT.SleepStatus       },
       };

       const struct acpi_fadt acpi_fadt_flags[] = {

               { 0,    "WBINVD",       ACPI_FADT_WBINVD                },
               { 1,    "WBINVD_FLUSH", ACPI_FADT_WBINVD_FLUSH          },
               { 2,    "PROC_C1",      ACPI_FADT_C1_SUPPORTED          },
               { 3,    "P_LVL2_UP",    ACPI_FADT_C2_MP_SUPPORTED       },
               { 4,    "PWR_BUTTON",   ACPI_FADT_POWER_BUTTON          },
               { 5,    "SLP_BUTTON",   ACPI_FADT_SLEEP_BUTTON          },
               { 6,    "FIX_RTC",      ACPI_FADT_FIXED_RTC             },
               { 7,    "RTC_S4",       ACPI_FADT_S4_RTC_WAKE           },
               { 8,    "TMR_VAL_EXT",  ACPI_FADT_32BIT_TIMER           },
               { 9,    "DCK_CAP",      ACPI_FADT_DOCKING_SUPPORTED     },
               { 10,   "RESET_REG_SUP",ACPI_FADT_RESET_REGISTER        },
               { 11,   "SEALED_CASE",  ACPI_FADT_SEALED_CASE           },
               { 12,   "HEADLESS",     ACPI_FADT_HEADLESS              },
               { 13,   "CPU_SW_SLP",   ACPI_FADT_SLEEP_TYPE            },
               { 14,   "PCI_EXP_WAK",  ACPI_FADT_PCI_EXPRESS_WAKE      },
               { 15,   "PLATFORM_CLK", ACPI_FADT_PLATFORM_CLOCK        },
               { 16,   "S4_RTC_STS",   ACPI_FADT_S4_RTC_VALID          },
               { 17,   "REMOTE_POWER", ACPI_FADT_REMOTE_POWER_ON       },
               { 18,   "APIC_CLUSTER", ACPI_FADT_APIC_CLUSTER          },
               { 19,   "APIC_PHYSICAL",ACPI_FADT_APIC_PHYSICAL         },
               { 20,   "HW_REDUCED",   ACPI_FADT_HW_REDUCED            },
               { 21,   "LOW_POWER_S0", ACPI_FADT_LOW_POWER_S0          },
       };

       for (i = 0; i < __arraycount(acpi_fadt_table); i++) {

               aprint_normal_dev(sc->sc_dev,
                   "[FADT] %-15s: 0x%016" PRIX64"\n",
                   acpi_fadt_table[i].fadt_name,
                   acpi_fadt_table[i].fadt_value);
       }

       for (i = 0; i < __arraycount(acpi_fadt_genaddr_table); i++) {

               aprint_normal_dev(sc->sc_dev,
                   "[FADT] %-15s: 0x%016" PRIX64", "
                   "SPACE ID %u, BIT WIDTH %u, BIT OFFSET %u, "
                   "ACCESS WIDTH %u\n",
                   acpi_fadt_genaddr_table[i].fadt_name,
                   acpi_fadt_genaddr_table[i].fadt_value.Address,
                   acpi_fadt_genaddr_table[i].fadt_value.SpaceId,
                   acpi_fadt_genaddr_table[i].fadt_value.BitWidth,
                   acpi_fadt_genaddr_table[i].fadt_value.BitOffset,
                   acpi_fadt_genaddr_table[i].fadt_value.AccessWidth);
       }

       for (i = 0; i < __arraycount(acpi_fadt_flags); i++) {

               aprint_normal_dev(sc->sc_dev,
                   "[FADT] %-15s: 0x%016" PRIX64"\n",
                   acpi_fadt_flags[i].fadt_name, AcpiGbl_FADT.Flags &
                   acpi_fadt_flags[i].fadt_value);

               KASSERT(i ==  acpi_fadt_flags[i].fadt_offset);
               KASSERT(__BIT(acpi_fadt_flags[i].fadt_offset) ==
                             acpi_fadt_flags[i].fadt_value);
       }
}

static void
acpi_print_devnodes(struct acpi_softc *sc)
{
       struct acpi_devnode *ad;
       ACPI_DEVICE_INFO *di;

       SIMPLEQ_FOREACH(ad, &sc->sc_head, ad_list) {

               di = ad->ad_devinfo;
               aprint_normal_dev(sc->sc_dev, "[%-4s] ", ad->ad_name);

               aprint_normal("HID %-10s ",
                   ((di->Valid & ACPI_VALID_HID) != 0) ?
                   di->HardwareId.String: "-");

               aprint_normal("UID %-4s ",
                   ((di->Valid & ACPI_VALID_UID) != 0) ?
                   di->UniqueId.String : "-");

               if ((di->Valid & ACPI_VALID_ADR) != 0)
                       aprint_normal("ADR 0x%016" PRIX64"", di->Address);
               else
                       aprint_normal("ADR -");

               aprint_normal("\n");
       }
       aprint_normal("\n");
}

static void
acpi_print_tree(struct acpi_devnode *ad, uint32_t level)
{
       struct acpi_devnode *child;
       char buf[5];
       uint32_t i;

       for (i = 0; i < level; i++)
               aprint_normal("    ");

       buf[0] = '\0';

       if ((ad->ad_flags & ACPI_DEVICE_POWER) != 0)
               (void)strlcat(buf, "P", sizeof(buf));

       if ((ad->ad_flags & ACPI_DEVICE_WAKEUP) != 0)
               (void)strlcat(buf, "W", sizeof(buf));

       if ((ad->ad_flags & ACPI_DEVICE_EJECT) != 0)
               (void)strlcat(buf, "E", sizeof(buf));

       aprint_normal("%-5s [%02u] [%s]", ad->ad_name, ad->ad_type, buf);

       if (ad->ad_device != NULL)
               aprint_normal(" <%s>", device_xname(ad->ad_device));

#if NPCI > 0
       if (ad->ad_pciinfo != NULL) {

               aprint_normal(" (PCI)");

               if ((ad->ad_pciinfo->ap_flags & ACPI_PCI_INFO_DEVICE) != 0)
                       aprint_normal(" @ 0x%02X:0x%02X:0x%02X:0x%02X",
                           ad->ad_pciinfo->ap_segment,
                           ad->ad_pciinfo->ap_bus,
                           ad->ad_pciinfo->ap_device,
                           ad->ad_pciinfo->ap_function);

               if ((ad->ad_devinfo->Flags & ACPI_PCI_ROOT_BRIDGE) != 0)
                       aprint_normal(" [R]");

               if ((ad->ad_pciinfo->ap_flags & ACPI_PCI_INFO_BRIDGE) != 0)
                       aprint_normal(" [B] -> 0x%02X:0x%02X",
                           ad->ad_pciinfo->ap_segment,
                           ad->ad_pciinfo->ap_downbus);

               device_t dev = acpi_pcidev_find_dev(ad);

               if (dev != NULL)
                       aprint_normal(" <%s>", device_xname(dev));
       }
#endif

       aprint_normal("\n");

       SIMPLEQ_FOREACH(child, &ad->ad_child_head, ad_child_list)
           acpi_print_tree(child, level + 1);
}