1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2018 Johannes Lundberg <johalun@FreeBSD.org> 5 * Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are 9 * 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 14 * the documentation and/or other materials provided with the 15 * distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR 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 30 #include "opt_acpi.h" 31 32 #include <sys/types.h> 33 #include <sys/bus.h> 34 #include <sys/eventhandler.h> 35 #include <sys/kernel.h> 36 #include <sys/power.h> 37 38 #include <contrib/dev/acpica/include/acpi.h> 39 #include <dev/acpica/acpivar.h> 40 41 #include <linux/notifier.h> 42 #include <linux/suspend.h> 43 #include <linux/uuid.h> 44 45 #include <acpi/acpi_bus.h> 46 #include <acpi/video.h> 47 48 #define ACPI_AC_CLASS "ac_adapter" 49 50 ACPI_MODULE_NAME("linux_acpi") 51 52 enum { 53 LINUX_ACPI_ACAD, 54 LINUX_ACPI_VIDEO, 55 LINUX_ACPI_TAGS /* must be last */ 56 }; 57 _Static_assert(LINUX_ACPI_TAGS <= LINUX_NOTIFY_TAGS, 58 "Not enough space for tags in notifier_block structure"); 59 60 #ifdef DEV_ACPI 61 62 suspend_state_t pm_suspend_target_state = PM_SUSPEND_ON; 63 64 static uint32_t linux_acpi_target_sleep_state = ACPI_STATE_S0; 65 66 static eventhandler_tag resume_tag; 67 static eventhandler_tag suspend_tag; 68 69 ACPI_HANDLE 70 bsd_acpi_get_handle(device_t bsddev) 71 { 72 return (acpi_get_handle(bsddev)); 73 } 74 75 bool 76 acpi_check_dsm(ACPI_HANDLE handle, const guid_t *uuid, int rev, uint64_t funcs) 77 { 78 UINT64 ret; 79 80 if (funcs == 0) 81 return (false); 82 83 /* 84 * From ACPI 6.3 spec 9.1.1: 85 * Bit 0 indicates whether there is support for any functions other 86 * than function 0 for the specified UUID and Revision ID. If set to 87 * zero, no functions are supported (other than function zero) for the 88 * specified UUID and Revision ID. 89 */ 90 funcs |= 1 << 0; 91 92 ret = acpi_DSMQuery(handle, (const uint8_t *)uuid, rev); 93 return ((ret & funcs) == funcs); 94 } 95 96 ACPI_OBJECT * 97 acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const guid_t *uuid, int rev, 98 int func, ACPI_OBJECT *argv4, ACPI_OBJECT_TYPE type) 99 { 100 ACPI_BUFFER buf; 101 ACPI_STATUS status; 102 103 status = acpi_EvaluateDSMTyped(handle, (const uint8_t *)uuid, rev, func, 104 argv4, &buf, type); 105 return (ACPI_SUCCESS(status) ? (ACPI_OBJECT *)buf.Pointer : NULL); 106 } 107 108 union linuxkpi_acpi_object * 109 acpi_evaluate_dsm(ACPI_HANDLE ObjHandle, const guid_t *guid, 110 UINT64 rev, UINT64 func, union linuxkpi_acpi_object *pkg) 111 { 112 ACPI_BUFFER buf; 113 ACPI_STATUS status; 114 115 status = acpi_EvaluateDSM(ObjHandle, (const uint8_t *)guid, rev, func, 116 (ACPI_OBJECT *)pkg, &buf); 117 return (ACPI_SUCCESS(status) ? 118 (union linuxkpi_acpi_object *)buf.Pointer : NULL); 119 } 120 121 static void 122 linux_handle_power_suspend_event(void *arg __unused, 123 enum power_stype stype __unused) 124 { 125 /* 126 * Only support S3 for now. 127 * acpi_sleep_event isn't always called so we use power_suspend_early 128 * instead which means we don't know what state we're switching to. 129 * TODO: Make acpi_sleep_event consistent 130 */ 131 linux_acpi_target_sleep_state = ACPI_STATE_S3; 132 pm_suspend_target_state = PM_SUSPEND_MEM; 133 } 134 135 static void 136 linux_handle_power_resume_event(void *arg __unused, 137 enum power_stype stype __unused) 138 { 139 linux_acpi_target_sleep_state = ACPI_STATE_S0; 140 pm_suspend_target_state = PM_SUSPEND_ON; 141 } 142 143 static void 144 linux_handle_acpi_acad_event(void *arg, int data) 145 { 146 struct notifier_block *nb = arg; 147 /* 148 * Event type information is lost ATM in FreeBSD ACPI event handler. 149 * Fortunately, drm-kmod do not distinct AC event types too, so we can 150 * use any type e.g. ACPI_NOTIFY_BUS_CHECK that suits notifier handler. 151 */ 152 struct acpi_bus_event abe = { 153 .device_class = ACPI_AC_CLASS, 154 .type = ACPI_NOTIFY_BUS_CHECK, 155 .data = data, 156 }; 157 158 nb->notifier_call(nb, 0, &abe); 159 } 160 161 static void 162 linux_handle_acpi_video_event(void *arg, int type) 163 { 164 struct notifier_block *nb = arg; 165 struct acpi_bus_event abe = { 166 .device_class = ACPI_VIDEO_CLASS, 167 .type = type, 168 .data = 0, 169 }; 170 171 nb->notifier_call(nb, 0, &abe); 172 } 173 174 int 175 register_acpi_notifier(struct notifier_block *nb) 176 { 177 nb->tags[LINUX_ACPI_ACAD] = EVENTHANDLER_REGISTER(acpi_acad_event, 178 linux_handle_acpi_acad_event, nb, EVENTHANDLER_PRI_FIRST); 179 nb->tags[LINUX_ACPI_VIDEO] = EVENTHANDLER_REGISTER(acpi_video_event, 180 linux_handle_acpi_video_event, nb, EVENTHANDLER_PRI_FIRST); 181 182 return (0); 183 } 184 185 int 186 unregister_acpi_notifier(struct notifier_block *nb) 187 { 188 EVENTHANDLER_DEREGISTER(acpi_acad_event, nb->tags[LINUX_ACPI_ACAD]); 189 EVENTHANDLER_DEREGISTER(acpi_video_event, nb->tags[LINUX_ACPI_VIDEO]); 190 191 return (0); 192 } 193 194 uint32_t 195 acpi_target_system_state(void) 196 { 197 return (linux_acpi_target_sleep_state); 198 } 199 200 struct acpi_dev_present_ctx { 201 const char *hid; 202 const char *uid; 203 int64_t hrv; 204 struct acpi_device *dev; 205 }; 206 207 static ACPI_STATUS 208 acpi_dev_present_cb(ACPI_HANDLE handle, UINT32 level, void *context, 209 void **result) 210 { 211 ACPI_DEVICE_INFO *devinfo; 212 struct acpi_device *dev; 213 struct acpi_dev_present_ctx *match = context; 214 bool present = false; 215 UINT32 sta, hrv; 216 int i; 217 218 if (handle == NULL) 219 return (AE_OK); 220 221 if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) && 222 !ACPI_DEVICE_PRESENT(sta)) 223 return (AE_OK); 224 225 if (ACPI_FAILURE(AcpiGetObjectInfo(handle, &devinfo))) 226 return (AE_OK); 227 228 if ((devinfo->Valid & ACPI_VALID_HID) != 0 && 229 strcmp(match->hid, devinfo->HardwareId.String) == 0) { 230 present = true; 231 } else if ((devinfo->Valid & ACPI_VALID_CID) != 0) { 232 for (i = 0; i < devinfo->CompatibleIdList.Count; i++) { 233 if (strcmp(match->hid, 234 devinfo->CompatibleIdList.Ids[i].String) == 0) { 235 present = true; 236 break; 237 } 238 } 239 } 240 if (present && match->uid != NULL && 241 ((devinfo->Valid & ACPI_VALID_UID) == 0 || 242 strcmp(match->uid, devinfo->UniqueId.String) != 0)) 243 present = false; 244 245 AcpiOsFree(devinfo); 246 if (!present) 247 return (AE_OK); 248 249 if (match->hrv != -1) { 250 if (ACPI_FAILURE(acpi_GetInteger(handle, "_HRV", &hrv))) 251 return (AE_OK); 252 if (hrv != match->hrv) 253 return (AE_OK); 254 } 255 256 dev = acpi_get_device(handle); 257 if (dev == NULL) 258 return (AE_OK); 259 match->dev = dev; 260 261 return (AE_ERROR); 262 } 263 264 bool 265 lkpi_acpi_dev_present(const char *hid, const char *uid, int64_t hrv) 266 { 267 struct acpi_dev_present_ctx match; 268 int rv; 269 270 match.hid = hid; 271 match.uid = uid; 272 match.hrv = hrv; 273 274 rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 275 ACPI_UINT32_MAX, acpi_dev_present_cb, NULL, &match, NULL); 276 277 return (rv == AE_ERROR); 278 } 279 280 struct acpi_device * 281 lkpi_acpi_dev_get_first_match_dev(const char *hid, const char *uid, 282 int64_t hrv) 283 { 284 struct acpi_dev_present_ctx match; 285 int rv; 286 287 match.hid = hid; 288 match.uid = uid; 289 match.hrv = hrv; 290 match.dev = NULL; 291 292 rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 293 ACPI_UINT32_MAX, acpi_dev_present_cb, NULL, &match, NULL); 294 295 return (rv == AE_ERROR ? match.dev : NULL); 296 } 297 298 static void 299 linux_register_acpi_event_handlers(void *arg __unused) 300 { 301 /* 302 * XXX johalun: acpi_{sleep,wakeup}_event can't be trusted, use 303 * power_{suspend_early,resume} 'acpiconf -s 3' or 'zzz' will not 304 * generate acpi_sleep_event... Lid open or wake on button generates 305 * acpi_wakeup_event on one of my Dell laptops but not the other 306 * (but it does power on)... is this a general thing? 307 */ 308 resume_tag = EVENTHANDLER_REGISTER(power_resume, 309 linux_handle_power_resume_event, NULL, EVENTHANDLER_PRI_FIRST); 310 suspend_tag = EVENTHANDLER_REGISTER(power_suspend_early, 311 linux_handle_power_suspend_event, NULL, EVENTHANDLER_PRI_FIRST); 312 } 313 314 static void 315 linux_deregister_acpi_event_handlers(void *arg __unused) 316 { 317 EVENTHANDLER_DEREGISTER(power_resume, resume_tag); 318 EVENTHANDLER_DEREGISTER(power_suspend_early, suspend_tag); 319 } 320 321 SYSINIT(linux_acpi_events, SI_SUB_DRIVERS, SI_ORDER_ANY, 322 linux_register_acpi_event_handlers, NULL); 323 SYSUNINIT(linux_acpi_events, SI_SUB_DRIVERS, SI_ORDER_ANY, 324 linux_deregister_acpi_event_handlers, NULL); 325 326 #else /* !DEV_ACPI */ 327 328 ACPI_HANDLE 329 bsd_acpi_get_handle(device_t bsddev) 330 { 331 return (NULL); 332 } 333 334 bool 335 acpi_check_dsm(ACPI_HANDLE handle, const guid_t *uuid, int rev, uint64_t funcs) 336 { 337 return (false); 338 } 339 340 ACPI_OBJECT * 341 acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const guid_t *uuid, int rev, 342 int func, ACPI_OBJECT *argv4, ACPI_OBJECT_TYPE type) 343 { 344 return (NULL); 345 } 346 347 union linuxkpi_acpi_object * 348 acpi_evaluate_dsm(ACPI_HANDLE ObjHandle, const guid_t *guid, 349 UINT64 rev, UINT64 func, union linuxkpi_acpi_object *pkg) 350 { 351 return (NULL); 352 } 353 354 int 355 register_acpi_notifier(struct notifier_block *nb) 356 { 357 return (0); 358 } 359 360 int 361 unregister_acpi_notifier(struct notifier_block *nb) 362 { 363 return (0); 364 } 365 366 uint32_t 367 acpi_target_system_state(void) 368 { 369 return (ACPI_STATE_S0); 370 } 371 372 bool 373 lkpi_acpi_dev_present(const char *hid, const char *uid, int64_t hrv) 374 { 375 return (false); 376 } 377 378 struct acpi_device * 379 lkpi_acpi_dev_get_first_match_dev(const char *hid, const char *uid, 380 int64_t hrv) 381 { 382 return (NULL); 383 } 384 385 #endif /* !DEV_ACPI */ 386