[samples] Add devfs client components.

Add a client components to interact with sample drivers that using
their exposed FIDL protocols over devfs.

Change-Id: I41dac95c4e047ac8b937d1ec9d9e16c610729a8b
Reviewed-on: https://fuchsia-review.googlesource.com/c/sdk-samples/drivers/+/714563
Commit-Queue: Dave Smith <smithdave@google.com>
Reviewed-by: Suraj Malhotra <surajmalhotra@google.com>
diff --git a/src/BUILD.bazel b/src/BUILD.bazel
index 5a4de0c..541da1d 100644
--- a/src/BUILD.bazel
+++ b/src/BUILD.bazel
@@ -12,6 +12,7 @@
     testonly = True,
 
     deps = [
+        "//src/acpi_multiply/client:pkg",
         "//src/acpi_multiply/controller:pkg",
         "//src/acpi_multiply/driver:pkg",
         "//src/bind_library/child:pkg",
@@ -20,6 +21,7 @@
         "//src/composite_sample/driver:pkg",
         "//src/example_driver:pkg",
         "//src/example_driver:test_pkg",
+        "//src/i2c_temperature/client:pkg",
         "//src/i2c_temperature/controller:pkg",
         "//src/i2c_temperature/driver:pkg",
         "//src/input_sample:pkg",
diff --git a/src/acpi_multiply/README.md b/src/acpi_multiply/README.md
index 316a25c..bfa6eff 100644
--- a/src/acpi_multiply/README.md
+++ b/src/acpi_multiply/README.md
@@ -23,6 +23,13 @@
 
 Use the following commands to load the driver components on a target device:
 
+1.  Add a test node for the `acpi_controller` driver:
+
+    ```
+    tools/ffx driver test-node add controller-node \
+      examples.driver.test.property=acpi-multiply-controller
+    ```
+
 1.  Load the `acpi_controller` driver component to create a virtual ACPI device node:
 
     ```
@@ -42,13 +49,27 @@
     tools/ffx log --tags acpi-multiply --tags acpi-multiply-controller
     ```
 
-You should see the `acpi_controller` driver log the request it receives `acpi_multiply` after
-the driver has successfully bound.
+### Driver client
+
+After the drivers are loaded, use the following commands to run the multiplier client:
+
+1.  Load the `multiply_client` component:
+
+    ```
+    tools/bazel run --config=fuchsia_x64 //src/acpi_multiply/client:pkg.component
+    ```
+
+1.  Open the device log viewer:
+
+    ```
+    tools/ffx log --filter multiply_client
+    ```
+
+You should see the client respond with an incrementing temperature value on each run:
 
 ```
-[acpi-multiply-controller,driver,acpi-multiply-controller][I]: [acpi_server.cc:42] Received map interrupt request.
-[acpi-multiply-controller,driver,acpi-multiply-controller][I]: [acpi_server.cc:60] Received MMIO region request.
-[acpi-multiply,driver,acpi-multiply][I]: [acpi_multiply.cc:32] Hardware resources initialized.
+[ffx-laboratory:multiply_client][I] Multiply(4294967295, 9): Invalid result: Overflow
+[ffx-laboratory:multiply_client][I] Multiply(2, 9) = 18
 ```
 
 ## Source layout
diff --git a/src/acpi_multiply/client/BUILD.bazel b/src/acpi_multiply/client/BUILD.bazel
new file mode 100644
index 0000000..649a252
--- /dev/null
+++ b/src/acpi_multiply/client/BUILD.bazel
@@ -0,0 +1,48 @@
+# 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",
+    "fuchsia_component_manifest",
+    "fuchsia_package",
+)
+
+fuchsia_cc_binary(
+    name = "multiply_client",
+    srcs = [
+        "main.cc",
+    ],
+    deps = [
+        "//src/acpi_multiply/fidl:examples.acpi.multiply_cc",
+        "@fuchsia_sdk//pkg/fdio",
+        "@fuchsia_sdk//pkg/fidl_cpp_wire",
+    ],
+)
+
+fuchsia_component_manifest(
+    name = "manifest",
+    src = "meta/multiply_client.cml",
+    includes = [
+        "@fuchsia_sdk//pkg/syslog:client",
+    ],
+)
+
+fuchsia_component(
+    name = "component",
+    manifest = ":manifest",
+    deps = [
+        ":multiply_client",
+    ],
+)
+
+fuchsia_package(
+    name = "pkg",
+    package_name = "multiply_client",
+    visibility = ["//visibility:public"],
+    components = [
+        ":component",
+    ],
+)
diff --git a/src/acpi_multiply/client/main.cc b/src/acpi_multiply/client/main.cc
new file mode 100644
index 0000000..acf5ceb
--- /dev/null
+++ b/src/acpi_multiply/client/main.cc
@@ -0,0 +1,95 @@
+// 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 <ctype.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <fidl/examples.acpi.multiply/cpp/wire.h>
+#include <getopt.h>
+#include <lib/fdio/directory.h>
+#include <libgen.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+constexpr char kDevicePath[] = "/dev/acpi-multiply-controller/acpi-multiply";
+
+int usage(const char* cmd) {
+  fprintf(stderr,
+          "\nInteract with the ACPI multiplier device:\n"
+          "   %s multiply                   Perform a multiply operation\n"
+          "   %s help                       Print this message\n",
+          cmd, cmd);
+  return -1;
+}
+
+// Returns "true" if the argument matches the prefix.
+// In this case, moves the argument past the prefix.
+bool prefix_match(const char** arg, const char* prefix) {
+  if (!strncmp(*arg, prefix, strlen(prefix))) {
+    *arg += strlen(prefix);
+    return true;
+  }
+  return false;
+}
+
+// Open a FIDL client connection to the examples.acpi.multiply/Device
+fidl::WireSyncClient<examples_acpi_multiply::Device> OpenDevice() {
+  int device = open(kDevicePath, O_RDWR);
+  if (device < 0) {
+    fprintf(stderr, "Failed to open temperature device: %s\n", strerror(errno));
+    return {};
+  }
+  fidl::ClientEnd<examples_acpi_multiply::Device> client_end;
+  zx_status_t st = fdio_get_service_handle(device, client_end.channel().reset_and_get_address());
+  if (st != ZX_OK) {
+    fprintf(stderr, "Failed to get service handle: %s\n", zx_status_get_string(st));
+    return {};
+  }
+  return fidl::WireSyncClient(std::move(client_end));
+}
+
+// Send a command to perform a multiply operation and print the result.
+// Returns 0 on success.
+int do_multiply(uint32_t a, uint32_t b) {
+  auto client = OpenDevice();
+  if (!client.is_valid()) {
+    return -1;
+  }
+
+  auto multiply_result = client->Multiply(a, b);
+  if (!multiply_result.ok()) {
+    fprintf(stderr, "Error: failed to read multply result: %s\n",
+            zx_status_get_string(multiply_result.status()));
+    return -1;
+  }
+
+  auto overflowed = multiply_result->value()->overflowed;
+  if (overflowed) {
+    printf("Multiply(%u, %u): Invalid result: Overflow\n", a, b);
+  } else {
+    printf("Multiply(%u, %u) = %d\n", a, b, multiply_result->value()->result);
+  }
+  return 0;
+}
+
+int main(int argc, char* argv[]) {
+  const char* cmd = basename(argv[0]);
+
+  // If no arguments passed, bail out after dumping
+  // usage information.
+  if (argc < 2) {
+    return usage(cmd);
+  }
+
+  const char* arg = argv[1];
+  if (prefix_match(&arg, "multiply")) {
+    do_multiply(UINT32_MAX, 9);
+    do_multiply(2, 9);
+    return 0;
+  }
+  return usage(cmd);
+}
diff --git a/src/acpi_multiply/client/meta/multiply_client.cml b/src/acpi_multiply/client/meta/multiply_client.cml
new file mode 100644
index 0000000..723011d
--- /dev/null
+++ b/src/acpi_multiply/client/meta/multiply_client.cml
@@ -0,0 +1,22 @@
+// 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: 'elf',
+        binary: 'bin/multiply_client',
+        args: [
+          'multiply',
+        ]
+    },
+    use: [
+        {
+            directory: "dev-topological",
+            rights: [ "rw*" ],
+            path: "/dev",
+        },
+    ],
+}
diff --git a/src/acpi_multiply/controller/BUILD.bazel b/src/acpi_multiply/controller/BUILD.bazel
index 74e1d8b..f0818f6 100644
--- a/src/acpi_multiply/controller/BUILD.bazel
+++ b/src/acpi_multiply/controller/BUILD.bazel
@@ -15,7 +15,7 @@
     output = "acpi_controller.bindbc",
     rules = "acpi_controller.bind",
     deps = [
-        "@fuchsia_sdk//bind/fuchsia.acpi",
+        "//src/testing/lib:examples.driver.test",
     ],
 )
 
