[acpi] Update the drivers to the DriverBase lib

Updates the acpi_multiply sample drivers to the new DriverBase library.
Also makes the following semi-related changes:
 - Remove unnecessary includes and build deps
 - Use bind library code gen library
 - Start using services for hardware.acpi
 - Split out hardware.acpi fidl server from driver

Bug: 110283, 110915, 106665
Tests: Ran through the README.md instructions successfully

Change-Id: Iafd44a4b777fbd6f0c19650ec8b102db776ddee7
Reviewed-on: https://fuchsia-review.googlesource.com/c/sdk-samples/drivers/+/733645
Reviewed-by: David Gilhooley <dgilhooley@google.com>
Commit-Queue: Novin Changizi <novinc@google.com>
Reviewed-by: Dave Smith <smithdave@google.com>
Reviewed-by: Suraj Malhotra <surajmalhotra@google.com>
diff --git a/src/acpi_multiply/controller/BUILD.bazel b/src/acpi_multiply/controller/BUILD.bazel
index f50ba34..e2020a2 100644
--- a/src/acpi_multiply/controller/BUILD.bazel
+++ b/src/acpi_multiply/controller/BUILD.bazel
@@ -4,7 +4,6 @@
 
 load(
     "@rules_fuchsia//fuchsia:defs.bzl",
-    "fuchsia_cc_binary",
     "fuchsia_component_manifest",
     "fuchsia_driver_bind_bytecode",
     "fuchsia_driver_component",
@@ -25,22 +24,18 @@
     srcs = [
         "acpi_controller.cc",
         "acpi_controller.h",
+        "acpi_server.cc",
+        "acpi_server.h",
     ],
     linkshared = True,
     deps = [
         "//src/acpi_multiply/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//bind/fuchsia.acpi:fuchsia.acpi_cc",
         "@fuchsia_sdk//fidl/fuchsia.hardware.acpi:fuchsia.hardware.acpi_bindlib_cc",
         "@fuchsia_sdk//fidl/fuchsia.hardware.acpi:fuchsia.hardware.acpi_llcpp_cc",
-        "@fuchsia_sdk//fidl/zx:zx_cc",
         "@fuchsia_sdk//pkg/async-cpp",
         "@fuchsia_sdk//pkg/driver2_cpp",
-        "@fuchsia_sdk//pkg/hwreg",
         "@fuchsia_sdk//pkg/mmio",
-        "@fuchsia_sdk//pkg/fidl_cpp_wire",
-        "@fuchsia_sdk//pkg/sys_component_cpp",
-        "@fuchsia_sdk//pkg/zx",
     ],
 )
 
@@ -52,7 +47,6 @@
     ],
 )
 
