blob: a35815b2181c17d1906ea9bfd8dbbbf4cb07a821 [file] [log] [blame]
// Copyright 2017 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 "hda-codec-connection.h"
#include <stdio.h>
#include <zircon/assert.h>
#include <memory>
#include <utility>
#include <fbl/algorithm.h>
#include <fbl/auto_lock.h>
#include <intel-hda/utils/codec-commands.h>
#include "debug-logging.h"
#include "intel-hda-controller.h"
#include "intel-hda-stream.h"
#include "utils.h"
namespace audio::intel_hda {
#define SET_DEVICE_PROP(_prop, _value) \
do { \
static_assert(PROP_##_prop < std::size(decltype(dev_props_){}), "Invalid Device Property ID"); \
dev_props_[PROP_##_prop].id = BIND_IHDA_CODEC_##_prop; \
dev_props_[PROP_##_prop].value = (_value); \
} while (false)
HdaCodecConnection::ProbeCommandListEntry HdaCodecConnection::PROBE_COMMANDS[] = {
{.verb = GET_PARAM(CodecParam::VENDOR_ID), .parse = &HdaCodecConnection::ParseVidDid},
{.verb = GET_PARAM(CodecParam::REVISION_ID), .parse = &HdaCodecConnection::ParseRevisionId},
};
zx_protocol_device_t HdaCodecConnection::CODEC_DEVICE_THUNKS = []() {
zx_protocol_device_t ops = {};
ops.version = DEVICE_OPS_VERSION;
ops.message = [](void* ctx, fidl_incoming_msg_t msg, device_fidl_txn_t txn) {
HdaCodecConnection* thiz = static_cast<HdaCodecConnection*>(ctx);
fidl::WireDispatch<fuchsia_hardware_intel_hda::CodecDevice>(
thiz, fidl::IncomingHeaderAndMessage::FromEncodedCMessage(msg),
ddk::FromDeviceFIDLTransaction(txn));
};
return ops;
}();
ihda_codec_protocol_ops_t HdaCodecConnection::CODEC_PROTO_THUNKS = {
.get_driver_channel = [](void* ctx, zx_handle_t* channel_out) -> zx_status_t {
ZX_DEBUG_ASSERT(ctx);
return static_cast<HdaCodecConnection*>(ctx)->CodecGetDispatcherChannel(channel_out);
},
};
HdaCodecConnection::HdaCodecConnection(IntelHDAController& controller, uint8_t codec_id)
: controller_(controller), codec_id_(codec_id), loop_(&kAsyncLoopConfigNeverAttachToThread) {
::memset(&dev_props_, 0, sizeof(dev_props_));
dev_props_[PROP_PROTOCOL].id = BIND_PROTOCOL;
dev_props_[PROP_PROTOCOL].value = ZX_PROTOCOL_IHDA_CODEC;
const auto& info = controller_.dev_info();
snprintf(log_prefix_, sizeof(log_prefix_), "IHDA Codec %02x:%02x.%01x/%02x", info.bus_id,
info.dev_id, info.func_id, codec_id_);
}
fbl::RefPtr<HdaCodecConnection> HdaCodecConnection::Create(IntelHDAController& controller,
uint8_t codec_id) {
ZX_DEBUG_ASSERT(codec_id < HDA_MAX_CODECS);
fbl::AllocChecker ac;
auto ret = fbl::AdoptRef(new (&ac) HdaCodecConnection(controller, codec_id));
if (!ac.check()) {
GLOBAL_LOG(ERROR, "Out of memory attempting to allocate codec");
return nullptr;
}
zx_status_t status = ret->loop_.StartThread("codec-connection-loop");
if (status != ZX_OK) {
GLOBAL_LOG(ERROR, "Could not start loop thread (res = %d)", status);
return nullptr;
}
return ret;
}
zx_status_t HdaCodecConnection::Startup() {
ZX_DEBUG_ASSERT(state_ == State::PROBING);
for (const auto& probe_cmd : PROBE_COMMANDS) {
CodecCommand cmd(id(), 0u, probe_cmd.verb);
auto job = CodecCmdJobAllocator::New(cmd);
if (job == nullptr) {
LOG(ERROR, "Failed to allocate job during initial codec probe!");
return ZX_ERR_NO_MEMORY;
}
zx_status_t res = controller_.QueueCodecCmd(std::move(job));
if (res != ZX_OK) {
LOG(ERROR, "Failed to queue job (res = %d) during initial codec probe!", res);
return res;
}
}
return ZX_OK;
}
void HdaCodecConnection::SendCORBResponse(const fbl::RefPtr<Channel>& channel,
const CodecResponse& resp,
uint32_t transaction_id) const {
ZX_DEBUG_ASSERT(channel != nullptr);
ihda_codec_send_corb_cmd_resp_t payload;
payload.hdr.transaction_id = transaction_id;
payload.hdr.cmd = IHDA_CODEC_SEND_CORB_CMD;
payload.data = resp.data;
payload.data_ex = resp.data_ex;
zx_status_t res = channel->Write(&payload, sizeof(payload));
if (res != ZX_OK) {
LOG(DEBUG, "Error writing CORB response (%08x, %08x) res = %d", resp.data, resp.data_ex, res);
}
}
void HdaCodecConnection::ProcessSolicitedResponse(const CodecResponse& resp,
std::unique_ptr<CodecCmdJob>&& job) {
if (state_ == State::PROBING) {
// Are we still in the PROBING stage of things? If so, this job should
// have no response channel assigned to it, and we should still be
// waiting for responses from the codec to complete the initial probe.
ZX_DEBUG_ASSERT(probe_rx_ndx_ < std::size(PROBE_COMMANDS));
const auto& cmd = PROBE_COMMANDS[probe_rx_ndx_];
zx_status_t res = (this->*cmd.parse)(resp);
if (res == ZX_OK) {
++probe_rx_ndx_;
} else {
LOG(ERROR, "Error parsing solicited response during codec probe! (data %08x)", resp.data);
// TODO(johngro) : shutdown and cleanup somehow.
state_ = State::FATAL_ERROR;
}
} else if (job->response_channel() != nullptr) {
LOG(TRACE, "Sending solicited response [%08x, %08x] to channel %p", resp.data, resp.data_ex,
job->response_channel().get());
// Does this job have a response channel? If so, attempt to send the
// response back on the channel (assuming that it is still open).
SendCORBResponse(job->response_channel(), resp, job->transaction_id());
}
}
void HdaCodecConnection::ProcessUnsolicitedResponse(const CodecResponse& resp) {
// If we still have a channel to our codec driver, grab a reference to it
// and send the unsolicited response to it.
fbl::RefPtr<Channel> codec_driver_channel;
{
fbl::AutoLock codec_driver_channel_lock(&codec_driver_channel_lock_);
codec_driver_channel = codec_driver_channel_;
}
if (codec_driver_channel)
SendCORBResponse(codec_driver_channel, resp);
}
void HdaCodecConnection::ProcessWakeupEvt() const {
// TODO(johngro) : handle wakeup events. Wakeup events are delivered for
// two reasons.
//
// 1) The codec had brought the controller out of a low power state for some
// reason.
// 2) The codec has been hot-unplugged.
//
// Currently, we support neither power management, nor hot-unplug. Just log
// the fact that we have been woken up and do nothing.
LOG(WARNING, "Wakeup event received - Don't know how to handle this yet!");
}
void HdaCodecConnection::Shutdown() {
// Close all existing connections and synchronize with any client threads
// who are currently processing requests.
state_ = State::SHUTTING_DOWN;
ProcessCodecDeactivate();
loop_.Shutdown();
// Give any active streams we had back to our controller.
IntelHDAStream::Tree streams;
{
fbl::AutoLock lock(&active_streams_lock_);
streams.swap(active_streams_);
}
while (!streams.is_empty())
controller_.ReturnStream(streams.pop_front());
state_ = State::SHUT_DOWN;
}
zx_status_t HdaCodecConnection::PublishDevice() {
// Generate our name.
char name[ZX_DEVICE_NAME_MAX];
snprintf(name, sizeof(name), "intel-hda-codec-%03u", codec_id_);
// Initialize our device and fill out the protocol hooks
device_add_args_t args = {};
args.version = DEVICE_ADD_ARGS_VERSION;
args.name = name;
args.ctx = this;
args.ops = &CODEC_DEVICE_THUNKS;
args.proto_id = ZX_PROTOCOL_IHDA_CODEC;
args.proto_ops = &CODEC_PROTO_THUNKS;
args.props = dev_props_;
args.prop_count = std::size(dev_props_);
// Publish the device.
zx_status_t res = device_add(controller_.dev_node(), &args, &dev_node_);
if (res != ZX_OK) {
LOG(ERROR, "Failed to add codec device for \"%s\" (res %d)", name, res);
return res;
}
return ZX_OK;
}
zx_status_t HdaCodecConnection::ParseVidDid(const CodecResponse& resp) {
props_.vid = static_cast<uint16_t>((resp.data >> 16) & 0xFFFF);
props_.did = static_cast<uint16_t>(resp.data & 0xFFFF);
SET_DEVICE_PROP(VID, props_.vid);
SET_DEVICE_PROP(DID, props_.did);
return (props_.vid != 0) ? ZX_OK : ZX_ERR_INTERNAL;
}
zx_status_t HdaCodecConnection::ParseRevisionId(const CodecResponse& resp) {
props_.ihda_vmaj = static_cast<uint8_t>((resp.data >> 20) & 0xF);
props_.ihda_vmin = static_cast<uint8_t>((resp.data >> 16) & 0xF);
props_.rev_id = static_cast<uint8_t>((resp.data >> 8) & 0xFF);
props_.step_id = static_cast<uint8_t>(resp.data & 0xFF);
SET_DEVICE_PROP(MAJOR_REV, props_.ihda_vmaj);
SET_DEVICE_PROP(MINOR_REV, props_.ihda_vmin);
SET_DEVICE_PROP(VENDOR_REV, props_.rev_id);
SET_DEVICE_PROP(VENDOR_STEP, props_.step_id);
state_ = State::FINDING_DRIVER;
return PublishDevice();
}
void HdaCodecConnection::GetChannel(GetChannelCompleter::Sync& completer) {
zx::channel channel_local;
zx::channel channel_remote;
if (zx_status_t status = zx::channel::create(0, &channel_local, &channel_remote);
status != ZX_OK) {
return completer.Close(status);
}
fbl::RefPtr<Channel> channel = Channel::Create(std::move(channel_local));
if (channel == nullptr) {
return completer.Close(ZX_ERR_NO_MEMORY);
}
fbl::AutoLock lock(&channel_lock_);
channel_ = std::move(channel);
channel_->SetHandler([codec = fbl::RefPtr(this)](async_dispatcher_t* dispatcher,
async::WaitBase* wait, zx_status_t status,
const zx_packet_signal_t* signal) {
codec->GetChannelSignalled(dispatcher, wait, status, signal);
});
if (zx_status_t status = channel_->BeginWait(loop_.dispatcher()); status != ZX_OK) {
channel_.reset();
// We let channel_remote go out of scope to trigger channel deactivation via peer close.
return completer.Close(status);
}
completer.Reply(std::move(channel_remote));
}
void HdaCodecConnection::GetChannelSignalled(async_dispatcher_t* dispatcher, async::WaitBase* wait,
zx_status_t status, const zx_packet_signal_t* signal) {
if (status != ZX_OK) {
if (status != ZX_ERR_CANCELED) { // Cancel is expected.
return;
}
}
bool readable_asserted = signal->observed & ZX_CHANNEL_READABLE;
bool peer_closed_asserted = signal->observed & ZX_CHANNEL_PEER_CLOSED;
if (readable_asserted) {
// Grab a reference to the channel, the processing may grab the lock.
fbl::RefPtr<Channel> channel;
{
fbl::AutoLock lock(&channel_lock_);
channel = channel_;
}
zx_status_t status = ProcessUserRequest(channel.get());
if (status != ZX_OK) {
peer_closed_asserted = true;
}
}
if (peer_closed_asserted) {
fbl::AutoLock lock(&channel_lock_);
channel_.reset();
} else if (readable_asserted) {
wait->Begin(dispatcher);
}
}
#define PROCESS_CMD_INNER(_req_ack, _ioctl, _payload, _handler) \
case _ioctl: \
if (req_size != sizeof(req._payload)) { \
LOG(DEBUG, "Bad " #_payload " request length (%u != %zu)", req_size, sizeof(req._payload)); \
return ZX_ERR_INVALID_ARGS; \
} \
if ((_req_ack) && (req.hdr.cmd & IHDA_NOACK_FLAG)) { \
LOG(DEBUG, "Cmd " #_payload \
" requires acknowledgement, but the " \
"NOACK flag was set!"); \
return ZX_ERR_INVALID_ARGS; \
}
#define PROCESS_CMD(_req_ack, _ioctl, _payload, _handler) \
PROCESS_CMD_INNER(_req_ack, _ioctl, _payload, _handler) \
return _handler(channel, req._payload)
#define PROCESS_CMD_WITH_HANDLE(_req_ack, _ioctl, _payload, _handler) \
PROCESS_CMD_INNER(_req_ack, _ioctl, _payload, _handler) \
return _handler(channel, req._payload, std::move(received_handle))
zx_status_t HdaCodecConnection::ProcessCodecRequest(Channel* channel) {
zx_status_t res;
uint32_t req_size;
union {
ihda_proto::CmdHdr hdr;
ihda_proto::SendCORBCmdReq corb_cmd;
ihda_proto::RequestStreamReq request_stream;
ihda_proto::ReleaseStreamReq release_stream;
ihda_proto::SetStreamFmtReq set_stream_fmt;
} req;
// TODO(johngro) : How large is too large?
static_assert(sizeof(req) <= 256, "Request buffer is too large to hold on the stack!");
// Read the user request.
ZX_DEBUG_ASSERT(channel != nullptr);
zx::handle received_handle;
res = channel->Read(&req, sizeof(req), &req_size, received_handle);
if (res != ZX_OK) {
LOG(DEBUG, "Failed to read user request (res %d)", res);
return res;
}
// Sanity checks.
if (req_size < sizeof(req.hdr)) {
LOG(DEBUG, "Client request too small to contain header (%u < %zu)", req_size, sizeof(req.hdr));
return ZX_ERR_INVALID_ARGS;
}
auto cmd_id = static_cast<ihda_cmd_t>(req.hdr.cmd & ~IHDA_NOACK_FLAG);
if (req.hdr.transaction_id == IHDA_INVALID_TRANSACTION_ID) {
LOG(DEBUG, "Invalid transaction ID in client request 0x%04x", cmd_id);
return ZX_ERR_INVALID_ARGS;
}
// Dispatch
LOG(TRACE, "Codec Request (cmd 0x%04x tid %u) len %u", req.hdr.cmd, req.hdr.transaction_id,
req_size);
switch (cmd_id) {
PROCESS_CMD(true, IHDA_CODEC_REQUEST_STREAM, request_stream, ProcessRequestStream);
PROCESS_CMD(false, IHDA_CODEC_RELEASE_STREAM, release_stream, ProcessReleaseStream);
PROCESS_CMD_WITH_HANDLE(false, IHDA_CODEC_SET_STREAM_FORMAT, set_stream_fmt,
ProcessSetStreamFmt);
PROCESS_CMD(false, IHDA_CODEC_SEND_CORB_CMD, corb_cmd, ProcessSendCORBCmd);
default:
LOG(DEBUG, "Unrecognized command ID 0x%04x", req.hdr.cmd);
return ZX_ERR_INVALID_ARGS;
}
}
zx_status_t HdaCodecConnection::ProcessUserRequest(Channel* channel) {
zx_status_t res;
uint32_t req_size;
union {
ihda_proto::CmdHdr hdr;
ihda_proto::GetIDsReq get_ids;
ihda_proto::SendCORBCmdReq corb_cmd;
} req;
// TODO(johngro) : How large is too large?
static_assert(sizeof(req) <= 256, "Request buffer is too large to hold on the stack!");
// Read the client request.
ZX_DEBUG_ASSERT(channel != nullptr);
res = channel->Read(&req, sizeof(req), &req_size);
if (res != ZX_OK) {
LOG(DEBUG, "Failed to read client request (res %d)", res);
return res;
}
// Sanity checks.
if (req_size < sizeof(req.hdr)) {
LOG(DEBUG, "Client request too small to contain header (%u < %zu)", req_size, sizeof(req.hdr));
return ZX_ERR_INVALID_ARGS;
}
auto cmd_id = static_cast<ihda_cmd_t>(req.hdr.cmd & ~IHDA_NOACK_FLAG);
if (req.hdr.transaction_id == IHDA_INVALID_TRANSACTION_ID) {
LOG(DEBUG, "Invalid transaction ID in client request 0x%04x", cmd_id);
return ZX_ERR_INVALID_ARGS;
}
// Dispatch
LOG(TRACE, "User Request (cmd 0x%04x tid %u) len %u", req.hdr.cmd, req.hdr.transaction_id,
req_size);
// We only allow CORB "get" requests.
if (cmd_id == IHDA_CODEC_SEND_CORB_CMD && CodecVerb(req.corb_cmd.verb).is_set()) {
LOG(DEBUG, "User attempted to perform privileged command.");
return ZX_ERR_ACCESS_DENIED;
}
switch (cmd_id) {
PROCESS_CMD(true, IHDA_CMD_GET_IDS, get_ids, ProcessGetIDs);
PROCESS_CMD(false, IHDA_CODEC_SEND_CORB_CMD, corb_cmd, ProcessSendCORBCmd);
default:
LOG(DEBUG, "Unrecognized command ID 0x%04x", req.hdr.cmd);
return ZX_ERR_INVALID_ARGS;
}
}
#undef PROCESS_CMD
void HdaCodecConnection::ProcessCodecDeactivate() {
// This should be the driver channel (client channels created with IOCTL do
// not register a deactivate handler). Start by releasing the internal
// channel reference from within the codec_driver_channel_lock.
{
fbl::AutoLock lock(&codec_driver_channel_lock_);
codec_driver_channel_.reset();
}
// Return any DMA streams the codec driver had owned back to the controller.
IntelHDAStream::Tree tmp;
{
fbl::AutoLock lock(&active_streams_lock_);
tmp = std::move(active_streams_);
}
while (!tmp.is_empty()) {
auto stream = tmp.pop_front();
stream->Deactivate();
controller_.ReturnStream(std::move(stream));
}
}
zx_status_t HdaCodecConnection::ProcessGetIDs(Channel* channel,
const ihda_proto::GetIDsReq& req) const {
ZX_DEBUG_ASSERT(channel != nullptr);
ihda_proto::GetIDsResp resp;
resp.hdr = req.hdr;
resp.vid = props_.vid;
resp.did = props_.did;
resp.ihda_vmaj = props_.ihda_vmaj;
resp.ihda_vmin = props_.ihda_vmin;
resp.rev_id = props_.rev_id;
resp.step_id = props_.step_id;
return channel->Write(&resp, sizeof(resp));
}
zx_status_t HdaCodecConnection::ProcessSendCORBCmd(Channel* channel,
const ihda_proto::SendCORBCmdReq& req) {
ZX_DEBUG_ASSERT(channel != nullptr);
CodecVerb verb(req.verb);
// Make sure that the command is well formed.
if (!CodecCommand::SanityCheck(id(), req.nid, verb)) {
LOG(DEBUG, "Bad SEND_CORB_CMD request values [%u, %hu, 0x%05x]", id(), req.nid, verb.val);
return ZX_ERR_INVALID_ARGS;
}
fbl::RefPtr<Channel> chan_ref = (req.hdr.cmd & IHDA_NOACK_FLAG) ? nullptr : fbl::RefPtr(channel);
auto job = CodecCmdJobAllocator::New(std::move(chan_ref), req.hdr.transaction_id,
CodecCommand(id(), req.nid, verb));
if (job == nullptr)
return ZX_ERR_NO_MEMORY;
zx_status_t res = controller_.QueueCodecCmd(std::move(job));
if (res != ZX_OK) {
LOG(DEBUG, "Failed to queue CORB command [%u, %hu, 0x%05x] (res %d)", id(), req.nid, verb.val,
res);
}
return res;
}
zx_status_t HdaCodecConnection::ProcessRequestStream(Channel* channel,
const ihda_proto::RequestStreamReq& req) {
ZX_DEBUG_ASSERT(channel != nullptr);
ihda_proto::RequestStreamResp resp;
resp.hdr = req.hdr;
// Attempt to get a stream of the proper type.
auto type = req.input ? IntelHDAStream::Type::INPUT : IntelHDAStream::Type::OUTPUT;
auto stream = controller_.AllocateStream(type);
if (stream != nullptr) {
// Success, send its ID and its tag back to the codec and add it to the
// set of active streams owned by this codec.
resp.result = ZX_OK;
resp.stream_id = stream->id();
resp.stream_tag = stream->tag();
fbl::AutoLock lock(&active_streams_lock_);
active_streams_.insert(std::move(stream));
} else {
// Failure; tell the codec that we are out of streams.
resp.result = ZX_ERR_NO_MEMORY;
resp.stream_id = 0;
resp.stream_tag = 0;
}
return channel->Write(&resp, sizeof(resp));
}
zx_status_t HdaCodecConnection::ProcessReleaseStream(Channel* channel,
const ihda_proto::ReleaseStreamReq& req) {
ZX_DEBUG_ASSERT(channel != nullptr);
// Remove the stream from the active set.
fbl::RefPtr<IntelHDAStream> stream;
{
fbl::AutoLock lock(&active_streams_lock_);
stream = active_streams_.erase(req.stream_id);
}
// If the stream was not active, our codec driver has some sort of internal
// inconsistency. Hang up the phone on it.
if (stream == nullptr)
return ZX_ERR_BAD_STATE;
// Give the stream back to the controller and (if an ack was requested) tell
// our codec driver that things went well.
stream->Deactivate();
controller_.ReturnStream(std::move(stream));
if (req.hdr.cmd & IHDA_NOACK_FLAG)
return ZX_OK;
ihda_proto::RequestStreamResp resp;
resp.hdr = req.hdr;
return channel->Write(&resp, sizeof(resp));
}
zx_status_t HdaCodecConnection::ProcessSetStreamFmt(Channel* channel,
const ihda_proto::SetStreamFmtReq& req,
zx::handle received_handle) {
ZX_DEBUG_ASSERT(channel != nullptr);
zx::channel server_channel;
zx_status_t res = ConvertHandle(&received_handle, &server_channel);
if (res != ZX_OK) {
LOG(DEBUG, "Failed to convert handle to channel (res %d)", res);
return res;
}
// Sanity check the requested format.
if (!StreamFormat(req.format).SanityCheck()) {
LOG(DEBUG, "Invalid encoded stream format 0x%04hx!", req.format);
return ZX_ERR_INVALID_ARGS;
}
// Grab a reference to the stream from the active set.
fbl::RefPtr<IntelHDAStream> stream;
{
fbl::AutoLock lock(&active_streams_lock_);
auto iter = active_streams_.find(req.stream_id);
if (iter.IsValid())
stream = iter.CopyPointer();
}
// If the stream was not active, our codec driver has some sort of internal
// inconsistency. Hang up the phone on it.
if (stream == nullptr)
return ZX_ERR_BAD_STATE;
// Set the stream format and assign the server channel to the stream. If
// this stream is already bound to a client, this will cause that connection
// to be closed.
res = stream->SetStreamFormat(loop_.dispatcher(), req.format, std::move(server_channel));
if (res != ZX_OK) {
LOG(DEBUG, "Failed to set stream format 0x%04hx for stream %hu (res %d)", req.format,
req.stream_id, res);
return res;
}
// Reply to the codec driver.
ihda_proto::SetStreamFmtResp resp;
resp.hdr = req.hdr;
res = channel->Write(&resp, sizeof(resp));
if (res != ZX_OK)
LOG(DEBUG, "Failed to send stream channel back to codec driver (res %d)", res);
return res;
}
zx_status_t HdaCodecConnection::CodecGetDispatcherChannel(zx_handle_t* remote_endpoint_out) {
if (!remote_endpoint_out)
return ZX_ERR_INVALID_ARGS;
zx::channel channel_local;
zx::channel channel_remote;
zx_status_t status = zx::channel::create(0, &channel_local, &channel_remote);
if (status != ZX_OK) {
return status;
}
fbl::RefPtr<Channel> channel = Channel::Create(std::move(channel_local));
if (channel == nullptr) {
return ZX_ERR_NO_MEMORY;
}
fbl::AutoLock lock(&codec_driver_channel_lock_);
codec_driver_channel_ = channel;
codec_driver_channel_->SetHandler(
[codec = fbl::RefPtr(this)](async_dispatcher_t* dispatcher, async::WaitBase* wait,
zx_status_t status, const zx_packet_signal_t* signal) {
codec->GetDispatcherChannelSignalled(dispatcher, wait, status, signal);
});
status = codec_driver_channel_->BeginWait(loop_.dispatcher());
if (status != ZX_OK) {
codec_driver_channel_.reset();
// We let channel_remote go out of scope to trigger channel deactivation via peer close.
return status;
}
if (status == ZX_OK) {
// If things went well, release the reference to the remote endpoint
// from the zx::channel instance into the unmanaged world of DDK
// protocols.
*remote_endpoint_out = channel_remote.release();
}
return status;
}
void HdaCodecConnection::GetDispatcherChannelSignalled(async_dispatcher_t* dispatcher,
async::WaitBase* wait, zx_status_t status,
const zx_packet_signal_t* signal) {
if (status != ZX_OK) {
if (status != ZX_ERR_CANCELED) { // Cancel is expected.
return;
}
}
bool readable_asserted = signal->observed & ZX_CHANNEL_READABLE;
bool peer_closed_asserted = signal->observed & ZX_CHANNEL_PEER_CLOSED;
if (readable_asserted) {
// Grab a reference to the codec driver channel, the processing may grab the lock.
fbl::RefPtr<Channel> codec_driver_channel;
{
fbl::AutoLock lock(&codec_driver_channel_lock_);
codec_driver_channel = codec_driver_channel_;
}
zx_status_t status = ProcessCodecRequest(codec_driver_channel.get());
if (status != ZX_OK) {
peer_closed_asserted = true;
}
}
if (peer_closed_asserted) {
fbl::AutoLock lock(&codec_driver_channel_lock_);
codec_driver_channel_.reset();
} else if (readable_asserted) {
wait->Begin(dispatcher);
}
}
} // namespace audio::intel_hda