diff --git a/src/acpi_multiply/controller/acpi_controller.bind b/src/acpi_multiply/controller/acpi_controller.bind
index eee5121..43b5cab 100644
--- a/src/acpi_multiply/controller/acpi_controller.bind
+++ b/src/acpi_multiply/controller/acpi_controller.bind
@@ -2,12 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-using fuchsia.acpi;
+using examples.driver.test;
 
-// Bind to an unused ACPI node in Qemu or Atlas.
-// TODO(fxb/100716): Bind to a test node once it's available.
-if fuchsia.acpi.hid == "QEMU0002" {
-    true;
-} else {
-    fuchsia.acpi.hid == "PNP0C02";
-}
+examples.driver.test.property == "acpi-multiply-controller";
diff --git a/src/acpi_multiply/controller/acpi_controller.cc b/src/acpi_multiply/controller/acpi_controller.cc
index 310dc02..3458f75 100644
--- a/src/acpi_multiply/controller/acpi_controller.cc
+++ b/src/acpi_multiply/controller/acpi_controller.cc
@@ -37,10 +37,21 @@
     return result.take_error();
   }
 
-  // Initialize the driver compat service context.
-  compat::Context::ConnectAndCreate(
-      &context(), dispatcher(),
-      fit::bind_member<&AcpiMultiplyController::InitializeCompatService>(this));
+  // Publish `fuchsia.driver.compat.Service` to the outgoing directory.
+  child_ = compat::DeviceServer(std::string(name()), 0, std::string(name()));
+  auto status = child_->Serve(dispatcher(), context().outgoing().get());
+  if (status != ZX_OK) {
+    FDF_SLOG(ERROR, "Failed to serve compat device server.",
+             KV("status", zx_status_get_string(status)));
+    return zx::error(status);
+  }
+
+  // Create a child node and offer capabilities to bound drivers.
+  result = AddChild();
+  if (!result.is_ok()) {
+    FDF_SLOG(ERROR, "Failed to create child node.", KV("status", result.status_string()));
+    return result.take_error();
+  }
 
   return zx::ok();
 }
