| // Copyright 2018 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 "fake-transport-base.h" |
| |
| #include <lib/async/cpp/task.h> |
| #include <lib/zx/channel.h> |
| #include <zircon/status.h> |
| #include <zircon/types.h> |
| |
| #include <cstdio> |
| #include <future> |
| #include <thread> |
| |
| #include <ddktl/fidl.h> |
| |
| namespace fidl_tel_transport = fuchsia_hardware_telephony_transport; |
| namespace fidl_tel_snoop = fuchsia_telephony_snoop; |
| |
| namespace tel_fake { |
| |
| Device::Device(zx_device_t* device) : parent_(device) {} |
| |
| void Device::SetChannel(SetChannelRequestView request, SetChannelCompleter::Sync& completer) { |
| zx_status_t status = ZX_OK; |
| zx_status_t set_channel_res = SetChannelToDevice(std::move(request->transport)); |
| if (set_channel_res == ZX_OK) { |
| completer.ReplySuccess(); |
| } else { |
| completer.ReplyError(set_channel_res); |
| } |
| if (status != ZX_OK) { |
| goto done; |
| } |
| if (set_channel_res == ZX_OK) { |
| status = SetAsyncWait(); |
| if (status != ZX_OK) { |
| CloseCtrlChannel(); |
| } |
| } |
| done: |
| return; |
| } |
| |
| void Device::SetNetwork(SetNetworkRequestView request, SetNetworkCompleter::Sync& completer) { |
| SetNetworkStatusToDevice(request->connected); |
| completer.Reply(); |
| } |
| |
| void Device::SetSnoopChannel(SetSnoopChannelRequestView request, |
| SetSnoopChannelCompleter::Sync& completer) { |
| zx_status_t set_snoop_res = SetSnoopChannelToDevice(std::move(request->interface)); |
| fidl_tel_transport::wire::QmiSetSnoopChannelResult result; |
| if (set_snoop_res == ZX_OK) { |
| completer.ReplySuccess(); |
| } else { |
| completer.ReplyError(set_snoop_res); |
| } |
| } |
| |
| void Device::DdkMessage(fidl_incoming_msg_t msg, device_fidl_txn_t txn) { |
| fidl::WireDispatch<fidl_tel_transport::Qmi>( |
| this, fidl::IncomingHeaderAndMessage::FromEncodedCMessage(msg), |
| ddk::FromDeviceFIDLTransaction(txn)); |
| } |
| |
| zx_status_t Device::SetSnoopChannelToDevice( |
| ::fidl::ClientEnd<::fidl_tel_snoop::Publisher> channel) { |
| zx_status_t result = ZX_OK; |
| zx_port_packet_t packet; |
| zx_status_t status; |
| // Initialize a port to watch whether the other handle of snoop channel has |
| // closed |
| if (!snoop_port_.is_valid()) { |
| status = zx::port::create(0, &snoop_port_); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, |
| "tel-fake-transport: failed to create a port to watch snoop channel: " |
| "%s\n", |
| zx_status_get_string(status)); |
| return status; |
| } |
| } else { |
| status = snoop_port_.wait(zx::deadline_after(zx::sec(0)), &packet); |
| if (status == ZX_ERR_TIMED_OUT) { |
| zxlogf(ERROR, "tel-fake-transport: timed out: %s", zx_status_get_string(status)); |
| } else if (packet.signal.observed & ZX_CHANNEL_PEER_CLOSED) { |
| zxlogf(INFO, "tel-fake-transport: snoop channel peer closed"); |
| // snoop_channel_.reset(); |
| (void)snoop_client_end_.TakeChannel().release(); |
| } |
| } |
| |
| if (snoop_client_end_.is_valid()) { |
| zxlogf(ERROR, "tel-fake-transport: snoop channel already connected"); |
| result = ZX_ERR_ALREADY_BOUND; |
| } else if (!channel.is_valid()) { |
| zxlogf(ERROR, "tel-fake-transport: get invalid snoop channel handle"); |
| result = ZX_ERR_BAD_HANDLE; |
| } else { |
| snoop_client_end_ = std::move(channel); |
| snoop_client_end_.channel().wait_async(snoop_port_, 0, ZX_CHANNEL_PEER_CLOSED, 0); |
| } |
| return result; |
| } |
| |
| zx_status_t Device::CloseCtrlChannel() { |
| zx_status_t ret_val = ZX_OK; |
| if (ctrl_channel_.is_valid()) { |
| ctrl_channel_.reset(); |
| } |
| return ret_val; |
| } |
| |
| zx_status_t Device::SetAsyncWait() { |
| zx_status_t status = ctrl_channel_.wait_async(ctrl_channel_port_, kChannelMsg, |
| ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED, 0); |
| return status; |
| } |
| |
| zx_status_t Device::EventLoopCleanup() { return ctrl_channel_port_.cancel(ctrl_channel_, 0); } |
| |
| void Device::Release() { delete this; } |
| |
| void Device::Unbind() { |
| if (!fake_ctrl_thread_.joinable()) { |
| zxlogf(ERROR, "tel-fake-transport: unbind(): thrd unjoinable"); |
| return; |
| } |
| zx_port_packet_t packet = {}; |
| packet.key = kTerminateMsg; |
| ctrl_channel_port_.queue(&packet); |
| zxlogf(INFO, "tel-fake-transport: unbind(): joining thread"); |
| fake_ctrl_thread_.join(); |
| |
| device_unbind_reply(tel_dev_); |
| } |
| |
| zx_status_t Device::GetProtocol(uint32_t proto_id, void* out_proto) { |
| if (proto_id != ZX_PROTOCOL_QMI_TRANSPORT) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t Device::SetChannelToDevice(zx::channel transport) { |
| zx_status_t result = ZX_OK; |
| if (ctrl_channel_.is_valid()) { |
| zxlogf(ERROR, "tel-fake-transport: already bound, failing"); |
| result = ZX_ERR_ALREADY_BOUND; |
| } else if (transport == ZX_HANDLE_INVALID) { |
| zxlogf(ERROR, "tel-fake-transport: invalid channel handle"); |
| result = ZX_ERR_BAD_HANDLE; |
| } else { |
| ctrl_channel_.reset(transport.release()); |
| } |
| return result; |
| } |
| |
| zx_status_t Device::SetNetworkStatusToDevice(bool connected) { |
| connected_ = connected; |
| return ZX_OK; |
| } |
| |
| } // namespace tel_fake |