-
 fuchsia_driver_component(
     name = "component",
     bind_bytecode = ":bind_bytecode",
diff --git a/src/acpi_multiply/controller/acpi_controller.cc b/src/acpi_multiply/controller/acpi_controller.cc
index 9145a32..4b9e9aa 100644
--- a/src/acpi_multiply/controller/acpi_controller.cc
+++ b/src/acpi_multiply/controller/acpi_controller.cc
@@ -4,215 +4,118 @@
 
 #include "acpi_controller.h"
 
-#include <fidl/fuchsia.component.decl/cpp/wire_types.h>
-#include <fidl/fuchsia.driver.framework/cpp/wire.h>
-#include <fidl/fuchsia.hardware.acpi/cpp/wire_types.h>
-#include <fidl/fuchsia.mem/cpp/wire_types.h>
-#include <lib/async/cpp/task.h>
-#include <lib/driver2/record_cpp.h>
-#include <lib/driver2/structured_logger.h>
-#include <lib/fidl/cpp/wire/connect_service.h>
-#include <lib/zx/clock.h>
-#include <zircon/errors.h>
-#include <zircon/syscalls.h>
-#include <zircon/types.h>
-
+#include <bind/fuchsia/acpi/cpp/bind.h>
 #include <bind/fuchsia/hardware/acpi/cpp/bind.h>
 
-#include "registers.h"
-
 namespace acpi_multiply {
 
-zx::status<std::unique_ptr<AcpiMultiplyController>> AcpiMultiplyController::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<AcpiMultiplyController>(
-      dispatcher->async_dispatcher(), std::move(node), std::move(ns), std::move(logger));
+zx::status<> AcpiMultiplyController::Start() {
+  node_.Bind(std::move(node()));
 
-  auto result = driver->Run(std::move(start_args.outgoing_dir()));
+  auto server_result = InitializeServer();
+  if (server_result.is_error()) {
+    FDF_LOG(ERROR, "Failed to initialize ACPI Server: %s",
+            zx_status_get_string(server_result.error_value()));
+    return server_result.take_error();
+  }
+
+  // Serve the fuchsia.hardware.acpi/Device protocol to clients through the
+  // fuchsia.hardware.acpi/Service wrapper.
+  driver::ServiceInstanceHandler handler;
+  fuchsia_hardware_acpi::Service::Handler service(&handler);
+  auto result =
+      service.add_device([this](fidl::ServerEnd<fuchsia_hardware_acpi::Device> request) -> void {
+        AcpiDeviceServer::BindDeviceClient(server_, async_dispatcher(), std::move(request));
+      });
   if (result.is_error()) {
     return result.take_error();
   }
 
-  return zx::ok(std::move(driver));
+  result = context().outgoing()->AddService<fuchsia_hardware_acpi::Service>(std::move(handler));
+  if (result.is_error()) {
+    FDF_SLOG(ERROR, "Failed to add service", KV("status", result.status_string()));
+    return result.take_error();
+  }
+
+  result = AddChild();
+  if (result.is_error()) {
+    return result.take_error();
+  }
+
+  return zx::ok();
 }
 
-zx::status<> AcpiMultiplyController::Run(fidl::ServerEnd<fuchsia_io::Directory> outgoing_dir) {
+zx::status<> AcpiMultiplyController::InitializeServer() {
   // Allocate an MMIO buffer representing the ACPI multiply device
   zx::vmo vmo;
   zx_status_t raw_status = zx::vmo::create(zx_system_get_page_size(), 0, &vmo);
   if (raw_status != ZX_OK) {
+    FDF_LOG(ERROR, "Failed to create vmo: %s.", zx_status_get_string(raw_status));
     return zx::error(raw_status);
   }
 
   auto buffer = fdf::MmioBuffer::Create(0, zx_system_get_page_size(), std::move(vmo),
                                         ZX_CACHE_POLICY_UNCACHED_DEVICE);
   if (buffer.is_error()) {
+    FDF_LOG(ERROR, "Failed to create mmio buffer: %s.", zx_status_get_string(buffer.error_value()));
     return buffer.take_error();
   }
-  buffer_ = std::move(buffer.value());
 
   // Create an IRQ resource for the ACPI multiply device
-  raw_status = zx::interrupt::create(zx::resource(), 0, ZX_INTERRUPT_VIRTUAL, &irq_);
+  zx::interrupt irq;
+  raw_status = zx::interrupt::create(zx::resource(), 0, ZX_INTERRUPT_VIRTUAL, &irq);
   if (raw_status != ZX_OK) {
+    FDF_LOG(ERROR, "Failed to create interrupt: %s.", zx_status_get_string(raw_status));
     return zx::error(raw_status);
   }
 
-  // Serve `fuchsia.hardware.acpi/Device` to child device nodes
-  auto service = [this](fidl::ServerEnd<fuchsia_hardware_acpi::Device> server_end) {
-    fidl::BindServer(dispatcher_, std::move(server_end), this);
-  };
-
-  auto status = outgoing_.AddProtocol<fuchsia_hardware_acpi::Device>(std::move(service));
-  if (status.is_error()) {
-    FDF_SLOG(ERROR, "Failed to add protocol", KV("status", status.status_string()));
-    return status;
-  }
-
-  status = AddChild();
-  if (status.is_error()) {
-    return status;
-  }
-
-  return outgoing_.Serve(std::move(outgoing_dir));
+  server_ = std::make_shared<AcpiDeviceServer>(std::move(irq), std::move(buffer.value()), logger_,
+                                               async_dispatcher());
+  return zx::ok();
 }
 
 zx::status<> AcpiMultiplyController::AddChild() {
   fidl::Arena arena;
-
-  // Offer fuchsia.hardware.acpi.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_acpi::Device>)
-          .target_name(arena, fidl::DiscoverableProtocolName<fuchsia_hardware_acpi::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);
+  // Offer fuchsia.hardware.acpi.Service to the driver that binds to the node.
+  auto offers = fidl::VectorView<fuchsia_component_decl::wire::Offer>(arena, 1);
+  offers[0] = driver::MakeOffer<fuchsia_hardware_acpi::Service>(arena, "default");
 
   // Set the properties of the node that a driver binds 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_acpi::DEVICE))
-                      .value(fuchsia_driver_framework::wire::NodePropertyValue::WithEnumValue(
-                          arena, bind_fuchsia_hardware_acpi::DEVICE_ZIRCONTRANSPORT))
-                      .Build();
+  properties[0] = driver::MakeEnumProperty(arena, bind_fuchsia_hardware_acpi::DEVICE,
+                                           bind_fuchsia_hardware_acpi::DEVICE_ZIRCONTRANSPORT);
+  properties[1] = driver::MakeProperty(arena, bind_fuchsia_acpi::HID, "FDFS0001");
 
-  properties[1] = fuchsia_driver_framework::wire::NodeProperty::Builder(arena)
-                      .key(fuchsia_driver_framework::wire::NodePropertyKey::WithStringValue(
-                          arena, fidl::StringView::FromExternal("fuchsia.acpi.hid")))
-                      .value(fuchsia_driver_framework::wire::NodePropertyValue::WithStringValue(
-                          arena, fidl::StringView::FromExternal("FDFS0001")))
-                      .Build();
-
-  auto args =
-      fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena)
-          .name(arena, "acpi-child")
-          .offers(fidl::VectorView<fuchsia_component_decl::wire::Offer>::FromExternal(&offer, 1))
-          .properties(properties)
-          .Build();
+  auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena)
+                  .name(arena, "acpi-child")
+                  .offers(offers)
+                  .properties(properties)
+                  .Build();
 
   // 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());
+    return endpoints.take_error();
   }
 
-  auto result = node_.sync()->AddChild(args, std::move(endpoints->server), {});
+  auto result = node_->AddChild(args, std::move(endpoints->server), {});
   if (!result.ok()) {
-    FDF_SLOG(ERROR, "Failed to add child", KV("status", result.status_string()));
+    FDF_SLOG(ERROR, "Failed to add child", KV("error_desc", result.FormatDescription().c_str()));
     return zx::error(result.status());
   }
 
+  if (result->is_error()) {
+    FDF_SLOG(ERROR, "Failed to add child",
+             KV("status", std::to_string(static_cast<uint32_t>(result->error_value())).c_str()));
+    return zx::error(ZX_ERR_INTERNAL);
+  }
+
+  controller_.Bind(std::move(endpoints->client));
+
   return zx::ok();
 }
 
