[i2c_temperature] Organize components into modules

Separate driver components and libraries into individual directories.
Add method comments and clean up imports.

Change-Id: I515544662b20ba2c9c2f60003fb0ca78b867bf9a
Reviewed-on: https://fuchsia-review.googlesource.com/c/sdk-samples/drivers/+/710245
Reviewed-by: Novin Changizi <novinc@google.com>
Reviewed-by: Sarah Chan <spqchan@google.com>
Reviewed-by: Suraj Malhotra <surajmalhotra@google.com>
Commit-Queue: Dave Smith <smithdave@google.com>
diff --git a/src/BUILD.bazel b/src/BUILD.bazel
index 16f6712..72e4ab0 100644
--- a/src/BUILD.bazel
+++ b/src/BUILD.bazel
@@ -18,8 +18,8 @@
         "//src/bind_library:parent_pkg",
         "//src/composite_sample:pkg",
         "//src/example_driver:pkg",
-        "//src/i2c_temperature:pkg",
-        "//src/i2c_temperature:test_controller_pkg",
+        "//src/i2c_temperature/controller:pkg",
+        "//src/i2c_temperature/driver:pkg",
         "//src/input_sample:pkg",
         "//src/qemu_edu/drivers:pkg",
         "//src/qemu_edu/tests:pkg",
