Path: blob/main/sys/compat/linuxkpi/common/src/linux_acpi.c
39586 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2018 Johannes Lundberg <[email protected]>4* Copyright (c) 2020 Vladimir Kondratyev <[email protected]>5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions are8* met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in13* the documentation and/or other materials provided with the14* distribution.15*16* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*/2829#include "opt_acpi.h"3031#include <sys/types.h>32#include <sys/bus.h>33#include <sys/eventhandler.h>34#include <sys/kernel.h>3536#include <contrib/dev/acpica/include/acpi.h>37#include <dev/acpica/acpivar.h>3839#include <linux/notifier.h>40#include <linux/suspend.h>41#include <linux/uuid.h>4243#include <acpi/acpi_bus.h>44#include <acpi/video.h>4546#define ACPI_AC_CLASS "ac_adapter"4748ACPI_MODULE_NAME("linux_acpi")4950enum {51LINUX_ACPI_ACAD,52LINUX_ACPI_VIDEO,53LINUX_ACPI_TAGS /* must be last */54};55_Static_assert(LINUX_ACPI_TAGS <= LINUX_NOTIFY_TAGS,56"Not enough space for tags in notifier_block structure");5758#ifdef DEV_ACPI5960suspend_state_t pm_suspend_target_state = PM_SUSPEND_ON;6162static uint32_t linux_acpi_target_sleep_state = ACPI_STATE_S0;6364static eventhandler_tag resume_tag;65static eventhandler_tag suspend_tag;6667ACPI_HANDLE68bsd_acpi_get_handle(device_t bsddev)69{70return (acpi_get_handle(bsddev));71}7273bool74acpi_check_dsm(ACPI_HANDLE handle, const guid_t *uuid, int rev, uint64_t funcs)75{76UINT64 ret;7778if (funcs == 0)79return (false);8081/*82* From ACPI 6.3 spec 9.1.1:83* Bit 0 indicates whether there is support for any functions other84* than function 0 for the specified UUID and Revision ID. If set to85* zero, no functions are supported (other than function zero) for the86* specified UUID and Revision ID.87*/88funcs |= 1 << 0;8990ret = acpi_DSMQuery(handle, (const uint8_t *)uuid, rev);91return ((ret & funcs) == funcs);92}9394ACPI_OBJECT *95acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const guid_t *uuid, int rev,96int func, ACPI_OBJECT *argv4, ACPI_OBJECT_TYPE type)97{98ACPI_BUFFER buf;99ACPI_STATUS status;100101status = acpi_EvaluateDSMTyped(handle, (const uint8_t *)uuid, rev, func,102argv4, &buf, type);103return (ACPI_SUCCESS(status) ? (ACPI_OBJECT *)buf.Pointer : NULL);104}105106union linuxkpi_acpi_object *107acpi_evaluate_dsm(ACPI_HANDLE ObjHandle, const guid_t *guid,108UINT64 rev, UINT64 func, union linuxkpi_acpi_object *pkg)109{110ACPI_BUFFER buf;111ACPI_STATUS status;112113status = acpi_EvaluateDSM(ObjHandle, (const uint8_t *)guid, rev, func,114(ACPI_OBJECT *)pkg, &buf);115return (ACPI_SUCCESS(status) ?116(union linuxkpi_acpi_object *)buf.Pointer : NULL);117}118119static void120linux_handle_power_suspend_event(void *arg __unused)121{122/*123* Only support S3 for now.124* acpi_sleep_event isn't always called so we use power_suspend_early125* instead which means we don't know what state we're switching to.126* TODO: Make acpi_sleep_event consistent127*/128linux_acpi_target_sleep_state = ACPI_STATE_S3;129pm_suspend_target_state = PM_SUSPEND_MEM;130}131132static void133linux_handle_power_resume_event(void *arg __unused)134{135linux_acpi_target_sleep_state = ACPI_STATE_S0;136pm_suspend_target_state = PM_SUSPEND_ON;137}138139static void140linux_handle_acpi_acad_event(void *arg, int data)141{142struct notifier_block *nb = arg;143/*144* Event type information is lost ATM in FreeBSD ACPI event handler.145* Fortunately, drm-kmod do not distinct AC event types too, so we can146* use any type e.g. ACPI_NOTIFY_BUS_CHECK that suits notifier handler.147*/148struct acpi_bus_event abe = {149.device_class = ACPI_AC_CLASS,150.type = ACPI_NOTIFY_BUS_CHECK,151.data = data,152};153154nb->notifier_call(nb, 0, &abe);155}156157static void158linux_handle_acpi_video_event(void *arg, int type)159{160struct notifier_block *nb = arg;161struct acpi_bus_event abe = {162.device_class = ACPI_VIDEO_CLASS,163.type = type,164.data = 0,165};166167nb->notifier_call(nb, 0, &abe);168}169170int171register_acpi_notifier(struct notifier_block *nb)172{173nb->tags[LINUX_ACPI_ACAD] = EVENTHANDLER_REGISTER(acpi_acad_event,174linux_handle_acpi_acad_event, nb, EVENTHANDLER_PRI_FIRST);175nb->tags[LINUX_ACPI_VIDEO] = EVENTHANDLER_REGISTER(acpi_video_event,176linux_handle_acpi_video_event, nb, EVENTHANDLER_PRI_FIRST);177178return (0);179}180181int182unregister_acpi_notifier(struct notifier_block *nb)183{184EVENTHANDLER_DEREGISTER(acpi_acad_event, nb->tags[LINUX_ACPI_ACAD]);185EVENTHANDLER_DEREGISTER(acpi_video_event, nb->tags[LINUX_ACPI_VIDEO]);186187return (0);188}189190uint32_t191acpi_target_system_state(void)192{193return (linux_acpi_target_sleep_state);194}195196struct acpi_dev_present_ctx {197const char *hid;198const char *uid;199int64_t hrv;200struct acpi_device *dev;201};202203static ACPI_STATUS204acpi_dev_present_cb(ACPI_HANDLE handle, UINT32 level, void *context,205void **result)206{207ACPI_DEVICE_INFO *devinfo;208struct acpi_device *dev;209struct acpi_dev_present_ctx *match = context;210bool present = false;211UINT32 sta, hrv;212int i;213214if (handle == NULL)215return (AE_OK);216217if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) &&218!ACPI_DEVICE_PRESENT(sta))219return (AE_OK);220221if (ACPI_FAILURE(AcpiGetObjectInfo(handle, &devinfo)))222return (AE_OK);223224if ((devinfo->Valid & ACPI_VALID_HID) != 0 &&225strcmp(match->hid, devinfo->HardwareId.String) == 0) {226present = true;227} else if ((devinfo->Valid & ACPI_VALID_CID) != 0) {228for (i = 0; i < devinfo->CompatibleIdList.Count; i++) {229if (strcmp(match->hid,230devinfo->CompatibleIdList.Ids[i].String) == 0) {231present = true;232break;233}234}235}236if (present && match->uid != NULL &&237((devinfo->Valid & ACPI_VALID_UID) == 0 ||238strcmp(match->uid, devinfo->UniqueId.String) != 0))239present = false;240241AcpiOsFree(devinfo);242if (!present)243return (AE_OK);244245if (match->hrv != -1) {246if (ACPI_FAILURE(acpi_GetInteger(handle, "_HRV", &hrv)))247return (AE_OK);248if (hrv != match->hrv)249return (AE_OK);250}251252dev = acpi_get_device(handle);253if (dev == NULL)254return (AE_OK);255match->dev = dev;256257return (AE_ERROR);258}259260bool261lkpi_acpi_dev_present(const char *hid, const char *uid, int64_t hrv)262{263struct acpi_dev_present_ctx match;264int rv;265266match.hid = hid;267match.uid = uid;268match.hrv = hrv;269270rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,271ACPI_UINT32_MAX, acpi_dev_present_cb, NULL, &match, NULL);272273return (rv == AE_ERROR);274}275276struct acpi_device *277lkpi_acpi_dev_get_first_match_dev(const char *hid, const char *uid,278int64_t hrv)279{280struct acpi_dev_present_ctx match;281int rv;282283match.hid = hid;284match.uid = uid;285match.hrv = hrv;286match.dev = NULL;287288rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,289ACPI_UINT32_MAX, acpi_dev_present_cb, NULL, &match, NULL);290291return (rv == AE_ERROR ? match.dev : NULL);292}293294static void295linux_register_acpi_event_handlers(void *arg __unused)296{297/*298* XXX johalun: acpi_{sleep,wakeup}_event can't be trusted, use299* power_{suspend_early,resume} 'acpiconf -s 3' or 'zzz' will not300* generate acpi_sleep_event... Lid open or wake on button generates301* acpi_wakeup_event on one of my Dell laptops but not the other302* (but it does power on)... is this a general thing?303*/304resume_tag = EVENTHANDLER_REGISTER(power_resume,305linux_handle_power_resume_event, NULL, EVENTHANDLER_PRI_FIRST);306suspend_tag = EVENTHANDLER_REGISTER(power_suspend_early,307linux_handle_power_suspend_event, NULL, EVENTHANDLER_PRI_FIRST);308}309310static void311linux_deregister_acpi_event_handlers(void *arg __unused)312{313EVENTHANDLER_DEREGISTER(power_resume, resume_tag);314EVENTHANDLER_DEREGISTER(power_suspend_early, suspend_tag);315}316317SYSINIT(linux_acpi_events, SI_SUB_DRIVERS, SI_ORDER_ANY,318linux_register_acpi_event_handlers, NULL);319SYSUNINIT(linux_acpi_events, SI_SUB_DRIVERS, SI_ORDER_ANY,320linux_deregister_acpi_event_handlers, NULL);321322#else /* !DEV_ACPI */323324ACPI_HANDLE325bsd_acpi_get_handle(device_t bsddev)326{327return (NULL);328}329330bool331acpi_check_dsm(ACPI_HANDLE handle, const guid_t *uuid, int rev, uint64_t funcs)332{333return (false);334}335336ACPI_OBJECT *337acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const guid_t *uuid, int rev,338int func, ACPI_OBJECT *argv4, ACPI_OBJECT_TYPE type)339{340return (NULL);341}342343union linuxkpi_acpi_object *344acpi_evaluate_dsm(ACPI_HANDLE ObjHandle, const guid_t *guid,345UINT64 rev, UINT64 func, union linuxkpi_acpi_object *pkg)346{347return (NULL);348}349350int351register_acpi_notifier(struct notifier_block *nb)352{353return (0);354}355356int357unregister_acpi_notifier(struct notifier_block *nb)358{359return (0);360}361362uint32_t363acpi_target_system_state(void)364{365return (ACPI_STATE_S0);366}367368bool369lkpi_acpi_dev_present(const char *hid, const char *uid, int64_t hrv)370{371return (false);372}373374struct acpi_device *375lkpi_acpi_dev_get_first_match_dev(const char *hid, const char *uid,376int64_t hrv)377{378return (NULL);379}380381#endif /* !DEV_ACPI */382383384