diff --git a/repos/libports/src/app/acpica/fujitsu.h b/repos/libports/src/app/acpica/fujitsu.h new file mode 100644 index 0000000000..529754236e --- /dev/null +++ b/repos/libports/src/app/acpica/fujitsu.h @@ -0,0 +1,195 @@ +/* + * \brief Support some Fujitsu ACPI devices + * \author Alexander Boettcher + * \date 2021-01-06 + */ + +/* + * Copyright (C) 2020-2021 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +class Fuj02e3 : public Acpica::Reporter, Acpica::Callback +{ + private: + + enum { + HID_FUJITSU_NOTIFY = 0x80, + }; + + enum Softkeys { + HID_FUJITSU_FLAG_RFKILL = 1u << 5, + HID_FUJITSU_FLAG_TOUCHPAD_TOGGLE = 1u << 26, + HID_FUJITSU_FLAG_MICROFON_MUTE = 1u << 29, + + HID_FUJITSU_FLAG_SOFTKEYS = HID_FUJITSU_FLAG_RFKILL | + HID_FUJITSU_FLAG_TOUCHPAD_TOGGLE | + HID_FUJITSU_FLAG_MICROFON_MUTE + }; + + enum Operation { + HID_FUJITSU_FUNC_FLAGS = 1u << 12, + HID_FUJITSU_FUNC_BUTTON = HID_FUJITSU_FUNC_FLAGS | 2, + }; + + Acpica::Reportstate *_report; + Genode::uint64_t _features { 0 }; + + struct Data + { + Genode::uint64_t count; + Genode::uint64_t data; + bool triggered; + } _data[3] { }; + + template + ACPI_STATUS _call_acpi_function(ACPI_HANDLE const hid, + enum Operation const function, + RESULT &result, unsigned const op, + unsigned const feature, + unsigned const state) + { + ACPI_OBJECT_LIST para_in; + ACPI_OBJECT values[5]; + + values[0].Type = ACPI_TYPE_INTEGER; + values[0].Integer.Value = function; + values[1].Type = ACPI_TYPE_INTEGER; + values[1].Integer.Value = op; + values[2].Type = ACPI_TYPE_INTEGER; + values[2].Integer.Value = feature; + values[3].Type = ACPI_TYPE_INTEGER; + values[3].Integer.Value = state; + values[4].Type = 0; + values[4].Integer.Value = 0; + + para_in.Count = 4; + para_in.Pointer = values; + + return AcpiEvaluateObjectTyped(hid, ACPI_STRING("FUNC"), + ¶_in, &result, + ACPI_TYPE_INTEGER); + } + + template + ACPI_STATUS device_features(ACPI_HANDLE const hid, RESULT &result) + { + return _call_acpi_function(hid, HID_FUJITSU_FUNC_FLAGS, result, + 0, 0, 0); + } + + template + ACPI_STATUS soft_keys(ACPI_HANDLE const hid, RESULT &result) + { + return _call_acpi_function(hid, HID_FUJITSU_FUNC_FLAGS, result, + 1, 0, 0); + } + + template + ACPI_STATUS read_button(ACPI_HANDLE const hid, RESULT &result) + { + return _call_acpi_function(hid, HID_FUJITSU_FUNC_BUTTON, result, + 1, 0, 0); + } + + public: + + Fuj02e3(void *report) + : _report(reinterpret_cast(report)) + { + if (_report) + _report->add_notify(this); + } + + void handle(ACPI_HANDLE const hid, UINT32 const value) + { + if (value != HID_FUJITSU_NOTIFY) + return; + + Acpica::Buffer irb; + ACPI_STATUS res = read_button(hid, irb); + if (ACPI_SUCCESS(res) && irb.object.Integer.Value != 0) + Genode::error("not implemented - irb value=", + Genode::Hex(irb.object.Integer.Value)); + + if (_features & HID_FUJITSU_FLAG_SOFTKEYS) { + Acpica::Buffer feature; + ACPI_STATUS res = soft_keys(hid, feature); + + if (ACPI_SUCCESS(res)) + { + UINT64 const value = feature.object.Integer.Value; + + if (value & HID_FUJITSU_FLAG_RFKILL) { + _data[0].data = HID_FUJITSU_FLAG_RFKILL; + _data[0].triggered = true; + _data[0].count ++; + } + + if (value & HID_FUJITSU_FLAG_TOUCHPAD_TOGGLE) { + _data[1].data = HID_FUJITSU_FLAG_TOUCHPAD_TOGGLE; + _data[1].triggered = true; + _data[1].count ++; + } + + if (value & HID_FUJITSU_FLAG_MICROFON_MUTE) { + _data[2].data = HID_FUJITSU_FLAG_MICROFON_MUTE; + _data[2].triggered = true; + _data[2].count ++; + } + + if (_report && (value & HID_FUJITSU_FLAG_SOFTKEYS)) + _report->hid_event(); + } + } + } + + static ACPI_STATUS detect(ACPI_HANDLE hid, UINT32, void *m, void **) + { + Acpica::Main *main = reinterpret_cast(m); + Fuj02e3 *obj = new (main->heap) Fuj02e3(main->report); + + ACPI_STATUS res = AcpiInstallNotifyHandler(hid, ACPI_DEVICE_NOTIFY, + handler, obj); + if (ACPI_FAILURE(res)) { + Genode::log("failed - ", __func__, " " + "res=", Genode::Hex(res), " Fujitsu adapter"); + delete obj; + return AE_OK; + } + + Genode::log("detected - Fujitsu HID"); + + Acpica::Buffer features; + res = obj->device_features(hid, features); + if (ACPI_FAILURE(res)) { + Genode::error("failed - '", __func__, "' " + "res=", Genode::Hex(res), " features"); + } + + obj->_features = features.object.Integer.Value; + + return AE_OK; + } + + void generate(Genode::Xml_generator &xml) override + { + xml.node("hid", [&] { + xml.attribute("device", "Fuj02e3"); + + for (unsigned i = 0; i < sizeof(_data) / sizeof(_data[0]); i++) { + + xml.node("data", [&] { + xml.attribute("value", Genode::String<12>(Genode::Hex(_data[i].data))); + xml.attribute("count", _data[i].count); + if (_data[i].triggered) { + xml.append("triggered"); + _data[i].triggered = false; + } + }); + } + }); + } +}; diff --git a/repos/libports/src/app/acpica/os.cc b/repos/libports/src/app/acpica/os.cc index e58690173d..aade55f8a8 100644 --- a/repos/libports/src/app/acpica/os.cc +++ b/repos/libports/src/app/acpica/os.cc @@ -210,6 +210,7 @@ struct Acpica::Main #include "sb.h" #include "ec.h" #include "bridge.h" +#include "fujitsu.h" ACPI_STATUS init_pic_mode() { @@ -250,7 +251,12 @@ void Acpica::Main::init_acpica(Wait_acpi_ready wait_acpi_ready, Acpica::init(env, heap, wait_acpi_ready, act_as_acpi_drv); /* enable debugging: */ - /* AcpiDbgLevel |= ACPI_LV_IO | ACPI_LV_INTERRUPTS | ACPI_LV_INIT_NAMES; */ + if (false) { + AcpiDbgLevel |= ACPI_LV_IO | ACPI_LV_INTERRUPTS | ACPI_LV_INIT_NAMES; + AcpiDbgLayer |= ACPI_TABLES; + Genode::log("debugging level=", Genode::Hex(AcpiDbgLevel), + " layers=", Genode::Hex(AcpiDbgLayer)); + } ACPI_STATUS status = AcpiInitializeSubsystem(); if (status != AE_OK) { @@ -351,6 +357,13 @@ void Acpica::Main::init_acpica(Wait_acpi_ready wait_acpi_ready, return; } + /* Fujitsu HID device */ + status = AcpiGetDevices(ACPI_STRING("FUJ02E3"), Fuj02e3::detect, this, nullptr); + if (status != AE_OK) { + Genode::error("AcpiGetDevices (FUJ02E3) failed, status=", status); + return; + } + if (act_as_acpi_drv.enabled) { /* lookup PCI root bridge */ void * pci_bridge = (void *)PCI_ROOT_HID_STRING; diff --git a/repos/libports/src/app/acpica/reporter.h b/repos/libports/src/app/acpica/reporter.h index 3506ef4136..fc89585d53 100644 --- a/repos/libports/src/app/acpica/reporter.h +++ b/repos/libports/src/app/acpica/reporter.h @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2016-2017 Genode Labs GmbH + * Copyright (C) 2016-2021 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. @@ -18,6 +18,22 @@ class Ec; class Fixed; class Lid; +namespace Acpica { + class Reportstate; + class Reporter; +}; + +class Acpica::Reporter : public Genode::List::Element +{ + public: + + Reporter() { } + + virtual void generate(Genode::Xml_generator &) = 0; + virtual ~Reporter() { } +}; + + class Acpica::Reportstate { private: @@ -27,18 +43,21 @@ class Acpica::Reportstate { Genode::Reporter _reporter_sb; Genode::Reporter _reporter_ec; Genode::Reporter _reporter_fix; + Genode::Reporter _reporter_hid; bool _changed_lid = false; bool _changed_ac = false; bool _changed_sb = false; bool _changed_ec = false; bool _changed_fixed = false; + bool _changed_hid = false; - Genode::List > _list_sb; - Genode::List > _list_ec; - Genode::List > _list_ac; - Callback * _fixed; - Callback * _lid; + Genode::List > _list_sb; + Genode::List > _list_ec; + Genode::List > _list_ac; + Genode::List _list_hid; + Callback * _fixed; + Callback * _lid; public: @@ -48,7 +67,8 @@ class Acpica::Reportstate { _reporter_ac (env, "acpi_ac"), _reporter_sb (env, "acpi_battery"), _reporter_ec (env, "acpi_ec"), - _reporter_fix(env, "acpi_fixed") + _reporter_fix(env, "acpi_fixed"), + _reporter_hid(env, "acpi_hid") { } void add_notify(Acpica::Callback * s) { _list_sb.insert(s); } @@ -56,6 +76,7 @@ class Acpica::Reportstate { void add_notify(Acpica::Callback * l) { _lid = l; } void add_notify(Acpica::Callback * e) { _list_ec.insert(e); } void add_notify(Acpica::Callback * a) { _list_ac.insert(a); } + void add_notify(Acpica::Reporter * r) { _list_hid.insert(r); } void enable() { _reporter_ac.enabled(true); @@ -63,6 +84,7 @@ class Acpica::Reportstate { _reporter_sb.enabled(true); _reporter_lid.enabled(true); _reporter_fix.enabled(true); + _reporter_hid.enabled(true); } void battery_event() { _changed_sb = true; } @@ -70,6 +92,7 @@ class Acpica::Reportstate { void fixed_event() { _changed_fixed = true; } void lid_event() { _changed_lid = true; } void ac_event() { _changed_ac = true; battery_event(); } + void hid_event() { _changed_hid = true; } bool generate_report(bool force = false) { @@ -116,6 +139,16 @@ class Acpica::Reportstate { }); } + if (_changed_hid || force) { + _changed_hid = false; + + if (_list_hid.first()) + Genode::Reporter::Xml_generator xml(_reporter_hid, [&] () { + for (auto * hid = _list_hid.first(); hid; hid = hid->next()) + hid->generate(xml); + }); + } + return changed; } };