diff --git a/src/i2c_temperature/BUILD.bazel b/src/i2c_temperature/BUILD.bazel
deleted file mode 100644
index 63cc3c9..0000000
--- a/src/i2c_temperature/BUILD.bazel
+++ /dev/null
@@ -1,140 +0,0 @@
-# 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.
-
-load(
-    "@rules_fuchsia//fuchsia:defs.bzl",
-    "fuchsia_cc_binary",
-    "fuchsia_component_manifest",
-    "fuchsia_driver_bytecode_bind_rules",
-    "fuchsia_driver_component",
-    "fuchsia_fidl_library",
-    "fuchsia_fidl_llcpp_library",
-    "fuchsia_package",
-)
-
-fuchsia_fidl_library(
-    name = "sample.i2ctemperature",
-    srcs = [
-        "i2c_temperature.fidl",
-    ],
-    library = "sample.i2ctemperature",
-    visibility = ["//visibility:public"],
-    deps = [
-        "@fuchsia_sdk//fidl/zx:zx",
-    ],
-)
-
-fuchsia_fidl_llcpp_library(
-    name = "sample.i2ctemperature_cc",
-    library = ":sample.i2ctemperature",
-    visibility = ["//visibility:public"],
-    deps = [
-        "@fuchsia_sdk//fidl/zx:zx_llcpp_cc",
-        "@fuchsia_sdk//pkg/fidl_cpp_wire",
-    ],
-)
-
-fuchsia_driver_bytecode_bind_rules(
-    name = "bind_bytecode",
-    output = "i2c_temperature.bindbc",
-    rules = "i2c_temperature.bind",
-    deps = [
-        "@fuchsia_sdk//bind/fuchsia.i2c",
-    ],
-)
-
-cc_binary(
-    name = "i2c_temperature",
-    srcs = [
-        "constants.h",
-        "i2c_channel.cc",
-        "i2c_channel.h",
-        "i2c_temperature.cc",
-        "i2c_temperature.h",
-    ],
-    linkshared = True,
-    deps = [
-        ":sample.i2ctemperature_cc",
-        "@fuchsia_sdk//fidl/fuchsia.hardware.i2c:fuchsia.hardware.i2c_llcpp_cc",
-        "@fuchsia_sdk//fidl/zx:zx_cc",
-        "@fuchsia_sdk//pkg/async-cpp",
-        "@fuchsia_sdk//pkg/driver2-llcpp",
-        "@fuchsia_sdk//pkg/fidl_cpp_wire",
-        "@fuchsia_sdk//pkg/sys_component_llcpp",
-        "@fuchsia_sdk//pkg/zx",
-    ],
-)
-
-fuchsia_component_manifest(
-    name = "manifest",
-    src = "meta/i2c_temperature.cml",
-)
-
-fuchsia_driver_component(
-    name = "component",
-    bind_bytecode = ":bind_bytecode",
-    driver_lib = ":i2c_temperature",
-    manifest = ":manifest",
-)
-
-fuchsia_package(
-    name = "pkg",
-    package_name = "i2c_temperature",
-    visibility = ["//visibility:public"],
-    deps = [
-        ":component",
-    ],
-)
-
-fuchsia_driver_bytecode_bind_rules(
-    name = "test_controller_bind_bytecode",
-    output = "test_controller.bindbc",
-    rules = "test_i2c_controller.bind",
-    deps = [
-        "@fuchsia_sdk//bind/fuchsia.acpi",
-    ],
-)
-
-cc_binary(
-    name = "test_controller",
-    srcs = [
-        "constants.h",
-        "test_i2c_controller.cc",
-        "test_i2c_controller.h",
-    ],
-    linkshared = True,
-    deps = [
-        "@fuchsia_sdk//fidl/fuchsia.device.fs:fuchsia.device.fs_llcpp_cc",
-        "@fuchsia_sdk//fidl/fuchsia.driver.compat:fuchsia.driver.compat_llcpp_cc",
-        "@fuchsia_sdk//fidl/fuchsia.hardware.i2c:fuchsia.hardware.i2c_llcpp_cc",
-        "@fuchsia_sdk//fidl/zx:zx_cc",
-        "@fuchsia_sdk//pkg/async-cpp",
-        "@fuchsia_sdk//pkg/driver2-llcpp",
-        "@fuchsia_sdk//pkg/fidl_cpp_wire",
-        "@fuchsia_sdk//pkg/sys_component_llcpp",
-        "@fuchsia_sdk//pkg/zx",
-    ],
-)
-
-fuchsia_component_manifest(
-    name = "test_controller_manifest",
-    src = "meta/test_i2c_controller.cml",
-)
-
-fuchsia_driver_component(
-    name = "test_controller_component",
-    bind_bytecode = ":test_controller_bind_bytecode",
-    driver_lib = ":test_controller",
-    manifest = ":test_controller_manifest",
-)
-
-fuchsia_package(
-    name = "test_controller_pkg",
-    package_name = "test_i2c_controller",
-    visibility = ["//visibility:public"],
-    deps = [
-        ":test_controller_component",
-    ],
-)
-
diff --git a/src/i2c_temperature/README.md b/src/i2c_temperature/README.md
new file mode 100644
index 0000000..3408cc6
--- /dev/null
+++ b/src/i2c_temperature/README.md
@@ -0,0 +1,60 @@
+# I2C Temperature Sample
+
+This sample project contains a driver component named `i2c_temperature` for a temperature
+sensor device connected to an I2C bus with device address `0xFF`.
+The driver interacts with the device hardware using the `fuchsia.hardware.i2c` protocol,
+and exposes a custom FIDL protocol (`sample.i2ctemperature`) for other components to
+consume.
+
+The `i2c_controller` driver component emulates an I2C bus controller, creating the child
+device nodes to the sensor device and responding to I2C protocol commands received from
+the child devices.
+
+## Building
+
+To build the `i2c_temperature` driver and related components, run the following commands:
+
+```
+tools/bazel build --config=fuchsia_x64 //src/i2c_temperature/controller:pkg
+tools/bazel build --config=fuchsia_x64 //src/i2c_temperature/driver:pkg
+```
+
+## Running
+
+Use the following commands to load the driver components on a target device:
+
+1.  Load the `i2c_controller` driver component to create a virtual I2C device node:
+
+    ```
+    tools/bazel run --config=fuchsia_x64 //src/i2c_temperature/controller:pkg.component
+    ```
+
+1.  Load the `i2c_temperature` driver component to bind to the device node and begin
+    sending requests:
+
+    ```
+    tools/bazel run --config=fuchsia_x64 //src/i2c_temperature/driver:pkg.component
+    ```
+
+1.  Open the device log viewer:
+
+    ```
+    tools/ffx log --filter i2c_controller --filter i2c_temperature
+    ```
+
+You should see the `i2c_controller` driver log the requests it receives from `i2c_temperature`
+after the driver has successfully bound.
+
+```
+[i2c-temperature-controller,driver][I]: [i2c_controller.cc:139] Received transfer request
+[i2c-temperature-controller,driver][I]: [i2c_controller.cc:126] Reset command received
+[i2c-temperature-controller,driver][I]: [i2c_controller.cc:139] Received transfer request
+[i2c-temperature,driver][I]: [i2c_temperature.cc:62] I2C temperature:  temperature=20
+```
+
+## Source layout
+
+*   `controller/` — Source code of the `i2c_controller` driver component.
+*   `driver/` — Source code of the `i2c_temperature` driver component.
+*   `fidl/` — FIDL library definition for `sample.i2ctemperature`.
+*   `lib/` — Common I2C constants shared between the controller and driver components.
diff --git a/src/i2c_temperature/controller/BUILD.bazel b/src/i2c_temperature/controller/BUILD.bazel
new file mode 100644
index 0000000..4d7f828
--- /dev/null
+++ b/src/i2c_temperature/controller/BUILD.bazel
@@ -0,0 +1,71 @@
+# 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.
+
+load(
+    "@rules_fuchsia//fuchsia:defs.bzl",
+    "fuchsia_cc_binary",
+    "fuchsia_component_manifest",
+    "fuchsia_driver_bytecode_bind_rules",
+    "fuchsia_driver_component",
+    "fuchsia_package",
+)
+
+# [START bind_rules]
+fuchsia_driver_bytecode_bind_rules(
+    name = "bind_bytecode",
+    output = "i2c_controller.bindbc",
+    rules = "i2c_controller.bind",
+    deps = [
+        "@fuchsia_sdk//bind/fuchsia.acpi",
+    ],
+)
+# [END bind_rules]
+
+# [START cc_binary]
+cc_binary(
+    name = "i2c_controller",
+    srcs = [
+        "i2c_controller.cc",
+        "i2c_controller.h",
+    ],
+    linkshared = True,
+    deps = [
+        "//src/i2c_temperature/lib",
+        "@fuchsia_sdk//fidl/fuchsia.device.fs:fuchsia.device.fs_llcpp_cc",
+        "@fuchsia_sdk//fidl/fuchsia.driver.compat:fuchsia.driver.compat_llcpp_cc",
+        "@fuchsia_sdk//fidl/fuchsia.hardware.i2c:fuchsia.hardware.i2c_bindlib_cc",
+        "@fuchsia_sdk//fidl/fuchsia.hardware.i2c:fuchsia.hardware.i2c_llcpp_cc",
+        "@fuchsia_sdk//fidl/zx:zx_cc",
+        "@fuchsia_sdk//pkg/async-cpp",
+        "@fuchsia_sdk//pkg/driver2-llcpp",
+        "@fuchsia_sdk//pkg/fidl_cpp_wire",
+        "@fuchsia_sdk//pkg/sys_component_llcpp",
+        "@fuchsia_sdk//pkg/zx",
+    ],
+)
+# [END cc_binary]
+
+fuchsia_component_manifest(
+    name = "manifest",
+    src = "meta/i2c_controller.cml",
+    includes = [
+        "@fuchsia_sdk//pkg/syslog:client",
+    ],
+)
+
+fuchsia_driver_component(
+    name = "component",
+    bind_bytecode = ":bind_bytecode",
+    driver_lib = ":i2c_controller",
+    manifest = ":manifest",
+)
+
+fuchsia_package(
+    name = "pkg",
+    package_name = "i2c_controller",
+    visibility = ["//visibility:public"],
+    deps = [
+        ":component",
+    ],
+)
diff --git a/src/i2c_temperature/test_i2c_controller.bind b/src/i2c_temperature/controller/i2c_controller.bind
similarity index 100%
rename from src/i2c_temperature/test_i2c_controller.bind
rename to src/i2c_temperature/controller/i2c_controller.bind
diff --git a/src/i2c_temperature/controller/i2c_controller.cc b/src/i2c_temperature/controller/i2c_controller.cc
new file mode 100644
index 0000000..d2e0010
--- /dev/null
+++ b/src/i2c_temperature/controller/i2c_controller.cc
@@ -0,0 +1,200 @@
+// 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>
+
+// [START bind_imports]
+#include <bind/fuchsia/hardware/i2c/cpp/bind.h>
+// [END bind_imports]
+
+#include "constants.h"
+
+namespace i2c_temperature {
+
+namespace {
+
+constexpr uint32_t kStartingTemp = 20;
+constexpr uint32_t kTempIncrement = 5;
+
+}  // namespace
+
+zx::status<std::unique_ptr<I2cTemperatureController>> I2cTemperatureController::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<I2cTemperatureController>(
+      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<> I2cTemperatureController::Run(fidl::ServerEnd<fuchsia_io::Directory> outgoing_dir) {
+  temperature_ = kStartingTemp;
+
+  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.status_value() != ZX_OK) {
+    FDF_SLOG(ERROR, "Failed to add protocol", KV("status", status.status_string()));
+    return status;
+  }
+
+  status = AddChild();
+  if (status.status_value() != ZX_OK) {
+    return status;
+  }
+
+  return outgoing_.Serve(std::move(outgoing_dir));
+}
+
+zx::status<> I2cTemperatureController::AddChild() {
+  fidl::Arena arena;
+
+  // [START add_child_offer]
+  // 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);
+  // [END add_child_offer]
+
+  // [START add_child_properties]
+  // 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(0xff))
+                      .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();
+  // [END add_child_properties]
+
+  // [START add_child_node]
+  // 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());
+  }
+  // [END add_child_node]
+
+  return zx::ok();
+}
+
+// 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 I2cTemperatureController::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 == i2c_temperature::kSoftResetCommand) {
+    FDF_SLOG(INFO, "Reset command received");
+    temperature_ = kStartingTemp;
+  } else if (command == i2c_temperature::kStartMeasurementCommand) {
+    FDF_SLOG(INFO, "Measurement command received");
+    temperature_ += kTempIncrement;
+  } else {
+    FDF_SLOG(WARNING, "Received unknown command ", KV("command", command));
+  }
+}
+
+// Protocol method of `fuchsia.hardware.i2c.Device` to handle data transfer requests.
+void I2cTemperatureController::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);
+}
+
+}  // namespace i2c_temperature
+
+FUCHSIA_DRIVER_RECORD_CPP_V1(i2c_temperature::I2cTemperatureController);
diff --git a/src/i2c_temperature/controller/i2c_controller.h b/src/i2c_temperature/controller/i2c_controller.h
new file mode 100644
index 0000000..15382e0
--- /dev/null
+++ b/src/i2c_temperature/controller/i2c_controller.h
@@ -0,0 +1,64 @@
+// 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.
+
+#ifndef FUCHSIA_SDK_EXAMPLES_SRC_I2C_TEMPERATURE_I2C_CONTROLLER_H_
+#define FUCHSIA_SDK_EXAMPLES_SRC_I2C_TEMPERATURE_I2C_CONTROLLER_H_
+
+#include <fidl/fuchsia.driver.framework/cpp/wire.h>
+#include <fidl/fuchsia.hardware.i2c/cpp/wire.h>
+#include <lib/async/cpp/executor.h>
+#include <lib/driver2/namespace.h>
+#include <lib/driver2/structured_logger.h>
+#include <lib/fdf/cpp/dispatcher.h>
+#include <lib/sys/component/llcpp/outgoing_directory.h>
+
+namespace i2c_temperature {
+
+// Sample driver that implements a `fuchsia.hardware.i2c` bus controller and emulates the
+// behavior of a temperature sensor device to interact with the i2c temperature driver.
+class I2cTemperatureController : public fidl::WireServer<fuchsia_hardware_i2c::Device> {
+ public:
+  I2cTemperatureController(async_dispatcher_t* dispatcher,
+                           fidl::WireSharedClient<fuchsia_driver_framework::Node> node,
+                           driver::Namespace ns, driver::Logger logger)
+      : dispatcher_(dispatcher),
+        outgoing_(component::OutgoingDirectory::Create(dispatcher)),
+        ns_(std::move(ns)),
+        logger_(std::move(logger)),
+        node_(std::move(node)) {}
+
+  virtual ~I2cTemperatureController() = default;
+
+  static constexpr const char* Name() { return "i2c-temperature-controller"; }
+
+  static zx::status<std::unique_ptr<I2cTemperatureController>> Start(
+      fuchsia_driver_framework::wire::DriverStartArgs& start_args,
+      fdf::UnownedDispatcher dispatcher,
+      fidl::WireSharedClient<fuchsia_driver_framework::Node> node, driver::Namespace ns,
+      driver::Logger logger);
+
+  // fuchsia.hardware.i2c/Device:
+  void Transfer(TransferRequestView request, TransferCompleter::Sync& completer);
+
+ private:
+  zx::status<> Run(fidl::ServerEnd<fuchsia_io::Directory> outgoing_dir);
+  zx::status<> AddChild();
+
+  void HandleWrite(const fidl::VectorView<uint8_t> write_data);
+
+  uint32_t temperature_;
+
+  async_dispatcher_t* const dispatcher_;
+
+  component::OutgoingDirectory outgoing_;
+  driver::Namespace ns_;
+  driver::Logger logger_;
+
+  fidl::WireSharedClient<fuchsia_driver_framework::Node> node_;
+  fidl::WireSharedClient<fuchsia_driver_framework::NodeController> controller_;
+};
+
+}  // namespace i2c_temperature
+
+#endif  // FUCHSIA_SDK_EXAMPLES_SRC_I2C_TEMPERATURE_I2C_CONTROLLER_H_
diff --git a/src/i2c_temperature/controller/meta/i2c_controller.cml b/src/i2c_temperature/controller/meta/i2c_controller.cml
new file mode 100644
index 0000000..2f18b9a
--- /dev/null
+++ b/src/i2c_temperature/controller/meta/i2c_controller.cml
@@ -0,0 +1,26 @@
+// 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: [
+        'syslog/client.shard.cml',
+    ],
+    program: {
+        runner: 'driver',
+        binary: 'lib/libi2c_controller.so',
+        bind: 'meta/bind/i2c_controller.bindbc'
+    },
+    use: [
+        { service: 'fuchsia.driver.compat.Service' },
+    ],
+    // Provide the I2C device capability to other components
+    capabilities: [
+        { protocol: 'fuchsia.hardware.i2c.Device' },
+    ],
+    expose: [
+        {
+            protocol: 'fuchsia.hardware.i2c.Device',
+            from: 'self',
+        },
+    ],
+}
diff --git a/src/i2c_temperature/driver/BUILD.bazel b/src/i2c_temperature/driver/BUILD.bazel
new file mode 100644
index 0000000..f32682d
--- /dev/null
+++ b/src/i2c_temperature/driver/BUILD.bazel
@@ -0,0 +1,71 @@
+# 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.
+
+load(
+    "@rules_fuchsia//fuchsia:defs.bzl",
+    "fuchsia_cc_binary",
+    "fuchsia_component_manifest",
+    "fuchsia_driver_bytecode_bind_rules",
+    "fuchsia_driver_component",
+    "fuchsia_package",
+)
+
+# [START bind_rules]
+fuchsia_driver_bytecode_bind_rules(
+    name = "bind_bytecode",
+    output = "i2c_temperature.bindbc",
+    rules = "i2c_temperature.bind",
+    deps = [
+        "@fuchsia_sdk//fidl/fuchsia.hardware.i2c:fuchsia.hardware.i2c_bindlib",
+    ],
+)
+# [END bind_rules]
+
+# [START cc_binary]
+cc_binary(
+    name = "i2c_temperature",
+    srcs = [
+        "i2c_channel.cc",
+        "i2c_channel.h",
+        "i2c_temperature.cc",
+        "i2c_temperature.h",
+    ],
+    linkshared = True,
+    deps = [
+        "//src/i2c_temperature/fidl:sample.i2ctemperature_cc",
+        "//src/i2c_temperature/lib",
+        "@fuchsia_sdk//fidl/fuchsia.hardware.i2c:fuchsia.hardware.i2c_llcpp_cc",
+        "@fuchsia_sdk//fidl/zx:zx_cc",
+        "@fuchsia_sdk//pkg/async-cpp",
+        "@fuchsia_sdk//pkg/driver2-llcpp",
+        "@fuchsia_sdk//pkg/fidl_cpp_wire",
+        "@fuchsia_sdk//pkg/sys_component_llcpp",
+        "@fuchsia_sdk//pkg/zx",
+    ],
+)
+# [END cc_binary]
+
+fuchsia_component_manifest(
+    name = "manifest",
+    src = "meta/i2c_temperature.cml",
+    includes = [
+        "@fuchsia_sdk//pkg/syslog:client",
+    ],
+)
+
+fuchsia_driver_component(
+    name = "component",
+    bind_bytecode = ":bind_bytecode",
+    driver_lib = ":i2c_temperature",
+    manifest = ":manifest",
+)
+
+fuchsia_package(
+    name = "pkg",
+    package_name = "i2c_temperature",
+    visibility = ["//visibility:public"],
+    deps = [
+        ":component",
+    ],
+)
diff --git a/src/i2c_temperature/i2c_channel.cc b/src/i2c_temperature/driver/i2c_channel.cc
similarity index 97%
rename from src/i2c_temperature/i2c_channel.cc
rename to src/i2c_temperature/driver/i2c_channel.cc
index 22744da..a711b75 100644
--- a/src/i2c_temperature/i2c_channel.cc
+++ b/src/i2c_temperature/driver/i2c_channel.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "src/i2c_temperature/i2c_channel.h"
+#include "i2c_channel.h"
 
 #include <endian.h>
 