@@ -119,36 +130,6 @@
   return zx::ok();
 }
 
-// Construct and serve the `fuchsia.driver.compat.Service` after the context is initialized.
-void AcpiMultiplyController::InitializeCompatService(
-    zx::result<std::shared_ptr<compat::Context>> compat_result) {
-  if (!compat_result.is_ok()) {
-    FDF_SLOG(ERROR, "Failed to create compat context.",
-             KV("status", compat_result.status_string()));
-    node().reset();
-    return;
-  }
-  compat_context_ = std::move(*compat_result);
-
-  // Publish `fuchsia.driver.compat.Service` to the outgoing directory.
-  child_ = compat::DeviceServer(std::string(name()), 0, compat_context_->TopologicalPath(name()));
-  auto status = child_->Serve(dispatcher(), context().outgoing().get());
-  if (status != ZX_OK) {
-    FDF_SLOG(ERROR, "Failed to serve compat device server.",
-             KV("status", zx_status_get_string(status)));
-    node().reset();
-    return;
-  }
-
-  // Create a child node and offer capabilities to bound drivers.
-  auto result = AddChild();
-  if (!result.is_ok()) {
-    FDF_SLOG(ERROR, "Failed to create child node.", KV("status", result.status_string()));
-    node().reset();
-    return;
-  }
-}
-
 }  // namespace acpi_multiply
 
 FUCHSIA_DRIVER_RECORD_CPP_V2(driver::Record<acpi_multiply::AcpiMultiplyController>);
