1#include <uacpi/internal/notify.h>
2#include <uacpi/internal/shareable.h>
3#include <uacpi/internal/namespace.h>
4#include <uacpi/internal/log.h>
5#include <uacpi/kernel_api.h>
6
7uacpi_handlers *uacpi_node_get_handlers(
8 uacpi_namespace_node *node
9)
10{
11 uacpi_object *obj;
12
13 obj = uacpi_namespace_node_get_object(node);
14 if (uacpi_unlikely(obj == UACPI_NULL))
15 return UACPI_NULL;
16
17 switch (obj->type) {
18 default:
19 /*
20 * Even though the '\' object doesn't have its type set to
21 * UACPI_OBJECT_DEVICE, it is one.
22 * See namespace.c:make_object_for_predefined for reasoning.
23 */
24 if (node != uacpi_namespace_root() ||
25 obj->type != UACPI_OBJECT_UNINITIALIZED)
26 return UACPI_NULL;
27 UACPI_FALLTHROUGH;
28 case UACPI_OBJECT_DEVICE:
29 case UACPI_OBJECT_THERMAL_ZONE:
30 case UACPI_OBJECT_PROCESSOR:
31 return obj->handlers;
32 }
33}
34
35struct notification_ctx {
36 uacpi_namespace_node *node;
37 uacpi_u64 value;
38 uacpi_device_notify_handler *node_handlers, *root_handlers;
39};
40
41static void do_notify(uacpi_handle opaque)
42{
43 struct notification_ctx *ctx = opaque;
44 uacpi_device_notify_handler *handler;
45 uacpi_bool did_notify_root = UACPI_FALSE;
46
47 handler = ctx->node_handlers;
48
49 for (;;) {
50 if (handler == UACPI_NULL) {
51 if (did_notify_root) {
52 uacpi_namespace_node_unref(node: ctx->node);
53 uacpi_free(ctx, sizeof(*ctx));
54 return;
55 }
56
57 handler = ctx->root_handlers;
58 did_notify_root = UACPI_TRUE;
59 continue;
60 }
61
62 handler->callback(handler->user_context, ctx->node, ctx->value);
63 handler = handler->next;
64 }
65}
66
67uacpi_status uacpi_notify_all(uacpi_namespace_node *node, uacpi_u64 value)
68{
69 uacpi_status ret;
70 struct notification_ctx *ctx;
71 uacpi_handlers *node_handlers, *root_handlers;
72
73 node_handlers = uacpi_node_get_handlers(node);
74 if (uacpi_unlikely(node_handlers == UACPI_NULL))
75 return UACPI_STATUS_INVALID_ARGUMENT;
76
77 root_handlers = uacpi_node_get_handlers(node: uacpi_namespace_root());
78
79 if (node_handlers->notify_head == UACPI_NULL &&
80 root_handlers->notify_head == UACPI_NULL)
81 return UACPI_STATUS_NO_HANDLER;
82
83 ctx = uacpi_kernel_alloc(size: sizeof(*ctx));
84 if (uacpi_unlikely(ctx == UACPI_NULL))
85 return UACPI_STATUS_OUT_OF_MEMORY;
86
87 ctx->node = node;
88 // In case this node goes out of scope
89 uacpi_shareable_ref(node);
90
91 ctx->value = value;
92 ctx->node_handlers = node_handlers->notify_head;
93 ctx->root_handlers = root_handlers->notify_head;
94
95 ret = uacpi_kernel_schedule_work(UACPI_WORK_NOTIFICATION, do_notify, ctx);
96 if (uacpi_unlikely_error(ret)) {
97 uacpi_warn("unable to schedule notification work: %s\n",
98 uacpi_status_to_string(ret));
99 uacpi_namespace_node_unref(node);
100 uacpi_free(ctx, sizeof(*ctx));
101 return ret;
102 }
103
104 return UACPI_STATUS_OK;
105}
106
107static uacpi_device_notify_handler *handler_container(
108 uacpi_handlers *handlers, uacpi_notify_handler target_handler
109)
110{
111 uacpi_device_notify_handler *handler = handlers->notify_head;
112
113 while (handler) {
114 if (handler->callback == target_handler)
115 return handler;
116
117 handler = handler->next;
118 }
119
120 return UACPI_NULL;
121}
122
123uacpi_status uacpi_install_notify_handler(
124 uacpi_namespace_node *node, uacpi_notify_handler handler,
125 uacpi_handle handler_context
126)
127{
128 uacpi_handlers *handlers;
129 uacpi_device_notify_handler *new_handler;
130
131 handlers = uacpi_node_get_handlers(node);
132 if (uacpi_unlikely(handlers == UACPI_NULL))
133 return UACPI_STATUS_INVALID_ARGUMENT;
134 if (handler_container(handlers, target_handler: handler) != UACPI_NULL)
135 return UACPI_STATUS_ALREADY_EXISTS;
136
137 new_handler = uacpi_kernel_calloc(count: 1, size: sizeof(*new_handler));
138 if (uacpi_unlikely(new_handler == UACPI_NULL))
139 return UACPI_STATUS_OUT_OF_MEMORY;
140
141 new_handler->callback = handler;
142 new_handler->user_context = handler_context;
143 new_handler->next = handlers->notify_head;
144
145 handlers->notify_head = new_handler;
146 return UACPI_STATUS_OK;
147}
148
149uacpi_status uacpi_uninstall_notify_handler(
150 uacpi_namespace_node *node, uacpi_notify_handler handler
151)
152{
153 uacpi_handlers *handlers;
154 uacpi_device_notify_handler *containing, *prev_handler;
155
156 handlers = uacpi_node_get_handlers(node);
157 if (uacpi_unlikely(handlers == UACPI_NULL))
158 return UACPI_STATUS_INVALID_ARGUMENT;
159
160 containing = handler_container(handlers, target_handler: handler);
161 if (containing == UACPI_NULL)
162 return UACPI_STATUS_NOT_FOUND;
163
164 prev_handler = handlers->notify_head;
165
166 // Are we the last linked handler?
167 if (prev_handler == containing) {
168 handlers->notify_head = containing->next;
169 goto out;
170 }
171
172 // Nope, we're somewhere in the middle. Do a search.
173 while (prev_handler) {
174 if (prev_handler->next == containing) {
175 prev_handler->next = containing->next;
176 goto out;
177 }
178
179 prev_handler = prev_handler->next;
180 }
181
182out:
183 uacpi_free(containing, sizeof(*containing));
184 return UACPI_STATUS_OK;
185}
186