-// Protocol method for `fuchsia.hardware.acpi/Device` to return a requested MMIO region.
-void AcpiMultiplyController::GetMmio(GetMmioRequestView request,
-                                     GetMmioCompleter::Sync &completer) {
-  if (request->index != 0) {
-    completer.ReplyError(ZX_ERR_OUT_OF_RANGE);
-    return;
-  }
-
-  zx::vmo clone;
-  zx_status_t status = buffer_->get_vmo()->duplicate(ZX_RIGHT_SAME_RIGHTS, &clone);
-  if (status != ZX_OK) {
-    completer.ReplyError(status);
-    return;
-  }
-
-  completer.ReplySuccess(fuchsia_mem::wire::Range{
-      .vmo = std::move(clone),
-      .offset = 0,
-      .size = zx_system_get_page_size(),
-  });
-}
-
-// Protocol method for `fuchsia.hardware.acpi/Device` to return the requested IRQ.
-void AcpiMultiplyController::MapInterrupt(MapInterruptRequestView request,
-                                          MapInterruptCompleter::Sync &completer) {
-  if (request->index != 0) {
-    completer.ReplyError(ZX_ERR_OUT_OF_RANGE);
-    return;
-  }
-
-  zx::interrupt clone;
-  zx_status_t status = irq_.duplicate(ZX_RIGHT_SAME_RIGHTS, &clone);
-  if (status != ZX_OK) {
-    completer.ReplyError(status);
-    return;
-  }
-
-  completer.ReplySuccess(std::move(clone));
-}
-
-// Protocol method for `fuchsia.hardware.acpi/Device` to interpret an invoke the control method
-// associated with the provided object path.
-void AcpiMultiplyController::EvaluateObject(EvaluateObjectRequestView request,
-                                            EvaluateObjectCompleter::Sync &completer) {
-  if (request->mode != fuchsia_hardware_acpi::wire::EvaluateObjectMode::kPlainObject) {
-    completer.ReplyError(fuchsia_hardware_acpi::wire::Status::kNotSupported);
-    return;
-  }
-
-  if (request->path.get() != "_MUL") {
-    completer.ReplyError(fuchsia_hardware_acpi::wire::Status::kNotFound);
-    return;
-  }
-
-  fidl::Arena arena;
-  completer.ReplySuccess(fuchsia_hardware_acpi::wire::EncodedObject());
-  // To simulate asynchronous hardware, we post a delayed task and trigger an interrupt when the
-  // task is complete.
-  async::PostDelayedTask(
-      dispatcher_,
-      [this]() mutable {
-        auto value1 = acpi_multiply::MultiplyArgumentReg::Get(true).ReadFrom(&buffer_.value());
-        auto value2 = acpi_multiply::MultiplyArgumentReg::Get(false).ReadFrom(&buffer_.value());
-        auto status_reg = acpi_multiply::MultiplyStatusReg::Get().FromValue(0);
-        auto result_reg = acpi_multiply::MultiplyResultReg::Get().FromValue(0);
-
-        uint32_t result = value1.operand() * value2.operand();
-        // Check for overflow
-        if (value1.operand() != 0 && result / value1.operand() != value2.operand()) {
-          status_reg.set_overflow(true);
-        }
-        result_reg.set_result(result);
-        result_reg.WriteTo(&buffer_.value());
-        status_reg.set_finished(true).WriteTo(&buffer_.value());
-        zx_status_t status = irq_.trigger(0, zx::clock::get_monotonic());
-        if (status != ZX_OK) {
-          FDF_SLOG(ERROR, "Failed to trigger interrupt",
-                   KV("status", zx_status_get_string(status)));
-        }
-      },
-      zx::msec(10));
-}
-
 }  // namespace acpi_multiply
 
-FUCHSIA_DRIVER_RECORD_CPP_V1(acpi_multiply::AcpiMultiplyController);
+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 805e390..e01c6e6 100644
--- a/src/acpi_multiply/controller/acpi_controller.h
+++ b/src/acpi_multiply/controller/acpi_controller.h
@@ -5,15 +5,9 @@
 #ifndef SRC_ACPI_MULTIPLY_ACPI_CONTROLLER_H_
 #define SRC_ACPI_MULTIPLY_ACPI_CONTROLLER_H_
 
-#include <fidl/fuchsia.driver.framework/cpp/markers.h>
-#include <fidl/fuchsia.driver.framework/cpp/wire_types.h>
-#include <fidl/fuchsia.hardware.acpi/cpp/wire.h>
-#include <lib/driver2/logger.h>
-#include <lib/driver2/namespace.h>
-#include <lib/fdf/cpp/dispatcher.h>
-#include <lib/mmio/mmio-buffer.h>
-#include <lib/sys/component/cpp/outgoing_directory.h>
-#include <lib/zx/status.h>
+#include <lib/driver2/driver2_cpp.h>
+
+#include "acpi_server.h"
 
 namespace acpi_multiply {
 
@@ -24,77 +18,25 @@
 // for an interrupt.
 // 3. This driver performs the multiplication, writes the result (64-bit) to 0x08
 // and triggers an interrupt.
-class AcpiMultiplyController : public fidl::WireServer<fuchsia_hardware_acpi::Device> {
+class AcpiMultiplyController : public driver::DriverBase {
  public:
-  AcpiMultiplyController(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)) {}
+  AcpiMultiplyController(driver::DriverStartArgs start_args,
+                         fdf::UnownedDispatcher driver_dispatcher)
+      : driver::DriverBase("sample-acpi-controller", std::move(start_args),
+                           std::move(driver_dispatcher)) {}
 
   virtual ~AcpiMultiplyController() = default;
 
-  static constexpr const char* Name() { return "sample-acpi-controller"; }
-  static zx::status<std::unique_ptr<AcpiMultiplyController>> 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.acpi/Device:
-  void MapInterrupt(MapInterruptRequestView request, MapInterruptCompleter::Sync& completer);
-  void GetMmio(GetMmioRequestView request, GetMmioCompleter::Sync& completer);
-  void EvaluateObject(EvaluateObjectRequestView request, EvaluateObjectCompleter::Sync& completer);
-
-  void GetBusId(GetBusIdCompleter::Sync& completer) { completer.ReplyError(ZX_ERR_BAD_STATE); }
-
-  void GetPio(GetPioRequestView request, GetPioCompleter::Sync& completer) {
-    completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
-  }
-  void GetBti(GetBtiRequestView request, GetBtiCompleter::Sync& completer) {
-    completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
-  }
-
-  void InstallNotifyHandler(InstallNotifyHandlerRequestView request,
-                            InstallNotifyHandlerCompleter::Sync& completer) {
-    completer.ReplyError(fuchsia_hardware_acpi::wire::Status::kNotImplemented);
-  }
-
-  void RemoveNotifyHandler(RemoveNotifyHandlerCompleter::Sync& completer) {
-    completer.ReplyError(fuchsia_hardware_acpi::wire::Status::kNotImplemented);
-  }
-
-  void AcquireGlobalLock(AcquireGlobalLockCompleter::Sync& completer) {
-    completer.ReplyError(fuchsia_hardware_acpi::wire::Status::kNotImplemented);
-  }
-
-  void InstallAddressSpaceHandler(InstallAddressSpaceHandlerRequestView request,
-                                  InstallAddressSpaceHandlerCompleter::Sync& completer) {
-    completer.ReplyError(fuchsia_hardware_acpi::wire::Status::kNotImplemented);
-  }
-
-  void SetWakeDevice(SetWakeDeviceRequestView request, SetWakeDeviceCompleter::Sync& completer) {
-    completer.ReplyError(fuchsia_hardware_acpi::wire::Status::kNotImplemented);
-  }
+  zx::status<> Start() override;
 
  private:
-  zx::status<> Run(fidl::ServerEnd<fuchsia_io::Directory> outgoing_dir);
+  zx::status<> InitializeServer();
   zx::status<> AddChild();
 
-  async_dispatcher_t* const dispatcher_;
-  component::OutgoingDirectory outgoing_;
-  driver::Namespace ns_;
-  driver::Logger logger_;
+  fidl::WireSyncClient<fuchsia_driver_framework::Node> node_;
+  fidl::WireSyncClient<fuchsia_driver_framework::NodeController> controller_;
 
-  zx::interrupt irq_;
-
-  std::optional<fdf::MmioBuffer> buffer_;
-
-  fidl::WireSharedClient<fuchsia_driver_framework::Node> node_;
-  fidl::WireSharedClient<fuchsia_driver_framework::NodeController> controller_;
+  std::shared_ptr<AcpiDeviceServer> server_;
 };
 
 }  // namespace acpi_multiply
