blob: 332c034acbcc6fdbca373fdedb0d7b3f4a594fae [file] [log] [blame]
// 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.
#include "proxy-iostate.h"
#include <fbl/auto_lock.h>
#include "../shared/log.h"
#include "connection-destroyer.h"
#include "zx-device.h"
namespace devmgr {
ProxyIostate::~ProxyIostate() {
fbl::AutoLock guard(&dev->proxy_ios_lock);
ZX_ASSERT(dev->proxy_ios != this);
}
// Handling RPC From Proxy Devices to BusDevs
void ProxyIostate::HandleRpc(fbl::unique_ptr<ProxyIostate> conn, async_dispatcher_t* dispatcher,
async::WaitBase* wait, zx_status_t status,
const zx_packet_signal_t* signal) {
auto handle_destroy = [&conn]() {
fbl::AutoLock guard(&conn->dev->proxy_ios_lock);
// If proxy_ios is not |conn|, then it's had a packet queued already to
// destroy it, so we should let the queued destruction handle things.
// Otherwise we should destroy it.
if (conn->dev->proxy_ios == conn.get()) {
// Mark proxy_ios as disconnected, so that CancelLocked doesn't try to
// destroy it too
conn->dev->proxy_ios = nullptr;
// The actual destruction will happen when |conn| goes out of scope.
} else {
__UNUSED auto ptr = conn.release();
}
};
if (status != ZX_OK) {
return handle_destroy();
}
if (conn->dev == nullptr) {
log(RPC_SDW, "proxy-rpc: stale rpc? (ios=%p)\n", conn.get());
// Do not re-issue the wait here
return handle_destroy();
}
if (signal->observed & ZX_CHANNEL_READABLE) {
log(RPC_SDW, "proxy-rpc: rpc readable (ios=%p,dev=%p)\n", conn.get(), conn->dev.get());
zx_status_t r = conn->dev->ops->rxrpc(conn->dev->ctx, wait->object());
if (r != ZX_OK) {
log(RPC_SDW, "proxy-rpc: rpc cb error %d (ios=%p,dev=%p)\n", r, conn.get(),
conn->dev.get());
return handle_destroy();
}
BeginWait(std::move(conn), dispatcher);
return;
}
if (signal->observed & ZX_CHANNEL_PEER_CLOSED) {
log(RPC_SDW, "proxy-rpc: peer closed (ios=%p,dev=%p)\n", conn.get(), conn->dev.get());
return handle_destroy();
}
log(ERROR, "devhost: no work? %08x\n", signal->observed);
BeginWait(std::move(conn), dispatcher);
}
zx_status_t ProxyIostate::Create(const fbl::RefPtr<zx_device_t>& dev, zx::channel rpc,
async_dispatcher_t* dispatcher) {
// This must be held for the adding of the channel to the port, since the
// async loop may run immediately after that point.
fbl::AutoLock guard(&dev->proxy_ios_lock);
if (dev->proxy_ios) {
dev->proxy_ios->CancelLocked(dispatcher);
}
auto ios = std::make_unique<ProxyIostate>(dev);
if (ios == nullptr) {
return ZX_ERR_NO_MEMORY;
}
ios->set_channel(std::move(rpc));
// |ios| is will be owned by the async loop. |dev| holds a reference that will be
// cleared prior to destruction.
dev->proxy_ios = ios.get();
zx_status_t status = BeginWait(std::move(ios), dispatcher);
if (status != ZX_OK) {
dev->proxy_ios = nullptr;
return status;
}
return ZX_OK;
}
void ProxyIostate::CancelLocked(async_dispatcher_t* dispatcher) {
ZX_ASSERT(this->dev->proxy_ios == this);
this->dev->proxy_ios = nullptr;
// TODO(teisenbe): We should probably check the return code in case the
// queue was full
ConnectionDestroyer::Get()->QueueProxyConnection(dispatcher, this);
}
} // namespace devmgr