| // 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); |