8#include "Ps2MouseCallbackRegistry.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/process/TerminationDeferral.h"
11#include "pedigree/kernel/process/Thread.h"
12#include "pedigree/kernel/processor/Processor.h"
13#include "pedigree/kernel/processor/ProcessorInformation.h"
14#include "pedigree/kernel/utilities/assert.h"
16Ps2MouseCallbackRegistry::CallbackSlot::CallbackSlot()
19 registration(nullptr),
24 deferredRemoval(false),
28Ps2MouseCallbackRegistry::Registration::Registration()
29 : m_pOwner(nullptr), m_pSlot(nullptr), m_Generation(0), m_Unregister(nullptr) {}
31Ps2MouseCallbackRegistry::Registration::~Registration() {
32 if (m_pSlot && !
reset()) {
33 FATAL(
"Live PS/2 mouse callback registration could not be retired.");
42 void* owner = m_pOwner;
44 const size_t generation = m_Generation;
45 UnregisterThunk unregister = m_Unregister;
46 if (!unregister(owner, slot, generation,
this)) {
50 releaseFromOwner(owner, slot, generation);
54Ps2MouseCallbackRegistry::Ps2MouseCallbackRegistry()
57#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
59 m_CallbackPinHook(nullptr)
64Ps2MouseCallbackRegistry::~Ps2MouseCallbackRegistry() {
66 const bool callbackContext = isCallbackContext(currentDispatchOwner());
68 if (callbackContext) {
69 FATAL(
"PS/2 mouse callback registry cannot be destroyed from callback context.");
72 for (
size_t i = 0; i < MaxCallbacks; ++i) {
73 CallbackSlot& slot = m_Callbacks[i];
74 bool registered =
false;
75 Registration* registration =
nullptr;
76 size_t generation = 0;
79 registered = slot.handler !=
nullptr;
80 registration = slot.registration;
81 generation = slot.generation;
85 if (registered && !unregister(&slot, generation, registration)) {
86 FATAL(
"PS/2 mouse callback registry teardown did not complete.");
91void* Ps2MouseCallbackRegistry::currentDispatchOwner() {
93 Thread* thread = information.getCurrentThread();
94 return thread ?
static_cast<void*
>(thread) : static_cast<void*>(&information);
97bool Ps2MouseCallbackRegistry::unregisterThunk(
void* owner,
void* slot,
size_t generation,
98 Registration* registration) {
100 reinterpret_cast<CallbackSlot*
>(slot), generation, registration);
103void Ps2MouseCallbackRegistry::clearSlot(CallbackSlot& slot) {
104 assert(!slot.inFlight);
105 assert(!slot.dispatches);
106 if (slot.registration) {
107 slot.registration->releaseFromOwner(
this, &slot, slot.generation);
109 slot.handler =
nullptr;
110 slot.parameter =
nullptr;
111 slot.registration =
nullptr;
112 slot.enabled =
false;
113 slot.draining =
false;
114 slot.deferredRemoval =
false;
117bool Ps2MouseCallbackRegistry::subscribe(Handler handler,
void* parameter,
118 Registration& registration) {
119 if (!handler || registration) {
124 for (
size_t i = 0; i < MaxCallbacks; ++i) {
125 CallbackSlot& slot = m_Callbacks[i];
130 assert(!slot.inFlight);
131 assert(!slot.dispatches);
133 if (!slot.generation) {
136 slot.handler = handler;
137 slot.parameter = parameter;
138 slot.registration = ®istration;
140 slot.draining =
false;
141 slot.deferredRemoval =
false;
142 registration.adopt(
this, &slot, slot.generation, unregisterThunk);
150bool Ps2MouseCallbackRegistry::isCallbackContext(
void* owner)
const {
151 for (
size_t i = 0; i < MaxCallbacks; ++i) {
152 for (CallbackDispatch* dispatch = m_Callbacks[i].dispatches; dispatch;
153 dispatch = dispatch->next) {
154 if (dispatch->owner == owner) {
162bool Ps2MouseCallbackRegistry::unregister(CallbackSlot* slot,
size_t generation,
163 Registration* registration) {
168 if (!slot || slot->generation != generation || !slot->handler) {
172 if (slot->registration && slot->registration != registration) {
177 slot->enabled =
false;
178 if (!slot->inFlight) {
184 void* owner = currentDispatchOwner();
185 bool ownsTarget =
false;
186 bool targetOwnedByPeer =
false;
187 for (CallbackDispatch* dispatch = slot->dispatches; dispatch; dispatch = dispatch->next) {
188 if (dispatch->owner == owner) {
191 targetOwnedByPeer =
true;
195 if (isCallbackContext(owner)) {
196 if (ownsTarget && !targetOwnedByPeer) {
200 slot->registration =
nullptr;
201 slot->deferredRemoval =
true;
212 if (slot->inFlight && !canYield) {
217 slot->draining =
true;
221 auto waitGuard = slot->drainWaiters.acquire();
223 if (slot->generation != generation || !slot->handler) {
227 if (!slot->inFlight) {
234 const WaitQueue::WakeReason reason =
236 reinterpret_cast<uintptr_t
>(slot->parameter));
241void Ps2MouseCallbackRegistry::releaseCallback(CallbackSlot& slot, CallbackDispatch& dispatch) {
242 bool wakeDrainer =
false;
244 CallbackDispatch** link = &slot.dispatches;
245 while (*link && *link != &dispatch) {
246 link = &(*link)->next;
248 assert(*link == &dispatch);
249 *link = dispatch.next;
250 assert(slot.inFlight);
252 wakeDrainer = !slot.inFlight && slot.draining;
253 if (!slot.inFlight && slot.deferredRemoval) {
259 slot.drainWaiters.wakeAll(WaitQueue::WakeReason::Signalled,
WaitQueue::Channel(&slot));
263void Ps2MouseCallbackRegistry::dispatch(
const void* buffer,
size_t length) {
265 for (
size_t i = 0; i < MaxCallbacks; ++i) {
266 CallbackDispatch dispatch = {currentDispatchOwner(),
nullptr};
267 Handler handler =
nullptr;
268 void* parameter =
nullptr;
271 CallbackSlot& slot = m_Callbacks[i];
272 if (slot.handler && slot.enabled) {
273 handler = slot.handler;
274 parameter = slot.parameter;
276 dispatch.next = slot.dispatches;
277 slot.dispatches = &dispatch;
285#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
286 CallbackPinHook hook = __atomic_load_n(&m_CallbackPinHook, __ATOMIC_ACQUIRE);
288 hook(handler, parameter);
292 handler(parameter, buffer, length);
293 releaseCallback(slot, dispatch);
297#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
298void Ps2MouseCallbackRegistry::setCallbackPinHook(CallbackPinHook hook) {
299 __atomic_store_n(&m_CallbackPinHook, hook, __ATOMIC_RELEASE);
static bool getInterrupts()
static ProcessorInformation & information()
bool acquire(bool recurse=false, bool safe=true)