diff --git a/src/i2c_temperature/i2c_channel.h b/src/i2c_temperature/driver/i2c_channel.h
similarity index 88%
rename from src/i2c_temperature/i2c_channel.h
rename to src/i2c_temperature/driver/i2c_channel.h
index b737369..f753b22 100644
--- a/src/i2c_temperature/i2c_channel.h
+++ b/src/i2c_temperature/driver/i2c_channel.h
@@ -16,8 +16,7 @@
   ~I2cChannel() = default;
 
   // Functions that perform read/write transactions through the client.
-  // The values are converted from big-endian order to host byte order for write
-  // and vice versa for read.
+  // The values are transferred across the I2C channel in big-endian order.
   zx::status<uint16_t> Read16();
   zx::status<> Write16(uint16_t value);
 
diff --git a/src/i2c_temperature/driver/i2c_temperature.bind b/src/i2c_temperature/driver/i2c_temperature.bind
new file mode 100644
index 0000000..0ea484c
--- /dev/null
+++ b/src/i2c_temperature/driver/i2c_temperature.bind
@@ -0,0 +1,10 @@
+// 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.
+
+// [START bind_rules]
+using fuchsia.hardware.i2c;
+
+fuchsia.hardware.i2c.Device == fuchsia.hardware.i2c.Device.ZirconTransport;
+fuchsia.BIND_I2C_ADDRESS == 0xFF;
+// [END bind_rules]
diff --git a/src/i2c_temperature/i2c_temperature.cc b/src/i2c_temperature/driver/i2c_temperature.cc
similarity index 72%
rename from src/i2c_temperature/i2c_temperature.cc
rename to src/i2c_temperature/driver/i2c_temperature.cc
index 1e77aa7..d7de4d6 100644
--- a/src/i2c_temperature/i2c_temperature.cc
+++ b/src/i2c_temperature/driver/i2c_temperature.cc
@@ -2,13 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "src/i2c_temperature/i2c_temperature.h"
+#include "i2c_temperature.h"
 
 #include <lib/driver2/record_cpp.h>
 
