blob: 9cced0ff344ed22f5654103d4614b6792ae3ad28 [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 "fragment.h"
#include <stdio.h>
#include <string.h>
#include <zircon/assert.h>
#include <zircon/errors.h>
#include <atomic>
#include <iterator>
#include <memory>
#include <ddk/debug.h>
#include <ddk/fragment-device.h>
#include <ddk/trace/event.h>
#include <fbl/algorithm.h>
#include "ddk/protocol/goldfish/addressspace.h"
#include "proxy-protocol.h"
namespace fragment {
namespace {
void MakeUniqueName(char name[ZX_DEVICE_NAME_MAX + 1]) {
static std::atomic<size_t> unique_id = 0;
snprintf(name, ZX_DEVICE_NAME_MAX + 1, "fragment-%zu", unique_id.fetch_add(1));
}
} // namespace
template <typename ProtoClientType, typename ProtoType>
ProtocolClient<ProtoClientType, ProtoType>::ProtocolClient(zx_device_t* parent, uint32_t proto_id) {
ProtoClientType* protoptr = &proto_client_;
zx_status_t status = device_open_protocol_session_multibindable(parent, proto_id, &proto_);
ZX_DEBUG_ASSERT(status == ZX_OK || status == ZX_ERR_NOT_SUPPORTED);
if (status == ZX_OK) {
is_session_ = true;
} else if (status == ZX_ERR_NOT_SUPPORTED) {
device_get_protocol(parent, proto_id, &proto_);
}
*protoptr = ProtoClientType(&proto_);
}
zx_status_t Fragment::Bind(void* ctx, zx_device_t* parent) {
char name[ZX_DEVICE_NAME_MAX + 1];
MakeUniqueName(name);
auto dev = std::make_unique<Fragment>(parent);
// The thing before the comma will become the process name, if a new process
// is created
const char* proxy_args = "composite-device,";
auto status = dev->DdkAdd(ddk::DeviceAddArgs(name)
.set_flags(DEVICE_ADD_NON_BINDABLE | DEVICE_ADD_MUST_ISOLATE)
.set_proxy_args(proxy_args));
if (status == ZX_OK) {
// devmgr owns the memory now
__UNUSED auto ptr = dev.release();
}
return status;
}
zx_status_t Fragment::RpcCanvas(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!canvas_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const AmlogicCanvasProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __func__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
auto* resp = reinterpret_cast<AmlogicCanvasProxyResponse*>(resp_buf);
*out_resp_size = sizeof(*resp);
switch (req->op) {
case AmlogicCanvasOp::CONFIG:
if (req_handle_count != 1) {
zxlogf(ERROR, "%s received %u handles, expecting 1", __func__, req_handle_count);
return ZX_ERR_INTERNAL;
}
return canvas_client_.proto_client().Config(zx::vmo(std::move(req_handles[0])), req->offset,
&req->info, &resp->canvas_idx);
case AmlogicCanvasOp::FREE:
if (req_handle_count != 0) {
zxlogf(ERROR, "%s received %u handles, expecting 0", __func__, req_handle_count);
return ZX_ERR_INTERNAL;
}
return canvas_client_.proto_client().Free(req->canvas_idx);
default:
zxlogf(ERROR, "%s: unknown clk op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcButtons(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!buttons_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const ButtonsProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __func__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
auto* resp = reinterpret_cast<ButtonsProxyResponse*>(resp_buf);
*out_resp_size = sizeof(*resp);
switch (req->op) {
case ButtonsOp::GET_NOTIFY_CHANNEL:
if (req_handle_count != 1) {
zxlogf(ERROR, "%s received %u handles, expecting 1", __func__, req_handle_count);
return ZX_ERR_INTERNAL;
}
return buttons_client_.proto_client().GetChannel(zx::channel(std::move(req_handles[0])));
default:
zxlogf(ERROR, "%s: unknown clk op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcClock(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!clock_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const ClockProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __func__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
auto* resp = reinterpret_cast<ClockProxyResponse*>(resp_buf);
*out_resp_size = sizeof(*resp);
switch (req->op) {
case ClockOp::ENABLE:
return clock_client_.proto_client().Enable();
case ClockOp::DISABLE:
return clock_client_.proto_client().Disable();
case ClockOp::IS_ENABLED:
return clock_client_.proto_client().IsEnabled(&resp->is_enabled);
case ClockOp::SET_RATE:
return clock_client_.proto_client().SetRate(req->rate);
case ClockOp::QUERY_SUPPORTED_RATE:
return clock_client_.proto_client().QuerySupportedRate(req->rate, &resp->rate);
case ClockOp::GET_RATE:
return clock_client_.proto_client().GetRate(&resp->rate);
case ClockOp::SET_INPUT:
return clock_client_.proto_client().SetInput(req->input_idx);
case ClockOp::GET_NUM_INPUTS:
return clock_client_.proto_client().GetNumInputs(&resp->num_inputs);
case ClockOp::GET_INPUT:
return clock_client_.proto_client().GetInput(&resp->current_input);
default:
zxlogf(ERROR, "%s: unknown clk op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcEthBoard(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!eth_board_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const EthBoardProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __func__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
auto* resp = reinterpret_cast<ProxyResponse*>(resp_buf);
*out_resp_size = sizeof(*resp);
switch (req->op) {
case EthBoardOp::RESET_PHY:
return eth_board_client_.proto_client().ResetPhy();
default:
zxlogf(ERROR, "%s: unknown ETH_BOARD op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcGoldfishAddressSpace(const uint8_t* req_buf, uint32_t req_size,
uint8_t* resp_buf, uint32_t* out_resp_size,
zx::handle* req_handles, uint32_t req_handle_count,
zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!goldfish_address_space_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const GoldfishAddressSpaceProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __func__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
uint32_t expected_handle_count;
switch (req->op) {
case GoldfishAddressSpaceOp::OPEN_CHILD_DRIVER:
expected_handle_count = 1;
break;
}
if (req_handle_count != expected_handle_count) {
zxlogf(ERROR, "%s received %u handles, expecting %u op %u", __func__, req_handle_count,
expected_handle_count, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
auto* resp = reinterpret_cast<GoldfishAddressSpaceProxyResponse*>(resp_buf);
*out_resp_size = sizeof(*resp);
switch (req->op) {
case GoldfishAddressSpaceOp::OPEN_CHILD_DRIVER: {
zx::channel channel(std::move(req_handles[0]));
return goldfish_address_space_client_.proto_client().OpenChildDriver(req->type,
std::move(channel));
}
default:
zxlogf(ERROR, "%s: unknown GoldfishPipe op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcGoldfishPipe(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!goldfish_pipe_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const GoldfishPipeProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __func__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
uint32_t expected_handle_count;
switch (req->op) {
case GoldfishPipeOp::SET_EVENT:
case GoldfishPipeOp::CONNECT_SYSMEM:
case GoldfishPipeOp::REGISTER_SYSMEM_HEAP:
expected_handle_count = 1;
break;
case GoldfishPipeOp::CREATE:
case GoldfishPipeOp::DESTROY:
case GoldfishPipeOp::OPEN:
case GoldfishPipeOp::EXEC:
case GoldfishPipeOp::GET_BTI:
expected_handle_count = 0;
break;
}
if (req_handle_count != expected_handle_count) {
zxlogf(ERROR, "%s received %u handles, expecting %u op %u", __func__, req_handle_count,
expected_handle_count, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
auto* resp = reinterpret_cast<GoldfishPipeProxyResponse*>(resp_buf);
*out_resp_size = sizeof(*resp);
switch (req->op) {
case GoldfishPipeOp::CREATE: {
int32_t id = 0;
zx::vmo vmo;
auto status = goldfish_pipe_client_.proto_client().Create(&id, &vmo);
if (status == ZX_OK) {
resp->id = id;
resp_handles[0] = std::move(vmo);
*resp_handle_count = 1;
}
return status;
}
case GoldfishPipeOp::DESTROY: {
goldfish_pipe_client_.proto_client().Destroy(req->id);
return ZX_OK;
}
case GoldfishPipeOp::SET_EVENT: {
zx::event pipe_event(std::move(req_handles[0]));
return goldfish_pipe_client_.proto_client().SetEvent(req->id, std::move(pipe_event));
}
case GoldfishPipeOp::OPEN: {
goldfish_pipe_client_.proto_client().Open(req->id);
return ZX_OK;
}
case GoldfishPipeOp::EXEC: {
goldfish_pipe_client_.proto_client().Exec(req->id);
return ZX_OK;
}
case GoldfishPipeOp::GET_BTI: {
zx::bti bti;
auto status = goldfish_pipe_client_.proto_client().GetBti(&bti);
if (status == ZX_OK) {
resp_handles[0] = std::move(bti);
*resp_handle_count = 1;
}
return status;
}
case GoldfishPipeOp::CONNECT_SYSMEM: {
zx::channel connection(std::move(req_handles[0]));
return goldfish_pipe_client_.proto_client().ConnectSysmem(std::move(connection));
}
case GoldfishPipeOp::REGISTER_SYSMEM_HEAP: {
zx::channel connection(std::move(req_handles[0]));
return goldfish_pipe_client_.proto_client().RegisterSysmemHeap(req->heap,
std::move(connection));
}
default:
zxlogf(ERROR, "%s: unknown GoldfishPipe op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcGpio(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!gpio_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const GpioProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __func__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
auto* resp = reinterpret_cast<GpioProxyResponse*>(resp_buf);
*out_resp_size = sizeof(*resp);
switch (req->op) {
case GpioOp::CONFIG_IN:
return gpio_client_.proto_client().ConfigIn(req->flags);
case GpioOp::CONFIG_OUT:
return gpio_client_.proto_client().ConfigOut(req->value);
case GpioOp::SET_ALT_FUNCTION:
return gpio_client_.proto_client().SetAltFunction(req->alt_function);
case GpioOp::READ:
return gpio_client_.proto_client().Read(&resp->value);
case GpioOp::WRITE:
return gpio_client_.proto_client().Write(req->value);
case GpioOp::GET_INTERRUPT: {
zx::interrupt irq;
auto status = gpio_client_.proto_client().GetInterrupt(req->flags, &irq);
if (status == ZX_OK) {
resp_handles[0] = std::move(irq);
*resp_handle_count = 1;
}
return status;
}
case GpioOp::RELEASE_INTERRUPT:
return gpio_client_.proto_client().ReleaseInterrupt();
case GpioOp::SET_POLARITY:
return gpio_client_.proto_client().SetPolarity(req->polarity);
case GpioOp::SET_DRIVE_STRENGTH:
return gpio_client_.proto_client().SetDriveStrength(req->ds_ua, &resp->out_actual_ds_ua);
default:
zxlogf(ERROR, "%s: unknown GPIO op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
void Fragment::I2cTransactCallback(void* cookie, zx_status_t status, const i2c_op_t* op_list,
size_t op_count) {
auto* ctx = static_cast<I2cTransactContext*>(cookie);
ctx->result = status;
if (status == ZX_OK && ctx->read_buf && ctx->read_length) {
memcpy(ctx->read_buf, op_list[0].data_buffer, ctx->read_length);
}
sync_completion_signal(&ctx->completion);
}
zx_status_t Fragment::RpcI2c(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
TRACE_DURATION("i2c", "I2c FragmentProxy RpcI2c");
if (!i2c_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const I2cProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __func__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
auto* resp = reinterpret_cast<I2cProxyResponse*>(resp_buf);
*out_resp_size = sizeof(*resp);
TRACE_FLOW_END("i2c", "I2c FragmentProxy I2cTransact Flow", req->trace_id);
switch (req->op) {
case I2cOp::TRANSACT: {
i2c_op_t i2c_ops[I2C_MAX_RW_OPS];
auto* rpc_ops = reinterpret_cast<const I2cProxyOp*>(&req[1]);
auto op_count = req->op_count;
if (op_count > countof(i2c_ops)) {
return ZX_ERR_BUFFER_TOO_SMALL;
}
auto* write_buf = reinterpret_cast<const uint8_t*>(&rpc_ops[op_count]);
size_t read_length = 0;
for (size_t i = 0; i < op_count; i++) {
if (rpc_ops[i].is_read) {
i2c_ops[i].data_buffer = nullptr;
read_length += rpc_ops[i].length;
} else {
i2c_ops[i].data_buffer = write_buf;
write_buf += rpc_ops[i].length;
}
i2c_ops[i].data_size = rpc_ops[i].length;
i2c_ops[i].is_read = rpc_ops[i].is_read;
i2c_ops[i].stop = rpc_ops[i].stop;
}
I2cTransactContext ctx = {};
ctx.read_buf = &resp[1];
ctx.read_length = read_length;
i2c_client_.proto_client().Transact(i2c_ops, op_count, I2cTransactCallback, &ctx);
auto status = sync_completion_wait(&ctx.completion, ZX_TIME_INFINITE);
if (status == ZX_OK) {
status = ctx.result;
}
if (status == ZX_OK) {
*out_resp_size = static_cast<uint32_t>(sizeof(*resp) + read_length);
}
return status;
}
case I2cOp::GET_MAX_TRANSFER_SIZE:
return i2c_client_.proto_client().GetMaxTransferSize(&resp->size);
case I2cOp::GET_INTERRUPT: {
zx::interrupt irq;
auto status = i2c_client_.proto_client().GetInterrupt(req->flags, &irq);
if (status == ZX_OK) {
resp_handles[0] = std::move(irq);
*resp_handle_count = 1;
}
return status;
}
default:
zxlogf(ERROR, "%s: unknown I2C op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcPdev(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!pdev_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const PdevProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __func__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
auto* resp = reinterpret_cast<PdevProxyResponse*>(resp_buf);
*out_resp_size = sizeof(*resp);
switch (req->op) {
case PdevOp::GET_MMIO: {
pdev_mmio_t mmio;
auto status = pdev_client_.proto_client().GetMmio(req->index, &mmio);
if (status == ZX_OK) {
resp->offset = mmio.offset;
resp->size = mmio.size;
resp_handles[0].reset(mmio.vmo);
*resp_handle_count = 1;
}
return status;
}
case PdevOp::GET_INTERRUPT: {
zx::interrupt irq;
auto status = pdev_client_.proto_client().GetInterrupt(req->index, req->flags, &irq);
if (status == ZX_OK) {
resp_handles[0] = std::move(irq);
*resp_handle_count = 1;
}
return status;
}
case PdevOp::GET_BTI: {
zx::bti bti;
auto status = pdev_client_.proto_client().GetBti(req->index, &bti);
if (status == ZX_OK) {
resp_handles[0] = std::move(bti);
*resp_handle_count = 1;
}
return status;
}
case PdevOp::GET_SMC: {
zx::resource resource;
auto status = pdev_client_.proto_client().GetSmc(req->index, &resource);
if (status == ZX_OK) {
resp_handles[0] = std::move(resource);
*resp_handle_count = 1;
}
return status;
}
case PdevOp::GET_DEVICE_INFO:
return pdev_client_.proto_client().GetDeviceInfo(&resp->device_info);
case PdevOp::GET_BOARD_INFO:
return pdev_client_.proto_client().GetBoardInfo(&resp->board_info);
default:
zxlogf(ERROR, "%s: unknown pdev op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcPower(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!power_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const PowerProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __FUNCTION__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
auto* resp = reinterpret_cast<PowerProxyResponse*>(resp_buf);
*out_resp_size = sizeof(*resp);
switch (req->op) {
case PowerOp::REGISTER:
return power_client_.proto_client().RegisterPowerDomain(req->min_voltage, req->max_voltage);
case PowerOp::UNREGISTER:
return power_client_.proto_client().UnregisterPowerDomain();
case PowerOp::GET_STATUS:
return power_client_.proto_client().GetPowerDomainStatus(&resp->status);
case PowerOp::GET_SUPPORTED_VOLTAGE_RANGE:
return power_client_.proto_client().GetSupportedVoltageRange(&resp->min_voltage,
&resp->max_voltage);
case PowerOp::REQUEST_VOLTAGE:
return power_client_.proto_client().RequestVoltage(req->set_voltage, &resp->actual_voltage);
case PowerOp::WRITE_PMIC_CTRL_REG:
return power_client_.proto_client().WritePmicCtrlReg(req->reg_addr, req->reg_value);
case PowerOp::READ_PMIC_CTRL_REG:
return power_client_.proto_client().ReadPmicCtrlReg(req->reg_addr, &resp->reg_value);
default:
zxlogf(ERROR, "%s: unknown Power op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcPwm(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!pwm_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const PwmProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __FUNCTION__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
auto* resp = reinterpret_cast<PwmProxyResponse*>(resp_buf);
*out_resp_size = sizeof(*resp);
switch (req->op) {
case PwmOp::GET_CONFIG: {
if (req->config.mode_config_size > MAX_MODE_CFG_SIZE * sizeof(uint8_t)) {
return ZX_ERR_NO_SPACE;
}
resp->config.mode_config_size = req->config.mode_config_size;
resp->config.mode_config_buffer = resp->mode_cfg;
return pwm_client_.proto_client().GetConfig(&resp->config);
}
case PwmOp::SET_CONFIG: {
if (req->config.mode_config_size > MAX_MODE_CFG_SIZE * sizeof(uint8_t)) {
return ZX_ERR_NO_SPACE;
}
uint8_t mode_cfg[MAX_MODE_CFG_SIZE] = {0};
memcpy(mode_cfg, req->mode_cfg, req->config.mode_config_size);
pwm_config_t cfg = {req->config.polarity, req->config.period_ns, req->config.duty_cycle,
mode_cfg, req->config.mode_config_size};
return pwm_client_.proto_client().SetConfig(&cfg);
}
case PwmOp::ENABLE:
return pwm_client_.proto_client().Enable();
case PwmOp::DISABLE:
return pwm_client_.proto_client().Disable();
default:
zxlogf(ERROR, "%s: unknown Pwm op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcSpi(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!spi_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto req = reinterpret_cast<const SpiProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __func__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
auto resp = reinterpret_cast<SpiProxyResponse*>(resp_buf);
*out_resp_size = sizeof(*resp);
auto txbuf = reinterpret_cast<const uint8_t*>(&req[1]);
auto rxbuf = reinterpret_cast<uint8_t*>(&resp[1]);
switch (req->op) {
case SpiOp::TRANSMIT: {
return spi_client_.proto_client().Transmit(txbuf, req->length);
}
case SpiOp::RECEIVE: {
size_t actual;
*out_resp_size += static_cast<uint32_t>(req->length);
return spi_client_.proto_client().Receive(static_cast<uint32_t>(req->length), rxbuf,
req->length, &actual);
}
case SpiOp::EXCHANGE: {
size_t actual;
*out_resp_size += static_cast<uint32_t>(req->length);
return spi_client_.proto_client().Exchange(txbuf, req->length, rxbuf, req->length, &actual);
}
default:
zxlogf(ERROR, "%s: unknown SPI op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcSysmem(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!sysmem_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const SysmemProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __func__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
uint32_t expected_handle_count;
switch (req->op) {
case SysmemOp::CONNECT:
case SysmemOp::REGISTER_HEAP:
case SysmemOp::REGISTER_SECURE_MEM:
expected_handle_count = 1;
break;
case SysmemOp::UNREGISTER_SECURE_MEM:
expected_handle_count = 0;
break;
}
if (req_handle_count != expected_handle_count) {
zxlogf(ERROR, "%s received %u handles, expecting %u op %u", __func__, req_handle_count,
expected_handle_count, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
*out_resp_size = sizeof(ProxyResponse);
switch (req->op) {
case SysmemOp::CONNECT:
return sysmem_client_.proto_client().Connect(zx::channel(std::move(req_handles[0])));
case SysmemOp::REGISTER_HEAP:
return sysmem_client_.proto_client().RegisterHeap(req->heap,
zx::channel(std::move(req_handles[0])));
case SysmemOp::REGISTER_SECURE_MEM:
return sysmem_client_.proto_client().RegisterSecureMem(
zx::channel(std::move(req_handles[0])));
case SysmemOp::UNREGISTER_SECURE_MEM:
return sysmem_client_.proto_client().UnregisterSecureMem();
default:
zxlogf(ERROR, "%s: unknown sysmem op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcTee(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!tee_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const TeeProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __func__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
if (req_handle_count < 1 || req_handle_count > 2) {
zxlogf(ERROR, "%s received %u handles, expecting 1-2", __func__, req_handle_count);
return ZX_ERR_INTERNAL;
}
*out_resp_size = sizeof(ProxyResponse);
switch (req->op) {
case TeeOp::CONNECT: {
zx::channel tee_device_request(std::move(req_handles[0]));
zx::channel service_provider;
if (req_handle_count == 2) {
service_provider.reset(req_handles[1].release());
}
return tee_client_.proto_client().Connect(std::move(tee_device_request),
std::move(service_provider));
}
default:
zxlogf(ERROR, "%s: unknown sysmem op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcUms(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!ums_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const UsbModeSwitchProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __FUNCTION__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
auto* resp = reinterpret_cast<ProxyResponse*>(resp_buf);
*out_resp_size = sizeof(*resp);
switch (req->op) {
case UsbModeSwitchOp::SET_MODE:
return ums_client_.proto_client().SetMode(req->mode);
default:
zxlogf(ERROR, "%s: unknown USB Mode Switch op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcCodec(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!codec_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const CodecProxyRequest*>(req_buf);
auto* resp = reinterpret_cast<ProxyResponse*>(resp_buf);
*out_resp_size = sizeof(*resp);
switch (req->op) {
case CodecOp::GET_CHANNEL:
if (req_handle_count != 1) {
zxlogf(ERROR, "%s received %u handles, expecting 1", __func__, req_handle_count);
return ZX_ERR_INTERNAL;
}
return codec_client_.proto_client().Connect(zx::channel(std::move(req_handles[0])));
default:
zxlogf(ERROR, "%s: unknown CODEC op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcDai(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!dai_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const DaiProxyRequest*>(req_buf);
auto* resp = reinterpret_cast<DaiProxyResponse*>(resp_buf);
*out_resp_size = sizeof(*resp);
switch (req->op) {
case DaiOp::GET_CHANNEL:
if (req_handle_count != 1) {
zxlogf(ERROR, "%s received %u handles, expecting 1", __func__, req_handle_count);
return ZX_ERR_INTERNAL;
}
return dai_client_.proto_client().Connect(zx::channel(std::move(req_handles[0])));
default:
zxlogf(ERROR, "%s: unknown DAI op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcRpmb(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!rpmb_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const RpmbProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __func__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
auto* resp = reinterpret_cast<ProxyResponse*>(resp_buf);
*out_resp_size = sizeof(*resp);
switch (req->op) {
case RpmbOp::CONNECT_SERVER:
if (req_handle_count != 1) {
zxlogf(ERROR, "%s: expected one handle for %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INVALID_ARGS;
}
rpmb_client_.proto_client().ConnectServer(zx::channel(req_handles[0].release()));
return ZX_OK;
default:
zxlogf(ERROR, "%s: unknown rpmb op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcRegisters(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!registers_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const RegistersProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __func__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
auto* resp = reinterpret_cast<ProxyResponse*>(resp_buf);
*out_resp_size = sizeof(*resp);
switch (req->op) {
case RegistersOp::CONNECT:
if (req_handle_count != 1) {
zxlogf(ERROR, "%s: expected one handle for %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INVALID_ARGS;
}
registers_client_.proto_client().Connect(zx::channel(req_handles[0].release()));
return ZX_OK;
default:
zxlogf(ERROR, "%s: unknown registers op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcVreg(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!vreg_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const VregProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __func__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
auto* resp = reinterpret_cast<VregProxyResponse*>(resp_buf);
*out_resp_size = sizeof(*resp);
switch (req->op) {
case VregOp::SET_VOLTAGE_STEP:
return vreg_client_.proto_client().SetVoltageStep(req->step);
case VregOp::GET_VOLTAGE_STEP:
resp->step = vreg_client_.proto_client().GetVoltageStep();
return ZX_OK;
case VregOp::GET_REGULATOR_PARAMS:
vreg_client_.proto_client().GetRegulatorParams(&resp->params);
return ZX_OK;
default:
zxlogf(ERROR, "%s: unknown vreg op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::RpcDsi(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
uint32_t* out_resp_size, zx::handle* req_handles,
uint32_t req_handle_count, zx::handle* resp_handles,
uint32_t* resp_handle_count) {
if (!dsi_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
auto* req = reinterpret_cast<const DsiProxyRequest*>(req_buf);
if (req_size < sizeof(*req)) {
zxlogf(ERROR, "%s received %u, expecting %zu", __func__, req_size, sizeof(*req));
return ZX_ERR_INTERNAL;
}
*out_resp_size = sizeof(ProxyResponse);
switch (req->op) {
case DsiOp::CONNECT:
if (req_handle_count != 1) {
zxlogf(ERROR, "%s: expected one handle for %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INVALID_ARGS;
}
return dsi_client_.proto_client().Connect(zx::channel(req_handles[0].release()));
default:
zxlogf(ERROR, "%s: unknown rpmb op %u", __func__, static_cast<uint32_t>(req->op));
return ZX_ERR_INTERNAL;
}
}
zx_status_t Fragment::DdkRxrpc(zx_handle_t raw_channel) {
zx::unowned_channel channel(raw_channel);
if (!channel->is_valid()) {
// This driver is stateless, so we don't need to reset anything here
return ZX_OK;
}
uint8_t req_buf[kProxyMaxTransferSize];
// Ensure all response messages are fully initialized.
uint8_t resp_buf[kProxyMaxTransferSize] = {};
auto* req_header = reinterpret_cast<ProxyRequest*>(&req_buf);
auto* resp_header = reinterpret_cast<ProxyResponse*>(&resp_buf);
uint32_t actual;
zx_handle_t req_handles_raw[ZX_CHANNEL_MAX_MSG_HANDLES];
uint32_t req_handle_count;
auto status = zx_channel_read(raw_channel, 0, &req_buf, req_handles_raw, sizeof(req_buf),
std::size(req_handles_raw), &actual, &req_handle_count);
if (status != ZX_OK) {
zxlogf(ERROR, "platform_dev_rxrpc: zx_channel_read failed %d", status);
return status;
}
// There is some expense in constructing/destructing these. If that becomes an issue, we could
// create an incremental construction array type to prevent constructing the extras.
zx::handle req_handles[ZX_CHANNEL_MAX_MSG_HANDLES];
for (uint32_t handle_index = 0; handle_index < req_handle_count; ++handle_index) {
// req_handles_raw handle values are ignored after this point, so no need to clear them.
req_handles[handle_index].reset(req_handles_raw[handle_index]);
}
constexpr uint32_t kMaxRespHandles = 1;
zx::handle resp_handles[kMaxRespHandles];
uint32_t resp_handle_count = 0;
resp_header->txid = req_header->txid;
uint32_t resp_len = 0;
switch (req_header->proto_id) {
case ZX_PROTOCOL_AMLOGIC_CANVAS:
status = RpcCanvas(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_BUTTONS:
status = RpcButtons(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_CLOCK:
status = RpcClock(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_ETH_BOARD:
status = RpcEthBoard(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_GOLDFISH_ADDRESS_SPACE:
status = RpcGoldfishAddressSpace(req_buf, actual, resp_buf, &resp_len, req_handles,
req_handle_count, resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_GOLDFISH_PIPE:
status = RpcGoldfishPipe(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_GPIO:
status = RpcGpio(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_I2C:
status = RpcI2c(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_PDEV:
status = RpcPdev(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_POWER:
status = RpcPower(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_PWM:
status = RpcPwm(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_SPI:
status = RpcSpi(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_SYSMEM:
status = RpcSysmem(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_TEE:
status = RpcTee(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_USB_MODE_SWITCH:
status = RpcUms(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_CODEC:
status = RpcCodec(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_DAI:
status = RpcDai(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_RPMB:
status = RpcRpmb(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_REGISTERS:
status = RpcRegisters(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_VREG:
status = RpcVreg(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
case ZX_PROTOCOL_DSI:
status = RpcDsi(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
resp_handles, &resp_handle_count);
break;
default:
zxlogf(ERROR, "%s: unknown protocol %u", __func__, req_header->proto_id);
return ZX_ERR_INTERNAL;
}
ZX_DEBUG_ASSERT(resp_handle_count <= kMaxRespHandles);
zx_handle_t resp_handles_raw[kMaxRespHandles];
for (uint32_t handle_index = 0; handle_index < resp_handle_count; ++handle_index) {
// Will be transferred or closed by zx_channel_write().
resp_handles_raw[handle_index] = resp_handles[handle_index].release();
}
// set op to match request so zx_channel_write will return our response
resp_header->status = status;
status = zx_channel_write(raw_channel, 0, resp_header, resp_len,
(resp_handle_count ? resp_handles_raw : nullptr), resp_handle_count);
if (status != ZX_OK) {
zxlogf(ERROR, "platform_dev_rxrpc: zx_channel_write failed %d", status);
}
return status;
}
void Fragment::DdkUnbind(ddk::UnbindTxn txn) { txn.Reply(); }
zx_status_t Fragment::DdkGetProtocol(uint32_t proto_id, void* out_protocol) {
switch (proto_id) {
case ZX_PROTOCOL_AMLOGIC_CANVAS: {
if (!canvas_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
canvas_client_.proto_client().GetProto(static_cast<amlogic_canvas_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_BUTTONS: {
if (!buttons_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
buttons_client_.proto_client().GetProto(static_cast<buttons_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_CLOCK: {
if (!clock_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
clock_client_.proto_client().GetProto(static_cast<clock_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_ETH_BOARD: {
if (!eth_board_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
eth_board_client_.proto_client().GetProto(static_cast<eth_board_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_GOLDFISH_ADDRESS_SPACE: {
if (!goldfish_address_space_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
goldfish_address_space_client_.proto_client().GetProto(
static_cast<goldfish_address_space_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_GOLDFISH_PIPE: {
if (!goldfish_pipe_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
goldfish_pipe_client_.proto_client().GetProto(
static_cast<goldfish_pipe_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_GPIO: {
if (!gpio_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
gpio_client_.proto_client().GetProto(static_cast<gpio_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_I2C: {
if (!i2c_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
i2c_client_.proto_client().GetProto(static_cast<i2c_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_CODEC: {
if (!codec_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
codec_client_.proto_client().GetProto(static_cast<codec_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_DAI: {
if (!dai_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
dai_client_.proto_client().GetProto(static_cast<dai_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_PDEV: {
if (!pdev_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
pdev_client_.proto_client().GetProto(static_cast<pdev_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_PWM: {
if (!pwm_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
pwm_client_.proto_client().GetProto(static_cast<pwm_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_SPI: {
if (!spi_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
spi_client_.proto_client().GetProto(static_cast<spi_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_SYSMEM: {
if (!sysmem_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
sysmem_client_.proto_client().GetProto(static_cast<sysmem_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_TEE: {
if (!tee_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
tee_client_.proto_client().GetProto(static_cast<tee_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_USB_MODE_SWITCH: {
if (!ums_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
ums_client_.proto_client().GetProto(static_cast<usb_mode_switch_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_POWER: {
if (!power_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
power_client_.proto_client().GetProto(static_cast<power_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_POWER_IMPL: {
if (!power_impl_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
power_impl_client_.proto_client().GetProto(static_cast<power_impl_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_DSI_IMPL: {
if (!dsi_impl_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
dsi_impl_client_.proto_client().GetProto(static_cast<dsi_impl_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_SDIO: {
if (!sdio_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
sdio_client_.proto_client().GetProto(static_cast<sdio_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_THERMAL: {
if (!thermal_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
thermal_client_.proto_client().GetProto(static_cast<thermal_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_ISP: {
if (!isp_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
isp_client_.proto_client().GetProto(static_cast<isp_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_SHARED_DMA: {
if (!shared_dma_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
shared_dma_client_.proto_client().GetProto(static_cast<shared_dma_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_USB_PHY: {
if (!usb_phy_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
usb_phy_client_.proto_client().GetProto(static_cast<usb_phy_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_MIPI_CSI: {
if (!mipi_csi_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
mipi_csi_client_.proto_client().GetProto(static_cast<mipi_csi_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_CAMERA_SENSOR2: {
if (!camera_sensor2_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
camera_sensor2_client_.proto_client().GetProto(
static_cast<camera_sensor2_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_SCPI: {
if (!scpi_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
scpi_client_.proto_client().GetProto(static_cast<scpi_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_GDC: {
if (!gdc_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
gdc_client_.proto_client().GetProto(static_cast<gdc_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_GE2D: {
if (!ge2d_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
ge2d_client_.proto_client().GetProto(static_cast<ge2d_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_RPMB: {
if (!rpmb_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
rpmb_client_.proto_client().GetProto(static_cast<rpmb_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_REGISTERS: {
if (!registers_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
registers_client_.proto_client().GetProto(static_cast<registers_protocol_t*>(out_protocol));
return ZX_OK;
}
case ZX_PROTOCOL_VREG: {
if (!vreg_client_.proto_client().is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
vreg_client_.proto_client().GetProto(static_cast<vreg_protocol_t*>(out_protocol));
return ZX_OK;
}
default:
return ZX_ERR_NOT_SUPPORTED;
}
}
void Fragment::DdkRelease() { delete this; }
const zx_driver_ops_t driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = Fragment::Bind;
return ops;
}();
} // namespace fragment
ZIRCON_DRIVER_BEGIN(fragment, fragment::driver_ops, "zircon", "0.1", 1)
BI_MATCH() // This driver is excluded from the normal matching process, so this is fine.
ZIRCON_DRIVER_END(fragment)