// Copyright 2020 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include #include #include #include #include #include #include "linux-headers/virtwl.h" // NOLINT(build/include_directory) #include "wayland_channel.h" // NOLINT(build/include_directory) #define VIRTWL_DEVICE "/dev/wl0" #define MAX_SEND_SIZE (DEFAULT_BUFFER_SIZE - sizeof(struct virtwl_ioctl_txn)) VirtWaylandChannel::~VirtWaylandChannel() { if (virtwl_ >= 0) close(virtwl_); } int32_t VirtWaylandChannel::init() { virtwl_ = open(VIRTWL_DEVICE, O_RDWR); int32_t ret; struct WaylandBufferCreateInfo create_info = {0}; struct WaylandBufferCreateOutput create_output = {0}; create_output.fd = -1; if (virtwl_ == -1) return -errno; create_info.dmabuf = true; supports_dmabuf_ = true; ret = allocate(create_info, create_output); if (ret && errno == ENOTTY) { fprintf(stderr, "warning: virtwl-dmabuf driver not supported by host," " using virtwl instead\n"); supports_dmabuf_ = false; } else if (create_output.fd >= 0) { // Close the returned dmabuf fd in case the invalid dmabuf metadata // given above actually manages to return an fd successfully. close(create_output.fd); create_output.fd = -1; } return 0; } bool VirtWaylandChannel::supports_dmabuf(void) { return supports_dmabuf_; } int32_t VirtWaylandChannel::create_context(int& out_channel_fd) { int ret; struct virtwl_ioctl_new new_ctx = { .type = VIRTWL_IOCTL_NEW_CTX, .fd = -1, .flags = 0, }; ret = ioctl(virtwl_, VIRTWL_IOCTL_NEW, &new_ctx); if (ret) return -errno; out_channel_fd = new_ctx.fd; return 0; } int32_t VirtWaylandChannel::create_pipe(int& out_pipe_fd) { int ret; struct virtwl_ioctl_new new_pipe = { .type = VIRTWL_IOCTL_NEW_PIPE_READ, .fd = -1, .flags = 0, }; new_pipe.size = 0; ret = ioctl(virtwl_, VIRTWL_IOCTL_NEW, &new_pipe); if (ret) return -errno; out_pipe_fd = new_pipe.fd; return 0; } int32_t VirtWaylandChannel::send(const struct WaylandSendReceive& send) { int ret; uint8_t ioctl_buffer[DEFAULT_BUFFER_SIZE]; struct virtwl_ioctl_txn* txn = (struct virtwl_ioctl_txn*)ioctl_buffer; void* send_data = &txn->data; if (send.data_size > max_send_size()) return -EINVAL; memcpy(send_data, send.data, send.data_size); for (uint32_t i = 0; i < WAYLAND_MAX_FDs; i++) { if (i < send.num_fds) { txn->fds[i] = send.fds[i]; } else { txn->fds[i] = -1; } } txn->len = send.data_size; ret = ioctl(send.channel_fd, VIRTWL_IOCTL_SEND, txn); if (ret) return -errno; return 0; } int32_t VirtWaylandChannel::handle_channel_event( enum WaylandChannelEvent& event_type, struct WaylandSendReceive& receive, int& out_read_pipe) { int ret; uint8_t ioctl_buffer[DEFAULT_BUFFER_SIZE]; struct virtwl_ioctl_txn* txn = (struct virtwl_ioctl_txn*)ioctl_buffer; size_t max_recv_size = sizeof(ioctl_buffer) - sizeof(struct virtwl_ioctl_txn); void* recv_data = &txn->data; txn->len = max_recv_size; ret = ioctl(receive.channel_fd, VIRTWL_IOCTL_RECV, txn); if (ret) return -errno; for (uint32_t i = 0; i < WAYLAND_MAX_FDs; i++) { if (txn->fds[i] >= 0) { receive.num_fds++; receive.fds[i] = txn->fds[i]; } else { break; } } if (txn->len > 0) { receive.data = reinterpret_cast(calloc(1, txn->len)); if (!receive.data) return -ENOMEM; memcpy(receive.data, recv_data, txn->len); } receive.data_size = txn->len; event_type = WaylandChannelEvent::Receive; return 0; } int32_t VirtWaylandChannel::allocate( const struct WaylandBufferCreateInfo& create_info, struct WaylandBufferCreateOutput& create_output) { int ret; struct virtwl_ioctl_new ioctl_new = {0}; if (create_info.dmabuf) { ioctl_new.type = VIRTWL_IOCTL_NEW_DMABUF; ioctl_new.fd = -1; ioctl_new.flags = 0; ioctl_new.dmabuf.width = create_info.width; ioctl_new.dmabuf.height = create_info.height; ioctl_new.dmabuf.format = create_info.drm_format; } else { ioctl_new.type = VIRTWL_IOCTL_NEW_ALLOC; ioctl_new.fd = -1; ioctl_new.flags = 0; ioctl_new.size = create_info.size; create_output.host_size = create_info.size; } ret = ioctl(virtwl_, VIRTWL_IOCTL_NEW, &ioctl_new); if (ret) return -errno; if (create_info.dmabuf) { create_output.strides[0] = ioctl_new.dmabuf.stride0; create_output.strides[1] = ioctl_new.dmabuf.stride1; create_output.strides[2] = ioctl_new.dmabuf.stride2; create_output.offsets[0] = ioctl_new.dmabuf.offset0; create_output.offsets[1] = ioctl_new.dmabuf.offset1; create_output.offsets[2] = ioctl_new.dmabuf.offset2; // The common layer will consider multi-planar sizes as needed. create_output.host_size = create_output.strides[0] * create_info.height; } create_output.fd = ioctl_new.fd; return 0; } int32_t VirtWaylandChannel::sync(int dmabuf_fd, uint64_t flags) { struct virtwl_ioctl_dmabuf_sync sync = {0}; int ret; sync.flags = flags; ret = ioctl(dmabuf_fd, VIRTWL_IOCTL_DMABUF_SYNC, &sync); if (ret) return -errno; return 0; } int32_t VirtWaylandChannel::handle_pipe(int read_fd, bool readable, bool& hang_up) { return 0; } size_t VirtWaylandChannel::max_send_size(void) { return MAX_SEND_SIZE; }