1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2022 Scott Long 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include "opt_acpi.h" 30 #include "opt_thunderbolt.h" 31 #include <sys/param.h> 32 #include <sys/conf.h> 33 #include <sys/uio.h> 34 #include <sys/proc.h> 35 #include <sys/kernel.h> 36 #include <sys/bus.h> 37 #include <sys/sbuf.h> 38 #include <sys/module.h> 39 #include <sys/sysctl.h> 40 41 #include <contrib/dev/acpica/include/acpi.h> 42 #include <contrib/dev/acpica/include/accommon.h> 43 #include <dev/acpica/acpivar.h> 44 #include "acpi_wmi_if.h" 45 46 ACPI_MODULE_NAME("THUNDERBOLT-NHI-WMI") 47 48 #define ACPI_INTEL_THUNDERBOLT_GUID "86CCFD48-205E-4A77-9C48-2021CBEDE341" 49 50 struct nhi_wmi_softc { 51 device_t dev; 52 device_t wmi_dev; 53 u_int state; 54 char *guid; 55 struct sysctl_ctx_list *sysctl_ctx; 56 struct sysctl_oid *sysctl_tree; 57 }; 58 59 ACPI_SERIAL_DECL(nhi_wmi, "Thunderbolt NHI WMI device"); 60 61 static void nhi_wmi_identify(driver_t *driver, device_t parent); 62 static int nhi_wmi_probe(device_t dev); 63 static int nhi_wmi_attach(device_t dev); 64 static int nhi_wmi_detach(device_t dev); 65 static int nhi_wmi_sysctl(SYSCTL_HANDLER_ARGS); 66 static int nhi_wmi_evaluate_method(struct nhi_wmi_softc *sc, 67 int method, uint32_t arg0, uint32_t *retval); 68 69 static device_method_t nhi_wmi_methods[] = { 70 DEVMETHOD(device_identify, nhi_wmi_identify), 71 DEVMETHOD(device_probe, nhi_wmi_probe), 72 DEVMETHOD(device_attach, nhi_wmi_attach), 73 DEVMETHOD(device_detach, nhi_wmi_detach), 74 75 DEVMETHOD_END 76 }; 77 78 static driver_t nhi_wmi_driver = { 79 "nhi_wmi", 80 nhi_wmi_methods, 81 sizeof(struct nhi_wmi_softc) 82 }; 83 84 DRIVER_MODULE(nhi_wmi, acpi_wmi, nhi_wmi_driver, 85 NULL, NULL); 86 MODULE_DEPEND(nhi_wmi, acpi_wmi, 1, 1, 1); 87 MODULE_DEPEND(nhi_wmi, acpi, 1, 1, 1); 88 89 static void 90 nhi_wmi_identify(driver_t *driver, device_t parent) 91 { 92 93 if (acpi_disabled("nhi_wmi") != 0) 94 return; 95 96 if (device_find_child(parent, "nhi_wmi", -1) != NULL) 97 return; 98 99 if (ACPI_WMI_PROVIDES_GUID_STRING(parent, 100 ACPI_INTEL_THUNDERBOLT_GUID) == 0) 101 return; 102 103 if (BUS_ADD_CHILD(parent, 0, "nhi_wmi", -1) == NULL) 104 device_printf(parent, "failed to add nhi_wmi\n"); 105 } 106 107 static int 108 nhi_wmi_probe(device_t dev) 109 { 110 111 if (ACPI_WMI_PROVIDES_GUID_STRING(device_get_parent(dev), 112 ACPI_INTEL_THUNDERBOLT_GUID) == 0) 113 return (EINVAL); 114 device_set_desc(dev, "Thunderbolt WMI Endpoint"); 115 return (BUS_PROBE_DEFAULT); 116 } 117 118 static int 119 nhi_wmi_attach(device_t dev) 120 { 121 struct nhi_wmi_softc *sc; 122 123 sc = device_get_softc(dev); 124 sc->dev = dev; 125 sc->wmi_dev = device_get_parent(dev); 126 127 sc->sysctl_ctx = device_get_sysctl_ctx(dev); 128 sc->sysctl_tree = device_get_sysctl_tree(dev); 129 sc->state = 0; 130 sc->guid = ACPI_INTEL_THUNDERBOLT_GUID; 131 132 SYSCTL_ADD_STRING(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), 133 OID_AUTO, "GUID", CTLFLAG_RD, sc->guid, 0, "WMI GUID"); 134 SYSCTL_ADD_PROC(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), 135 OID_AUTO, "force_power", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_MPSAFE, 136 sc, 0, nhi_wmi_sysctl, "I", "Force controller power on"); 137 138 return (0); 139 } 140 141 static int 142 nhi_wmi_detach(device_t dev) 143 { 144 145 return (0); 146 } 147 148 static int 149 nhi_wmi_sysctl(SYSCTL_HANDLER_ARGS) 150 { 151 struct nhi_wmi_softc *sc; 152 int error, arg; 153 154 sc = (struct nhi_wmi_softc *)arg1; 155 arg = !!sc->state; 156 error = sysctl_handle_int(oidp, &arg, 0, req); 157 if (!error && req->newptr != NULL) { 158 ACPI_SERIAL_BEGIN(nhi_wmi); 159 error = nhi_wmi_evaluate_method(sc, 1, arg, NULL); 160 ACPI_SERIAL_END(nhi_wmi); 161 if (error == 0) 162 sc->state = arg; 163 } 164 return (error); 165 } 166 167 static int 168 nhi_wmi_evaluate_method(struct nhi_wmi_softc *sc, int method, uint32_t arg0, 169 uint32_t *retval) 170 { 171 ACPI_OBJECT *obj; 172 ACPI_BUFFER in, out; 173 uint32_t val, params[1]; 174 175 params[0] = arg0; 176 in.Pointer = ¶ms; 177 in.Length = sizeof(params); 178 out.Pointer = NULL; 179 out.Length = ACPI_ALLOCATE_BUFFER; 180 181 if (ACPI_FAILURE(ACPI_WMI_EVALUATE_CALL(sc->wmi_dev, 182 ACPI_INTEL_THUNDERBOLT_GUID, 0, method, &in, &out))) { 183 AcpiOsFree(out.Pointer); 184 return (EINVAL); 185 } 186 187 obj = out.Pointer; 188 if (obj != NULL && obj->Type == ACPI_TYPE_INTEGER) 189 val = (uint32_t)obj->Integer.Value; 190 else 191 val = 0; 192 193 AcpiOsFree(out.Pointer); 194 if (retval) 195 *retval = val; 196 197 return (0); 198 } 199