| // 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 LIB_DRIVER_RUNTIME_INCLUDE_LIB_FDF_CHANNEL_H_ |
| #define LIB_DRIVER_RUNTIME_INCLUDE_LIB_FDF_CHANNEL_H_ |
| |
| #include <lib/fdf/arena.h> |
| #include <lib/fdf/dispatcher.h> |
| #include <lib/fdf/types.h> |
| |
| __BEGIN_CDECLS |
| |
| // Usage Notes: |
| // |
| // fdf_channel_t is a bi-directional message transport capable of sending |
| // arena-managed raw data bytes and handles from one side to the other. |
| // |
| // Example: |
| // |
| // // Read handler for asynchronous channel reads. |
| // void handler(fdf_dispatcher_t* dispatcher, fdf_channel_read_t* read, fdf_status_t status); |
| // |
| // // Sends a request to the peer of |channel| and asynchronously waits for a response. |
| // void send_request(fdf_handle_t channel, fdf_dispatcher_t* dispatcher dispatcher) { |
| // fdf_arena_t* arena; |
| // fdf_status_t status = fdf_arena_create(0, "", 0, &arena); |
| // |
| // void* data = fdf_arena_allocate(arena, 0x1000); |
| // // Set the data to transfer |
| // ... |
| // |
| // // Write the request to the channel. |
| // status = fdf_channel_write(ch0, 0, arena, data, 0x1000, NULL, 0); |
| // |
| // // Asynchronously wait for a response. |
| // fdf_channel_read_t* channel_read = calloc(1, sizeof(fdf_channel_read_t)); |
| // channel_read->handler = handler; |
| // channel_read->channel = ch0; |
| // status = fdf_channel_wait_async(dispatcher, channel_read, 0); |
| // |
| // // We are done with the arena. |
| // fdf_arena_destroy(arena); |
| // } |
| // |
| // void handler(fdf_dispatcher_t* dispatcher, fdf_channel_read_t* read, fdf_status_t status) { |
| // fdf_arena_t* arena; |
| // void* data data; |
| // uint32_t data_size; |
| // zx_handle_t* handles; |
| // uint32_t num_handles; |
| // fdf_status_t status = fdf_channel_read(read->channel, 0, &arena, &data, &data_size, |
| // &handles, &num_handles); |
| // // Process the read data. |
| // ... |
| // |
| // // Read provides you with an arena which you may reuse for other requests. |
| // // You are in charge of destroying it. |
| // fdf_arena_destroy(arena); |
| // free(read); |
| // } |
| // |
| |
| // Defined in <lib/fdf/channel_read.h> |
| struct fdf_channel_read; |
| |
| fdf_status_t fdf_channel_create(uint32_t options, fdf_handle_t* out0, fdf_handle_t* out1); |
| |
| // Attempts to write a message to the channel specified by |channel|. |
| // The pointers |data| and |handles| may be NULL if their respective sizes are zero. |
| // |
| // |data| and |handles| must be a pointers managed by |arena| if they are not NULL. |
| // |handles| may be a mix of zircon handles and fdf handles. |
| // The caller retains ownership of |arena|, which must be destroyed via |fdf_arena_destroy|. |
| // It is okay to destroy the arena as soon as the write call returns as the lifetime of |
| // the arena is extended until the data is read. |
| // |
| // Handles with a pending callback registered via |fdf_channel_wait_async| cannot be transferred. |
| // All handles are consumed and are no longer available to the caller, on success or failure. |
| // |
| // Returns |ZX_OK| if the write was successful. |
| // Returns |ZX_ERR_BAD_HANDLE| if |channel| is not a valid handle. |
| // Returns |ZX_ERR_INVALID_ARGS| if |data| or |handles| are not pointers managed by |arena|, |
| // or at least one of |handles| has a pending callback registered via |fdf_channel_wait_async|. |
| // Returns |ZX_ERR_PEER_CLOSED| if the other side of the channel is closed. |
| // |
| // This operation is thread-safe. |
| fdf_status_t fdf_channel_write(fdf_handle_t channel, uint32_t options, fdf_arena_t* arena, |
| void* data, uint32_t num_bytes, zx_handle_t* handles, |
| uint32_t num_handles); |
| |
| // Attempts to read the first message from the channel specified by |channel| into |
| // the |data| and |handles| buffers. |
| // |
| // The lifetime of |data| and |handles| are tied to the lifetime of |arena|. |
| // |handles| may be a mix of zircon handles and fdf handles. |
| // |
| // Provides ownership of |arena|, which must be destroyed via |fdf_arena_destroy|. |
| // |
| // Returns |ZX_OK| if the read was successful. |
| // Returns |ZX_ERR_BAD_HANDLE| if |channel| is not a valid handle. |
| // Returns |ZX_ERR_INVALID_ARGS| if |arena| is NULL when |data| or |handles| are non-NULL. |
| // Returns |ZX_ERR_SHOULD_WAIT| if the channel contained no messages to read. |
| // Returns |ZX_ERR_PEER_CLOSED| if there are no available messages and the other |
| // side of the channel is closed. |
| // |
| // This operation is thread-safe. |
| fdf_status_t fdf_channel_read(fdf_handle_t channel, uint32_t options, fdf_arena_t** arena, |
| void** data, uint32_t* num_bytes, zx_handle_t** handles, |
| uint32_t* num_handles); |
| |
| // Begins asynchronously waiting for the channel set in |channel_read| to be readable. |
| // The |dispatcher| invokes the handler when the wait completes. |
| // Only one dispatcher can be registered at a time. The dispatcher will |
| // be considered unregistered immediately before the read handler is invoked. |
| // |
| // After successfully scheduling the read, the client is responsible for retaining |
| // the |channel_read| structure in memory (and unmodified) until the read handler runs, |
| // or the dispatcher shuts down. Thereafter, the |channel_read| may be started again |
| // or destroyed. |
| // |
| // The read handler will be invoked exactly once. When the dispatcher is |
| // shutting down (being destroyed), the handlers of any remaining wait |
| // may be invoked with a status of |ZX_ERR_CANCELED|. |
| // |
| // Returns |ZX_OK| if the wait was successfully begun. |
| // Returns |ZX_ERR_PEER_CLOSED| if there are no available messages and the other |
| // side of the channel is closed. |
| // Returns |ZX_ERR_BAD_STATE| if there is already a dispatcher waiting |
| // on this channel. |
| // Returns |ZX_ERR_UNAVAILABLE| if |dispatcher| is shutting down. |
| // |
| // This operation is thread-safe. |
| fdf_status_t fdf_channel_wait_async(struct fdf_dispatcher* dispatcher, |
| struct fdf_channel_read* channel_read, uint32_t options); |
| |
| // Cancels any pending callback registered via |fdf_channel_wait_async|. |
| // How it is handled depends on whether the dispatcher it was registered with is |
| // synchronized. |
| // If the dispatcher is synchronized, this must only be called from a dispatcher |
| // thread, and any pending callback will be canceled synchronously. |
| // If the dispatcher is unsynchronized, the callback will be scheduled to be called. |
| // |
| // Returns |ZX_OK| if the wait was pending and it has been successfully |
| // canceled; if the dispatcher is unsynchronized, its handler will run with |
| // status ZX_ERR_CANCELED. |
| // Returns |ZX_ERR_NOT_FOUND| if there was no pending wait either because it |
| // is currently running (perhaps in a different thread), already scheduled to be run, |
| // already completed, or had not been started. |
| fdf_status_t fdf_channel_cancel_wait(fdf_handle_t handle); |
| |
| // fdf_channel_call() is like a combined fdf_channel_write(), fdf_channel_wait_async(), |
| // and fdf_channel_read(), with the addition of a feature where a transaction id at |
| // the front of the message payload bytes is used to match reply messages with send messages, |
| // enabling multiple calling threads to share a channel without any additional client-side |
| // bookkeeping. |
| // |
| // The first four bytes of the written and read back messages are treated as a |
| // transaction ID of type fdf_txid_t. The runtime generates a txid for the |
| // written message, replacing that part of the message as read from the user. |
| // The runtime generated txid will be between 0x80000000 and 0xFFFFFFFF, |
| // and will not collide with any txid from any other fdf_channel_call() |
| // in progress against this channel endpoint. If the written message has a |
| // length of fewer than four bytes, an error is reported. |
| // |
| // While |deadline| has not passed, if an inbound message arrives with a matching txid, |
| // instead of being added to the tail of the general inbound message queue, |
| // it is delivered directly to the thread waiting in fdf_channel_call(). |
| // |
| // If such a reply arrives after |deadline| has passed, it will arrive in the |
| // general inbound message queue. |
| // |
| // All written handles are consumed and are no longer available to the caller, |
| // on success or failure. |
| // |
| // Returns |ZX_OK| if the call completed successfully. |
| // Returns |ZX_ERR_BAD_HANDLE| if |channel| is not a valid handle. |
| // Returns |ZX_ERR_INVALID_ARGS| if |args| is NULL, |
| // or |wr_data| or |wr_handles| are non-NULL and not pointers managed by |wr_arena|, |
| // or |wr_num_bytes| is less than four, |
| // or at least one of |wr_handles| has a pending callback registered via |fdf_channel_wait_async|, |
| // or |rd_arena| is NULL when |rd_data| or |rd_handles| are non-NULL. |
| // Returns |ZX_ERR_PEER_CLOSED| if the other side of the channel is closed. |
| // Returns |ZX_ERR_TIMED_OUT| if |deadline| passed before a reply matching |
| // the correct txid was received. |
| // Returns |ZX_ERR_BAD_STATE| if this is called from a driver runtime managed thread |
| // that does not allow sync calls. |
| // |
| // This operation is thread-safe. |
| fdf_status_t fdf_channel_call(fdf_handle_t handle, uint32_t options, zx_time_t deadline, |
| const fdf_channel_call_args_t* args); |
| |
| // If there is a pending callback registered via |fdf_channel_wait_async|, |
| // it must be cancelled before this is called. For unsynchronized dispatchers, |
| // cancellation is not considered complete until the callback is invoked. |
| // This is safe to call from any thread. |
| void fdf_handle_close(fdf_handle_t handle); |
| |
| __END_CDECLS |
| |
| #endif // LIB_DRIVER_RUNTIME_INCLUDE_LIB_FDF_CHANNEL_H_ |