blob: 4fba8f16bdd82a4c718f8375a05ef8cc68bedfa1 [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_server.h"
#include <endian.h>
#include <lib/driver/component/cpp/structured_logger.h>
#include "constants.h"
namespace i2c_temperature {
// Static
// Handle incoming connection requests from FIDL clients
fidl::ServerBindingRef<fuchsia_hardware_i2c::Device> I2cDeviceServer::BindDeviceClient(
std::shared_ptr<I2cDeviceServer> server_impl, async_dispatcher_t* dispatcher,
fidl::ServerEnd<fuchsia_hardware_i2c::Device> request) {
// Bind each connection request to the shared instance.
return fidl::BindServer(dispatcher, std::move(request), server_impl,
std::mem_fn(&I2cDeviceServer::OnUnbound));
}
// This method is called when a server connection is torn down.
void I2cDeviceServer::OnUnbound(fidl::UnbindInfo info,
fidl::ServerEnd<fuchsia_hardware_i2c::Device> server_end) {
if (info.is_peer_closed()) {
FDF_LOG(DEBUG, "Client disconnected");
} else if (!info.is_user_initiated()) {
FDF_LOG(ERROR, "Client connection unbound: %s", info.status_string());
}
}
// Protocol method of `fuchsia.hardware.i2c.Device` to handle data transfer requests.
void I2cDeviceServer::Transfer(TransferRequestView request, TransferCompleter::Sync& completer) {
FDF_SLOG(INFO, "Received transfer request");
if (request->transactions.count() < 1) {
completer.ReplyError(ZX_ERR_INVALID_ARGS);
return;
}
// Create a vector of read transfers
fidl::Arena allocator;
std::vector<fidl::VectorView<uint8_t>> read_data;
for (size_t i = 0; i < request->transactions.count(); ++i) {
if (!request->transactions[i].has_data_transfer()) {
completer.ReplyError(ZX_ERR_INVALID_ARGS);
return;
}
const auto& transfer = request->transactions[i].data_transfer();
if (transfer.is_write_data()) {
// Handle write transaction.
if (transfer.write_data().count() <= 0) {
completer.ReplyError(ZX_ERR_INVALID_ARGS);
return;
}
HandleWrite(transfer.write_data());
} else {
// Handle read transaction.
if (transfer.read_size() <= 0) {
completer.ReplyError(ZX_ERR_INVALID_ARGS);
return;
}
// Convert read data from local to big-endian
auto read_value = htobe16(temperature_);
auto read_bytes = fidl::VectorView<uint8_t>(allocator, transfer.read_size());
for (size_t i = 0; i < read_bytes.count(); i++) {
read_bytes[i] = read_value >> (i * 8) & 0xff;
}
read_data.push_back(read_bytes);
}
}
// Complete the transaction, returning any read data
auto read_results = fidl::VectorView<fidl::VectorView<uint8_t>>(allocator, read_data.size());
for (size_t i = 0; i < read_data.size(); i++) {
read_results[i] = read_data[i];
}
completer.ReplySuccess(read_results);
}
void I2cDeviceServer::GetName(GetNameCompleter::Sync& completer) {
completer.ReplySuccess("test-i2c-server");
}
// Process data transfer WRITE requests. Parse the command from the driver and update:
// - kSoftResetCommand: Reset internal temperature to initial value
// - kStartMeasurementCommand: Increment internal temperature value
void I2cDeviceServer::HandleWrite(const fidl::VectorView<uint8_t> write_data) {
// For simplicity, assume only integer commands from I2cTemperatureDriver.
if (write_data.count() > 4) {
return;
}
// Convert write data from big-endian to local
int command;
std::memcpy(&command, write_data.data(), sizeof(int));
command = be16toh(command);
if (command == kSoftResetCommand) {
FDF_SLOG(INFO, "Reset command received");
temperature_ = kStartingTemp;
} else if (command == kStartMeasurementCommand) {
FDF_SLOG(INFO, "Measurement command received");
temperature_ += kTempIncrement;
} else {
FDF_SLOG(WARNING, "Received unknown command ", KV("command", command));
}
}
} // namespace i2c_temperature