blob: acf026026cccc8aef722d26c7c82b505db832764 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_DEVICES_BIN_DRIVER_RUNTIME_CALLBACK_REQUEST_H_
#define SRC_DEVICES_BIN_DRIVER_RUNTIME_CALLBACK_REQUEST_H_
#include <lib/fdf/types.h>
#include <lib/fit/function.h>
#include <optional>
#include <fbl/intrusive_double_list.h>
#include "src/devices/bin/driver_runtime/driver_context.h"
#include "src/devices/bin/driver_runtime/object.h"
// Defined in "src/devices/bin/driver_runtime/dispatcher.h"
namespace driver_runtime {
class Dispatcher;
} // namespace driver_runtime
namespace driver_runtime {
class CallbackRequest;
// The inline target size is configured to fit the 3 arguments captured by the callback request
// created in |Dispatcher::ScheduleTokenCallback|.
using Callback =
fit::inline_callback<void(std::unique_ptr<CallbackRequest>, zx_status_t), sizeof(void*) * 3>;
// Wraps a callback so that it can be added to a list.
class CallbackRequest
: public fbl::DoublyLinkedListable<std::unique_ptr<CallbackRequest>,
fbl::NodeOptions::AllowRemoveFromContainer> {
public:
enum class RequestType {
kIrq,
kWait,
kTask,
kOther,
};
explicit CallbackRequest(RequestType request_type = RequestType::kOther)
: request_type_(request_type) {}
// Initializes the callback to be queued.
// Sets the dispatcher, and the callback that will be called by |Call|.
// |async_operation| is the async_dispatcher_t operation that this callback request manages.
void SetCallback(driver_runtime::Dispatcher* dispatcher, Callback callback,
void* async_operation = nullptr) {
ZX_ASSERT(!dispatcher_);
ZX_ASSERT(!callback_);
ZX_ASSERT(!async_operation_);
dispatcher_ = dispatcher;
callback_ = std::move(callback);
if (async_operation) {
async_operation_ = async_operation;
}
initiating_driver_ = driver_context::GetCurrentDriver();
initiating_dispatcher_ = driver_context::GetCurrentDispatcher();
}
// Calls the callback, returning ownership of the request back the original requester,
void Call(std::unique_ptr<CallbackRequest> callback_request, zx_status_t status) {
// If no particular callback reason was set, we will use the status provided by the dispatcher.
if (reason_.has_value() && (*reason_ != ZX_OK)) {
status = *reason_;
}
dispatcher_ = nullptr;
reason_ = std::nullopt;
async_operation_ = std::nullopt;
callback_(std::move(callback_request), status);
}
void SetCallbackReason(zx_status_t callback_reason) { reason_ = callback_reason; }
// Returns whether a callback has been set via |SetCallback| and not yet been called.
bool IsPending() { return !!callback_; }
// Clears the callback request state.
void Reset() {
dispatcher_ = nullptr;
reason_ = std::nullopt;
async_operation_ = std::nullopt;
callback_ = nullptr;
}
// Returns whether this callback manages an async_dispatcher_t operation.
bool has_async_operation() const { return async_operation_.has_value() && *async_operation_; }
// Returns whether this callback manages |operation|.
bool holds_async_operation(void* operation) const {
return async_operation_.has_value() && *async_operation_ == operation;
}
RequestType request_type() { return request_type_; }
void* async_operation() { return async_operation_.value_or(nullptr); }
const void* initiating_driver() { return initiating_driver_; }
driver_runtime::Dispatcher* initiating_dispatcher() { return initiating_dispatcher_; }
private:
const RequestType request_type_;
driver_runtime::Dispatcher* dispatcher_ = nullptr;
Callback callback_;
// Reason for scheduling the callback.
std::optional<zx_status_t> reason_;
// The async_dispatcher_t operation that this callback request is wrapping around.
std::optional<void*> async_operation_;
// This is for tracking who caused the callback request to be queued.
const void* initiating_driver_ = nullptr;
driver_runtime::Dispatcher* initiating_dispatcher_ = nullptr;
};
} // namespace driver_runtime
#endif // SRC_DEVICES_BIN_DRIVER_RUNTIME_CALLBACK_REQUEST_H_