diff options
author | Ian Moffett <ian@osmora.org> | 2025-05-17 21:56:07 -0400 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2025-05-17 21:58:44 -0400 |
commit | 08eeb79db14145d83578025e1f0e7f7af460ee25 (patch) | |
tree | b6af572a4b8dceb4f044f1e0bf5697f5c18dc0fd /sys/dev/acpi/uacpi/notify.c | |
parent | 9c64c3e69fa60b3657d33e829a411cb37064a169 (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.c | 255 |
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 |