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, enum power_stype stype) 123 { 124 switch (stype) { 125 case POWER_STYPE_SUSPEND_TO_IDLE: 126 /* 127 * XXX: obiwac Not 100% sure this is correct, but 128 * acpi_target_sleep_state does seem to be set to 129 * ACPI_STATE_S3 during s2idle on Linux. 130 */ 131 linux_acpi_target_sleep_state = ACPI_STATE_S3; 132 pm_suspend_target_state = PM_SUSPEND_TO_IDLE; 133 break; 134 case POWER_STYPE_SUSPEND_TO_MEM: 135 linux_acpi_target_sleep_state = ACPI_STATE_S3; 136 pm_suspend_target_state = PM_SUSPEND_MEM; 137 break; 138 default: 139 printf("%s: sleep type %d not yet supported\n", 140 __func__, stype); 141 break; 142 } 143 } 144 145 static void 146 linux_handle_power_resume_event(void *arg __unused, 147 enum power_stype stype __unused) 148 { 149 linux_acpi_target_sleep_state = ACPI_STATE_S0; 150 pm_suspend_target_state = PM_SUSPEND_ON; 151 } 152 153 static void 154 linux_handle_acpi_acad_event(void *arg, int data) 155 { 156 struct notifier_block *nb = arg; 157 /* 158 * Event type information is lost ATM in FreeBSD ACPI event handler. 159 * Fortunately, drm-kmod do not distinct AC event types too, so we can 160 * use any type e.g. ACPI_NOTIFY_BUS_CHECK that suits notifier handler. 161 */ 162 struct acpi_bus_event abe = { 163 .device_class = ACPI_AC_CLASS, 164 .type = ACPI_NOTIFY_BUS_CHECK, 165 .data = data, 166 }; 167 168 nb->notifier_call(nb, 0, &abe); 169 } 170 171 static void 172 linux_handle_acpi_video_event(void *arg, int type) 173 { 174 struct notifier_block *nb = arg; 175 struct acpi_bus_event abe = { 176 .device_class = ACPI_VIDEO_CLASS, 177 .type = type, 178 .data = 0, 179 }; 180 181 nb->notifier_call(nb, 0, &abe); 182 } 183 184 int 185 register_acpi_notifier(struct notifier_block *nb) 186 { 187 nb->tags[LINUX_ACPI_ACAD] = EVENTHANDLER_REGISTER(acpi_acad_event, 188 linux_handle_acpi_acad_event, nb, EVENTHANDLER_PRI_FIRST); 189 nb->tags[LINUX_ACPI_VIDEO] = EVENTHANDLER_REGISTER(acpi_video_event, 190 linux_handle_acpi_video_event, nb, EVENTHANDLER_PRI_FIRST); 191 192 return (0); 193 } 194 195 int 196 unregister_acpi_notifier(struct notifier_block *nb) 197 { 198 EVENTHANDLER_DEREGISTER(acpi_acad_event, nb->tags[LINUX_ACPI_ACAD]); 199 EVENTHANDLER_DEREGISTER(acpi_video_event, nb->tags[LINUX_ACPI_VIDEO]); 200 201 return (0); 202 } 203 204 uint32_t 205 acpi_target_system_state(void) 206 { 207 return (linux_acpi_target_sleep_state); 208 } 209 210 struct acpi_dev_present_ctx { 211 const char *hid; 212 const char *uid; 213 int64_t hrv; 214 struct acpi_device *dev; 215 }; 216 217 static ACPI_STATUS 218 acpi_dev_present_cb(ACPI_HANDLE handle, UINT32 level, void *context, 219 void **result) 220 { 221 ACPI_DEVICE_INFO *devinfo; 222 struct acpi_device *dev; 223 struct acpi_dev_present_ctx *match = context; 224 bool present = false; 225 UINT32 sta, hrv; 226 int i; 227 228 if (handle == NULL) 229 return (AE_OK); 230 231 if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) && 232 !ACPI_DEVICE_PRESENT(sta)) 233 return (AE_OK); 234 235 if (ACPI_FAILURE(AcpiGetObjectInfo(handle, &devinfo))) 236 return (AE_OK); 237 238 if ((devinfo->Valid & ACPI_VALID_HID) != 0 && 239 strcmp(match->hid, devinfo->HardwareId.String) == 0) { 240 present = true; 241 } else if ((devinfo->Valid & ACPI_VALID_CID) != 0) { 242 for (i = 0; i < devinfo->CompatibleIdList.Count; i++) { 243 if (strcmp(match->hid, 244 devinfo->CompatibleIdList.Ids[i].String) == 0) { 245 present = true; 246 break; 247 } 248 } 249 } 250 if (present && match->uid != NULL && 251 ((devinfo->Valid & ACPI_VALID_UID) == 0 || 252 strcmp(match->uid, devinfo->UniqueId.String) != 0)) 253 present = false; 254 255 AcpiOsFree(devinfo); 256 if (!present) 257 return (AE_OK); 258 259 if (match->hrv != -1) { 260 if (ACPI_FAILURE(acpi_GetInteger(handle, "_HRV", &hrv))) 261 return (AE_OK); 262 if (hrv != match->hrv) 263 return (AE_OK); 264 } 265 266 dev = acpi_get_device(handle); 267 if (dev == NULL) 268 return (AE_OK); 269 match->dev = dev; 270 271 return (AE_ERROR); 272 } 273 274 bool 275 lkpi_acpi_dev_present(const char *hid, const char *uid, int64_t hrv) 276 { 277 struct acpi_dev_present_ctx match; 278 int rv; 279 280 match.hid = hid; 281 match.uid = uid; 282 match.hrv = hrv; 283 284 rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 285 ACPI_UINT32_MAX, acpi_dev_present_cb, NULL, &match, NULL); 286 287 return (rv == AE_ERROR); 288 } 289 290 struct acpi_device * 291 lkpi_acpi_dev_get_first_match_dev(const char *hid, const char *uid, 292 int64_t hrv) 293 { 294 struct acpi_dev_present_ctx match; 295 int rv; 296 297 match.hid = hid; 298 match.uid = uid; 299 match.hrv = hrv; 300 match.dev = NULL; 301 302 rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 303 ACPI_UINT32_MAX, acpi_dev_present_cb, NULL, &match, NULL); 304 305 return (rv == AE_ERROR ? match.dev : NULL); 306 } 307 308 static void 309 linux_register_acpi_event_handlers(void *arg __unused) 310 { 311 /* 312 * XXX johalun: acpi_{sleep,wakeup}_event can't be trusted, use 313 * power_{suspend_early,resume} 'acpiconf -s 3' or 'zzz' will not 314 * generate acpi_sleep_event... Lid open or wake on button generates 315 * acpi_wakeup_event on one of my Dell laptops but not the other 316 * (but it does power on)... is this a general thing? 317 */ 318 resume_tag = EVENTHANDLER_REGISTER(power_resume, 319 linux_handle_power_resume_event, NULL, EVENTHANDLER_PRI_FIRST); 320 suspend_tag = EVENTHANDLER_REGISTER(power_suspend_early, 321 linux_handle_power_suspend_event, NULL, EVENTHANDLER_PRI_FIRST); 322 } 323 324 static void 325 linux_deregister_acpi_event_handlers(void *arg __unused) 326 { 327 EVENTHANDLER_DEREGISTER(power_resume, resume_tag); 328 EVENTHANDLER_DEREGISTER(power_suspend_early, suspend_tag); 329 } 330 331 SYSINIT(linux_acpi_events, SI_SUB_DRIVERS, SI_ORDER_ANY, 332 linux_register_acpi_event_handlers, NULL); 333 SYSUNINIT(linux_acpi_events, SI_SUB_DRIVERS, SI_ORDER_ANY, 334 linux_deregister_acpi_event_handlers, NULL); 335 336 #else /* !DEV_ACPI */ 337 338 ACPI_HANDLE 339 bsd_acpi_get_handle(device_t bsddev) 340 { 341 return (NULL); 342 } 343 344 bool 345 acpi_check_dsm(ACPI_HANDLE handle, const guid_t *uuid, int rev, uint64_t funcs) 346 { 347 return (false); 348 } 349 350 ACPI_OBJECT * 351 acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const guid_t *uuid, int rev, 352 int func, ACPI_OBJECT *argv4, ACPI_OBJECT_TYPE type) 353 { 354 return (NULL); 355 } 356 357 union linuxkpi_acpi_object * 358 acpi_evaluate_dsm(ACPI_HANDLE ObjHandle, const guid_t *guid, 359 UINT64 rev, UINT64 func, union linuxkpi_acpi_object *pkg) 360 { 361 return (NULL); 362 } 363 364 int 365 register_acpi_notifier(struct notifier_block *nb) 366 { 367 return (0); 368 } 369 370 int 371 unregister_acpi_notifier(struct notifier_block *nb) 372 { 373 return (0); 374 } 375 376 uint32_t 377 acpi_target_system_state(void) 378 { 379 return (ACPI_STATE_S0); 380 } 381 382 bool 383 lkpi_acpi_dev_present(const char *hid, const char *uid, int64_t hrv) 384 { 385 return (false); 386 } 387 388 struct acpi_device * 389 lkpi_acpi_dev_get_first_match_dev(const char *hid, const char *uid, 390 int64_t hrv) 391 { 392 return (NULL); 393 } 394 395 #endif /* !DEV_ACPI */ 396