diff --git a/src/acpi_multiply/controller/acpi_controller.h b/src/acpi_multiply/controller/acpi_controller.h
index e9239cb..e2430de 100644
--- a/src/acpi_multiply/controller/acpi_controller.h
+++ b/src/acpi_multiply/controller/acpi_controller.h
@@ -7,7 +7,6 @@
 
 #include <lib/driver2/driver2_cpp.h>
 #include <lib/driver_compat/compat.h>
-#include <lib/driver_compat/context.h>
 
 #include "acpi_server.h"
 
@@ -34,10 +33,8 @@
  private:
   zx::result<> InitializeServer();
   zx::result<> AddChild();
-  void InitializeCompatService(zx::result<std::shared_ptr<compat::Context>> result);
 
   std::optional<compat::DeviceServer> child_;
-  std::shared_ptr<compat::Context> compat_context_;
   fidl::WireSyncClient<fuchsia_driver_framework::Node> node_;
   fidl::WireSyncClient<fuchsia_driver_framework::NodeController> controller_;
 
diff --git a/src/composite_sample/controller/BUILD.bazel b/src/composite_sample/controller/BUILD.bazel
index 2ae1f44..ce1d14d 100644
--- a/src/composite_sample/controller/BUILD.bazel
+++ b/src/composite_sample/controller/BUILD.bazel
@@ -18,7 +18,7 @@
     ],
     linkshared = True,
     deps = [
-        "//src/composite_sample/lib:examples.driver.test_cc",
+        "//src/testing/lib:examples.driver.test_cc",
         "@fuchsia_sdk//pkg/driver2_cpp",
         "@fuchsia_sdk//pkg/driver_compat",
     ],
@@ -29,7 +29,7 @@
     output = "controller-driver.bindbc",
     rules = "controller-driver.bind",
     deps = [
-        "//src/composite_sample/lib:examples.driver.test",
+        "//src/testing/lib:examples.driver.test",
     ],
 )
 
diff --git a/src/composite_sample/driver/BUILD.bazel b/src/composite_sample/driver/BUILD.bazel
index 35ced59..e6f1a10 100644
--- a/src/composite_sample/driver/BUILD.bazel
+++ b/src/composite_sample/driver/BUILD.bazel
@@ -15,7 +15,7 @@
     output = "composite_sample.bindbc",
     rules = "composite_sample.bind",
     deps = [
-        "//src/composite_sample/lib:examples.driver.test",
+        "//src/testing/lib:examples.driver.test",
     ],
 )
 
diff --git a/src/i2c_temperature/README.md b/src/i2c_temperature/README.md
index 8a99061..8bd6cab 100644
--- a/src/i2c_temperature/README.md
+++ b/src/i2c_temperature/README.md
@@ -23,6 +23,13 @@
 
 Use the following commands to load the driver components on a target device:
 
+1.  Add a test node for the `i2c_controller` driver:
+
+    ```
+    tools/ffx driver test-node add controller-node \
+      examples.driver.test.property=i2c-temperature-controller
+    ```
+
 1.  Load the `i2c_controller` driver component to create a virtual I2C device node:
 
     ```
@@ -50,6 +57,28 @@
 [i2c-temperature-controller,driver][I]: [i2c_controller.cc:126] Reset command received
 ```
 