-#include "src/i2c_temperature/constants.h"
-
-namespace fio = fuchsia_io;
+#include "constants.h"
 
 namespace i2c_temperature {
 
@@ -27,27 +25,46 @@
   return zx::ok(std::move(driver));
 }
 
-zx::status<> I2cTemperatureDriver::Run(fidl::ServerEnd<fio::Directory> outgoing_dir) {
+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.status_value() != ZX_OK) {
-    FDF_SLOG(ERROR, "Failed to add protocol", KV("status", status.status_string()));
-    return status;
-  }
-
-  status = SetupI2cChannel();
   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();
   }
 
-  // Reset the sensor.
-  auto result = i2c_channel_->Write16(kSoftResetCommand);
+  // Connect to the I2C bus controller.
+  auto result = SetupI2cChannel();
   if (result.is_error()) {
-    FDF_SLOG(ERROR, "Failed to send reset command: ", KV("status", result.status_string()));
+    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();
@@ -64,6 +81,7 @@
   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();
@@ -93,6 +111,7 @@
   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();
diff --git a/src/i2c_temperature/i2c_temperature.h b/src/i2c_temperature/driver/i2c_temperature.h
similarity index 94%
rename from src/i2c_temperature/i2c_temperature.h
rename to src/i2c_temperature/driver/i2c_temperature.h
index c6b9f73..af83d17 100644
--- a/src/i2c_temperature/i2c_temperature.h
+++ b/src/i2c_temperature/driver/i2c_temperature.h
@@ -18,7 +18,7 @@
 
 namespace i2c_temperature {
 
-// Sample driver that demonstrates how to communicate through an I2C protocol.
+// Sample driver for a virtual temperature sensor device connected to an I2C bus controller.
 class I2cTemperatureDriver : public fidl::WireServer<sample_i2ctemperature::Device> {
  public:
   I2cTemperatureDriver(async_dispatcher_t* dispatcher,
@@ -40,7 +40,7 @@
       fidl::WireSharedClient<fuchsia_driver_framework::Node> node, driver::Namespace ns,
       driver::Logger logger);
 
-  // fuchsia.hardware.i2ctemperature/Device:
+  // sample.i2ctemperature/Device:
   void ReadTemperature(ReadTemperatureRequestView request,
                        ReadTemperatureCompleter::Sync& completer);
   void ResetSensor(ResetSensorRequestView request, ResetSensorCompleter::Sync& completer);
diff --git a/src/i2c_temperature/driver/meta/i2c_temperature.cml b/src/i2c_temperature/driver/meta/i2c_temperature.cml
new file mode 100644
index 0000000..1b87733
--- /dev/null
+++ b/src/i2c_temperature/driver/meta/i2c_temperature.cml
@@ -0,0 +1,28 @@
+// 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: [
+        'syslog/client.shard.cml',
+    ],
+    program: {
+        runner: 'driver',
+        binary: 'lib/libi2c_temperature.so',
+        bind: 'meta/bind/i2c_temperature.bindbc'
+    },
+    // Consume the I2C device capability from the parent
+    use: [
+        { protocol: 'fuchsia.hardware.i2c.Device' },
+        { service: 'fuchsia.driver.compat.Service' },
+    ],
+    // Provide the temperature device capability to other components
+    capabilities: [
+        { protocol: 'sample.i2ctemperature.Device' },
+    ],
+    expose: [
+        {
+            protocol: 'sample.i2ctemperature.Device',
+            from: 'self',
+        },
+    ],
+}
diff --git a/src/i2c_temperature/fidl/BUILD.bazel b/src/i2c_temperature/fidl/BUILD.bazel
new file mode 100644
index 0000000..8aa38fb
--- /dev/null
+++ b/src/i2c_temperature/fidl/BUILD.bazel
@@ -0,0 +1,31 @@
+# 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.
+
+load(
+    "@rules_fuchsia//fuchsia:defs.bzl",
+    "fuchsia_fidl_library",
+    "fuchsia_fidl_llcpp_library",
+)
+
+fuchsia_fidl_library(
+    name = "sample.i2ctemperature",
+    srcs = [
+        "i2c_temperature.fidl",
+    ],
+    library = "sample.i2ctemperature",
+    visibility = ["//visibility:public"],
+    deps = [
+        "@fuchsia_sdk//fidl/zx:zx",
+    ],
+)
+
+fuchsia_fidl_llcpp_library(
+    name = "sample.i2ctemperature_cc",
+    library = ":sample.i2ctemperature",
+    visibility = ["//visibility:public"],
+    deps = [
+        "@fuchsia_sdk//fidl/zx:zx_llcpp_cc",
+        "@fuchsia_sdk//pkg/fidl_cpp_wire",
+    ],
+)
diff --git a/src/i2c_temperature/i2c_temperature.fidl b/src/i2c_temperature/fidl/i2c_temperature.fidl
similarity index 100%
rename from src/i2c_temperature/i2c_temperature.fidl
rename to src/i2c_temperature/fidl/i2c_temperature.fidl
diff --git a/src/i2c_temperature/i2c_temperature.bind b/src/i2c_temperature/i2c_temperature.bind
deleted file mode 100644
index bbf5f17..0000000
--- a/src/i2c_temperature/i2c_temperature.bind
+++ /dev/null
@@ -1,8 +0,0 @@
-// 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.
-
-using fuchsia.i2c;
-
-fuchsia.BIND_PROTOCOL == fuchsia.i2c.BIND_PROTOCOL.DEVICE;
-fuchsia.BIND_I2C_ADDRESS == 0xFF;
\ No newline at end of file
diff --git a/src/i2c_temperature/lib/BUILD.bazel b/src/i2c_temperature/lib/BUILD.bazel
new file mode 100644
index 0000000..671b8c9
--- /dev/null
+++ b/src/i2c_temperature/lib/BUILD.bazel
@@ -0,0 +1,10 @@
+# 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.
+
+cc_library(
+    name = "lib",
+    hdrs = ["include/constants.h"],
+    includes = ["include"],
+    visibility = ["//visibility:public"],
+)
diff --git a/src/i2c_temperature/constants.h b/src/i2c_temperature/lib/include/constants.h
similarity index 100%
rename from src/i2c_temperature/constants.h
rename to src/i2c_temperature/lib/include/constants.h
diff --git a/src/i2c_temperature/meta/i2c_temperature.cml b/src/i2c_temperature/meta/i2c_temperature.cml
deleted file mode 100644
index 42cb06a..0000000
--- a/src/i2c_temperature/meta/i2c_temperature.cml
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-    program: {
-        runner: 'driver',
-        binary: 'lib/libi2c_temperature.so',
-        bind: 'meta/bind/i2c_temperature.bindbc'
-    },
-    use: [
-        {
-            protocol: [
-              'fuchsia.logger.LogSink',
-              'fuchsia.hardware.i2c.Device',
-            ],
-        },
-        { service: "fuchsia.driver.compat.Service" },
-    ],
-}
diff --git a/src/i2c_temperature/meta/test_i2c_controller.cml b/src/i2c_temperature/meta/test_i2c_controller.cml
deleted file mode 100644
index 314bc14..0000000
--- a/src/i2c_temperature/meta/test_i2c_controller.cml
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-    program: {
-        runner: 'driver',
-        binary: 'lib/libtest_controller.so',
-        bind: 'meta/bind/test_controller.bindbc'
-    },
-    use: [
-        {
-            protocol: [
-              'fuchsia.logger.LogSink',
-            ],
-        },
-        { service: "fuchsia.driver.compat.Service" },
-    ],
-    capabilities: [
-        { protocol: 'fuchsia.hardware.i2c.Device' },
-    ],
-    expose: [
-        {
-            protocol: 'fuchsia.hardware.i2c.Device',
-            from: 'self',
-        },
-    ],
-}
diff --git a/src/i2c_temperature/test_i2c_controller.cc b/src/i2c_temperature/test_i2c_controller.cc
deleted file mode 100644
index 3396fc9..0000000
--- a/src/i2c_temperature/test_i2c_controller.cc
+++ /dev/null
@@ -1,185 +0,0 @@
-// 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 "src/i2c_temperature/test_i2c_controller.h"
-
-#include <endian.h>
-#include <fidl/fuchsia.component.decl/cpp/wire.h>
-#include <lib/driver2/record_cpp.h>
-
-#include "src/i2c_temperature/constants.h"
-
-namespace fdf {
-using namespace fuchsia_driver_framework;
-}  // namespace fdf
-
-namespace test_i2c_controller {
-
-namespace fcd = fuchsia_component_decl;
-
-namespace {
-
-constexpr uint32_t kStartingTemp = 20;
-constexpr uint32_t kTempIncrement = 5;
-
-}  // namespace
-
-zx::status<std::unique_ptr<TestI2cController>> TestI2cController::Start(
-    fdf::wire::DriverStartArgs& start_args, fdf::UnownedDispatcher dispatcher,
-    fidl::WireSharedClient<fdf::Node> node, driver::Namespace ns, driver::Logger logger) {
-  auto driver = std::make_unique<TestI2cController>(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<> TestI2cController::Run(fidl::ServerEnd<fuchsia_io::Directory> outgoing_dir) {
-  temperature_ = kStartingTemp;
-
-  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.status_value() != ZX_OK) {
-    FDF_SLOG(ERROR, "Failed to add protocol", KV("status", status.status_string()));
-    return status;
-  }
-
-  status = AddChild();
-  if (status.status_value() != ZX_OK) {
-    return status;
-  }
-
-  return outgoing_.Serve(std::move(outgoing_dir));
-}
-
-zx::status<> TestI2cController::AddChild() {
-  fidl::Arena arena;
-
-  // Offer `fuchsia.hardware.i2c.Device` to the driver that binds to the node.
-  auto protocol_offer =
-      fcd::wire::OfferProtocol::Builder(arena)
-          .source_name(arena, fidl::DiscoverableProtocolName<fuchsia_hardware_i2c::Device>)
-          .target_name(arena, fidl::DiscoverableProtocolName<fuchsia_hardware_i2c::Device>)
-          .dependency_type(fcd::wire::DependencyType::kStrong)
-          .Build();
-  fcd::wire::Offer offer = fcd::wire::Offer::WithProtocol(arena, protocol_offer);
-
-  // Set the properties of the node that a driver will bind to.
-  // TODO(fxb/98831): Replace this once bind library protocol definitions are available.
-  auto properties = fidl::VectorView<fdf::wire::NodeProperty>(arena, 2);
-  properties[0] = fdf::wire::NodeProperty::Builder(arena)
-                      .key(fdf::wire::NodePropertyKey::WithIntValue(1 /* BIND_PROTOCOL */))
-                      .value(fdf::wire::NodePropertyValue::WithIntValue(24))
-                      .Build();
-  properties[1] = fdf::wire::NodeProperty::Builder(arena)
-                      .key(fdf::wire::NodePropertyKey::WithIntValue(0x0A02 /* BIND_I2C_ADDRESS */))
-                      .value(fdf::wire::NodePropertyValue::WithIntValue(0xff))
-                      .Build();
-
-  auto args = fdf::wire::NodeAddArgs::Builder(arena)
-                  .name(arena, "i2c-child")
-                  .offers(fidl::VectorView<fcd::wire::Offer>::FromExternal(&offer, 1))
-                  .properties(properties)
-                  .Build();
-
-  // Create endpoints of the `NodeController` for the node.
-  auto endpoints = fidl::CreateEndpoints<fdf::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();
-}
-
-void TestI2cController::HandleWrite(const fidl::VectorView<uint8_t> write_data) {
-  // For simplicity, assume that we'll only receive integer commands from
-  // I2cTemperatureDriver.
-  if (write_data.count() > 4) {
-    return;
-  }
-
-  uint8_t bytes[4];
-  for (uint32_t i = 0; i < write_data.count(); i++) {
-    bytes[i] = write_data.at(i);
-  }
-
-  int value;
-  std::memcpy(&value, bytes, sizeof(int));
-  value = be16toh(value);
-
-  if (value == i2c_temperature::kSoftResetCommand) {
-    FDF_SLOG(INFO, "Reset command received");
-    temperature_ = kStartingTemp;
-  } else if (value == i2c_temperature::kStartMeasurementCommand) {
-    FDF_SLOG(INFO, "Measurement command received");
-    temperature_ += kTempIncrement;
-  } else {
-    FDF_SLOG(WARNING, "Received unknown command ", KV("command", value));
-  }
-}
-
-void TestI2cController::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();
-
-    // Handle write transaction.
-    if (transfer.is_write_data()) {
-      if (transfer.write_data().count() <= 0) {
-        completer.ReplyError(ZX_ERR_INVALID_ARGS);
-        return;
-      }
-
-      HandleWrite(transfer.write_data());
-      continue;
-    }
-
-    // Handle read transaction.
-    auto read_value = htobe16(temperature_);
-    auto read_bytes = fidl::VectorView<uint8_t>(allocator, 4);
-    for (size_t i = 0; i < 4; i++) {
-      read_bytes[i] = read_value >> (i * 8) & 0xff;
-    }
-    read_data.push_back(read_bytes);
-  }
-
-  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);
-}
-
-}  // namespace test_i2c_controller
-
-FUCHSIA_DRIVER_RECORD_CPP_V1(test_i2c_controller::TestI2cController);
diff --git a/src/i2c_temperature/test_i2c_controller.h b/src/i2c_temperature/test_i2c_controller.h
deleted file mode 100644
index 403fe81..0000000
--- a/src/i2c_temperature/test_i2c_controller.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// 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.
-
-#ifndef FUCHSIA_SDK_EXAMPLES_SRC_I2C_TEMPERATURE_TESTING_TEST_I2C_CONTROLLER_H_
-#define FUCHSIA_SDK_EXAMPLES_SRC_I2C_TEMPERATURE_TESTING_TEST_I2C_CONTROLLER_H_
-
-#include <fidl/fuchsia.driver.framework/cpp/wire.h>
-#include <fidl/fuchsia.hardware.i2c/cpp/wire.h>
-#include <lib/async/cpp/executor.h>
-#include <lib/driver2/namespace.h>
-#include <lib/driver2/structured_logger.h>
-#include <lib/fdf/cpp/dispatcher.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
-
-namespace test_i2c_controller {
-
-// Test driver that interacts with the i2c temperature driver. It implements the I2c protocol
-// and emulates a behavior where it receives commands for a temperature and returns a fake
-// temperature. The temperature it returns increase by 5 each time it receives a measurement
-// command. When it receives a reset command, the temperature is reset back to the starting
-// value.
-class TestI2cController : public fidl::WireServer<fuchsia_hardware_i2c::Device> {
- public:
-  TestI2cController(async_dispatcher_t* dispatcher,
-                    fidl::WireSharedClient<fuchsia_driver_framework::Node> node,
-                    driver::Namespace ns, driver::Logger logger)
-      : dispatcher_(dispatcher),
-        outgoing_(component::OutgoingDirectory::Create(dispatcher)),
-        ns_(std::move(ns)),
-        logger_(std::move(logger)),
-        node_(std::move(node)) {}
-
-  virtual ~TestI2cController() = default;
-
-  static constexpr const char* Name() { return "test-i2c-controller"; }
-
-  static zx::status<std::unique_ptr<TestI2cController>> Start(
-      fuchsia_driver_framework::wire::DriverStartArgs& start_args,
-      fdf::UnownedDispatcher dispatcher,
-      fidl::WireSharedClient<fuchsia_driver_framework::Node> node, driver::Namespace ns,
-      driver::Logger logger);
-
-  void Transfer(TransferRequestView request, TransferCompleter::Sync& completer);
-
- private:
-  zx::status<> Run(fidl::ServerEnd<fuchsia_io::Directory> outgoing_dir);
-  zx::status<> AddChild();
-
-  void HandleWrite(const fidl::VectorView<uint8_t> write_data);
-
-  uint32_t temperature_;
-
-  async_dispatcher_t* const dispatcher_;
-
-  component::OutgoingDirectory outgoing_;
-  driver::Namespace ns_;
-  driver::Logger logger_;
-
-  fidl::WireSharedClient<fuchsia_driver_framework::Node> node_;
-  fidl::WireSharedClient<fuchsia_driver_framework::NodeController> controller_;
-};
-
-}  // namespace test_i2c_controller
-
-#endif  // FUCHSIA_SDK_EXAMPLES_SRC_I2C_TEMPERATURE_TESTING_TEST_I2C_CONTROLLER_H_