diff --git a/src/acpi_multiply/controller/acpi_server.cc b/src/acpi_multiply/controller/acpi_server.cc
new file mode 100644
index 0000000..2d9e522
--- /dev/null
+++ b/src/acpi_multiply/controller/acpi_server.cc
@@ -0,0 +1,117 @@
+// 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 "acpi_server.h"
+
+#include <lib/async/cpp/task.h>
+#include <lib/driver2/structured_logger.h>
+#include <lib/zx/clock.h>
+
+#include "registers.h"
+
+namespace acpi_multiply {
+
+// Static
+// Handle incoming connection requests from FIDL clients
+fidl::ServerBindingRef<fuchsia_hardware_acpi::Device> AcpiDeviceServer::BindDeviceClient(
+    std::shared_ptr<AcpiDeviceServer> server_impl, async_dispatcher_t* dispatcher,
+    fidl::ServerEnd<fuchsia_hardware_acpi::Device> request) {
+  // Bind each connection request to the shared instance.
+  return fidl::BindServer(dispatcher, std::move(request), server_impl,
+                          std::mem_fn(&AcpiDeviceServer::OnUnbound));
+}
+
+// This method is called when a server connection is torn down.
+void AcpiDeviceServer::OnUnbound(fidl::UnbindInfo info,
+                                 fidl::ServerEnd<fuchsia_hardware_acpi::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 for `fuchsia.hardware.acpi/Device` to return the requested IRQ.
+void AcpiDeviceServer::MapInterrupt(MapInterruptRequestView request,
+                                    MapInterruptCompleter::Sync& completer) {
+  if (request->index != 0) {
+    completer.ReplyError(ZX_ERR_OUT_OF_RANGE);
+    return;
+  }
+
+  zx::interrupt clone;
+  zx_status_t status = irq_.duplicate(ZX_RIGHT_SAME_RIGHTS, &clone);
+  if (status != ZX_OK) {
+    completer.ReplyError(status);
+    return;
+  }
+
+  completer.ReplySuccess(std::move(clone));
+}
+
+// Protocol method for `fuchsia.hardware.acpi/Device` to return a requested MMIO region.
+void AcpiDeviceServer::GetMmio(GetMmioRequestView request, GetMmioCompleter::Sync& completer) {
+  if (request->index != 0) {
+    completer.ReplyError(ZX_ERR_OUT_OF_RANGE);
+    return;
+  }
+
+  zx::vmo clone;
+  zx_status_t status = buffer_.get_vmo()->duplicate(ZX_RIGHT_SAME_RIGHTS, &clone);
+  if (status != ZX_OK) {
+    completer.ReplyError(status);
+    return;
+  }
+
+  completer.ReplySuccess(fuchsia_mem::wire::Range{
+      .vmo = std::move(clone),
+      .offset = 0,
+      .size = zx_system_get_page_size(),
+  });
+}
+
+// Protocol method for `fuchsia.hardware.acpi/Device` to interpret an invoke the control method
+// associated with the provided object path.
+void AcpiDeviceServer::EvaluateObject(EvaluateObjectRequestView request,
+                                      EvaluateObjectCompleter::Sync& completer) {
+  if (request->mode != fuchsia_hardware_acpi::wire::EvaluateObjectMode::kPlainObject) {
+    completer.ReplyError(fuchsia_hardware_acpi::wire::Status::kNotSupported);
+    return;
+  }
+
+  if (request->path.get() != "_MUL") {
+    completer.ReplyError(fuchsia_hardware_acpi::wire::Status::kNotFound);
+    return;
+  }
+
+  fidl::Arena arena;
+  completer.ReplySuccess(fuchsia_hardware_acpi::wire::EncodedObject());
+  // To simulate asynchronous hardware, we post a delayed task and trigger an interrupt when the
+  // task is complete.
+  async::PostDelayedTask(
+      dispatcher_,
+      [this]() mutable {
+        auto value1 = acpi_multiply::MultiplyArgumentReg::Get(true).ReadFrom(&buffer_);
+        auto value2 = acpi_multiply::MultiplyArgumentReg::Get(false).ReadFrom(&buffer_);
+        auto status_reg = acpi_multiply::MultiplyStatusReg::Get().FromValue(0);
+        auto result_reg = acpi_multiply::MultiplyResultReg::Get().FromValue(0);
+
+        uint32_t result = value1.operand() * value2.operand();
+        // Check for overflow
+        if (value1.operand() != 0 && result / value1.operand() != value2.operand()) {
+          status_reg.set_overflow(true);
+        }
+        result_reg.set_result(result);
+        result_reg.WriteTo(&buffer_);
+        status_reg.set_finished(true).WriteTo(&buffer_);
+        zx_status_t status = irq_.trigger(0, zx::clock::get_monotonic());
+        if (status != ZX_OK) {
+          FDF_SLOG(ERROR, "Failed to trigger interrupt",
+                   KV("status", zx_status_get_string(status)));
+        }
+      },
+      zx::msec(10));
+}
+
+}  // namespace acpi_multiply
diff --git a/src/acpi_multiply/controller/acpi_server.h b/src/acpi_multiply/controller/acpi_server.h
new file mode 100644
index 0000000..df4a0bd
--- /dev/null
+++ b/src/acpi_multiply/controller/acpi_server.h
@@ -0,0 +1,83 @@
+// 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 SRC_ACPI_MULTIPLY_ACPI_SERVER_H_
+#define SRC_ACPI_MULTIPLY_ACPI_SERVER_H_
+
+#include <fidl/fuchsia.hardware.acpi/cpp/wire.h>
+#include <lib/driver2/logger.h>
+#include <lib/mmio/mmio-buffer.h>
+
+namespace acpi_multiply {
+
+// FIDL server implementation for the `fuchsia.hardware.acpi/Device` protocol
+class AcpiDeviceServer : public fidl::WireServer<fuchsia_hardware_acpi::Device> {
+ public:
+  AcpiDeviceServer(zx::interrupt irq, fdf::MmioBuffer buffer, driver::Logger& logger,
+                   async_dispatcher_t* dispatcher)
+      : irq_(std::move(irq)),
+        buffer_(std::move(buffer)),
+        logger_(logger),
+        dispatcher_(dispatcher) {}
+
+  static fidl::ServerBindingRef<fuchsia_hardware_acpi::Device> BindDeviceClient(
+      std::shared_ptr<AcpiDeviceServer> server_impl, async_dispatcher_t* dispatcher,
+      fidl::ServerEnd<fuchsia_hardware_acpi::Device> request);
+
+  void OnUnbound(fidl::UnbindInfo info, fidl::ServerEnd<fuchsia_hardware_acpi::Device> server_end);
+
+  // fidl::WireServer<fuchsia_hardware_acpi::Device>
+
+  void MapInterrupt(MapInterruptRequestView request,
+                    MapInterruptCompleter::Sync& completer) override;
+
+  void GetMmio(GetMmioRequestView request, GetMmioCompleter::Sync& completer) override;
+
+  void EvaluateObject(EvaluateObjectRequestView request,
+                      EvaluateObjectCompleter::Sync& completer) override;
+
+  void GetBusId(GetBusIdCompleter::Sync& completer) override {
+    completer.ReplyError(ZX_ERR_BAD_STATE);
+  }
+
+  void GetPio(GetPioRequestView request, GetPioCompleter::Sync& completer) override {
+    completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
+  }
+  void GetBti(GetBtiRequestView request, GetBtiCompleter::Sync& completer) override {
+    completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
+  }
+
+  void InstallNotifyHandler(InstallNotifyHandlerRequestView request,
+                            InstallNotifyHandlerCompleter::Sync& completer) override {
+    completer.ReplyError(fuchsia_hardware_acpi::wire::Status::kNotImplemented);
+  }
+
+  void RemoveNotifyHandler(RemoveNotifyHandlerCompleter::Sync& completer) override {
+    completer.ReplyError(fuchsia_hardware_acpi::wire::Status::kNotImplemented);
+  }
+
+  void AcquireGlobalLock(AcquireGlobalLockCompleter::Sync& completer) override {
+    completer.ReplyError(fuchsia_hardware_acpi::wire::Status::kNotImplemented);
+  }
+
+  void InstallAddressSpaceHandler(InstallAddressSpaceHandlerRequestView request,
+                                  InstallAddressSpaceHandlerCompleter::Sync& completer) override {
+    completer.ReplyError(fuchsia_hardware_acpi::wire::Status::kNotImplemented);
+  }
+
+  void SetWakeDevice(SetWakeDeviceRequestView request,
+                     SetWakeDeviceCompleter::Sync& completer) override {
+    completer.ReplyError(fuchsia_hardware_acpi::wire::Status::kNotImplemented);
+  }
+
+ private:
+  zx::interrupt irq_;
+  fdf::MmioBuffer buffer_;
+  driver::Logger& logger_;
+  async_dispatcher_t* dispatcher_;
+};
+
+}  // namespace acpi_multiply
+
+#endif  // SRC_ACPI_MULTIPLY_ACPI_SERVER_H_
diff --git a/src/acpi_multiply/controller/meta/acpi_controller.cml b/src/acpi_multiply/controller/meta/acpi_controller.cml
index cddfeb7..ab6b620 100644
--- a/src/acpi_multiply/controller/meta/acpi_controller.cml
+++ b/src/acpi_multiply/controller/meta/acpi_controller.cml
@@ -13,14 +13,13 @@
     use: [
         { service: 'fuchsia.driver.compat.Service' },
     ],
-    // Provide the ACPI device capability to other components
-    // TODO(fxbug.dev/106665): Route as a service capability from the SDK
+    // Provide the ACPI service capability to other components
     capabilities: [
-        { protocol: 'fuchsia.hardware.acpi.Device' },
+        { service: 'fuchsia.hardware.acpi.Service' },
     ],
     expose: [
         {
-            protocol: 'fuchsia.hardware.acpi.Device',
+            service: 'fuchsia.hardware.acpi.Service',
             from: 'self',
         },
     ],
diff --git a/src/acpi_multiply/driver/BUILD.bazel b/src/acpi_multiply/driver/BUILD.bazel
index ddf2d36..8941d43 100644
--- a/src/acpi_multiply/driver/BUILD.bazel
+++ b/src/acpi_multiply/driver/BUILD.bazel
@@ -4,7 +4,6 @@
 
 load(
     "@rules_fuchsia//fuchsia:defs.bzl",
-    "fuchsia_cc_binary",
     "fuchsia_component_manifest",
     "fuchsia_driver_bind_bytecode",
     "fuchsia_driver_component",
@@ -28,20 +27,17 @@
         "acpi_multiply.h",
         "multiplier.cc",
         "multiplier.h",
+        "multiply_server.cc",
+        "multiply_server.h",
     ],
     linkshared = True,
     deps = [
         "//src/acpi_multiply/fidl:fuchsia.examples.acpi.multiply_cc",
         "//src/acpi_multiply/lib",
         "@fuchsia_sdk//fidl/fuchsia.hardware.acpi:fuchsia.hardware.acpi_llcpp_cc",
-        "@fuchsia_sdk//fidl/zx:zx_cc",
         "@fuchsia_sdk//pkg/async-cpp",
-        "@fuchsia_sdk//pkg/mmio",
-        "@fuchsia_sdk//pkg/hwreg",
         "@fuchsia_sdk//pkg/driver2_cpp",
-        "@fuchsia_sdk//pkg/fidl_cpp_wire",
-        "@fuchsia_sdk//pkg/sys_component_cpp",
-        "@fuchsia_sdk//pkg/zx",
+        "@fuchsia_sdk//pkg/mmio",
     ],
 )
 
