blob: f7245d366790e7836f17c401492a69fc27125e0a [file] [log] [blame]
// Copyright 2022 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 "i2c_controller.h"
#include <endian.h>
#include <fidl/fuchsia.component.decl/cpp/wire.h>
#include <lib/driver2/record_cpp.h>
#include <bind/fuchsia/hardware/i2c/cpp/bind.h>
namespace test_i2c_controller {
zx::status<std::unique_ptr<TestDa7219I2cController>> TestDa7219I2cController::Start(
fuchsia_driver_framework::wire::DriverStartArgs& start_args, fdf::UnownedDispatcher dispatcher,
fidl::WireSharedClient<fuchsia_driver_framework::Node> node, driver::Namespace ns,
driver::Logger logger) {
auto driver = std::make_unique<TestDa7219I2cController>(
dispatcher->async_dispatcher(), std::move(node), std::move(ns), std::move(logger));
auto result = driver->Run(std::move(start_args.outgoing_dir()));
if (result.is_error()) {
return result.take_error();
}
return zx::ok(std::move(driver));
}
zx::status<> TestDa7219I2cController::Run(fidl::ServerEnd<fuchsia_io::Directory> outgoing_dir) {
auto service = [this](fidl::ServerEnd<fuchsia_hardware_i2c::Device> server_end) {
fidl::BindServer(dispatcher_, std::move(server_end), this);
};
auto status = outgoing_.AddProtocol<fuchsia_hardware_i2c::Device>(std::move(service));
if (status.is_error()) {
FDF_SLOG(ERROR, "Failed to add protocol", KV("status", status.status_string()));
return status;
}
status = AddChild();
if (status.is_error()) {
return status;
}
return outgoing_.Serve(std::move(outgoing_dir));
}
zx::status<> TestDa7219I2cController::AddChild() {
fidl::Arena arena;
// Offer `fuchsia.hardware.i2c.Device` to the driver that binds to the node.
auto protocol_offer =
fuchsia_component_decl::wire::OfferProtocol::Builder(arena)
.source_name(arena, fidl::DiscoverableProtocolName<fuchsia_hardware_i2c::Device>)
.target_name(arena, fidl::DiscoverableProtocolName<fuchsia_hardware_i2c::Device>)
.dependency_type(fuchsia_component_decl::wire::DependencyType::kStrong)
.Build();
fuchsia_component_decl::wire::Offer offer =
fuchsia_component_decl::wire::Offer::WithProtocol(arena, protocol_offer);
// Set the properties of the node that a driver will bind to.
auto properties = fidl::VectorView<fuchsia_driver_framework::wire::NodeProperty>(arena, 2);
properties[0] = fuchsia_driver_framework::wire::NodeProperty::Builder(arena)
.key(fuchsia_driver_framework::wire::NodePropertyKey::WithStringValue(
arena, bind_fuchsia_hardware_i2c::DEVICE))
.value(fuchsia_driver_framework::wire::NodePropertyValue::WithEnumValue(
arena, bind_fuchsia_hardware_i2c::DEVICE_ZIRCONTRANSPORT))
.Build();
properties[1] = fuchsia_driver_framework::wire::NodeProperty::Builder(arena)
.key(fuchsia_driver_framework::wire::NodePropertyKey::WithIntValue(
0x0A02 /* BIND_I2C_ADDRESS */))
.value(fuchsia_driver_framework::wire::NodePropertyValue::WithIntValue(
0x1a /* I2C address used by the DA7219 codec */))
.Build();
auto args =
fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena)
.name(arena, "i2c-child")
.offers(fidl::VectorView<fuchsia_component_decl::wire::Offer>::FromExternal(&offer, 1))
.properties(properties)
.Build();
// Create endpoints of the `NodeController` for the node.
auto endpoints = fidl::CreateEndpoints<fuchsia_driver_framework::NodeController>();
if (endpoints.is_error()) {
FDF_SLOG(ERROR, "Failed to create endpoint", KV("status", endpoints.status_string()));
return zx::error(endpoints.status_value());
}
auto result = node_.sync()->AddChild(args, std::move(endpoints->server), {});
if (!result.ok()) {
FDF_SLOG(ERROR, "Failed to add child", KV("status", result.status_string()));
return zx::error(result.status());
}
return zx::ok();
}
// Protocol method of `fuchsia.hardware.i2c.Device` to handle data transfer requests.
void TestDa7219I2cController::Transfer(TransferRequestView request,
TransferCompleter::Sync& completer) {
FDF_SLOG(DEBUG, "Received transfer request");
// Must be either a write transfer (2 bytes write) or
// a read transfer (1 byte write and 1 byte read).
if (request->transactions.count() == 1) { // Write transfer.
ZX_ASSERT(request->transactions[0].has_data_transfer());
const auto& transfer = request->transactions[0].data_transfer();
ZX_ASSERT(transfer.is_write_data() && !transfer.is_read_size());
ZX_ASSERT(transfer.write_data().count() == 2);
FDF_SLOG(INFO, "Received write", KV("address", static_cast<int>(transfer.write_data()[0])));
FDF_SLOG(INFO, "Received write", KV("data", static_cast<int>(transfer.write_data()[1])));
completer.ReplySuccess({});
} else if (request->transactions.count() == 2) { // Read transfer.
ZX_ASSERT(request->transactions[0].has_data_transfer());
const auto& transfer0 = request->transactions[0].data_transfer();
ZX_ASSERT(transfer0.is_write_data() && !transfer0.is_read_size());
ZX_ASSERT(transfer0.write_data().count() == 1);
FDF_SLOG(INFO, "Received write", KV("address", static_cast<int>(transfer0.write_data()[0])));
const auto& transfer1 = request->transactions[1].data_transfer();
ZX_ASSERT(!transfer1.is_write_data() && transfer1.is_read_size());
ZX_ASSERT(transfer1.read_size() == 1);
fidl::Arena allocator;
auto read_bytes = fidl::VectorView<uint8_t>(allocator, 1);
// Test read by sending arbitrary values.
constexpr uint8_t kTestArbitraryDataToRead = 12;
read_bytes[0] = kTestArbitraryDataToRead;
FDF_SLOG(INFO, "Read data to send", KV("data", static_cast<int>(read_bytes[0])));
auto read_results = fidl::VectorView<fidl::VectorView<uint8_t>>(allocator, 1);
read_results[0] = read_bytes;
completer.ReplySuccess(read_results);
} else {
completer.ReplyError(ZX_ERR_INVALID_ARGS);
}
}
} // namespace test_i2c_controller
FUCHSIA_DRIVER_RECORD_CPP_V1(test_i2c_controller::TestDa7219I2cController);