Path: blob/main/sys/compat/linuxkpi/common/src/linux_acpi.c
102423 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>35#include <sys/power.h>3637#include <contrib/dev/acpica/include/acpi.h>38#include <dev/acpica/acpivar.h>3940#include <linux/notifier.h>41#include <linux/suspend.h>42#include <linux/uuid.h>4344#include <acpi/acpi_bus.h>45#include <acpi/video.h>4647#define ACPI_AC_CLASS "ac_adapter"4849ACPI_MODULE_NAME("linux_acpi")5051enum {52LINUX_ACPI_ACAD,53LINUX_ACPI_VIDEO,54LINUX_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");5859#ifdef DEV_ACPI6061suspend_state_t pm_suspend_target_state = PM_SUSPEND_ON;6263static uint32_t linux_acpi_target_sleep_state = ACPI_STATE_S0;6465static eventhandler_tag resume_tag;66static eventhandler_tag suspend_tag;6768ACPI_HANDLE69bsd_acpi_get_handle(device_t bsddev)70{71return (acpi_get_handle(bsddev));72}7374bool75acpi_check_dsm(ACPI_HANDLE handle, const guid_t *uuid, int rev, uint64_t funcs)76{77UINT64 ret;7879if (funcs == 0)80return (false);8182/*83* From ACPI 6.3 spec 9.1.1:84* Bit 0 indicates whether there is support for any functions other85* than function 0 for the specified UUID and Revision ID. If set to86* zero, no functions are supported (other than function zero) for the87* specified UUID and Revision ID.88*/89funcs |= 1 << 0;9091ret = acpi_DSMQuery(handle, (const uint8_t *)uuid, rev);92return ((ret & funcs) == funcs);93}9495ACPI_OBJECT *96acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const guid_t *uuid, int rev,97int func, ACPI_OBJECT *argv4, ACPI_OBJECT_TYPE type)98{99ACPI_BUFFER buf;100ACPI_STATUS status;101102status = acpi_EvaluateDSMTyped(handle, (const uint8_t *)uuid, rev, func,103argv4, &buf, type);104return (ACPI_SUCCESS(status) ? (ACPI_OBJECT *)buf.Pointer : NULL);105}106107union linuxkpi_acpi_object *108acpi_evaluate_dsm(ACPI_HANDLE ObjHandle, const guid_t *guid,109UINT64 rev, UINT64 func, union linuxkpi_acpi_object *pkg)110{111ACPI_BUFFER buf;112ACPI_STATUS status;113114status = acpi_EvaluateDSM(ObjHandle, (const uint8_t *)guid, rev, func,115(ACPI_OBJECT *)pkg, &buf);116return (ACPI_SUCCESS(status) ?117(union linuxkpi_acpi_object *)buf.Pointer : NULL);118}119120static void121linux_handle_power_suspend_event(void *arg __unused, enum power_stype stype)122{123switch (stype) {124case POWER_STYPE_SUSPEND_TO_IDLE:125/*126* XXX: obiwac Not 100% sure this is correct, but127* acpi_target_sleep_state does seem to be set to128* ACPI_STATE_S3 during s2idle on Linux.129*/130linux_acpi_target_sleep_state = ACPI_STATE_S3;131pm_suspend_target_state = PM_SUSPEND_TO_IDLE;132break;133case POWER_STYPE_SUSPEND_TO_MEM:134linux_acpi_target_sleep_state = ACPI_STATE_S3;135pm_suspend_target_state = PM_SUSPEND_MEM;136break;137default:138printf("%s: sleep type %d not yet supported\n",139__func__, stype);140break;141}142}143144static void145linux_handle_power_resume_event(void *arg __unused,146enum power_stype stype __unused)147{148linux_acpi_target_sleep_state = ACPI_STATE_S0;149pm_suspend_target_state = PM_SUSPEND_ON;150}151152static void153linux_handle_acpi_acad_event(void *arg, int data)154{155struct notifier_block *nb = arg;156/*157* Event type information is lost ATM in FreeBSD ACPI event handler.158* Fortunately, drm-kmod do not distinct AC event types too, so we can159* use any type e.g. ACPI_NOTIFY_BUS_CHECK that suits notifier handler.160*/161struct acpi_bus_event abe = {162.device_class = ACPI_AC_CLASS,163.type = ACPI_NOTIFY_BUS_CHECK,164.data = data,165};166167nb->notifier_call(nb, 0, &abe);168}169170static void171linux_handle_acpi_video_event(void *arg, int type)172{173struct notifier_block *nb = arg;174struct acpi_bus_event abe = {175.device_class = ACPI_VIDEO_CLASS,176.type = type,177.data = 0,178};179180nb->notifier_call(nb, 0, &abe);181}182183int184register_acpi_notifier(struct notifier_block *nb)185{186nb->tags[LINUX_ACPI_ACAD] = EVENTHANDLER_REGISTER(acpi_acad_event,187linux_handle_acpi_acad_event, nb, EVENTHANDLER_PRI_FIRST);188nb->tags[LINUX_ACPI_VIDEO] = EVENTHANDLER_REGISTER(acpi_video_event,189linux_handle_acpi_video_event, nb, EVENTHANDLER_PRI_FIRST);190191return (0);192}193194int195unregister_acpi_notifier(struct notifier_block *nb)196{197EVENTHANDLER_DEREGISTER(acpi_acad_event, nb->tags[LINUX_ACPI_ACAD]);198EVENTHANDLER_DEREGISTER(acpi_video_event, nb->tags[LINUX_ACPI_VIDEO]);199200return (0);201}202203uint32_t204acpi_target_system_state(void)205{206return (linux_acpi_target_sleep_state);207}208209struct acpi_dev_present_ctx {210const char *hid;211const char *uid;212int64_t hrv;213struct acpi_device *dev;214};215216static ACPI_STATUS217acpi_dev_present_cb(ACPI_HANDLE handle, UINT32 level, void *context,218void **result)219{220ACPI_DEVICE_INFO *devinfo;221struct acpi_device *dev;222struct acpi_dev_present_ctx *match = context;223bool present = false;224UINT32 sta, hrv;225int i;226227if (handle == NULL)228return (AE_OK);229230if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) &&231!ACPI_DEVICE_PRESENT(sta))232return (AE_OK);233234if (ACPI_FAILURE(AcpiGetObjectInfo(handle, &devinfo)))235return (AE_OK);236237if ((devinfo->Valid & ACPI_VALID_HID) != 0 &&238strcmp(match->hid, devinfo->HardwareId.String) == 0) {239present = true;240} else if ((devinfo->Valid & ACPI_VALID_CID) != 0) {241for (i = 0; i < devinfo->CompatibleIdList.Count; i++) {242if (strcmp(match->hid,243devinfo->CompatibleIdList.Ids[i].String) == 0) {244present = true;245break;246}247}248}249if (present && match->uid != NULL &&250((devinfo->Valid & ACPI_VALID_UID) == 0 ||251strcmp(match->uid, devinfo->UniqueId.String) != 0))252present = false;253254AcpiOsFree(devinfo);255if (!present)256return (AE_OK);257258if (match->hrv != -1) {259if (ACPI_FAILURE(acpi_GetInteger(handle, "_HRV", &hrv)))260return (AE_OK);261if (hrv != match->hrv)262return (AE_OK);263}264265dev = acpi_get_device(handle);266if (dev == NULL)267return (AE_OK);268match->dev = dev;269270return (AE_ERROR);271}272273bool274lkpi_acpi_dev_present(const char *hid, const char *uid, int64_t hrv)275{276struct acpi_dev_present_ctx match;277int rv;278279match.hid = hid;280match.uid = uid;281match.hrv = hrv;282283rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,284ACPI_UINT32_MAX, acpi_dev_present_cb, NULL, &match, NULL);285286return (rv == AE_ERROR);287}288289struct acpi_device *290lkpi_acpi_dev_get_first_match_dev(const char *hid, const char *uid,291int64_t hrv)292{293struct acpi_dev_present_ctx match;294int rv;295296match.hid = hid;297match.uid = uid;298match.hrv = hrv;299match.dev = NULL;300301rv = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,302ACPI_UINT32_MAX, acpi_dev_present_cb, NULL, &match, NULL);303304return (rv == AE_ERROR ? match.dev : NULL);305}306307static void308linux_register_acpi_event_handlers(void *arg __unused)309{310/*311* XXX johalun: acpi_{sleep,wakeup}_event can't be trusted, use312* power_{suspend_early,resume} 'acpiconf -s 3' or 'zzz' will not313* generate acpi_sleep_event... Lid open or wake on button generates314* acpi_wakeup_event on one of my Dell laptops but not the other315* (but it does power on)... is this a general thing?316*/317resume_tag = EVENTHANDLER_REGISTER(power_resume,318linux_handle_power_resume_event, NULL, EVENTHANDLER_PRI_FIRST);319suspend_tag = EVENTHANDLER_REGISTER(power_suspend_early,320linux_handle_power_suspend_event, NULL, EVENTHANDLER_PRI_FIRST);321}322323static void324linux_deregister_acpi_event_handlers(void *arg __unused)325{326EVENTHANDLER_DEREGISTER(power_resume, resume_tag);327EVENTHANDLER_DEREGISTER(power_suspend_early, suspend_tag);328}329330SYSINIT(linux_acpi_events, SI_SUB_DRIVERS, SI_ORDER_ANY,331linux_register_acpi_event_handlers, NULL);332SYSUNINIT(linux_acpi_events, SI_SUB_DRIVERS, SI_ORDER_ANY,333linux_deregister_acpi_event_handlers, NULL);334335#else /* !DEV_ACPI */336337ACPI_HANDLE338bsd_acpi_get_handle(device_t bsddev)339{340return (NULL);341}342343bool344acpi_check_dsm(ACPI_HANDLE handle, const guid_t *uuid, int rev, uint64_t funcs)345{346return (false);347}348349ACPI_OBJECT *350acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const guid_t *uuid, int rev,351int func, ACPI_OBJECT *argv4, ACPI_OBJECT_TYPE type)352{353return (NULL);354}355356union linuxkpi_acpi_object *357acpi_evaluate_dsm(ACPI_HANDLE ObjHandle, const guid_t *guid,358UINT64 rev, UINT64 func, union linuxkpi_acpi_object *pkg)359{360return (NULL);361}362363int364register_acpi_notifier(struct notifier_block *nb)365{366return (0);367}368369int370unregister_acpi_notifier(struct notifier_block *nb)371{372return (0);373}374375uint32_t376acpi_target_system_state(void)377{378return (ACPI_STATE_S0);379}380381bool382lkpi_acpi_dev_present(const char *hid, const char *uid, int64_t hrv)383{384return (false);385}386387struct acpi_device *388lkpi_acpi_dev_get_first_match_dev(const char *hid, const char *uid,389int64_t hrv)390{391return (NULL);392}393394#endif /* !DEV_ACPI */395396397