aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/acpi/uacpi/notify.c
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2025-05-17 21:56:07 -0400
committerIan Moffett <ian@osmora.org>2025-05-17 21:58:44 -0400
commit08eeb79db14145d83578025e1f0e7f7af460ee25 (patch)
treeb6af572a4b8dceb4f044f1e0bf5697f5c18dc0fd /sys/dev/acpi/uacpi/notify.c
parent9c64c3e69fa60b3657d33e829a411cb37064a169 (diff)
kernel: acpi: Add uACPI portexpt
See https://github.com/uACPI/uACPI/ Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'sys/dev/acpi/uacpi/notify.c')
-rw-r--r--sys/dev/acpi/uacpi/notify.c255
1 files changed, 255 insertions, 0 deletions
diff --git a/sys/dev/acpi/uacpi/notify.c b/sys/dev/acpi/uacpi/notify.c
new file mode 100644
index 0000000..b413df9
--- /dev/null
+++ b/sys/dev/acpi/uacpi/notify.c
@@ -0,0 +1,255 @@
+#include <uacpi/internal/notify.h>
+#include <uacpi/internal/shareable.h>
+#include <uacpi/internal/namespace.h>
+#include <uacpi/internal/log.h>
+#include <uacpi/internal/mutex.h>
+#include <uacpi/internal/utilities.h>
+#include <uacpi/internal/stdlib.h>
+#include <uacpi/kernel_api.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+static uacpi_handle notify_mutex;
+
+uacpi_status uacpi_initialize_notify(void)
+{
+ notify_mutex = uacpi_kernel_create_mutex();
+ if (uacpi_unlikely(notify_mutex == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ return UACPI_STATUS_OK;
+}
+
+void uacpi_deinitialize_notify(void)
+{
+ if (notify_mutex != UACPI_NULL)
+ uacpi_kernel_free_mutex(notify_mutex);
+
+ notify_mutex = UACPI_NULL;
+}
+
+struct notification_ctx {
+ uacpi_namespace_node *node;
+ uacpi_u64 value;
+ uacpi_object *node_object;
+};
+
+static void free_notification_ctx(struct notification_ctx *ctx)
+{
+ uacpi_namespace_node_release_object(ctx->node_object);
+ uacpi_namespace_node_unref(ctx->node);
+ uacpi_free(ctx, sizeof(*ctx));
+}
+
+static void do_notify(uacpi_handle opaque)
+{
+ struct notification_ctx *ctx = opaque;
+ uacpi_device_notify_handler *handler;
+ uacpi_bool did_notify_root = UACPI_FALSE;
+
+ handler = ctx->node_object->handlers->notify_head;
+
+ for (;;) {
+ if (handler == UACPI_NULL) {
+ if (did_notify_root) {
+ free_notification_ctx(ctx);
+ return;
+ }
+
+ handler = g_uacpi_rt_ctx.root_object->handlers->notify_head;
+ did_notify_root = UACPI_TRUE;
+ continue;
+ }
+
+ handler->callback(handler->user_context, ctx->node, ctx->value);
+ handler = handler->next;
+ }
+}
+
+uacpi_status uacpi_notify_all(uacpi_namespace_node *node, uacpi_u64 value)
+{
+ uacpi_status ret;
+ struct notification_ctx *ctx;
+ uacpi_object *node_object;
+
+ node_object = uacpi_namespace_node_get_object_typed(
+ node, UACPI_OBJECT_DEVICE_BIT | UACPI_OBJECT_THERMAL_ZONE_BIT |
+ UACPI_OBJECT_PROCESSOR_BIT
+ );
+ if (uacpi_unlikely(node_object == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ ret = uacpi_acquire_native_mutex(notify_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (node_object->handlers->notify_head == UACPI_NULL &&
+ g_uacpi_rt_ctx.root_object->handlers->notify_head == UACPI_NULL) {
+ ret = UACPI_STATUS_NO_HANDLER;
+ goto out;
+ }
+
+ ctx = uacpi_kernel_alloc(sizeof(*ctx));
+ if (uacpi_unlikely(ctx == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ ctx->node = node;
+ // In case this node goes out of scope
+ uacpi_shareable_ref(node);
+
+ ctx->value = value;
+ ctx->node_object = uacpi_namespace_node_get_object(node);
+ uacpi_object_ref(ctx->node_object);
+
+ ret = uacpi_kernel_schedule_work(UACPI_WORK_NOTIFICATION, do_notify, ctx);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_warn("unable to schedule notification work: %s\n",
+ uacpi_status_to_string(ret));
+ free_notification_ctx(ctx);
+ }
+
+out:
+ uacpi_release_native_mutex(notify_mutex);
+ return ret;
+}
+
+static uacpi_device_notify_handler *handler_container(
+ uacpi_handlers *handlers, uacpi_notify_handler target_handler
+)
+{
+ uacpi_device_notify_handler *handler = handlers->notify_head;
+
+ while (handler) {
+ if (handler->callback == target_handler)
+ return handler;
+
+ handler = handler->next;
+ }
+
+ return UACPI_NULL;
+}
+
+uacpi_status uacpi_install_notify_handler(
+ uacpi_namespace_node *node, uacpi_notify_handler handler,
+ uacpi_handle handler_context
+)
+{
+ uacpi_status ret;
+ uacpi_object *obj;
+ uacpi_handlers *handlers;
+ uacpi_device_notify_handler *new_handler;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (node == uacpi_namespace_root()) {
+ obj = g_uacpi_rt_ctx.root_object;
+ } else {
+ ret = uacpi_namespace_node_acquire_object_typed(
+ node, UACPI_OBJECT_DEVICE_BIT | UACPI_OBJECT_THERMAL_ZONE_BIT |
+ UACPI_OBJECT_PROCESSOR_BIT, &obj
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ ret = uacpi_acquire_native_mutex(notify_mutex);
+ if (uacpi_unlikely_error(ret))
+ goto out_no_mutex;
+
+ uacpi_kernel_wait_for_work_completion();
+
+ handlers = obj->handlers;
+
+ if (handler_container(handlers, handler) != UACPI_NULL) {
+ ret = UACPI_STATUS_ALREADY_EXISTS;
+ goto out;
+ }
+
+ new_handler = uacpi_kernel_alloc_zeroed(sizeof(*new_handler));
+ if (uacpi_unlikely(new_handler == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ new_handler->callback = handler;
+ new_handler->user_context = handler_context;
+ new_handler->next = handlers->notify_head;
+
+ handlers->notify_head = new_handler;
+
+out:
+ uacpi_release_native_mutex(notify_mutex);
+out_no_mutex:
+ if (node != uacpi_namespace_root())
+ uacpi_object_unref(obj);
+
+ return ret;
+}
+
+uacpi_status uacpi_uninstall_notify_handler(
+ uacpi_namespace_node *node, uacpi_notify_handler handler
+)
+{
+ uacpi_status ret;
+ uacpi_object *obj;
+ uacpi_handlers *handlers;
+ uacpi_device_notify_handler *prev_handler, *containing = UACPI_NULL;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (node == uacpi_namespace_root()) {
+ obj = g_uacpi_rt_ctx.root_object;
+ } else {
+ ret = uacpi_namespace_node_acquire_object_typed(
+ node, UACPI_OBJECT_DEVICE_BIT | UACPI_OBJECT_THERMAL_ZONE_BIT |
+ UACPI_OBJECT_PROCESSOR_BIT, &obj
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ ret = uacpi_acquire_native_mutex(notify_mutex);
+ if (uacpi_unlikely_error(ret))
+ goto out_no_mutex;
+
+ uacpi_kernel_wait_for_work_completion();
+
+ handlers = obj->handlers;
+
+ containing = handler_container(handlers, handler);
+ if (containing == UACPI_NULL) {
+ ret = UACPI_STATUS_NOT_FOUND;
+ goto out;
+ }
+
+ prev_handler = handlers->notify_head;
+
+ // Are we the last linked handler?
+ if (prev_handler == containing) {
+ handlers->notify_head = containing->next;
+ goto out;
+ }
+
+ // Nope, we're somewhere in the middle. Do a search.
+ while (prev_handler) {
+ if (prev_handler->next == containing) {
+ prev_handler->next = containing->next;
+ goto out;
+ }
+
+ prev_handler = prev_handler->next;
+ }
+
+out:
+ uacpi_release_native_mutex(notify_mutex);
+out_no_mutex:
+ if (node != uacpi_namespace_root())
+ uacpi_object_unref(obj);
+
+ if (uacpi_likely_success(ret))
+ uacpi_free(containing, sizeof(*containing));
+
+ return ret;
+}
+
+#endif // !UACPI_BAREBONES_MODE