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