blob: d7de4d68b9cadb0bf8c6535e574dacdc557a65c5 [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_temperature.h"
#include <lib/driver2/record_cpp.h>
#include "constants.h"
namespace i2c_temperature {
// static
zx::status<std::unique_ptr<I2cTemperatureDriver>> I2cTemperatureDriver::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<I2cTemperatureDriver>(
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<> I2cTemperatureDriver::Run(fidl::ServerEnd<fuchsia_io::Directory> outgoing_dir) {
// Serve the `sample.i2ctemperature` protocol to clients
auto status = outgoing_.AddProtocol<sample_i2ctemperature::Device>(this, Name());
if (status.is_error()) {
FDF_SLOG(ERROR, "Failed to add protocol", KV("status", status.status_string()));
return status.take_error();
}
status = outgoing_.Serve(std::move(outgoing_dir));
if (status.is_error()) {
FDF_SLOG(ERROR, "Failed to serve outgoing directory", KV("status", status.status_string()));
return status.take_error();
}
// Connect to the I2C bus controller.
auto result = SetupI2cChannel();
if (result.is_error()) {
return result.take_error();
}
// Reset the sensor.
auto write_result = i2c_channel_->Write16(kSoftResetCommand);
if (write_result.is_error()) {
FDF_SLOG(ERROR, "Failed to send reset command: ", KV("status", write_result.status_string()));
return write_result.take_error();
}
// Read the initial temperature value.
auto read_result = i2c_channel_->Read16();
if (read_result.is_error()) {
FDF_SLOG(ERROR, "Failed to read temperature: ", KV("status", read_result.status_string()));
return read_result.take_error();
}
uint32_t temperature = read_result.value();
FDF_SLOG(INFO, "I2C temperature: ", KV("temperature", temperature));
return zx::ok();
}
// Connect to the `fuchsia.hardware.i2c/Device` protocol offered by the parent device node.
zx::status<> I2cTemperatureDriver::SetupI2cChannel() {
if (i2c_channel_) {
return zx::ok();
}
auto client_endpoint = ns_.Connect<fuchsia_hardware_i2c::Device>();
if (client_endpoint.status_value() != ZX_OK) {
FDF_SLOG(ERROR, "Failed to setup I2cChannel", KV("status", client_endpoint.status_string()));
return client_endpoint.take_error();
}
auto i2c_client = fidl::WireClient(std::move(*client_endpoint), dispatcher_);
i2c_channel_ = std::make_unique<I2cChannel>(std::move(i2c_client));
return zx::ok();
}
// Protocol method in `sample.i2ctemperature` to return the current temperature
void I2cTemperatureDriver::ReadTemperature(ReadTemperatureRequestView request,
ReadTemperatureCompleter::Sync &completer) {
auto channel_status = SetupI2cChannel();
if (channel_status.is_error()) {
completer.ReplyError(channel_status.status_value());
return;
}
// Send a command to start the measurement.
auto status = i2c_channel_->Write16(kStartMeasurementCommand);
if (status.is_error()) {
FDF_SLOG(ERROR, "Failed to send measurement command: ", KV("status", status.status_string()));
completer.ReplyError(status.status_value());
return;
}
// Read the temperature measurement.
auto temp_result = i2c_channel_->Read16();
if (temp_result.is_error()) {
FDF_SLOG(ERROR, "Failed to read temperature: ", KV("status", temp_result.status_string()));
completer.ReplyError(temp_result.status_value());
return;
}
auto temp_value = static_cast<uint32_t>(temp_result.value());
FDF_SLOG(INFO, "Received temperature: ", KV("value", temp_value));
return completer.ReplySuccess(temp_value);
}
// Protocol method in `sample.i2ctemperature` to reset the temperature senseor
void I2cTemperatureDriver::ResetSensor(ResetSensorRequestView request,
ResetSensorCompleter::Sync &completer) {
auto channel_status = SetupI2cChannel();
if (channel_status.is_error()) {
completer.ReplyError(channel_status.status_value());
return;
}
auto result = i2c_channel_->Write16(kSoftResetCommand);
if (result.is_error()) {
FDF_SLOG(ERROR, "Failed to send reset command: ", KV("status", result.status_string()));
completer.ReplyError(result.status_value());
return;
}
}
} // namespace i2c_temperature
FUCHSIA_DRIVER_RECORD_CPP_V1(i2c_temperature::I2cTemperatureDriver);