diff --git a/src/acpi_multiply/driver/acpi_multiply.cc b/src/acpi_multiply/driver/acpi_multiply.cc
index 5b10eab..cb2fe02 100644
--- a/src/acpi_multiply/driver/acpi_multiply.cc
+++ b/src/acpi_multiply/driver/acpi_multiply.cc
@@ -4,96 +4,18 @@
 
 #include "acpi_multiply.h"
 
+#include <fidl/fuchsia.hardware.acpi/cpp/wire.h>
 #include <lib/async/cpp/task.h>
+#include <lib/driver2/service_client.h>
 
-namespace {
-
-// FIDL server implementation for the `fuchsia.examples.acpi.multiply/Device` protocol
-class AcpiMultiplyServer : public fidl::WireServer<fuchsia_examples_acpi_multiply::Device> {
- public:
-  explicit AcpiMultiplyServer(driver::Namespace& ns, async_dispatcher_t* dispatcher,
-                              std::weak_ptr<acpi_multiply::AcpiMultiplier> multiplier)
-      : multiplier_(multiplier) {
-    auto logger_result = driver::Logger::Create(ns, dispatcher, "acpi-multiply-server");
-    ZX_ASSERT(logger_result.is_ok());
-    logger_ = std::move(*logger_result);
-  }
-
-  // Handle incoming connection requests from FIDL clients
-  static fidl::ServerBindingRef<fuchsia_examples_acpi_multiply::Device> BindDeviceClient(
-      driver::Namespace& ns, async_dispatcher_t* dispatcher,
-      std::weak_ptr<acpi_multiply::AcpiMultiplier> multiplier,
-      fidl::ServerEnd<fuchsia_examples_acpi_multiply::Device> request) {
-    // Bind each connection request to a unique FIDL server instance
-    auto server_impl = std::make_unique<AcpiMultiplyServer>(ns, dispatcher, multiplier);
-    return fidl::BindServer(dispatcher, std::move(request), std::move(server_impl),
-                            std::mem_fn(&AcpiMultiplyServer::OnUnbound));
-  }
-
-  // This method is called when a server connection is torn down.
-  void OnUnbound(fidl::UnbindInfo info,
-                 fidl::ServerEnd<fuchsia_examples_acpi_multiply::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());
-    }
-  }
-
-  void Multiply(MultiplyRequestView request, MultiplyCompleter::Sync& completer) override;
-
- private:
-  driver::Logger logger_;
-  std::weak_ptr<acpi_multiply::AcpiMultiplier> multiplier_;
-};
-
-// Protocol method in `fuchsia.examples.acpi.multiply` to execute a multiply operation
-void AcpiMultiplyServer::Multiply(MultiplyRequestView request, MultiplyCompleter::Sync& completer) {
-  auto multiplier = multiplier_.lock();
-  if (!multiplier) {
-    FDF_LOG(ERROR, "Unable to access ACPI resources.");
-    completer.ReplyError(ZX_ERR_BAD_STATE);
-    return;
-  }
-
-  multiplier->QueueMultiplyOperation(acpi_multiply::AcpiMultiplier::Operation{
-      .a = request->a,
-      .b = request->b,
-      .callback =
-          [completer = completer.ToAsync()](
-              zx::status<acpi_multiply::AcpiMultiplier::MultiplyResult> status) mutable {
-            if (status.is_error()) {
-              completer.ReplyError(status.error_value());
-              return;
-            }
-
-            acpi_multiply::AcpiMultiplier::MultiplyResult result = status.value();
-            completer.ReplySuccess(result.value, result.overflow);
-          },
-  });
-}
-
-}  // namespace
+#include "multiply_server.h"
 
 namespace acpi_multiply {
 
-zx::status<std::unique_ptr<AcpiMultiplyDriver>> AcpiMultiplyDriver::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<AcpiMultiplyDriver>(
-      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<> AcpiMultiplyDriver::Run(fidl::ServerEnd<fuchsia_io::Directory> outgoing_dir) {
+zx::status<> AcpiMultiplyDriver::Start() {
   // Connect to the ACPI protocol exposed by the parent driver.
-  auto client_endpoint = ns_.Connect<fuchsia_hardware_acpi::Device>();
+  auto client_endpoint =
+      driver::Connect<fuchsia_hardware_acpi::Service::Device>(*context().incoming(), "default");
   if (client_endpoint.is_error()) {
     FDF_SLOG(ERROR, "Failed to connect to ACPI", KV("status", client_endpoint.status_string()));
     return client_endpoint.take_error();
@@ -101,7 +23,8 @@
   auto acpi_client = fidl::WireSyncClient(std::move(*client_endpoint));
 
   // Initialize ACPI hardware resources
-  multiplier_ = std::make_shared<AcpiMultiplier>(ns_, dispatcher_, std::move(acpi_client));
+  multiplier_ =
+      std::make_shared<AcpiMultiplier>(logger_, async_dispatcher(), std::move(acpi_client));
   auto init_result = multiplier_->SetupMmioAndInterrupts();
   if (init_result.is_error()) {
     FDF_SLOG(ERROR, "Failed to initialize ACPI resources.",
@@ -111,40 +34,36 @@
 
   // Serve the fuchsia.examples.acpi.multiply/Device protocol to clients through the
   // fuchsia.examples.acpi.multiply/Service wrapper.
-  component::ServiceHandler handler;
+  driver::ServiceInstanceHandler handler;
   fuchsia_examples_acpi_multiply::Service::Handler service(&handler);
 
   auto result = service.add_device(
       [this](fidl::ServerEnd<fuchsia_examples_acpi_multiply::Device> request) -> void {
-        AcpiMultiplyServer::BindDeviceClient(ns_, dispatcher_, multiplier_, std::move(request));
+        AcpiMultiplyServer::BindDeviceClient(logger_, async_dispatcher(), multiplier_,
+                                             std::move(request));
       });
   ZX_ASSERT(result.is_ok());
 
-  result = outgoing_.AddService<fuchsia_examples_acpi_multiply::Service>(std::move(handler));
+  result =
+      context().outgoing()->AddService<fuchsia_examples_acpi_multiply::Service>(std::move(handler));
   if (result.is_error()) {
     FDF_SLOG(ERROR, "Failed to add service", KV("status", result.status_string()));
     return result.take_error();
   }
 
-  auto 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();
-  }
-
   // This is not strictly necessary, but it does a multiplication to prove that the driver
   // behaves as expected.
-  async::PostTask(dispatcher_, [this]() {
+  async::PostTask(async_dispatcher(), [this]() {
     auto endpoints = fidl::CreateEndpoints<fuchsia_examples_acpi_multiply::Device>();
     if (endpoints.is_error()) {
       FDF_LOG(ERROR, "Failed to create endpoints");
       return;
     }
 
-    auto server_impl = std::make_unique<AcpiMultiplyServer>(ns_, dispatcher_, multiplier_);
-    fidl::BindServer(dispatcher_, std::move(endpoints->server), std::move(server_impl),
+    auto server_impl = std::make_unique<AcpiMultiplyServer>(logger_, multiplier_);
+    fidl::BindServer(async_dispatcher(), std::move(endpoints->server), std::move(server_impl),
                      std::mem_fn(&AcpiMultiplyServer::OnUnbound));
-    client_.Bind(std::move(endpoints->client), dispatcher_);
+    client_.Bind(std::move(endpoints->client), async_dispatcher());
     // Do the multiply
     client_->Multiply(UINT32_MAX, 9)
         .Then([this](fidl::WireUnownedResult<fuchsia_examples_acpi_multiply::Device::Multiply>&
@@ -179,4 +98,4 @@
 
 }  // namespace acpi_multiply
 
-FUCHSIA_DRIVER_RECORD_CPP_V1(acpi_multiply::AcpiMultiplyDriver);
+FUCHSIA_DRIVER_RECORD_CPP_V2(driver::Record<acpi_multiply::AcpiMultiplyDriver>);
diff --git a/src/acpi_multiply/driver/acpi_multiply.h b/src/acpi_multiply/driver/acpi_multiply.h
index 4bd8e2e..40c4887 100644
--- a/src/acpi_multiply/driver/acpi_multiply.h
+++ b/src/acpi_multiply/driver/acpi_multiply.h
@@ -5,54 +5,24 @@
 #ifndef SRC_ACPI_MULTIPLY_ACPI_MULTIPLY_H_
 #define SRC_ACPI_MULTIPLY_ACPI_MULTIPLY_H_
 
-#include <fidl/fuchsia.driver.framework/cpp/wire.h>
 #include <fidl/fuchsia.examples.acpi.multiply/cpp/wire.h>
-#include <fidl/fuchsia.hardware.acpi/cpp/wire.h>
-#include <lib/driver2/logger.h>
-#include <lib/driver2/namespace.h>
-#include <lib/driver2/record_cpp.h>
-#include <lib/driver2/structured_logger.h>
-#include <lib/fdf/cpp/dispatcher.h>
-#include <lib/sys/component/cpp/outgoing_directory.h>
-#include <lib/zx/status.h>
+#include <lib/driver2/driver2_cpp.h>
 
 #include "multiplier.h"
 
 namespace acpi_multiply {
 
 // Sample driver for a virtual "multiplier" device described using ACPI.
-class AcpiMultiplyDriver {
+class AcpiMultiplyDriver : public driver::DriverBase {
  public:
-  AcpiMultiplyDriver(async_dispatcher_t* dispatcher,
-                     fidl::WireSharedClient<fuchsia_driver_framework::Node> node,
-                     driver::Namespace ns, driver::Logger logger)
-      : dispatcher_(dispatcher),
-        node_(std::move(node)),
-        outgoing_(component::OutgoingDirectory::Create(dispatcher)),
-        ns_(std::move(ns)),
-        logger_(std::move(logger)) {}
+  AcpiMultiplyDriver(driver::DriverStartArgs start_args, fdf::UnownedDispatcher driver_dispatcher)
+      : driver::DriverBase("acpi-multiply", std::move(start_args), std::move(driver_dispatcher)) {}
 
   virtual ~AcpiMultiplyDriver() = default;
 
-  static constexpr const char* Name() { return "acpi-multiply"; }
-
-  static zx::status<std::unique_ptr<AcpiMultiplyDriver>> Start(
-      fuchsia_driver_framework::wire::DriverStartArgs& start_args,
-      fdf::UnownedDispatcher dispatcher,
-      fidl::WireSharedClient<fuchsia_driver_framework::Node> node, driver::Namespace ns,
-      driver::Logger logger);
+  zx::status<> Start() override;
 
  private:
-  zx::status<> Run(fidl::ServerEnd<fuchsia_io::Directory> outgoing_dir);
-
-  async_dispatcher_t* const dispatcher_;
-
-  fidl::WireSharedClient<fuchsia_driver_framework::Node> node_;
-
-  component::OutgoingDirectory outgoing_;
-  driver::Namespace ns_;
-  driver::Logger logger_;
-
   std::shared_ptr<AcpiMultiplier> multiplier_;
 
   // Used by the self-test.
diff --git a/src/acpi_multiply/driver/meta/acpi_multiply.cml b/src/acpi_multiply/driver/meta/acpi_multiply.cml
index 07aba91..a6f8154 100644
--- a/src/acpi_multiply/driver/meta/acpi_multiply.cml
+++ b/src/acpi_multiply/driver/meta/acpi_multiply.cml
@@ -10,10 +10,10 @@
         binary: 'lib/libacpi_multiply.so',
         bind: 'meta/bind/acpi_multiply.bindbc'
     },
-    // Consume the ACPI device capability from the parent
+    // Consume the ACPI service capability from the parent
     use: [
-        { protocol: 'fuchsia.hardware.acpi.Device' },
         { service: 'fuchsia.driver.compat.Service' },
+        { service: 'fuchsia.hardware.acpi.Service' },
     ],
     // Provide the multiply device capability to other components
     capabilities: [
diff --git a/src/acpi_multiply/driver/multiplier.h b/src/acpi_multiply/driver/multiplier.h
index 8ef4504..4e0bc87 100644
--- a/src/acpi_multiply/driver/multiplier.h
+++ b/src/acpi_multiply/driver/multiplier.h
@@ -7,9 +7,7 @@
 
 #include <fidl/fuchsia.hardware.acpi/cpp/wire.h>
 #include <lib/async/cpp/irq.h>
-#include <lib/driver2/logger.h>
-#include <lib/driver2/namespace.h>
-#include <lib/driver2/structured_logger.h>
+#include <lib/driver2/driver2_cpp.h>
 #include <lib/mmio/mmio-buffer.h>
 #include <lib/zx/interrupt.h>
 
@@ -20,13 +18,9 @@
 // Invokes ACPI methods using a fuchsia.hardware.acpi client.
 class AcpiMultiplier {
  public:
-  explicit AcpiMultiplier(driver::Namespace& ns, async_dispatcher_t* dispatcher,
+  explicit AcpiMultiplier(driver::Logger& logger, async_dispatcher_t* dispatcher,
                           fidl::WireSyncClient<fuchsia_hardware_acpi::Device> acpi)
-      : dispatcher_(dispatcher), acpi_(std::move(acpi)) {
-    auto logger_result = driver::Logger::Create(ns, dispatcher, "acpi-multiplier");
-    ZX_ASSERT(logger_result.is_ok());
-    logger_ = std::move(*logger_result);
-  }
+      : logger_(logger), dispatcher_(dispatcher), acpi_(std::move(acpi)) {}
 
   zx::status<> SetupMmioAndInterrupts();
 
@@ -53,7 +47,7 @@
 
   void DoMultiply(Operation operation);
 
-  driver::Logger logger_;
+  driver::Logger& logger_;
   async_dispatcher_t* const dispatcher_;
 
   std::optional<fdf::MmioBuffer> mmio_;
diff --git a/src/acpi_multiply/driver/multiply_server.cc b/src/acpi_multiply/driver/multiply_server.cc
new file mode 100644
index 0000000..2bcc91d
--- /dev/null
+++ b/src/acpi_multiply/driver/multiply_server.cc
@@ -0,0 +1,57 @@
+// 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 "multiply_server.h"
+
+namespace acpi_multiply {
+
+// Static
+// Handle incoming connection requests from FIDL clients
+fidl::ServerBindingRef<fuchsia_examples_acpi_multiply::Device> AcpiMultiplyServer::BindDeviceClient(
+    driver::Logger& logger, async_dispatcher_t* dispatcher,
+    std::weak_ptr<AcpiMultiplier> multiplier,
+    fidl::ServerEnd<fuchsia_examples_acpi_multiply::Device> request) {
+  // Bind each connection request to a unique FIDL server instance
+  auto server_impl = std::make_unique<AcpiMultiplyServer>(logger, multiplier);
+  return fidl::BindServer(dispatcher, std::move(request), std::move(server_impl),
+                          std::mem_fn(&AcpiMultiplyServer::OnUnbound));
+}
+
+// This method is called when a server connection is torn down.
+void AcpiMultiplyServer::OnUnbound(
+    fidl::UnbindInfo info, fidl::ServerEnd<fuchsia_examples_acpi_multiply::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 in `fuchsia.examples.acpi.multiply` to execute a multiply operation
+void AcpiMultiplyServer::Multiply(MultiplyRequestView request, MultiplyCompleter::Sync& completer) {
+  auto multiplier = multiplier_.lock();
+  if (!multiplier) {
+    FDF_LOG(ERROR, "Unable to access ACPI resources.");
+    completer.ReplyError(ZX_ERR_BAD_STATE);
+    return;
+  }
+
+  multiplier->QueueMultiplyOperation(AcpiMultiplier::Operation{
+      .a = request->a,
+      .b = request->b,
+      .callback =
+          [completer =
+               completer.ToAsync()](zx::status<AcpiMultiplier::MultiplyResult> status) mutable {
+            if (status.is_error()) {
+              completer.ReplyError(status.error_value());
+              return;
+            }
+
+            AcpiMultiplier::MultiplyResult result = status.value();
+            completer.ReplySuccess(result.value, result.overflow);
+          },
+  });
+}
+
+}  // namespace acpi_multiply
diff --git a/src/acpi_multiply/driver/multiply_server.h b/src/acpi_multiply/driver/multiply_server.h
new file mode 100644
index 0000000..d1ed5ad
--- /dev/null
+++ b/src/acpi_multiply/driver/multiply_server.h
@@ -0,0 +1,40 @@
+// 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 SRC_ACPI_MULTIPLY_MULTIPLY_SERVER_H_
+#define SRC_ACPI_MULTIPLY_MULTIPLY_SERVER_H_
+
+#include <fidl/fuchsia.examples.acpi.multiply/cpp/wire.h>
+#include <lib/driver2/logger.h>
+
+#include "multiplier.h"
+
+namespace acpi_multiply {
+
+// FIDL server implementation for the `fuchsia.examples.acpi.multiply/Device` protocol
+class AcpiMultiplyServer : public fidl::WireServer<fuchsia_examples_acpi_multiply::Device> {
+ public:
+  AcpiMultiplyServer(driver::Logger& logger, std::weak_ptr<AcpiMultiplier> multiplier)
+      : logger_(logger), multiplier_(multiplier) {}
+
+  static fidl::ServerBindingRef<fuchsia_examples_acpi_multiply::Device> BindDeviceClient(
+      driver::Logger& logger, async_dispatcher_t* dispatcher,
+      std::weak_ptr<AcpiMultiplier> multiplier,
+      fidl::ServerEnd<fuchsia_examples_acpi_multiply::Device> request);
+
+  void OnUnbound(fidl::UnbindInfo info,
+                 fidl::ServerEnd<fuchsia_examples_acpi_multiply::Device> server_end);
+
+  // fidl::WireServer<fuchsia_examples_acpi_multiply::Device>
+
+  void Multiply(MultiplyRequestView request, MultiplyCompleter::Sync& completer) override;
+
+ private:
+  driver::Logger& logger_;
+  std::weak_ptr<AcpiMultiplier> multiplier_;
+};
+
+}  // namespace acpi_multiply
+
+#endif /* SRC_ACPI_MULTIPLY_MULTIPLY_SERVER_H_ */
diff --git a/src/acpi_multiply/lib/BUILD.bazel b/src/acpi_multiply/lib/BUILD.bazel
index 65726c0..3182b4d 100644
--- a/src/acpi_multiply/lib/BUILD.bazel
+++ b/src/acpi_multiply/lib/BUILD.bazel
@@ -7,4 +7,5 @@
     hdrs = ["include/registers.h"],
     includes = ["include"],
     visibility = ["//visibility:public"],
+    deps = ["@fuchsia_sdk//pkg/hwreg"],
 )