+### Driver client
+
+After the drivers are loaded, use the following commands to run the temperature client:
+
+1.  Load the `temperature_client` component:
+
+    ```
+    tools/bazel run --config=fuchsia_x64 //src/i2c_temperature/client:pkg.component
+    ```
+
+1.  Open the device log viewer:
+
+    ```
+    tools/ffx log --filter temperature_client
+    ```
+
+You should see the client respond with an incrementing temperature value on each run:
+
+```
+[ffx-laboratory:temperature_client][I] Current temperature: 25.000000
+```
+
 ## Source layout
 
 *   `controller/` — Source code of the `i2c_controller` driver component.
diff --git a/src/i2c_temperature/client/BUILD.bazel b/src/i2c_temperature/client/BUILD.bazel
new file mode 100644
index 0000000..12bcad0
--- /dev/null
+++ b/src/i2c_temperature/client/BUILD.bazel
@@ -0,0 +1,48 @@
+# 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",
+    "fuchsia_component_manifest",
+    "fuchsia_package",
+)
+
+fuchsia_cc_binary(
+    name = "temperature_client",
+    srcs = [
+        "main.cc",
+    ],
+    deps = [
+        "//src/i2c_temperature/fidl:examples.i2c.temperature_cc",
+        "@fuchsia_sdk//pkg/fdio",
+        "@fuchsia_sdk//pkg/fidl_cpp_wire",
+    ],
+)
+
+fuchsia_component_manifest(
+    name = "manifest",
+    src = "meta/temperature_client.cml",
+    includes = [
+        "@fuchsia_sdk//pkg/syslog:client",
+    ],
+)
+
+fuchsia_component(
+    name = "component",
+    manifest = ":manifest",
+    deps = [
+        ":temperature_client",
+    ],
+)
+
+fuchsia_package(
+    name = "pkg",
+    package_name = "temperature_client",
+    visibility = ["//visibility:public"],
+    components = [
+        ":component",
+    ],
+)
diff --git a/src/i2c_temperature/client/main.cc b/src/i2c_temperature/client/main.cc
new file mode 100644
index 0000000..6322fa4
--- /dev/null
+++ b/src/i2c_temperature/client/main.cc
@@ -0,0 +1,110 @@
+// 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 <ctype.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <fidl/examples.i2c.temperature/cpp/wire.h>
+#include <getopt.h>
+#include <lib/fdio/directory.h>
+#include <libgen.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+constexpr char kDevicePath[] = "/dev/i2c-temperature-controller/i2c-temperature";
+
+int usage(const char* cmd) {
+  fprintf(stderr,
+          "\nInteract with the temperature sensor device:\n"
+          "   %s read                       Reads the current temperature value\n"
+          "   %s reset                      Reset the temperature sensor\n"
+          "   %s help                       Print this message\n",
+          cmd, cmd, cmd);
+  return -1;
+}
+
+// Returns "true" if the argument matches the prefix.
+// In this case, moves the argument past the prefix.
+bool prefix_match(const char** arg, const char* prefix) {
+  if (!strncmp(*arg, prefix, strlen(prefix))) {
+    *arg += strlen(prefix);
+    return true;
+  }
+  return false;
+}
+
+// Open a FIDL client connection to the examples.i2c.temperature/Device
+fidl::WireSyncClient<examples_i2c_temperature::Device> OpenDevice() {
+  int device = open(kDevicePath, O_RDWR);
+  if (device < 0) {
+    fprintf(stderr, "Failed to open temperature device: %s\n", strerror(errno));
+    return {};
+  }
+  fidl::ClientEnd<examples_i2c_temperature::Device> client_end;
+  zx_status_t st = fdio_get_service_handle(device, client_end.channel().reset_and_get_address());
+  if (st != ZX_OK) {
+    fprintf(stderr, "Failed to get service handle: %s\n", zx_status_get_string(st));
+    return {};
+  }
+  return fidl::WireSyncClient(std::move(client_end));
+}
+
+// Send a command to measure the temperature and print the result.
+// Returns 0 on success.
+int read_temperature() {
+  auto client = OpenDevice();
+  if (!client.is_valid()) {
+    return -1;
+  }
+
+  auto temperature_result = client->ReadTemperature();
+  if (!temperature_result.ok()) {
+    fprintf(stderr, "Error: failed to read temperature result: %s\n",
+            zx_status_get_string(temperature_result.status()));
+    return -1;
+  }
+
+  printf("Current temperature: %f\n", temperature_result->value()->temperature);
+  return 0;
+}
+
+// Send a command to reset the temperature sensor.
+// Returns 0 on success.
+int reset_temperature() {
+  auto client = OpenDevice();
+  if (!client.is_valid()) {
+    return -1;
+  }
+
+  auto reset_result = client->ResetSensor();
+  if (!reset_result.ok()) {
+    fprintf(stderr, "Error: failed to reset temperature sensor: %s\n",
+            zx_status_get_string(reset_result.status()));
+    return -1;
+  }
+
+  printf("Sensor reset successfully\n");
+  return 0;
+}
+
+int main(int argc, char* argv[]) {
+  const char* cmd = basename(argv[0]);
+
+  // If no arguments passed, bail out after dumping
+  // usage information.
+  if (argc < 2) {
+    return usage(cmd);
+  }
+
+  const char* arg = argv[1];
+  if (prefix_match(&arg, "read")) {
+    return read_temperature();
+  } else if (prefix_match(&arg, "reset")) {
+    return reset_temperature();
+  }
+  return usage(cmd);
+}
diff --git a/src/i2c_temperature/client/meta/temperature_client.cml b/src/i2c_temperature/client/meta/temperature_client.cml
new file mode 100644
index 0000000..4a6ec1e
--- /dev/null
+++ b/src/i2c_temperature/client/meta/temperature_client.cml
@@ -0,0 +1,22 @@
+// 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: 'elf',
+        binary: 'bin/temperature_client',
+        args: [
+          'read',
+        ]
+    },
+    use: [
+        {
+            directory: "dev-topological",
+            rights: [ "rw*" ],
+            path: "/dev",
+        },
+    ],
+}
diff --git a/src/i2c_temperature/controller/BUILD.bazel b/src/i2c_temperature/controller/BUILD.bazel
index 43cab5d..20c6d22 100644
--- a/src/i2c_temperature/controller/BUILD.bazel
+++ b/src/i2c_temperature/controller/BUILD.bazel
@@ -16,7 +16,7 @@
     output = "i2c_controller.bindbc",
     rules = "i2c_controller.bind",
     deps = [
-        "@fuchsia_sdk//bind/fuchsia.acpi",
+        "//src/testing/lib:examples.driver.test",
     ],
 )
 # [END bind_rules]
