| // Copyright 2019 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. |
| library zx; |
| |
| alias HandleOp = uint32; |
| |
| // TODO(scottmg): ZX_HANDLE_OP_xyz here. |
| |
| type HandleInfo = resource struct { |
| handle Handle; |
| type ObjType; |
| rights Rights; |
| unused uint32; |
| }; |
| |
| // TODO(fxbug.dev/110021): We cannot yet conveniently express this. |
| type ChannelCallArgs = resource struct {}; |
| |
| type HandleDisposition = resource struct { |
| operation HandleOp; |
| handle Handle; |
| type ObjType; |
| rights Rights; |
| result Status; |
| }; |
| |
| // TODO(fxbug.dev/110021): We cannot yet conveniently express this. |
| type ChannelCallEtcArgs = resource struct {}; |
| |
| @transport("Syscall") |
| closed protocol Channel { |
| /// ## Summary |
| /// |
| /// Create a channel. |
| /// |
| /// ## Declaration |
| /// |
| /// ```c |
| /// #include <zircon/syscalls.h> |
| /// |
| /// zx_status_t zx_channel_create(uint32_t options, |
| /// zx_handle_t* out0, |
| /// zx_handle_t* out1); |
| /// ``` |
| /// |
| /// ## Description |
| /// |
| /// `zx_channel_create()` creates a channel, a bi-directional |
| /// datagram-style message transport capable of sending raw data bytes |
| /// as well as handles from one side to the other. |
| /// |
| /// Two handles are returned on success, providing access to both sides |
| /// of the channel. Messages written to one handle may be read from |
| /// the opposite. |
| /// |
| /// The handles will have these rights: |
| /// |
| /// - **ZX_RIGHT_TRANSFER**: allowing them to be sent to another process via |
| /// [`zx_channel_write()`]. |
| /// - **ZX_RIGHT_WAIT**: allowing one to wait for its signals. |
| /// - **ZX_RIGHT_INSPECT** |
| /// - **ZX_RIGHT_READ**: allowing messages to be read from them. |
| /// - **ZX_RIGHT_WRITE**: allowing messages to be written to them. |
| /// - **ZX_RIGHT_SIGNAL** |
| /// - **ZX_RIGHT_SIGNAL_PEER** |
| /// |
| /// ## Rights |
| /// |
| /// Caller job policy must allow **ZX_POL_NEW_CHANNEL**. |
| /// |
| /// ## Return value |
| /// |
| /// `zx_channel_create()` returns **ZX_OK** on success. In the event |
| /// of failure, a negative error value is returned. |
| /// |
| /// ## Errors |
| /// |
| /// **ZX_ERR_INVALID_ARGS** *out0* or *out1* is an invalid pointer or NULL or |
| /// *options* is any value other than 0. |
| /// |
| /// **ZX_ERR_NO_MEMORY** Failure due to lack of memory. |
| /// There is no good way for userspace to handle this (unlikely) error. |
| /// In a future build this error will no longer occur. |
| /// |
| /// ## See also |
| /// |
| /// - [`zx_channel_call()`] |
| /// - [`zx_channel_read()`] |
| /// - [`zx_channel_write()`] |
| /// - [`zx_handle_close()`] |
| /// - [`zx_handle_replace()`] |
| /// - [`zx_object_wait_async()`] |
| /// - [`zx_object_wait_many()`] |
| /// - [`zx_object_wait_one()`] |
| /// |
| /// [`zx_channel_call()`]: channel_call.md |
| /// [`zx_channel_read()`]: channel_read.md |
| /// [`zx_channel_write()`]: channel_write.md |
| /// [`zx_handle_close()`]: handle_close.md |
| /// [`zx_handle_replace()`]: handle_replace.md |
| /// [`zx_object_wait_async()`]: object_wait_async.md |
| /// [`zx_object_wait_many()`]: object_wait_many.md |
| /// [`zx_object_wait_one()`]: object_wait_one.md |
| strict Create(struct { |
| options uint32; |
| }) -> (resource struct { |
| out0 Handle; |
| out1 Handle; |
| }) error Status; |
| |
| /// ## Summary |
| /// |
| /// Read a message from a channel. |
| /// |
| /// ## Declaration |
| /// |
| /// ```c |
| /// #include <zircon/syscalls.h> |
| /// |
| /// zx_status_t zx_channel_read(zx_handle_t handle, |
| /// uint32_t options, |
| /// void* bytes, |
| /// zx_handle_t* handles, |
| /// uint32_t num_bytes, |
| /// uint32_t num_handles, |
| /// uint32_t* actual_bytes, |
| /// uint32_t* actual_handles); |
| /// ``` |
| /// |
| /// ## Description |
| /// |
| /// `zx_channel_read()` attempts to read the first message from the channel |
| /// specified by *handle* into the provided *bytes* and/or *handles* buffers. |
| /// |
| /// The parameters *num_bytes* and *num_handles* are used to specify the size of the |
| /// respective read buffers. *num_bytes* is a count of bytes, and |
| /// *num_handles* is a count of elements of type `zx_handle_t`. |
| /// |
| /// The length of *bytes*, in bytes, is stored in the location pointed to by |
| /// *actual_bytes*. The number of handles is stored in the location pointed to by |
| /// *actual_handles*. Either *actual_bytes* or *actual_handles* may be NULL, in |
| /// which case they will be ignored. |
| /// |
| /// Channel messages may contain both byte data and handle payloads and may |
| /// only be read in their entirety. Partial reads are not possible. |
| /// |
| /// The *bytes* buffer is written before the *handles* buffer. In the event of |
| /// overlap between these two buffers, the contents written to *handles* |
| /// will overwrite the portion of *bytes* it overlaps. |
| /// |
| /// When communicating to an untrusted party over a channel, it is recommended that |
| /// the [`zx_channel_read_etc()`] form is used and each handle type |
| /// and rights are validated against the expected values. |
| /// |
| /// ## Rights |
| /// |
| /// *handle* must be of type **ZX_OBJ_TYPE_CHANNEL** and have **ZX_RIGHT_READ**. |
| /// |
| /// ## Return value |
| /// |
| /// Returns **ZX_OK** on success. If non-NULL, the locations pointed to by |
| /// *actual_bytes* and *actual_handles* contain the exact number of bytes and count |
| /// of handles read. |
| /// |
| /// ## Errors |
| /// |
| /// **ZX_ERR_BAD_HANDLE** *handle* is not a valid handle. |
| /// |
| /// **ZX_ERR_WRONG_TYPE** *handle* is not a channel handle. |
| /// |
| /// **ZX_ERR_INVALID_ARGS** If any of *bytes*, *handles*, *actual_bytes*, or |
| /// *actual_handles* are non-NULL and an invalid pointer. |
| /// |
| /// **ZX_ERR_ACCESS_DENIED** *handle* does not have **ZX_RIGHT_READ**. |
| /// |
| /// **ZX_ERR_SHOULD_WAIT** The channel contained no messages to read and the other side of the |
| /// channel is open. |
| /// |
| /// **ZX_ERR_PEER_CLOSED** The channel contained no messages to read and the other side of the |
| /// channel is closed. |
| /// |
| /// **ZX_ERR_NO_MEMORY** Failure due to lack of memory. |
| /// There is no good way for userspace to handle this (unlikely) error. |
| /// In a future build this error will no longer occur. |
| /// |
| /// **ZX_ERR_BUFFER_TOO_SMALL** The provided *bytes* or *handles* buffers |
| /// are too small (in which case, the minimum sizes necessary to receive |
| /// the message will be written to *actual_bytes* and *actual_handles*, |
| /// provided they are non-NULL). If *options* has **ZX_CHANNEL_READ_MAY_DISCARD** |
| /// set, then the message is discarded. |
| /// |
| /// ## See also |
| /// |
| /// - [`zx_channel_call()`] |
| /// - [`zx_channel_create()`] |
| /// - [`zx_channel_read_etc()`] |
| /// - [`zx_channel_write()`] |
| /// - [`zx_channel_write_etc()`] |
| /// - [`zx_handle_close()`] |
| /// - [`zx_handle_replace()`] |
| /// - [`zx_object_wait_async()`] |
| /// - [`zx_object_wait_many()`] |
| /// - [`zx_object_wait_one()`] |
| /// |
| /// [`zx_channel_call()`]: channel_call.md |
| /// [`zx_channel_create()`]: channel_create.md |
| /// [`zx_channel_read_etc()`]: channel_read_etc.md |
| /// [`zx_channel_write()`]: channel_write.md |
| /// [`zx_channel_write_etc()`]: channel_write_etc.md |
| /// [`zx_handle_close()`]: handle_close.md |
| /// [`zx_handle_replace()`]: handle_replace.md |
| /// [`zx_object_wait_async()`]: object_wait_async.md |
| /// [`zx_object_wait_many()`]: object_wait_many.md |
| /// [`zx_object_wait_one()`]: object_wait_one.md |
| @handle_unchecked |
| strict Read(resource struct { |
| handle Handle:CHANNEL; |
| options uint32; |
| @out |
| @voidptr |
| bytes experimental_pointer<byte>; |
| @out |
| handles experimental_pointer<Handle>; |
| num_bytes uint32; |
| num_handles uint32; |
| }) -> (resource struct { |
| actual_bytes uint32; |
| actual_handles uint32; |
| }) error Status; |
| |
| /// ## Summary |
| /// |
| /// Read a message from a channel. |
| /// |
| /// ## Declaration |
| /// |
| /// ```c |
| /// #include <zircon/syscalls.h> |
| /// |
| /// zx_status_t zx_channel_read_etc(zx_handle_t handle, |
| /// uint32_t options, |
| /// void* bytes, |
| /// zx_handle_info_t* handles, |
| /// uint32_t num_bytes, |
| /// uint32_t num_handles, |
| /// uint32_t* actual_bytes, |
| /// uint32_t* actual_handles); |
| /// ``` |
| /// |
| /// ## Description |
| /// |
| /// See [`zx_channel_read()`] for a full description. |
| /// |
| /// Both forms of read behave the same except that [`zx_channel_read()`] returns an |
| /// array of raw `zx_handle_t` handle values while `zx_channel_read_etc()` returns |
| /// an array of `zx_handle_info_t` structures of the form: |
| /// |
| /// ``` |
| /// typedef struct { |
| /// zx_handle_t handle; // handle value |
| /// zx_obj_type_t type; // type of object, see ZX_OBJ_TYPE_ |
| /// zx_rights_t rights; // handle rights |
| /// uint32_t unused; // set to zero |
| /// } zx_handle_info_t; |
| /// ``` |
| /// |
| /// When communicating to an untrusted party over a channel, it is recommended |
| /// that the `zx_channel_read_etc()` form is used and each handle type and rights |
| /// are validated against the expected values. |
| /// |
| /// ## Rights |
| /// |
| /// *handle* must be of type **ZX_OBJ_TYPE_CHANNEL** and have **ZX_RIGHT_READ**. |
| /// |
| /// ## Return value |
| /// |
| /// Both forms of read return **ZX_OK** on success, if *actual_bytes* |
| /// and *actual_handles* (if non-NULL), contain the exact number of bytes |
| /// and count of handles read. |
| /// |
| /// ## Errors |
| /// |
| /// **ZX_ERR_BAD_HANDLE** *handle* is not a valid handle. |
| /// |
| /// **ZX_ERR_WRONG_TYPE** *handle* is not a channel handle. |
| /// |
| /// **ZX_ERR_INVALID_ARGS** If any of *bytes*, *handles*, *actual_bytes*, or |
| /// *actual_handles* are non-NULL and an invalid pointer. |
| /// |
| /// **ZX_ERR_ACCESS_DENIED** *handle* does not have **ZX_RIGHT_READ**. |
| /// |
| /// **ZX_ERR_SHOULD_WAIT** The channel contained no messages to read and the other side of the |
| /// channel is open. |
| /// |
| /// **ZX_ERR_PEER_CLOSED** The channel contained no messages to read and the other side of the |
| /// channel is closed. |
| /// |
| /// **ZX_ERR_NO_MEMORY** Failure due to lack of memory. |
| /// There is no good way for userspace to handle this (unlikely) error. |
| /// In a future build this error will no longer occur. |
| /// |
| /// **ZX_ERR_BUFFER_TOO_SMALL** The provided *bytes* or *handles* buffers |
| /// are too small (in which case, the minimum sizes necessary to receive |
| /// the message will be written to *actual_bytes* and *actual_handles*, |
| /// provided they are non-NULL). If *options* has **ZX_CHANNEL_READ_MAY_DISCARD** |
| /// set, then the message is discarded. |
| /// |
| /// ## Notes |
| /// |
| /// *num_handles* and *actual_handles* are counts of the number of elements |
| /// in the *handles* array, not its size in bytes. |
| /// |
| /// ## See also |
| /// |
| /// - [`zx_channel_call()`] |
| /// - [`zx_channel_create()`] |
| /// - [`zx_channel_read()`] |
| /// - [`zx_channel_write()`] |
| /// - [`zx_channel_write_etc()`] |
| /// - [`zx_handle_close()`] |
| /// - [`zx_handle_replace()`] |
| /// - [`zx_object_wait_async()`] |
| /// - [`zx_object_wait_many()`] |
| /// - [`zx_object_wait_one()`] |
| /// |
| /// [`zx_channel_call()`]: channel_call.md |
| /// [`zx_channel_create()`]: channel_create.md |
| /// [`zx_channel_read()`]: channel_read.md |
| /// [`zx_channel_write()`]: channel_write.md |
| /// [`zx_channel_write_etc()`]: channel_write_etc.md |
| /// [`zx_handle_close()`]: handle_close.md |
| /// [`zx_handle_replace()`]: handle_replace.md |
| /// [`zx_object_wait_async()`]: object_wait_async.md |
| /// [`zx_object_wait_many()`]: object_wait_many.md |
| /// [`zx_object_wait_one()`]: object_wait_one.md |
| strict ReadEtc(resource struct { |
| handle Handle:CHANNEL; |
| options uint32; |
| @out |
| @voidptr |
| bytes experimental_pointer<byte>; |
| @out |
| handles experimental_pointer<HandleInfo>; |
| num_bytes uint32; |
| num_handles uint32; |
| }) -> (resource struct { |
| actual_bytes uint32; |
| actual_handles uint32; |
| }) error Status; |
| |
| /// ## Summary |
| /// |
| /// Write a message to a channel. |
| /// |
| /// ## Declaration |
| /// |
| /// ```c |
| /// #include <zircon/syscalls.h> |
| /// |
| /// zx_status_t zx_channel_write(zx_handle_t handle, |
| /// uint32_t options, |
| /// const void* bytes, |
| /// uint32_t num_bytes, |
| /// const zx_handle_t* handles, |
| /// uint32_t num_handles); |
| /// ``` |
| /// |
| /// ## Description |
| /// |
| /// `zx_channel_write()` attempts to write a message of *num_bytes* |
| /// bytes and *num_handles* handles to the channel specified by |
| /// *handle*. The pointers *handles* and *bytes* may be NULL if their |
| /// respective sizes are zero. |
| /// |
| /// On success, all *num_handles* of the handles in the *handles* array |
| /// are attached to the message and will become available to the reader |
| /// of that message from the opposite end of the channel. |
| /// |
| /// All handles are discarded and no longer available to the caller, on |
| /// success or failure. Use [`zx_channel_write_etc()`] if handles need |
| /// to be preserved by the sender. |
| /// |
| /// It is invalid to include *handle* (the handle of the channel being written |
| /// to) in the *handles* array (the handles being sent in the message). |
| /// |
| /// The maximum number of handles that may be sent in a message is |
| /// **ZX_CHANNEL_MAX_MSG_HANDLES**, which is 64. |
| /// |
| /// The maximum number of bytes that may be sent in a message is |
| /// **ZX_CHANNEL_MAX_MSG_BYTES**, which is 65536. |
| /// |
| /// Messages are drained by [`zx_channel_read()`] or [`zx_channel_read_etc()`]. |
| /// Failure to drain the messages in a timely fashion can cause excessive kernel |
| /// memory to be used, which might generate an exception. See |
| /// [ipc limits](/docs/concepts/kernel/ipc_limits.md) for details. |
| /// |
| /// ### ZX_CHANNEL_WRITE_USE_IOVEC option |
| /// |
| /// When the **ZX_CHANNEL_WRITE_USE_IOVEC** option is specified, `bytes` is |
| /// interpreted as an array of `zx_channel_iovec_t`, specifying slices of bytes to |
| /// sequentially copy to the message in order. `num_bytes` specifies the number of |
| /// `zx_channel_iovec_t` array elements in `bytes`. |
| /// |
| /// ```c |
| /// typedef struct zx_channel_iovec { |
| /// const void* buffer; // User-space bytes. |
| /// uint32_t capacity; // Number of bytes. |
| /// uint32_t reserved; // Reserved. |
| /// } zx_channel_iovec_t; |
| /// ``` |
| /// |
| /// There can be at most **ZX_CHANNEL_MAX_MSG_IOVEC** or `8192` |
| /// `zx_channel_iovec_t` elements of the `bytes` array with the sum of `capacity` |
| /// across all `zx_channel_iovec_t` not exceeding **ZX_CHANNEL_MAX_MSG_BYTES** or |
| /// `65536` bytes. `buffer` need not be aligned and it may only be `NULL` if |
| /// `capacity` is zero. `reserved` must be set to zero. |
| /// |
| /// Either all `zx_channel_iovec_t` are copied and the message is sent, or none |
| /// are copied and the message is not sent. Usage for sending handles is unchanged. |
| /// |
| /// ## Rights |
| /// |
| /// *handle* must be of type **ZX_OBJ_TYPE_CHANNEL** and have **ZX_RIGHT_WRITE**. |
| /// |
| /// Every entry of *handles* must have **ZX_RIGHT_TRANSFER**. |
| /// |
| /// ## Return value |
| /// |
| /// `zx_channel_write()` returns **ZX_OK** on success. |
| /// |
| /// ## Errors |
| /// |
| /// **ZX_ERR_BAD_HANDLE** `handle` is not a valid handle, any element in |
| /// `handles` is not a valid handle, or there are repeated handles among the |
| /// handles in the `handles` array. |
| /// |
| /// **ZX_ERR_WRONG_TYPE** `handle` is not a channel handle. |
| /// |
| /// **ZX_ERR_INVALID_ARGS** `bytes` is an invalid pointer, `handles` |
| /// is an invalid pointer, or `options` contains an invalid option bit. |
| /// If the **ZX_CHANNEL_WRITE_USE_IOVEC** option is specified, |
| /// **ZX_ERR_INVALID_ARGS** will be produced if the `buffer` field contains an |
| /// invalid pointer or if the reserved field is non-zero. |
| /// |
| /// **ZX_ERR_NOT_SUPPORTED** `handle` was found in the `handles` array. |
| /// A handle to the channel performing the write cannot be included in the |
| /// `handles` array. In other words a channel handle cannot be written to its own channel. |
| /// Fix the error by making sure that `handle` is not in the `handles` array. |
| /// |
| /// **ZX_ERR_ACCESS_DENIED** `handle` does not have **ZX_RIGHT_WRITE** or |
| /// any element in `handles` does not have **ZX_RIGHT_TRANSFER**. |
| /// |
| /// **ZX_ERR_PEER_CLOSED** The other side of the channel is closed. |
| /// |
| /// **ZX_ERR_NO_MEMORY** Failure due to lack of memory. |
| /// There is no good way for userspace to handle this (unlikely) error. |
| /// In a future build this error will no longer occur. |
| /// |
| /// **ZX_ERR_OUT_OF_RANGE** `num_bytes` or `num_handles` are larger than |
| /// **ZX_CHANNEL_MAX_MSG_BYTES** or **ZX_CHANNEL_MAX_MSG_HANDLES** respectively. |
| /// If the **ZX_CHANNEL_WRITE_USE_IOVEC** option is specified, |
| /// **ZX_ERR_OUT_OF_RANGE** will be produced if `num_bytes` is larger than |
| /// **ZX_CHANNEL_MAX_MSG_IOVEC** or the sum of the iovec capacities exceeds |
| /// **ZX_CHANNEL_MAX_MSG_BYTES**. |
| /// |
| /// ## Notes |
| /// |
| /// *num_handles* is a count of the number of elements in the *handles* |
| /// array, not its size in bytes. |
| /// |
| /// The byte size limitation on messages is not yet finalized. |
| /// |
| /// ## See also |
| /// |
| /// - [`zx_channel_call()`] |
| /// - [`zx_channel_create()`] |
| /// - [`zx_channel_read()`] |
| /// - [`zx_channel_read_etc()`] |
| /// - [`zx_channel_write_etc()`] |
| /// - [`zx_handle_close()`] |
| /// - [`zx_handle_replace()`] |
| /// - [`zx_object_wait_async()`] |
| /// - [`zx_object_wait_many()`] |
| /// - [`zx_object_wait_one()`] |
| /// |
| /// [`zx_channel_call()`]: channel_call.md |
| /// [`zx_channel_create()`]: channel_create.md |
| /// [`zx_channel_read()`]: channel_read.md |
| /// [`zx_channel_read_etc()`]: channel_read_etc.md |
| /// [`zx_channel_write_etc()`]: channel_write_etc.md |
| /// [`zx_handle_close()`]: handle_close.md |
| /// [`zx_handle_replace()`]: handle_replace.md |
| /// [`zx_object_wait_async()`]: object_wait_async.md |
| /// [`zx_object_wait_many()`]: object_wait_many.md |
| /// [`zx_object_wait_one()`]: object_wait_one.md |
| strict Write(resource struct { |
| handle Handle:CHANNEL; |
| options uint32; |
| @voidptr |
| @size32 |
| bytes vector<byte>:CHANNEL_MAX_MSG_BYTES; |
| @release |
| @size32 |
| handles vector<Handle>:CHANNEL_MAX_MSG_HANDLES; |
| }) -> () error Status; |
| |
| /// ## Summary |
| /// |
| /// Write a message to a channel. |
| /// |
| /// ## Declaration |
| /// |
| /// ```c |
| /// #include <zircon/syscalls.h> |
| /// |
| /// zx_status_t zx_channel_write_etc(zx_handle_t handle, |
| /// uint32_t options, |
| /// const void* bytes, |
| /// uint32_t num_bytes, |
| /// zx_handle_disposition_t* handles, |
| /// uint32_t num_handles); |
| /// ``` |
| /// |
| /// ## Description |
| /// |
| /// Like [`zx_channel_write()`] it attempts to write a message of *num_bytes* |
| /// bytes and *num_handles* handles to the channel specified by *handle*, but in |
| /// addition it will perform operations for the handles that are being |
| /// transferred with *handles* being an array of `zx_handle_disposition_t`: |
| /// |
| /// ``` |
| /// typedef struct zx_handle_disposition { |
| /// zx_handle_op_t operation; |
| /// zx_handle_t handle; |
| /// zx_obj_type_t type; |
| /// zx_rights_t rights; |
| /// zx_status_t result; |
| /// } zx_handle_disposition_t; |
| /// ``` |
| /// In zx_handle_disposition_t, *handle* is the source handle to be operated on, |
| /// *rights* is the desired final rights (not a mask) and *result* must be set |
| /// to **ZX_OK**. All source handles must have **ZX_RIGHT_TRANSFER**, but |
| /// it can be removed in *rights* so that it is not available to the message |
| /// receiver. |
| /// |
| /// *type* is used to perform validation of the object type that the caller |
| /// expects *handle* to be. It can be *ZX_OBJ_TYPE_NONE* to skip validation |
| /// checks or one of `zx_obj_type_t` defined types. |
| /// |
| /// The operation applied to *handle* is one of: |
| /// |
| /// * **ZX_HANDLE_OP_MOVE** This is equivalent to first issuing [`zx_handle_replace()`] then |
| /// [`zx_channel_write()`]. The source handle is always closed. |
| /// |
| /// * **ZX_HANDLE_OP_DUPLICATE** This is equivalent to first issuing [`zx_handle_duplicate()`] |
| /// then [`zx_channel_write()`]. The source handle always remains open and accessible to the |
| /// caller. |
| /// |
| /// *handle* will be transferred with capability *rights* which can be **ZX_RIGHT_SAME_RIGHTS** |
| /// or a reduced set of rights, or **ZX_RIGHT_NONE**. In addition, this operation allows removing |
| /// **ZX_RIGHT_TRANSFER** in *rights* so that capability is not available for the receiver. |
| /// |
| /// If any operation fails, the error code for that source handle is written to *result*, and the |
| /// first failure is made available in the return value for `zx_channel_write_etc()`. All |
| /// operations in the *handles* array are attempted, even if one or more operations fail. |
| /// |
| /// All operations for each entry must succeed for the message to be written. On success, handles |
| /// are attached to the message and will become available to the reader of that message from the |
| /// opposite end of the channel. |
| /// |
| /// It is invalid to include *handle* (the handle of the channel being written to) in the |
| /// *handles* array (the handles being sent in the message). |
| /// |
| /// The maximum number of handles that may be sent in a message is **ZX_CHANNEL_MAX_MSG_HANDLES**, |
| /// which is 64. |
| /// |
| /// The maximum number of bytes that may be sent in a message is **ZX_CHANNEL_MAX_MSG_BYTES**, |
| /// which is 65536. |
| /// |
| /// Messages are drained by [`zx_channel_read()`] or [`zx_channel_read_etc()`]. Failure to drain the |
| /// messages in a timely fashion can cause excessive kernel memory to be used, which might generate an |
| /// exception. See [ipc limits](/docs/concepts/kernel/ipc_limits.md) for details. |
| /// |
| /// ### ZX_CHANNEL_WRITE_USE_IOVEC option |
| /// |
| /// When the **ZX_CHANNEL_WRITE_USE_IOVEC** option is specified, `bytes` is |
| /// interpreted as an array of `zx_channel_iovec_t`, specifying slices of bytes to |
| /// sequentially copy to the message in order. `num_bytes` specifies the number of |
| /// `zx_channel_iovec_t` array elements in `bytes`. |
| /// |
| /// ```c |
| /// typedef struct zx_channel_iovec { |
| /// const void* buffer; // User-space bytes. |
| /// uint32_t capacity; // Number of bytes. |
| /// uint32_t reserved; // Reserved. |
| /// } zx_channel_iovec_t; |
| /// ``` |
| /// |
| /// There can be at most **ZX_CHANNEL_MAX_MSG_IOVEC** or `8192` |
| /// `zx_channel_iovec_t` elements of the `bytes` array with the sum of `capacity` |
| /// across all `zx_channel_iovec_t` not exceeding **ZX_CHANNEL_MAX_MSG_BYTES** or |
| /// `65536` bytes. `buffer` need not be aligned and it may only be `NULL` if |
| /// `capacity` is zero. `reserved` must be set to zero. |
| /// |
| /// Either all `zx_channel_iovec_t` are copied and the message is sent, or none |
| /// are copied and the message is not sent. Usage for sending handles is unchanged. |
| /// |
| /// ## Rights |
| /// |
| /// *handle* must be of type **ZX_OBJ_TYPE_CHANNEL** and have **ZX_RIGHT_WRITE**. |
| /// |
| /// Every entry of *handles* must have **ZX_RIGHT_TRANSFER**. |
| /// |
| /// ## Return value |
| /// |
| /// `zx_channel_write_etc()` returns **ZX_OK** on success. |
| /// |
| /// ## Errors |
| /// |
| /// **ZX_ERR_BAD_HANDLE** *handle* is not a valid handle, any source handle in |
| /// *handles* is not a valid handle, or there are repeated handles |
| /// in the *handles* array if **ZX_HANDLE_OP_DUPLICATE** flags is not present. |
| /// |
| /// **ZX_ERR_WRONG_TYPE** *handle* is not a channel handle, or any source handle |
| /// in *handles* did not match the object type *type*. |
| /// |
| /// **ZX_ERR_INVALID_ARGS** *bytes* is an invalid pointer, *handles* |
| /// is an invalid pointer, or *options* is nonzero, or *operation* is not |
| /// one of ZX_HANDLE_OP_MOVE or ZX_HANDLE_OP_DUPLICATE, or any source |
| /// handle in *handles\[i\]->handle* did not have the rights specified in |
| /// *whandle\[i\]->rights*. |
| /// If the **ZX_CHANNEL_WRITE_USE_IOVEC** option is specified, |
| /// **ZX_ERR_INVALID_ARGS** will be produced if the *buffer* field contains an |
| /// invalid pointer or if the reserved field is non-zero. |
| /// |
| /// **ZX_ERR_NOT_SUPPORTED** *handle* is included in the *handles* array. |
| /// |
| /// **ZX_ERR_ACCESS_DENIED** *handle* does not have **ZX_RIGHT_WRITE** or |
| /// any source handle in *handles* does not have **ZX_RIGHT_TRANSFER**, or |
| /// any source handle in *handles* does not have **ZX_RIGHT_DUPLICATE** when |
| /// **ZX_HANDLE_OP_DUPLICATE** operation is specified. |
| /// |
| /// **ZX_ERR_PEER_CLOSED** The other side of the channel is closed. |
| /// |
| /// **ZX_ERR_NO_MEMORY** Failure due to lack of memory. |
| /// There is no good way for userspace to handle this (unlikely) error. |
| /// In a future build this error will no longer occur. |
| /// |
| /// **ZX_ERR_OUT_OF_RANGE** *num_bytes* or *num_handles* are larger than |
| /// **ZX_CHANNEL_MAX_MSG_BYTES** or **ZX_CHANNEL_MAX_MSG_HANDLES** respectively. |
| /// If the **ZX_CHANNEL_WRITE_USE_IOVEC** option is specified, |
| /// **ZX_ERR_OUT_OF_RANGE** will be produced if *num_bytes* is larger than |
| /// **ZX_CHANNEL_MAX_MSG_IOVEC** or the sum of the iovec capacities exceeds |
| /// **ZX_CHANNEL_MAX_MSG_BYTES**. |
| /// |
| /// ## Notes |
| /// |
| /// If the caller removes the **ZX_RIGHT_TRANSFER** to a handle attached |
| /// to a message, the reader of the message will receive a handle that cannot |
| /// be written to any other channel, but still can be using according to its |
| /// rights and can be closed if not needed. |
| /// |
| /// ## See also |
| /// |
| /// - [`zx_channel_call()`] |
| /// - [`zx_channel_create()`] |
| /// - [`zx_channel_read()`] |
| /// - [`zx_channel_read_etc()`] |
| /// - [`zx_channel_write()`] |
| /// |
| /// [`zx_channel_call()`]: channel_call.md |
| /// [`zx_channel_create()`]: channel_create.md |
| /// [`zx_channel_read()`]: channel_read.md |
| /// [`zx_channel_read_etc()`]: channel_read_etc.md |
| /// [`zx_channel_write()`]: channel_write.md |
| /// [`zx_handle_duplicate()`]: handle_duplicate.md |
| /// [`zx_handle_replace()`]: handle_replace.md |
| strict WriteEtc(resource struct { |
| handle Handle:CHANNEL; |
| options uint32; |
| @voidptr |
| @size32 |
| bytes vector<byte>:CHANNEL_MAX_MSG_BYTES; |
| @inout |
| @size32 |
| handles vector<HandleDisposition>:CHANNEL_MAX_MSG_HANDLES; |
| }) -> () error Status; |
| |
| @internal |
| strict CallNoretry(resource struct { |
| handle Handle:CHANNEL; |
| options uint32; |
| deadline Time; |
| args ChannelCallArgs; |
| }) -> (struct { |
| actual_bytes uint32; |
| actual_handles uint32; |
| }) error Status; |
| |
| @internal |
| strict CallFinish(resource struct { |
| deadline Time; |
| args ChannelCallArgs; |
| }) -> (struct { |
| actual_bytes uint32; |
| actual_handles uint32; |
| }) error Status; |
| |
| // TODO(scottmg): Express "All wr_handles of args must have ZX_RIGHT_TRANSFER." |
| /// ## Summary |
| /// |
| /// Send a message to a channel and await a reply. |
| /// |
| /// ## Declaration |
| /// |
| /// ```c |
| /// #include <zircon/syscalls.h> |
| /// |
| /// zx_status_t zx_channel_call(zx_handle_t handle, |
| /// uint32_t options, |
| /// zx_time_t deadline, |
| /// const zx_channel_call_args_t* args, |
| /// uint32_t* actual_bytes, |
| /// uint32_t* actual_handles); |
| /// ``` |
| /// |
| /// ## Description |
| /// |
| /// `zx_channel_call()` is like a combined [`zx_channel_write()`], [`zx_object_wait_one()`], |
| /// and [`zx_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 |
| /// userspace bookkeeping. |
| /// |
| /// The write and read phases of this operation behave like [`zx_channel_write()`] and |
| /// [`zx_channel_read()`] with the difference that their parameters are provided via the |
| /// `zx_channel_call_args_t` structure. |
| /// |
| /// The first four bytes of the written and read back messages are treated as a |
| /// transaction ID of type `zx_txid_t`. The kernel generates a txid for the |
| /// written message, replacing that part of the message as read from userspace. |
| /// The kernel generated txid will be between 0x80000000 and 0xFFFFFFFF, and will |
| /// not collide with any txid from any other `zx_channel_call()` in progress against |
| /// this channel endpoint. If the written message has a length of fewer than four |
| /// bytes, an error is reported. |
| /// |
| /// When the outbound message is written, simultaneously an interest is registered |
| /// for inbound messages of the matching txid. |
| /// |
| /// *deadline* may be automatically adjusted according to the job's [timer slack] |
| /// policy. |
| /// |
| /// While the slack-adjusted *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 |
| /// `zx_channel_call()`. |
| /// |
| /// If such a reply arrives after the slack-adjusted *deadline* has passed, it will |
| /// arrive in the general inbound message queue, cause **ZX_CHANNEL_READABLE** to be |
| /// signaled, etc. |
| /// |
| /// Inbound messages that are too large to fit in *rd_num_bytes* and *rd_num_handles* |
| /// are discarded and **ZX_ERR_BUFFER_TOO_SMALL** is returned in that case. |
| /// |
| /// As with [`zx_channel_write()`], the handles in *handles* are always consumed by |
| /// `zx_channel_call()` and no longer exist in the calling process. |
| /// |
| /// ### ZX_CHANNEL_WRITE_USE_IOVEC option |
| /// |
| /// When the **ZX_CHANNEL_WRITE_USE_IOVEC** option is specified, `wr_bytes` is |
| /// interpreted as an array of `zx_channel_iovec_t`, specifying slices of bytes to |
| /// sequentially copy to the message in order. `num_wr_bytes` specifies the number |
| /// of `zx_channel_iovec_t` array elements in `wr_bytes`. |
| /// |
| /// ```c |
| /// typedef struct zx_channel_iovec { |
| /// const void* buffer; // User-space bytes. |
| /// uint32_t capacity; // Number of bytes. |
| /// uint32_t reserved; // Reserved. |
| /// } zx_channel_iovec_t; |
| /// ``` |
| /// |
| /// There can be at most **ZX_CHANNEL_MAX_MSG_IOVEC** or `8192` |
| /// `zx_channel_iovec_t` elements of the `wr_bytes` array with the sum of |
| /// `capacity` across all `zx_channel_iovec_t` not exceeding |
| /// **ZX_CHANNEL_MAX_MSG_BYTES** or `65536` bytes. `buffer` need not be aligned and |
| /// it may only be `NULL` if `capacity` is zero. `reserved` must be set to zero. |
| /// |
| /// Either all `zx_channel_iovec_t` are copied and the message is sent, or none |
| /// are copied and the message is not sent. Usage for sending handles is unchanged. |
| /// |
| /// ## Rights |
| /// |
| /// *handle* must be of type **ZX_OBJ_TYPE_CHANNEL** and have **ZX_RIGHT_READ** and have **ZX_RIGHT_WRITE**. |
| /// |
| /// All wr_handles of *args* must have **ZX_RIGHT_TRANSFER**. |
| /// |
| /// ## Return value |
| /// |
| /// `zx_channel_call()` returns **ZX_OK** on success and the number of bytes and |
| /// count of handles in the reply message are returned via *actual_bytes* and |
| /// *actual_handles*, respectively. |
| /// |
| /// ## Errors |
| /// |
| /// **ZX_ERR_BAD_HANDLE** *handle* is not a valid handle, any element in |
| /// *handles* is not a valid handle, or there are duplicates among the handles |
| /// in the *handles* array. |
| /// |
| /// **ZX_ERR_WRONG_TYPE** *handle* is not a channel handle. |
| /// |
| /// **ZX_ERR_INVALID_ARGS** any of the provided pointers are invalid or null, |
| /// or *wr_num_bytes* is less than four, or *options* is nonzero. |
| /// If the **ZX_CHANNEL_WRITE_USE_IOVEC** option is specified, |
| /// **ZX_ERR_INVALID_ARGS** will be produced if the *buffer* field contains an |
| /// invalid pointer or if the reserved field is non-zero. |
| /// |
| /// **ZX_ERR_ACCESS_DENIED** *handle* does not have **ZX_RIGHT_WRITE** or |
| /// any element in *handles* does not have **ZX_RIGHT_TRANSFER**. |
| /// |
| /// **ZX_ERR_PEER_CLOSED** The other side of the channel was closed or became |
| /// closed while waiting for the reply. |
| /// |
| /// **ZX_ERR_CANCELED** *handle* was closed while waiting for a reply. TODO(fxbug.dev/34013): |
| /// Transferring a channel with pending calls currently leads to undefined behavior. With |
| /// the current implementation, transferring such a channel does not interrupt the |
| /// pending calls, as it does not close the underlying channel endpoint. Programs should |
| /// be aware of this behavior, but they **must not** rely on it. |
| /// |
| /// **ZX_ERR_NO_MEMORY** Failure due to lack of memory. |
| /// There is no good way for userspace to handle this (unlikely) error. |
| /// In a future build this error will no longer occur. |
| /// |
| /// **ZX_ERR_OUT_OF_RANGE** *wr_num_bytes* or *wr_num_handles* are larger than the |
| /// largest allowable size for channel messages. |
| /// If the **ZX_CHANNEL_WRITE_USE_IOVEC** option is specified, |
| /// **ZX_ERR_OUT_OF_RANGE** will be produced if *num_bytes* is larger than |
| /// **ZX_CHANNEL_MAX_MSG_IOVEC** or the sum of the iovec capacities exceeds |
| /// **ZX_CHANNEL_MAX_MSG_BYTES**. |
| /// |
| /// **ZX_ERR_BUFFER_TOO_SMALL** *rd_num_bytes* or *rd_num_handles* are too small |
| /// to contain the reply message. |
| /// |
| /// **ZX_ERR_NOT_SUPPORTED** one of the handles in *handles* was *handle* |
| /// (the handle to the channel being written to). |
| /// |
| /// ## Notes |
| /// |
| /// The facilities provided by `zx_channel_call()` can interoperate with message dispatchers |
| /// using [`zx_channel_read()`] and [`zx_channel_write()`] directly, provided the following rules |
| /// are observed: |
| /// |
| /// 1. A server receiving synchronous messages via [`zx_channel_read()`] should ensure that the |
| /// txid of incoming messages is reflected back in outgoing responses via [`zx_channel_write()`] |
| /// so that clients using `zx_channel_call()` can correctly route the replies. |
| /// |
| /// 2. A client sending messages via [`zx_channel_write()`] that will be replied to should ensure |
| /// that it uses txids between 0 and 0x7FFFFFFF only, to avoid colliding with other threads |
| /// communicating via `zx_channel_call()`. |
| /// |
| /// If a `zx_channel_call()` returns due to **ZX_ERR_TIMED_OUT**, if the server eventually replies, |
| /// at some point in the future, the reply *could* match another outbound request (provided about |
| /// 2^31 `zx_channel_call()`s have happened since the original request. This syscall is designed |
| /// around the expectation that timeouts are generally fatal and clients do not expect to continue |
| /// communications on a channel that is timing out. |
| /// |
| /// ## See also |
| /// |
| /// - [timer slack] |
| /// - [`zx_channel_create()`] |
| /// - [`zx_channel_read()`] |
| /// - [`zx_channel_write()`] |
| /// - [`zx_handle_close()`] |
| /// - [`zx_handle_replace()`] |
| /// - [`zx_object_wait_async()`] |
| /// - [`zx_object_wait_many()`] |
| /// - [`zx_object_wait_one()`] |
| /// |
| /// [timer slack]: /docs/concepts/kernel/timer_slack.md |
| /// [`zx_channel_create()`]: channel_create.md |
| /// [`zx_channel_read()`]: channel_read.md |
| /// [`zx_channel_write()`]: channel_write.md |
| /// [`zx_handle_close()`]: handle_close.md |
| /// [`zx_handle_replace()`]: handle_replace.md |
| /// [`zx_object_wait_async()`]: object_wait_async.md |
| /// [`zx_object_wait_many()`]: object_wait_many.md |
| /// [`zx_object_wait_one()`]: object_wait_one.md |
| @blocking |
| @vdsocall |
| strict Call(resource struct { |
| handle Handle:CHANNEL; |
| options uint32; |
| deadline Time; |
| args ChannelCallArgs; |
| }) -> (struct { |
| actual_bytes uint32; |
| actual_handles uint32; |
| }) error Status; |
| |
| @internal |
| strict CallEtcNoretry(resource struct { |
| handle Handle:CHANNEL; |
| options uint32; |
| deadline Time; |
| @inout |
| args ChannelCallEtcArgs; |
| }) -> (struct { |
| actual_bytes uint32; |
| actual_handles uint32; |
| }) error Status; |
| |
| @internal |
| strict CallEtcFinish(resource struct { |
| deadline Time; |
| @inout |
| args ChannelCallEtcArgs; |
| }) -> (struct { |
| actual_bytes uint32; |
| actual_handles uint32; |
| }) error Status; |
| |
| /// ## Summary |
| /// |
| /// Send a message to a channel and await a reply. |
| /// |
| /// ## Declaration |
| /// |
| /// ```c |
| /// #include <zircon/syscalls.h> |
| /// |
| /// zx_status_t zx_channel_call_etc(zx_handle_t handle, |
| /// uint32_t options, |
| /// zx_time_t deadline, |
| /// zx_channel_call_etc_args_t* args, |
| /// uint32_t* actual_bytes, |
| /// uint32_t* actual_handles); |
| /// ``` |
| /// |
| /// ## Description |
| /// |
| /// `zx_channel_call_etc()` writes a request to a channel and blocks until it |
| /// receives a response. It is an extension of [`zx_channel_call()`] that |
| /// incorporates the functionality of [`zx_channel_write_etc()`] and |
| /// [`zx_channel_read_etc()`] for write and read phases, instead of the more basic |
| /// [`zx_channel_write()`] and [`zx_channel_read()`]. See [`zx_channel_call()`] for |
| /// a full description of channel calls. |
| /// |
| /// The effect of a call to `zx_channel_call_etc()` is similar to performing a |
| /// sequence of calls to [`zx_channel_write_etc()`], [`zx_object_wait_one()`] and |
| /// [`zx_channel_read_etc()`] in that order. However, a key difference is that |
| /// `zx_channel_call_etc()` will wait for an incoming message matches the outgoing |
| /// message's transaction id. The arguments that would be supplied to |
| /// `zx_channel_read_etc()` and `zx_channel_write_etc()` are instead specified with |
| /// `zx_channel_call_etc_args_t`: |
| /// |
| /// ``` |
| /// typedef struct { |
| /// const void* wr_bytes; |
| /// zx_handle_disposition_t* wr_handles; |
| /// void *rd_bytes; |
| /// zx_handle_info_t* rd_handles; |
| /// uint32_t wr_num_bytes; |
| /// uint32_t wr_num_handles; |
| /// uint32_t rd_num_bytes; |
| /// uint32_t rd_num_handles; |
| /// } zx_channel_call_etc_args_t; |
| /// ``` |
| /// |
| /// ### ZX_CHANNEL_WRITE_USE_IOVEC option |
| /// |
| /// When the **ZX_CHANNEL_WRITE_USE_IOVEC** option is specified, `wr_bytes` is |
| /// interpreted as an array of `zx_channel_iovec_t`, specifying slices of bytes to |
| /// sequentially copy to the message in order. `num_wr_bytes` specifies the number |
| /// of `zx_channel_iovec_t` array elements in `wr_bytes`. |
| /// |
| /// ```c |
| /// typedef struct zx_channel_iovec { |
| /// const void* buffer; // User-space bytes. |
| /// uint32_t capacity; // Number of bytes. |
| /// uint32_t reserved; // Reserved. |
| /// } zx_channel_iovec_t; |
| /// ``` |
| /// |
| /// There can be at most **ZX_CHANNEL_MAX_MSG_IOVEC** or `8192` |
| /// `zx_channel_iovec_t` elements of the `wr_bytes` array with the sum of |
| /// `capacity` across all `zx_channel_iovec_t` not exceeding |
| /// **ZX_CHANNEL_MAX_MSG_BYTES** or `65536` bytes. `buffer` need not be aligned and |
| /// it may only be `NULL` if `capacity` is zero. `reserved` must be set to zero. |
| /// |
| /// Either all `zx_channel_iovec_t` are copied and the message is sent, or none |
| /// are copied and the message is not sent. Usage for sending handles is unchanged. |
| /// |
| /// ## Rights |
| /// |
| /// *handle* must be of type **ZX_OBJ_TYPE_CHANNEL** and have **ZX_RIGHT_READ** and have **ZX_RIGHT_WRITE**. |
| /// |
| /// All wr_handles of *args* must have **ZX_RIGHT_TRANSFER**. |
| /// |
| /// ## Return value |
| /// |
| /// `zx_channel_call_etc()` returns **ZX_OK** on success and the number of bytes and |
| /// count of handles in the reply message are returned via *actual_bytes* and |
| /// *actual_handles*, respectively. |
| /// |
| /// ## Errors |
| /// |
| /// **ZX_ERR_BAD_HANDLE** *handle* is not a valid handle, any element in |
| /// *handles* is not a valid handle, or there are duplicates among the handles |
| /// in the *handles* array. |
| /// |
| /// **ZX_ERR_WRONG_TYPE** *handle* is not a channel handle, or any source |
| /// handle in *wr_handles* did not match the object type type. |
| /// |
| /// **ZX_ERR_INVALID_ARGS** any of the provided pointers are invalid or null, |
| /// or *wr_num_bytes* is less than four, or *options* is nonzero, or any source |
| /// handle in *wr_handles\[i\]->handle* did not have the rights specified in |
| /// *wr_handle\[i\]->rights*. |
| /// If the **ZX_CHANNEL_WRITE_USE_IOVEC** option is specified, |
| /// **ZX_ERR_INVALID_ARGS** will be produced if the *buffer* field contains an |
| /// invalid pointer or if the reserved field is non-zero. |
| /// |
| /// **ZX_ERR_ACCESS_DENIED** *handle* does not have **ZX_RIGHT_WRITE** or |
| /// any element in *handles* does not have **ZX_RIGHT_TRANSFER**. |
| /// |
| /// **ZX_ERR_PEER_CLOSED** The other side of the channel was closed or became |
| /// closed while waiting for the reply. |
| /// |
| /// **ZX_ERR_CANCELED** *handle* was closed while waiting for a reply. TODO(fxbug.dev/34013): |
| /// Transferring a channel with pending calls currently leads to undefined behavior. With |
| /// the current implementation, transferring such a channel does not interrupt the |
| /// pending calls, as it does not close the underlying channel endpoint. Programs should |
| /// be aware of this behavior, but they **must not** rely on it. |
| /// |
| /// **ZX_ERR_NO_MEMORY** Failure due to lack of memory. |
| /// There is no good way for userspace to handle this (unlikely) error. |
| /// In a future build this error will no longer occur. |
| /// |
| /// **ZX_ERR_OUT_OF_RANGE** *wr_num_bytes* or *wr_num_handles* are larger than the |
| /// largest allowable size for channel messages. |
| /// If the **ZX_CHANNEL_WRITE_USE_IOVEC** option is specified, |
| /// **ZX_ERR_OUT_OF_RANGE** will be produced if *num_bytes* is larger than |
| /// **ZX_CHANNEL_MAX_MSG_IOVEC** or the sum of the iovec capacities exceeds |
| /// **ZX_CHANNEL_MAX_MSG_BYTES**. |
| /// |
| /// **ZX_ERR_BUFFER_TOO_SMALL** *rd_num_bytes* or *rd_num_handles* are too small |
| /// to contain the reply message. |
| /// |
| /// **ZX_ERR_NOT_SUPPORTED** one of the handles in *handles* was *handle* |
| /// (the handle to the channel being written to). |
| /// |
| /// [`zx_channel_call()`]: channel_call.md |
| /// [`zx_channel_create()`]: channel_create.md |
| /// [`zx_channel_read()`]: channel_read.md |
| /// [`zx_channel_read_etc()`]: channel_read_etc.md |
| /// [`zx_channel_write()`]: channel_write.md |
| /// [`zx_channel_write_etc()`]: channel_write_etc.md |
| /// [`zx_object_wait_one()`]: object_wait_one.md |
| @blocking |
| @vdsocall |
| // TODO(scottmg): Express "All wr_handles of args must have ZX_RIGHT_TRANSFER." |
| strict CallEtc(resource struct { |
| handle Handle:CHANNEL; |
| options uint32; |
| deadline Time; |
| @inout |
| args ChannelCallEtcArgs; |
| }) -> (struct { |
| actual_bytes uint32; |
| actual_handles uint32; |
| }) error Status; |
| }; |