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