diff --git a/src/i2c_temperature/controller/i2c_controller.bind b/src/i2c_temperature/controller/i2c_controller.bind
index eee5121..a08bcce 100644
--- a/src/i2c_temperature/controller/i2c_controller.bind
+++ b/src/i2c_temperature/controller/i2c_controller.bind
@@ -2,12 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-using fuchsia.acpi;
+using examples.driver.test;
 
-// Bind to an unused ACPI node in Qemu or Atlas.
-// TODO(fxb/100716): Bind to a test node once it's available.
-if fuchsia.acpi.hid == "QEMU0002" {
-    true;
-} else {
-    fuchsia.acpi.hid == "PNP0C02";
-}
+examples.driver.test.property == "i2c-temperature-controller";
diff --git a/src/i2c_temperature/controller/i2c_controller.cc b/src/i2c_temperature/controller/i2c_controller.cc
index 422c8ca..f6a9274 100644
--- a/src/i2c_temperature/controller/i2c_controller.cc
+++ b/src/i2c_temperature/controller/i2c_controller.cc
@@ -31,10 +31,22 @@
     return result.take_error();
   }
 
-  // Initialize the driver compat service context.
-  compat::Context::ConnectAndCreate(
-      &context(), dispatcher(),
-      fit::bind_member<&I2cTemperatureController::InitializeCompatService>(this));
+  // Publish `fuchsia.driver.compat.Service` to the outgoing directory.
+  child_ = compat::DeviceServer(std::string(name()), 0, std::string(name()));
+  auto status = child_->Serve(dispatcher(), context().outgoing().get());
+  if (status != ZX_OK) {
+    FDF_SLOG(ERROR, "Failed to serve compat device server.",
+             KV("status", zx_status_get_string(status)));
+    return zx::error(status);
+  }
+
+  // Create a child node and offer capabilities to bound drivers.
+  result = AddChild();
+  if (!result.is_ok()) {
+    FDF_SLOG(ERROR, "Failed to create child node.", KV("status", result.status_string()));
+
+    return result.take_error();
+  }
 
   return zx::ok();
 }
