| // Copyright 2016 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. |
| |
| #include "devhost.h" |
| #include "device-internal.h" |
| |
| #include <assert.h> |
| #include <fcntl.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <threads.h> |
| |
| #include <lib/sync/completion.h> |
| #include <ddk/device.h> |
| #include <ddk/driver.h> |
| |
| #include <zircon/device/device.h> |
| #include <zircon/device/vfs.h> |
| |
| #include <zircon/processargs.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/types.h> |
| |
| #include <fuchsia/io/c/fidl.h> |
| #include <lib/fdio/debug.h> |
| #include <lib/fdio/io.h> |
| #include <lib/fdio/vfs.h> |
| #include <lib/fidl/coding.h> |
| |
| #define ZXDEBUG 0 |
| |
| #define CAN_WRITE(ios) (ios->flags & ZX_FS_RIGHT_WRITABLE) |
| #define CAN_READ(ios) (ios->flags & ZX_FS_RIGHT_READABLE) |
| |
| void describe_error(zx_handle_t h, zx_status_t status) { |
| zxrio_describe_t msg; |
| memset(&msg, 0, sizeof(msg)); |
| msg.hdr.ordinal = ZXFIDL_ON_OPEN; |
| msg.status = status; |
| zx_channel_write(h, 0, &msg, sizeof(msg), nullptr, 0); |
| zx_handle_close(h); |
| } |
| |
| static zx_status_t create_description(zx_device_t* dev, zxrio_describe_t* msg, |
| zx_handle_t* handle) { |
| memset(msg, 0, sizeof(*msg)); |
| msg->hdr.ordinal = ZXFIDL_ON_OPEN; |
| msg->extra.tag = FDIO_PROTOCOL_DEVICE; |
| msg->status = ZX_OK; |
| msg->extra_ptr = (zxrio_node_info_t*)FIDL_ALLOC_PRESENT; |
| *handle = ZX_HANDLE_INVALID; |
| if (dev->event != ZX_HANDLE_INVALID) { |
| zx_status_t r; |
| if ((r = zx_handle_duplicate(dev->event, ZX_RIGHTS_BASIC, |
| handle)) != ZX_OK) { |
| msg->status = r; |
| return r; |
| } |
| msg->extra.device.e = FIDL_HANDLE_PRESENT; |
| } else { |
| msg->extra.device.e = FIDL_HANDLE_ABSENT; |
| } |
| |
| return ZX_OK; |
| } |
| |
| devhost_iostate_t* create_devhost_iostate(zx_device_t* dev) { |
| devhost_iostate_t* ios; |
| if ((ios = static_cast<devhost_iostate_t*>(calloc(1, sizeof(devhost_iostate_t)))) == nullptr) { |
| return nullptr; |
| } |
| ios->dev = dev; |
| return ios; |
| } |
| |
| static zx_status_t devhost_get_handles(zx_handle_t rh, zx_device_t* dev, |
| const char* path, uint32_t flags) { |
| zx_status_t r; |
| devhost_iostate_t* newios; |
| // detect response directives and discard all other |
| // protocol flags |
| bool describe = flags & ZX_FS_FLAG_DESCRIBE; |
| flags &= (~ZX_FS_FLAG_DESCRIBE); |
| |
| if ((newios = create_devhost_iostate(dev)) == nullptr) { |
| if (describe) { |
| describe_error(rh, ZX_ERR_NO_MEMORY); |
| } |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| newios->flags = flags; |
| |
| if ((r = device_open_at(dev, &dev, path, flags)) < 0) { |
| fprintf(stderr, "devhost_get_handles(%p:%s) open path='%s', r=%d\n", |
| dev, dev->name, path ? path : "", r); |
| goto fail; |
| } |
| newios->dev = dev; |
| |
| if (describe) { |
| zxrio_describe_t info; |
| zx_handle_t handle; |
| if ((r = create_description(dev, &info, &handle)) != ZX_OK) { |
| goto fail_open; |
| } |
| uint32_t hcount = (handle != ZX_HANDLE_INVALID) ? 1 : 0; |
| r = zx_channel_write(rh, 0, &info, sizeof(info), &handle, hcount); |
| if (r != ZX_OK) { |
| goto fail_open; |
| } |
| } |
| |
| // If we can't add the new ios and handle to the dispatcher our only option |
| // is to give up and tear down. In practice, this should never happen. |
| if ((r = devhost_start_iostate(newios, rh)) < 0) { |
| fprintf(stderr, "devhost_get_handles: failed to start iostate\n"); |
| goto fail; |
| } |
| return ZX_OK; |
| |
| fail_open: |
| device_close(dev, flags); |
| fail: |
| free(newios); |
| if (describe) { |
| describe_error(rh, r); |
| } else { |
| zx_handle_close(rh); |
| } |
| return r; |
| } |
| |
| #define DO_READ 0 |
| #define DO_WRITE 1 |
| |
| static ssize_t do_sync_io(zx_device_t* dev, uint32_t opcode, void* buf, size_t count, zx_off_t off) { |
| size_t actual; |
| zx_status_t r; |
| if (opcode == DO_READ) { |
| r = dev_op_read(dev, buf, count, off, &actual); |
| } else { |
| r = dev_op_write(dev, buf, count, off, &actual); |
| } |
| if (r < 0) { |
| return r; |
| } else { |
| return actual; |
| } |
| } |
| |
| static ssize_t do_ioctl(zx_device_t* dev, uint32_t op, const void* in_buf, size_t in_len, |
| void* out_buf, size_t out_len, size_t* out_actual) { |
| zx_status_t r; |
| switch (op) { |
| case IOCTL_DEVICE_BIND: { |
| char* drv_libname = in_len > 0 ? (char*)in_buf : nullptr; |
| if (in_len > PATH_MAX) { |
| return ZX_ERR_BAD_PATH; |
| } |
| drv_libname[in_len] = 0; |
| if ((r = device_bind(dev, drv_libname) < 0)) { |
| return r; |
| } |
| *out_actual = r; |
| return ZX_OK; |
| } |
| case IOCTL_DEVICE_GET_EVENT_HANDLE: { |
| if (out_len < sizeof(zx_handle_t)) { |
| return ZX_ERR_BUFFER_TOO_SMALL; |
| } |
| auto event = static_cast<zx_handle_t*>(out_buf); |
| if ((r = zx_handle_duplicate(dev->event, ZX_RIGHTS_BASIC | ZX_RIGHT_READ, event)) != ZX_OK) { |
| return r; |
| } |
| *out_actual = sizeof(zx_handle_t); |
| return ZX_OK; |
| } |
| case IOCTL_DEVICE_GET_DRIVER_NAME: { |
| if (!dev->driver) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| const char* name = dev->driver->name; |
| if (name == nullptr) { |
| name = "unknown"; |
| } |
| size_t len = strlen(name); |
| if (out_len <= len) { |
| r = ZX_ERR_BUFFER_TOO_SMALL; |
| } else { |
| strncpy(static_cast<char*>(out_buf), name, len); |
| *out_actual = len; |
| r = ZX_OK; |
| } |
| return r; |
| } |
| case IOCTL_DEVICE_GET_DEVICE_NAME: { |
| size_t actual = strlen(dev->name) + 1; |
| if (out_len < actual) { |
| return ZX_ERR_BUFFER_TOO_SMALL; |
| } |
| memcpy(out_buf, dev->name, actual); |
| *out_actual = actual; |
| return ZX_OK; |
| } |
| case IOCTL_DEVICE_GET_TOPO_PATH: { |
| size_t actual; |
| if ((r = devhost_get_topo_path(dev, static_cast<char*>(out_buf), out_len, &actual)) < 0) { |
| return r; |
| } |
| *out_actual = actual; |
| return ZX_OK; |
| } |
| case IOCTL_DEVICE_DEBUG_SUSPEND: { |
| return dev_op_suspend(dev, 0); |
| } |
| case IOCTL_DEVICE_DEBUG_RESUME: { |
| return dev_op_resume(dev, 0); |
| } |
| case IOCTL_VFS_QUERY_FS: { |
| const char* devhost_name = "devfs:host"; |
| if (out_len < sizeof(vfs_query_info_t) + strlen(devhost_name)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| vfs_query_info_t* info = (vfs_query_info_t*) out_buf; |
| memset(info, 0, sizeof(*info)); |
| memcpy(info->name, devhost_name, strlen(devhost_name)); |
| *out_actual = sizeof(vfs_query_info_t) + strlen(devhost_name); |
| return ZX_OK; |
| } |
| case IOCTL_DEVICE_GET_DRIVER_LOG_FLAGS: { |
| if (!dev->driver) { |
| return ZX_ERR_UNAVAILABLE; |
| } |
| if (out_len < sizeof(uint32_t)) { |
| return ZX_ERR_BUFFER_TOO_SMALL; |
| } |
| *((uint32_t *)out_buf) = dev->driver->driver_rec->log_flags; |
| *out_actual = sizeof(uint32_t); |
| return ZX_OK; |
| } |
| case IOCTL_DEVICE_SET_DRIVER_LOG_FLAGS: { |
| if (!dev->driver) { |
| return ZX_ERR_UNAVAILABLE; |
| } |
| if (in_len < sizeof(driver_log_flags_t)) { |
| return ZX_ERR_BUFFER_TOO_SMALL; |
| } |
| driver_log_flags_t* flags = (driver_log_flags_t *)in_buf; |
| dev->driver->driver_rec->log_flags &= ~flags->clear; |
| dev->driver->driver_rec->log_flags |= flags->set; |
| *out_actual = sizeof(driver_log_flags_t); |
| return ZX_OK; |
| } |
| default: { |
| return dev_op_ioctl(dev, op, in_buf, in_len, out_buf, out_len, out_actual); |
| } |
| } |
| } |
| |
| static zx_status_t fidl_node_clone(void* ctx, uint32_t flags, zx_handle_t object) { |
| auto ios = static_cast<devhost_iostate_t*>(ctx); |
| flags = ios->flags | (flags & ZX_FS_FLAG_DESCRIBE); |
| devhost_get_handles(object, ios->dev, nullptr, flags); |
| return ZX_OK; |
| } |
| |
| static zx_status_t fidl_node_close(void* ctx, fidl_txn_t* txn) { |
| auto ios = static_cast<devhost_iostate_t*>(ctx); |
| device_close(ios->dev, ios->flags); |
| // The ios released its reference to this device by calling |
| // device_close() Put an invalid pointer in its dev field to ensure any |
| // use-after-release attempts explode. |
| ios->dev = reinterpret_cast<zx_device_t*>(0xdead); |
| fuchsia_io_NodeClose_reply(txn, ZX_OK); |
| return ERR_DISPATCHER_DONE; |
| } |
| |
| static zx_status_t fidl_node_describe(void* ctx, fidl_txn_t* txn) { |
| auto ios = static_cast<devhost_iostate_t*>(ctx); |
| zx_device_t* dev = ios->dev; |
| fuchsia_io_NodeInfo info; |
| memset(&info, 0, sizeof(info)); |
| info.tag = fuchsia_io_NodeInfoTag_device; |
| if (dev->event != ZX_HANDLE_INVALID) { |
| zx_status_t status = zx_handle_duplicate( |
| dev->event, ZX_RIGHTS_BASIC, &info.device.event); |
| if (status != ZX_OK) { |
| return status; |
| } |
| } |
| return fuchsia_io_NodeDescribe_reply(txn, &info); |
| } |
| |
| static zx_status_t fidl_directory_open(void* ctx, uint32_t flags, uint32_t mode, |
| const char* path_data, size_t path_size, |
| zx_handle_t object) { |
| auto ios = static_cast<devhost_iostate_t*>(ctx); |
| zx_device_t* dev = ios->dev; |
| if ((path_size < 1) || (path_size > 1024)) { |
| zx_handle_close(object); |
| return ZX_OK; |
| } |
| // TODO(smklein): Avoid assuming paths are null-terminated; this is only |
| // safe because the path is the last secondary object in the DirectoryOpen |
| // request. |
| ((char*) path_data)[path_size] = 0; |
| |
| if (!strcmp(path_data, ".")) { |
| path_data = nullptr; |
| } |
| devhost_get_handles(object, dev, path_data, flags); |
| return ZX_OK; |
| } |
| |
| static zx_status_t fidl_directory_unlink(void* ctx, const char* path_data, |
| size_t path_size, fidl_txn_t* txn) { |
| return fuchsia_io_DirectoryUnlink_reply(txn, ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| static zx_status_t fidl_directory_readdirents(void* ctx, uint64_t max_out, fidl_txn_t* txn) { |
| return fuchsia_io_DirectoryReadDirents_reply(txn, ZX_ERR_NOT_SUPPORTED, nullptr, 0); |
| } |
| |
| static zx_status_t fidl_directory_rewind(void* ctx, fidl_txn_t* txn) { |
| return fuchsia_io_DirectoryRewind_reply(txn, ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| static zx_status_t fidl_directory_gettoken(void* ctx, fidl_txn_t* txn) { |
| return fuchsia_io_DirectoryGetToken_reply(txn, ZX_ERR_NOT_SUPPORTED, ZX_HANDLE_INVALID); |
| } |
| |
| static zx_status_t fidl_directory_rename(void* ctx, const char* src_data, |
| size_t src_size, zx_handle_t dst_parent_token, |
| const char* dst_data, size_t dst_size, |
| fidl_txn_t* txn) { |
| zx_handle_close(dst_parent_token); |
| return fuchsia_io_DirectoryRename_reply(txn, ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| static zx_status_t fidl_directory_link(void* ctx, const char* src_data, |
| size_t src_size, zx_handle_t dst_parent_token, |
| const char* dst_data, size_t dst_size, |
| fidl_txn_t* txn) { |
| zx_handle_close(dst_parent_token); |
| return fuchsia_io_DirectoryLink_reply(txn, ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| static const fuchsia_io_Directory_ops_t kDirectoryOps = []() { |
| fuchsia_io_Directory_ops_t ops; |
| ops.Open = fidl_directory_open; |
| ops.Unlink = fidl_directory_unlink; |
| ops.ReadDirents = fidl_directory_readdirents; |
| ops.Rewind = fidl_directory_rewind; |
| ops.GetToken = fidl_directory_gettoken; |
| ops.Rename = fidl_directory_rename; |
| ops.Link = fidl_directory_link; |
| return ops; |
| }(); |
| |
| static zx_status_t fidl_file_read(void* ctx, uint64_t count, fidl_txn_t* txn) { |
| auto ios = static_cast<devhost_iostate_t*>(ctx); |
| zx_device_t* dev = ios->dev; |
| if (!CAN_READ(ios)) { |
| return fuchsia_io_FileRead_reply(txn, ZX_ERR_ACCESS_DENIED, nullptr, 0); |
| } else if (count > ZXFIDL_MAX_MSG_BYTES) { |
| return fuchsia_io_FileRead_reply(txn, ZX_ERR_INVALID_ARGS, nullptr, 0); |
| } |
| |
| uint8_t data[count]; |
| size_t actual = 0; |
| zx_status_t status = ZX_OK; |
| ssize_t r = do_sync_io(dev, DO_READ, data, count, ios->io_off); |
| if (r >= 0) { |
| ios->io_off += r; |
| actual = r; |
| } else { |
| status = static_cast<zx_status_t>(r); |
| } |
| return fuchsia_io_FileRead_reply(txn, status, data, actual); |
| } |
| |
| static zx_status_t fidl_file_readat(void* ctx, uint64_t count, uint64_t offset, fidl_txn_t* txn) { |
| auto ios = static_cast<devhost_iostate_t*>(ctx); |
| if (!CAN_READ(ios)) { |
| return fuchsia_io_FileReadAt_reply(txn, ZX_ERR_ACCESS_DENIED, nullptr, 0); |
| } else if (count > ZXFIDL_MAX_MSG_BYTES) { |
| return fuchsia_io_FileReadAt_reply(txn, ZX_ERR_INVALID_ARGS, nullptr, 0); |
| } |
| |
| uint8_t data[count]; |
| size_t actual = 0; |
| zx_status_t status = ZX_OK; |
| ssize_t r = do_sync_io(ios->dev, DO_READ, data, count, offset); |
| if (r >= 0) { |
| actual = r; |
| } else { |
| status = static_cast<zx_status_t>(r); |
| } |
| return fuchsia_io_FileReadAt_reply(txn, status, data, actual); |
| } |
| |
| static zx_status_t fidl_file_write(void* ctx, const uint8_t* data, size_t count, fidl_txn_t* txn) { |
| auto ios = static_cast<devhost_iostate_t*>(ctx); |
| if (!CAN_WRITE(ios)) { |
| return fuchsia_io_FileWrite_reply(txn, ZX_ERR_ACCESS_DENIED, 0); |
| } |
| size_t actual = 0; |
| zx_status_t status = ZX_OK; |
| ssize_t r = do_sync_io(ios->dev, DO_WRITE, (uint8_t*) data, count, ios->io_off); |
| if (r >= 0) { |
| ios->io_off += r; |
| actual = r; |
| } else { |
| status = static_cast<zx_status_t>(r); |
| } |
| return fuchsia_io_FileWrite_reply(txn, status, actual); |
| } |
| |
| static zx_status_t fidl_file_writeat(void* ctx, const uint8_t* data, size_t count, |
| uint64_t offset, fidl_txn_t* txn) { |
| auto ios = static_cast<devhost_iostate_t*>(ctx); |
| if (!CAN_WRITE(ios)) { |
| return fuchsia_io_FileWriteAt_reply(txn, ZX_ERR_ACCESS_DENIED, 0); |
| } |
| |
| size_t actual = 0; |
| zx_status_t status = ZX_OK; |
| ssize_t r = do_sync_io(ios->dev, DO_WRITE, (uint8_t*) data, count, offset); |
| if (r >= 0) { |
| actual = r; |
| } else { |
| status = static_cast<zx_status_t>(r); |
| } |
| return fuchsia_io_FileWriteAt_reply(txn, status, actual); |
| } |
| |
| static zx_status_t fidl_file_seek(void* ctx, int64_t offset, fuchsia_io_SeekOrigin start, |
| fidl_txn_t* txn) { |
| auto ios = static_cast<devhost_iostate_t*>(ctx); |
| size_t end, n; |
| end = dev_op_get_size(ios->dev); |
| switch (start) { |
| case fuchsia_io_SeekOrigin_START: |
| if ((offset < 0) || ((size_t)offset > end)) { |
| goto bad_args; |
| } |
| n = offset; |
| break; |
| case fuchsia_io_SeekOrigin_CURRENT: |
| // TODO: track seekability with flag, don't update off |
| // at all on read/write if not seekable |
| n = ios->io_off + offset; |
| if (offset < 0) { |
| // if negative seek |
| if (n > ios->io_off) { |
| // wrapped around |
| goto bad_args; |
| } |
| } else { |
| // positive seek |
| if (n < ios->io_off) { |
| // wrapped around |
| goto bad_args; |
| } |
| } |
| break; |
| case fuchsia_io_SeekOrigin_END: |
| n = end + offset; |
| if (offset <= 0) { |
| // if negative or exact-end seek |
| if (n > end) { |
| // wrapped around |
| goto bad_args; |
| } |
| } else { |
| if (n < end) { |
| // wrapped around |
| goto bad_args; |
| } |
| } |
| break; |
| default: |
| goto bad_args; |
| } |
| if (n > end) { |
| // devices may not seek past the end |
| goto bad_args; |
| } |
| ios->io_off = n; |
| return fuchsia_io_FileSeek_reply(txn, ZX_OK, ios->io_off); |
| |
| bad_args: |
| return fuchsia_io_FileSeek_reply(txn, ZX_ERR_INVALID_ARGS, 0); |
| } |
| |
| static zx_status_t fidl_file_truncate(void* ctx, uint64_t length, fidl_txn_t* txn) { |
| return fuchsia_io_FileTruncate_reply(txn, ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| static zx_status_t fidl_file_getflags(void* ctx, fidl_txn_t* txn) { |
| return fuchsia_io_FileGetFlags_reply(txn, ZX_ERR_NOT_SUPPORTED, 0); |
| } |
| |
| static zx_status_t fidl_file_setflags(void* ctx, uint32_t flags, fidl_txn_t* txn) { |
| return fuchsia_io_FileSetFlags_reply(txn, ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| static zx_status_t fidl_file_getvmo(void* ctx, uint32_t flags, fidl_txn_t* txn) { |
| return fuchsia_io_FileGetVmo_reply(txn, ZX_ERR_NOT_SUPPORTED, ZX_HANDLE_INVALID); |
| } |
| |
| static const fuchsia_io_File_ops_t kFileOps = []() { |
| fuchsia_io_File_ops_t ops; |
| ops.Read = fidl_file_read; |
| ops.ReadAt = fidl_file_readat; |
| ops.Write = fidl_file_write; |
| ops.WriteAt = fidl_file_writeat; |
| ops.Seek = fidl_file_seek; |
| ops.Truncate = fidl_file_truncate; |
| ops.GetFlags = fidl_file_getflags; |
| ops.SetFlags = fidl_file_setflags; |
| ops.GetVmo = fidl_file_getvmo; |
| return ops; |
| }(); |
| |
| static zx_status_t fidl_node_sync(void* ctx, fidl_txn_t* txn) { |
| auto ios = static_cast<devhost_iostate_t*>(ctx); |
| size_t actual; |
| ssize_t r = do_ioctl(ios->dev, IOCTL_DEVICE_SYNC, nullptr, 0, nullptr, 0, &actual); |
| auto status = static_cast<zx_status_t>(r); |
| return fuchsia_io_NodeSync_reply(txn, status); |
| } |
| |
| static zx_status_t fidl_node_getattr(void* ctx, fidl_txn_t* txn) { |
| auto ios = static_cast<devhost_iostate_t*>(ctx); |
| fuchsia_io_NodeAttributes attributes; |
| memset(&attributes, 0, sizeof(attributes)); |
| attributes.mode = V_TYPE_CDEV | V_IRUSR | V_IWUSR; |
| attributes.content_size = dev_op_get_size(ios->dev); |
| attributes.link_count = 1; |
| return fuchsia_io_NodeGetAttr_reply(txn, ZX_OK, &attributes); |
| } |
| |
| static zx_status_t fidl_node_setattr(void* ctx, uint32_t flags, |
| const fuchsia_io_NodeAttributes* attributes, |
| fidl_txn_t* txn) { |
| return fuchsia_io_NodeSetAttr_reply(txn, ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| static zx_status_t fidl_node_ioctl(void* ctx, uint32_t opcode, uint64_t max_out, |
| const zx_handle_t* handles_data, size_t handles_count, |
| const uint8_t* in_data, size_t in_count, |
| fidl_txn_t* txn) { |
| auto ios = static_cast<devhost_iostate_t*>(ctx); |
| char in_buf[FDIO_IOCTL_MAX_INPUT]; |
| size_t hsize = handles_count * sizeof(zx_handle_t); |
| if ((in_count > FDIO_IOCTL_MAX_INPUT) || (max_out > ZXFIDL_MAX_MSG_BYTES)) { |
| zx_handle_close_many(handles_data, handles_count); |
| return fuchsia_io_NodeIoctl_reply(txn, ZX_ERR_INVALID_ARGS, nullptr, 0, |
| nullptr, 0); |
| } |
| memcpy(in_buf, in_data, in_count); |
| memcpy(in_buf, handles_data, hsize); |
| |
| uint8_t out[max_out]; |
| zx_handle_t* out_handles = (zx_handle_t*) out; |
| size_t out_count = 0; |
| ssize_t r = do_ioctl(ios->dev, opcode, in_buf, in_count, out, max_out, &out_count); |
| size_t out_hcount = 0; |
| if (r >= 0) { |
| switch (IOCTL_KIND(opcode)) { |
| case IOCTL_KIND_GET_HANDLE: |
| out_hcount = 1; |
| break; |
| case IOCTL_KIND_GET_TWO_HANDLES: |
| out_hcount = 2; |
| break; |
| case IOCTL_KIND_GET_THREE_HANDLES: |
| out_hcount = 3; |
| break; |
| default: |
| out_hcount = 0; |
| break; |
| } |
| } |
| |
| auto status = static_cast<zx_status_t>(r); |
| return fuchsia_io_NodeIoctl_reply(txn, status, out_handles, out_hcount, out, out_count); |
| } |
| |
| static const fuchsia_io_Node_ops_t kNodeOps = { |
| .Clone = fidl_node_clone, |
| .Close = fidl_node_close, |
| .Describe = fidl_node_describe, |
| .Sync = fidl_node_sync, |
| .GetAttr = fidl_node_getattr, |
| .SetAttr = fidl_node_setattr, |
| .Ioctl = fidl_node_ioctl, |
| }; |
| |
| zx_status_t devhost_fidl_handler(fidl_msg_t* msg, fidl_txn_t* txn, void* cookie) { |
| fidl_message_header_t* hdr = (fidl_message_header_t*) msg->bytes; |
| if (hdr->ordinal >= fuchsia_io_NodeCloneOrdinal && |
| hdr->ordinal <= fuchsia_io_NodeIoctlOrdinal) { |
| return fuchsia_io_Node_dispatch(cookie, txn, msg, &kNodeOps); |
| } else if (hdr->ordinal >= fuchsia_io_FileReadOrdinal && |
| hdr->ordinal <= fuchsia_io_FileGetVmoOrdinal) { |
| return fuchsia_io_File_dispatch(cookie, txn, msg, &kFileOps); |
| } else if (hdr->ordinal >= fuchsia_io_DirectoryOpenOrdinal && |
| hdr->ordinal <= fuchsia_io_DirectoryLinkOrdinal) { |
| return fuchsia_io_Directory_dispatch(cookie, txn, msg, &kDirectoryOps); |
| } else { |
| auto ios = static_cast<devhost_iostate_t*>(cookie); |
| return dev_op_message(ios->dev, msg, txn); |
| } |
| } |