@@ -85,36 +97,6 @@
   return zx::ok();
 }
 
-// Construct and serve the `fuchsia.driver.compat.Service` after the context is initialized.
-void I2cTemperatureController::InitializeCompatService(
-    zx::result<std::shared_ptr<compat::Context>> compat_result) {
-  if (!compat_result.is_ok()) {
-    FDF_SLOG(ERROR, "Failed to create compat context.",
-             KV("status", compat_result.status_string()));
-    node().reset();
-    return;
-  }
-  compat_context_ = std::move(*compat_result);
-
-  // Publish `fuchsia.driver.compat.Service` to the outgoing directory.
-  child_ = compat::DeviceServer(std::string(name()), 0, compat_context_->TopologicalPath(name()));
-  auto status = child_->Serve(dispatcher(), context().outgoing().get());
-  if (status != ZX_OK) {
-    FDF_SLOG(ERROR, "Failed to serve compat device server.",
-             KV("status", zx_status_get_string(status)));
-    node().reset();
-    return;
-  }
-
-  // Create a child node and offer capabilities to bound drivers.
-  auto result = AddChild();
-  if (!result.is_ok()) {
-    FDF_SLOG(ERROR, "Failed to create child node.", KV("status", result.status_string()));
-    node().reset();
-    return;
-  }
-}
-
 }  // namespace i2c_temperature
 
 FUCHSIA_DRIVER_RECORD_CPP_V2(driver::Record<i2c_temperature::I2cTemperatureController>);
diff --git a/src/i2c_temperature/controller/i2c_controller.h b/src/i2c_temperature/controller/i2c_controller.h
index 5cdc3cc..4b77262 100644
--- a/src/i2c_temperature/controller/i2c_controller.h
+++ b/src/i2c_temperature/controller/i2c_controller.h
@@ -7,7 +7,6 @@
 
 #include <lib/driver2/driver2_cpp.h>
 #include <lib/driver_compat/compat.h>
-#include <lib/driver_compat/context.h>
 
 #include "i2c_server.h"
 
@@ -28,10 +27,8 @@
 
  private:
   zx::result<> AddChild();
-  void InitializeCompatService(zx::result<std::shared_ptr<compat::Context>> result);
 
   std::optional<compat::DeviceServer> child_;
-  std::shared_ptr<compat::Context> compat_context_;
   fidl::WireSharedClient<fuchsia_driver_framework::Node> node_;
   fidl::WireSharedClient<fuchsia_driver_framework::NodeController> controller_;
   std::shared_ptr<I2cDeviceServer> i2c_server_;
diff --git a/src/testing/README.md b/src/testing/README.md
new file mode 100644
index 0000000..6fee8f6
--- /dev/null
+++ b/src/testing/README.md
@@ -0,0 +1,4 @@
+# Driver samples testing support
+
+This directory contains support files and resources to facilitate running and
+testing the sample code in this repository.
diff --git a/src/composite_sample/lib/BUILD.bazel b/src/testing/lib/BUILD.bazel
similarity index 100%
rename from src/composite_sample/lib/BUILD.bazel
rename to src/testing/lib/BUILD.bazel
diff --git a/src/composite_sample/lib/fuchsia_driver_test.bind b/src/testing/lib/fuchsia_driver_test.bind
similarity index 100%
rename from src/composite_sample/lib/fuchsia_driver_test.bind
rename to src/testing/lib/fuchsia_driver_test.bind