[DriverFramework] Add support for querying power capabilities.

This change defines device power states and adds support for
drivers to publish their power states while adding devices.
At times, the device needs to initialize completely to publish
the device power states. In a future changeset, DdkMakeVisible
will be modified to publish the power states in those cases.

Test: Automated tests with test driver that adds second child device
to tests various cases of DdkAdd. runtests -t ddk-power-test.

Change-Id: I6e8785db70fb173d58ff46c37a242fde1eb9854f
diff --git a/sdk/lib/ddk/ddk.api b/sdk/lib/ddk/ddk.api
index da187a00..26f2d0a 100644
--- a/sdk/lib/ddk/ddk.api
+++ b/sdk/lib/ddk/ddk.api
@@ -1,8 +1,9 @@
 {
   "pkg/ddk/include/ddk/binding.h": "8a3132cf4f2bd6bbf0a9c692c64430d3",
   "pkg/ddk/include/ddk/debug.h": "62d8ab91159ee3cd6d14a5917633a6c5",
+  "pkg/ddk/include/ddk/device-power-states.h": "27e4ede08c473a49485d1d61b0cc3288",
   "pkg/ddk/include/ddk/device.h": "65aa52b35e37509ddb314f31234214c9",
-  "pkg/ddk/include/ddk/driver.h": "19464e515f1c6373b5a5e0ab3687b3b0",
+  "pkg/ddk/include/ddk/driver.h": "f20ded4b9545d400a85951f57bf4aa8e",
   "pkg/ddk/include/ddk/io-buffer.h": "f437e5d02e615b6366248c0363b69e8d",
   "pkg/ddk/include/ddk/metadata.h": "623bd21f37a20563c19f0fa251bf782a",
   "pkg/ddk/include/ddk/metadata/bad-block.h": "b149d916398535681783481380bfbea0",
@@ -13,9 +14,9 @@
   "pkg/ddk/include/ddk/mmio-buffer.h": "d755d3a84678907984a921d807271e14",
   "pkg/ddk/include/ddk/phys-iter.h": "0b7f17747fad48f94644169a20bd8d73",
   "pkg/ddk/include/ddk/physiter.h": "2226a43dfaeb7229463ace48538c30e7",
-  "pkg/ddk/include/ddk/platform-defs.h": "0236bf184140605dd53f2c738d244cef",
+  "pkg/ddk/include/ddk/platform-defs.h": "fbd6538e07aedbf1691248e3dab238da",
   "pkg/ddk/include/ddk/protocol/auxdata.h": "1e39aa361e071383c0cba04ca9baf680",
-  "pkg/ddk/include/ddk/protodefs.h": "9628256ae1258d9af66685250a2b9cbf",
+  "pkg/ddk/include/ddk/protodefs.h": "c35fd878a945ba4b05b4af2cd832ea57",
   "pkg/ddk/include/ddk/trace/event.h": "29fbf2c43995de14c020f0dda372093a",
   "pkg/ddk/include/hw/arch_ops.h": "76796e5764b0a8c54bd4cccd09be73fa",
   "pkg/ddk/include/hw/inout.h": "c694fd6bd2a02579aa11d66a02acaf7a",
diff --git a/sdk/lib/ddktl/ddktl.api b/sdk/lib/ddktl/ddktl.api
index 99a635e..5c668aec 100644
--- a/sdk/lib/ddktl/ddktl.api
+++ b/sdk/lib/ddktl/ddktl.api
@@ -1,4 +1,4 @@
 {
   "pkg/ddktl/include/ddktl/device-internal.h": "4df4e5323f5db3277ea9004a09b19542",
-  "pkg/ddktl/include/ddktl/device.h": "cbf5a92cb9e5fa1bef41e5e46b0a8e54"
+  "pkg/ddktl/include/ddktl/device.h": "90ed5a177d1b3da94add53603e9a2403"
 }
\ No newline at end of file
diff --git a/zircon/system/core/devmgr/devhost/api.cc b/zircon/system/core/devmgr/devhost/api.cc
index dec1e34..4606ada 100644
--- a/zircon/system/core/devmgr/devhost/api.cc
+++ b/zircon/system/core/devmgr/devhost/api.cc
@@ -78,6 +78,20 @@
       dev->flags |= DEV_FLAG_ALLOW_MULTI_COMPOSITE;
     }
 
+    if (!args->power_states) {
+      //TODO(fxb/34081): Remove when all drivers declare power states
+      //Temporarily allocate working and non-working power states
+      device_power_state_info_t power_states[2];
+      power_states[0].state_id = fuchsia_device_DevicePowerState_DEVICE_POWER_STATE_D0;
+      power_states[1].state_id = fuchsia_device_DevicePowerState_DEVICE_POWER_STATE_D3COLD;
+      r = dev->SetPowerStates(power_states, 2);
+    } else {
+      r = dev->SetPowerStates(args->power_states, args->power_state_count);
+    }
+    if (r != ZX_OK) {
+      return r;
+    }
+
     // out must be set before calling devhost_device_add().
     // devhost_device_add() may result in child devices being created before it returns,
     // and those children may call ops on the device before device_add() returns.
diff --git a/zircon/system/core/devmgr/devhost/zx-device.cc b/zircon/system/core/devmgr/devhost/zx-device.cc
index 6a6f23c..a2247c9 100644
--- a/zircon/system/core/devmgr/devhost/zx-device.cc
+++ b/zircon/system/core/devmgr/devhost/zx-device.cc
@@ -48,6 +48,37 @@
   return true;
 }
 
+zx_status_t zx_device::SetPowerStates(const device_power_state_info_t* power_states,
+                               uint8_t count) {
+  if (count < fuchsia_device_MIN_DEVICE_POWER_STATES ||
+      count > fuchsia_device_MAX_DEVICE_POWER_STATES) {
+    return ZX_ERR_INVALID_ARGS;
+  }
+  bool visited[fuchsia_device_MAX_DEVICE_POWER_STATES] = {false};
+  for (uint8_t i = 0; i < count; i++) {
+    const auto& info = power_states[i];
+    if (info.state_id >= fbl::count_of(visited)) {
+      return ZX_ERR_INVALID_ARGS;
+    }
+    if (visited[info.state_id]) {
+      return ZX_ERR_INVALID_ARGS;
+    }
+    fuchsia_device_DevicePowerStateInfo* state = &power_states_[info.state_id];
+    state->state_id = info.state_id;
+    state->is_supported = true;
+    state->restore_latency = info.restore_latency;
+    state->wakeup_capable = info.wakeup_capable;
+    state->system_wake_state = info.system_wake_state;
+    visited[info.state_id] = true;
+  }
+  if (!(power_states_[fuchsia_device_DevicePowerState_DEVICE_POWER_STATE_D0].is_supported) ||
+      !(power_states_[fuchsia_device_DevicePowerState_DEVICE_POWER_STATE_D3COLD].is_supported)) {
+    return ZX_ERR_INVALID_ARGS;
+  }
+  return ZX_OK;
+}
+
+
 // We must disable thread-safety analysis due to not being able to statically
 // guarantee the lock holding invariant.  Instead, we acquire the lock if
 // it's not already being held by the current thread.
diff --git a/zircon/system/core/devmgr/devhost/zx-device.h b/zircon/system/core/devmgr/devhost/zx-device.h
index 81bc367..b200dc1 100644
--- a/zircon/system/core/devmgr/devhost/zx-device.h
+++ b/zircon/system/core/devmgr/devhost/zx-device.h
@@ -6,6 +6,8 @@
 #define ZIRCON_SYSTEM_CORE_DEVMGR_DEVHOST_ZX_DEVICE_H_
 
 #include <ddk/device.h>
+#include <ddk/device-power-states.h>
+#include <ddk/driver.h>
 #include <fbl/intrusive_double_list.h>
 #include <fbl/intrusive_wavl_tree.h>
 #include <fbl/mutex.h>
@@ -153,6 +155,8 @@
   void set_composite(fbl::RefPtr<devmgr::CompositeDevice> composite);
   fbl::RefPtr<devmgr::CompositeDevice> take_composite();
 
+  zx_status_t SetPowerStates(const device_power_state_info_t* power_states,
+                             uint8_t count);
  private:
   zx_device() = default;
 
@@ -191,6 +195,8 @@
   fbl::Mutex test_compatibility_conn_lock_;
   fbl::Vector<fs::FidlConnection> test_compatibility_conn_
       TA_GUARDED(test_compatibility_conn_lock_);
+
+  fuchsia_device_DevicePowerStateInfo power_states_[fuchsia_device_MAX_DEVICE_POWER_STATES];
 };
 
 // zx_device_t objects must be created or initialized by the driver manager's
diff --git a/zircon/system/dev/BUILD.gn b/zircon/system/dev/BUILD.gn
index de22538..a351648 100644
--- a/zircon/system/dev/BUILD.gn
+++ b/zircon/system/dev/BUILD.gn
@@ -44,6 +44,8 @@
     "$zx/system/utest/platform-bus:test",
     "test/ddk-runcompatibilityhook:ddk-runcompatibilityhook-test",
     "test/ddk-runcompatibilityhook:ddk-runcompatibilityhook-test-child",
+    "test/ddk-power:ddk-power-test",
+    "test/ddk-power:ddk-power-test-child",
     "test/fidl-llcpp-driver",
     "test/isolateddevmgr:isolateddevmgr-test",
     "test/mock-device",
diff --git a/zircon/system/dev/display/vim-display/vim-display.cc b/zircon/system/dev/display/vim-display/vim-display.cc
index 782bd81..90117cbb 100644
--- a/zircon/system/dev/display/vim-display/vim-display.cc
+++ b/zircon/system/dev/display/vim-display/vim-display.cc
@@ -1039,19 +1039,18 @@
   display->color_depth = _gcolor_depth;
   init_hdmi_hardware(display);
 
-  device_add_args_t add_args = {
-      .version = DEVICE_ADD_ARGS_VERSION,
-      .name = "vim2-display",
-      .ctx = display,
-      .ops = &main_device_proto,
-      .props = nullptr,
-      .prop_count = 0,
-      .proto_id = ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL,
-      .proto_ops = &display_controller_ops,
-      .proxy_args = nullptr,
-      .flags = 0,
-      .client_remote = ZX_HANDLE_INVALID,
-  };
+  device_add_args_t add_args = {};
+  add_args.version = DEVICE_ADD_ARGS_VERSION;
+  add_args.name = "vim2-display";
+  add_args.ctx = display;
+  add_args.ops = &main_device_proto;
+  add_args.props = nullptr;
+  add_args.prop_count = 0;
+  add_args.proto_id = ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL;
+  add_args.proto_ops = &display_controller_ops;
+  add_args.proxy_args = nullptr;
+  add_args.flags = 0;
+  add_args.client_remote = ZX_HANDLE_INVALID;
 
   status = device_add(display->parent, &add_args, &display->mydevice);
   if (status != ZX_OK) {
diff --git a/zircon/system/dev/test/BUILD.gn b/zircon/system/dev/test/BUILD.gn
index 96c0617..6c6ca9c 100644
--- a/zircon/system/dev/test/BUILD.gn
+++ b/zircon/system/dev/test/BUILD.gn
@@ -7,6 +7,7 @@
   deps = [
     "ddk-fidl-test",
     "ddk-runcompatibilityhook",
+    "ddk-power",
     "ddk-test",
     "isolateddevmgr",
     "mock-device",
diff --git a/zircon/system/dev/test/ddk-power/BUILD.gn b/zircon/system/dev/test/ddk-power/BUILD.gn
new file mode 100644
index 0000000..042b027
--- /dev/null
+++ b/zircon/system/dev/test/ddk-power/BUILD.gn
@@ -0,0 +1,70 @@
+# Copyright 2019 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.
+
+import("$zx/public/gn/fidl.gni")
+
+fidl_library("fuchsia.device.power.test") {
+  visibility = [ ":*" ]
+  sources = [
+    "test.fidl",
+  ]
+  public_deps = [
+    "$zx/system/fidl/fuchsia-device",
+  ]
+}
+
+if (current_toolchain != default_toolchain) {
+    driver("ddk-power-test") {
+      sources = [
+        "test-driver.cc",
+      ]
+      deps = [
+        "$zx/system/ulib/ddk",
+        "$zx/system/ulib/ddktl",
+        "$zx/system/ulib/fdio",
+        "$zx/system/ulib/zircon",
+        ":fuchsia.device.power.test.llcpp",
+      ]
+
+      # TODO(ZX-2863): This driver violates the allowed shlib deps policy.
+      # Tests fail when using #"$zx/system/ulib/fdio:static",
+      deprecated_inhibit_driver_shlib_allowlist = true
+    }
+
+    driver("ddk-power-test-child") {
+      sources = [
+        "test-driver-child.cc",
+      ]
+      deps = [
+        "$zx/system/ulib/ddk",
+        "$zx/system/ulib/ddktl",
+        "$zx/system/ulib/fdio",
+        "$zx/system/ulib/zircon",
+        ":fuchsia.device.power.test.llcpp",
+      ]
+
+      # TODO(ZX-2863): This driver violates the allowed shlib deps policy.
+      deprecated_inhibit_driver_shlib_allowlist = true
+    }
+
+    test("ddk-power") {
+      sources = [
+        "test.cc",
+      ]
+      deps = [
+        "$zx/system/fidl/fuchsia-device:c",
+        "$zx/system/fidl/fuchsia-device-manager:c",
+        ":fuchsia.device.power.test.llcpp",
+        "$zx/system/ulib/ddk",
+        "$zx/system/ulib/devmgr-integration-test",
+        "$zx/system/ulib/devmgr-launcher",
+        "$zx/system/ulib/driver-integration-test",
+        "$zx/system/ulib/fbl",
+        "$zx/system/ulib/fdio",
+        "$zx/system/ulib/zircon",
+        "$zx/system/ulib/zx",
+        "$zx/system/ulib/zxtest",
+      ]
+    }
+}
diff --git a/zircon/system/dev/test/ddk-power/gen/llcpp/fidl.cc b/zircon/system/dev/test/ddk-power/gen/llcpp/fidl.cc
new file mode 100644
index 0000000..17349d0
--- /dev/null
+++ b/zircon/system/dev/test/ddk-power/gen/llcpp/fidl.cc
@@ -0,0 +1,218 @@
+// WARNING: This file is machine generated by fidlgen.
+
+#include <fuchsia/device/power/test/llcpp/fidl.h>
+#include <memory>
+
+namespace llcpp {
+
+namespace fuchsia {
+namespace device {
+namespace power {
+namespace test {
+
+::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Result::TestDevice_AddDeviceWithPowerArgs_Result() {
+  tag_ = Tag::Invalid;
+}
+
+::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Result::~TestDevice_AddDeviceWithPowerArgs_Result() {
+  Destroy();
+}
+
+void ::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Result::Destroy() {
+  switch (which()) {
+  case Tag::kResponse:
+    response_.~TestDevice_AddDeviceWithPowerArgs_Response();
+    break;
+  default:
+    break;
+  }
+  tag_ = Tag::Invalid;
+}
+
+void ::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Result::MoveImpl_(TestDevice_AddDeviceWithPowerArgs_Result&& other) {
+  switch (other.which()) {
+  case Tag::kResponse:
+    mutable_response() = std::move(other.mutable_response());
+    break;
+  case Tag::kErr:
+    mutable_err() = std::move(other.mutable_err());
+    break;
+  default:
+    break;
+  }
+  other.Destroy();
+}
+
+void ::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Result::SizeAndOffsetAssertionHelper() {
+  static_assert(offsetof(::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Result, response_) == 4);
+  static_assert(offsetof(::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Result, err_) == 4);
+  static_assert(sizeof(::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Result) == ::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Result::PrimarySize);
+}
+
+
+TestDevice_AddDeviceWithPowerArgs_Response& ::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Result::mutable_response() {
+  if (which() != Tag::kResponse) {
+    Destroy();
+    new (&response_) TestDevice_AddDeviceWithPowerArgs_Response;
+  }
+  tag_ = Tag::kResponse;
+  return response_;
+}
+
+int32_t& ::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Result::mutable_err() {
+  if (which() != Tag::kErr) {
+    Destroy();
+    new (&err_) int32_t;
+  }
+  tag_ = Tag::kErr;
+  return err_;
+}
+
+
+namespace {
+
+[[maybe_unused]]
+constexpr uint64_t kTestDevice_AddDeviceWithPowerArgs_GenOrdinal = 0x5d89a15400000000lu;
+extern "C" const fidl_type_t fuchsia_device_power_test_TestDeviceAddDeviceWithPowerArgsRequestTable;
+extern "C" const fidl_type_t fuchsia_device_power_test_TestDeviceAddDeviceWithPowerArgsResponseTable;
+
+}  // namespace
+template <>
+TestDevice::ResultOf::AddDeviceWithPowerArgs_Impl<TestDevice::AddDeviceWithPowerArgsResponse>::AddDeviceWithPowerArgs_Impl(zx::unowned_channel _client_end, ::fidl::VectorView<::llcpp::fuchsia::device::DevicePowerStateInfo> info) {
+  constexpr uint32_t _kWriteAllocSize = ::fidl::internal::ClampedMessageSize<AddDeviceWithPowerArgsRequest, ::fidl::MessageDirection::kSending>();
+  std::unique_ptr _write_bytes_boxed = std::make_unique<::fidl::internal::AlignedBuffer<_kWriteAllocSize>>();
+  auto& _write_bytes_array = *_write_bytes_boxed;
+  AddDeviceWithPowerArgsRequest _request = {};
+  _request.info = std::move(info);
+  auto _linearize_result = ::fidl::Linearize(&_request, _write_bytes_array.view());
+  if (_linearize_result.status != ZX_OK) {
+    Super::SetFailure(std::move(_linearize_result));
+    return;
+  }
+  ::fidl::DecodedMessage<AddDeviceWithPowerArgsRequest> _decoded_request = std::move(_linearize_result.message);
+  Super::SetResult(
+      TestDevice::InPlace::AddDeviceWithPowerArgs(std::move(_client_end), std::move(_decoded_request), Super::response_buffer()));
+}
+
+TestDevice::ResultOf::AddDeviceWithPowerArgs TestDevice::SyncClient::AddDeviceWithPowerArgs(::fidl::VectorView<::llcpp::fuchsia::device::DevicePowerStateInfo> info) {
+  return ResultOf::AddDeviceWithPowerArgs(zx::unowned_channel(this->channel_), std::move(info));
+}
+
+TestDevice::ResultOf::AddDeviceWithPowerArgs TestDevice::Call::AddDeviceWithPowerArgs(zx::unowned_channel _client_end, ::fidl::VectorView<::llcpp::fuchsia::device::DevicePowerStateInfo> info) {
+  return ResultOf::AddDeviceWithPowerArgs(std::move(_client_end), std::move(info));
+}
+
+template <>
+TestDevice::UnownedResultOf::AddDeviceWithPowerArgs_Impl<TestDevice::AddDeviceWithPowerArgsResponse>::AddDeviceWithPowerArgs_Impl(zx::unowned_channel _client_end, ::fidl::BytePart _request_buffer, ::fidl::VectorView<::llcpp::fuchsia::device::DevicePowerStateInfo> info, ::fidl::BytePart _response_buffer) {
+  if (_request_buffer.capacity() < AddDeviceWithPowerArgsRequest::PrimarySize) {
+    Super::SetFailure(::fidl::DecodeResult<AddDeviceWithPowerArgsResponse>(ZX_ERR_BUFFER_TOO_SMALL, ::fidl::internal::kErrorRequestBufferTooSmall));
+    return;
+  }
+  AddDeviceWithPowerArgsRequest _request = {};
+  _request.info = std::move(info);
+  auto _linearize_result = ::fidl::Linearize(&_request, std::move(_request_buffer));
+  if (_linearize_result.status != ZX_OK) {
+    Super::SetFailure(std::move(_linearize_result));
+    return;
+  }
+  ::fidl::DecodedMessage<AddDeviceWithPowerArgsRequest> _decoded_request = std::move(_linearize_result.message);
+  Super::SetResult(
+      TestDevice::InPlace::AddDeviceWithPowerArgs(std::move(_client_end), std::move(_decoded_request), std::move(_response_buffer)));
+}
+
+TestDevice::UnownedResultOf::AddDeviceWithPowerArgs TestDevice::SyncClient::AddDeviceWithPowerArgs(::fidl::BytePart _request_buffer, ::fidl::VectorView<::llcpp::fuchsia::device::DevicePowerStateInfo> info, ::fidl::BytePart _response_buffer) {
+  return UnownedResultOf::AddDeviceWithPowerArgs(zx::unowned_channel(this->channel_), std::move(_request_buffer), std::move(info), std::move(_response_buffer));
+}
+
+TestDevice::UnownedResultOf::AddDeviceWithPowerArgs TestDevice::Call::AddDeviceWithPowerArgs(zx::unowned_channel _client_end, ::fidl::BytePart _request_buffer, ::fidl::VectorView<::llcpp::fuchsia::device::DevicePowerStateInfo> info, ::fidl::BytePart _response_buffer) {
+  return UnownedResultOf::AddDeviceWithPowerArgs(std::move(_client_end), std::move(_request_buffer), std::move(info), std::move(_response_buffer));
+}
+
+::fidl::DecodeResult<TestDevice::AddDeviceWithPowerArgsResponse> TestDevice::InPlace::AddDeviceWithPowerArgs(zx::unowned_channel _client_end, ::fidl::DecodedMessage<AddDeviceWithPowerArgsRequest> params, ::fidl::BytePart response_buffer) {
+  params.message()->_hdr = {};
+  params.message()->_hdr.ordinal = kTestDevice_AddDeviceWithPowerArgs_GenOrdinal;
+  auto _encode_request_result = ::fidl::Encode(std::move(params));
+  if (_encode_request_result.status != ZX_OK) {
+    return ::fidl::DecodeResult<TestDevice::AddDeviceWithPowerArgsResponse>::FromFailure(
+        std::move(_encode_request_result));
+  }
+  auto _call_result = ::fidl::Call<AddDeviceWithPowerArgsRequest, AddDeviceWithPowerArgsResponse>(
+    std::move(_client_end), std::move(_encode_request_result.message), std::move(response_buffer));
+  if (_call_result.status != ZX_OK) {
+    return ::fidl::DecodeResult<TestDevice::AddDeviceWithPowerArgsResponse>::FromFailure(
+        std::move(_call_result));
+  }
+  return ::fidl::Decode(std::move(_call_result.message));
+}
+
+
+bool TestDevice::TryDispatch(Interface* impl, fidl_msg_t* msg, ::fidl::Transaction* txn) {
+  if (msg->num_bytes < sizeof(fidl_message_header_t)) {
+    zx_handle_close_many(msg->handles, msg->num_handles);
+    txn->Close(ZX_ERR_INVALID_ARGS);
+    return true;
+  }
+  fidl_message_header_t* hdr = reinterpret_cast<fidl_message_header_t*>(msg->bytes);
+  switch (hdr->ordinal) {
+    case kTestDevice_AddDeviceWithPowerArgs_GenOrdinal:
+    {
+      auto result = ::fidl::DecodeAs<AddDeviceWithPowerArgsRequest>(msg);
+      if (result.status != ZX_OK) {
+        txn->Close(ZX_ERR_INVALID_ARGS);
+        return true;
+      }
+      auto message = result.message.message();
+      impl->AddDeviceWithPowerArgs(std::move(message->info),
+        Interface::AddDeviceWithPowerArgsCompleter::Sync(txn));
+      return true;
+    }
+    default: {
+      return false;
+    }
+  }
+}
+
+bool TestDevice::Dispatch(Interface* impl, fidl_msg_t* msg, ::fidl::Transaction* txn) {
+  bool found = TryDispatch(impl, msg, txn);
+  if (!found) {
+    zx_handle_close_many(msg->handles, msg->num_handles);
+    txn->Close(ZX_ERR_NOT_SUPPORTED);
+  }
+  return found;
+}
+
+
+void TestDevice::Interface::AddDeviceWithPowerArgsCompleterBase::Reply(TestDevice_AddDeviceWithPowerArgs_Result result) {
+  constexpr uint32_t _kWriteAllocSize = ::fidl::internal::ClampedMessageSize<AddDeviceWithPowerArgsResponse, ::fidl::MessageDirection::kSending>();
+  FIDL_ALIGNDECL uint8_t _write_bytes[_kWriteAllocSize] = {};
+  auto& _response = *reinterpret_cast<AddDeviceWithPowerArgsResponse*>(_write_bytes);
+  _response._hdr.ordinal = kTestDevice_AddDeviceWithPowerArgs_GenOrdinal;
+  _response.result = std::move(result);
+  ::fidl::BytePart _response_bytes(_write_bytes, _kWriteAllocSize, sizeof(AddDeviceWithPowerArgsResponse));
+  CompleterBase::SendReply(::fidl::DecodedMessage<AddDeviceWithPowerArgsResponse>(std::move(_response_bytes)));
+}
+
+void TestDevice::Interface::AddDeviceWithPowerArgsCompleterBase::Reply(::fidl::BytePart _buffer, TestDevice_AddDeviceWithPowerArgs_Result result) {
+  if (_buffer.capacity() < AddDeviceWithPowerArgsResponse::PrimarySize) {
+    CompleterBase::Close(ZX_ERR_INTERNAL);
+    return;
+  }
+  auto& _response = *reinterpret_cast<AddDeviceWithPowerArgsResponse*>(_buffer.data());
+  _response._hdr.ordinal = kTestDevice_AddDeviceWithPowerArgs_GenOrdinal;
+  _response.result = std::move(result);
+  _buffer.set_actual(sizeof(AddDeviceWithPowerArgsResponse));
+  CompleterBase::SendReply(::fidl::DecodedMessage<AddDeviceWithPowerArgsResponse>(std::move(_buffer)));
+}
+
+void TestDevice::Interface::AddDeviceWithPowerArgsCompleterBase::Reply(::fidl::DecodedMessage<AddDeviceWithPowerArgsResponse> params) {
+  params.message()->_hdr = {};
+  params.message()->_hdr.ordinal = kTestDevice_AddDeviceWithPowerArgs_GenOrdinal;
+  CompleterBase::SendReply(std::move(params));
+}
+
+
+}  // namespace test
+}  // namespace power
+}  // namespace device
+}  // namespace fuchsia
+}  // namespace llcpp
diff --git a/zircon/system/dev/test/ddk-power/gen/llcpp/include/fuchsia/device/power/test/llcpp/fidl.h b/zircon/system/dev/test/ddk-power/gen/llcpp/include/fuchsia/device/power/test/llcpp/fidl.h
new file mode 100644
index 0000000..b35171b
--- /dev/null
+++ b/zircon/system/dev/test/ddk-power/gen/llcpp/include/fuchsia/device/power/test/llcpp/fidl.h
@@ -0,0 +1,339 @@
+// WARNING: This file is machine generated by fidlgen.
+
+#pragma once
+
+#include <lib/fidl/internal.h>
+#include <lib/fidl/cpp/vector_view.h>
+#include <lib/fidl/cpp/string_view.h>
+#include <lib/fidl/llcpp/array.h>
+#include <lib/fidl/llcpp/coding.h>
+#include <lib/fidl/llcpp/sync_call.h>
+#include <lib/fidl/llcpp/traits.h>
+#include <lib/fidl/llcpp/transaction.h>
+#include <lib/fit/function.h>
+#include <lib/zx/channel.h>
+#include <zircon/fidl.h>
+
+#include <fuchsia/device/llcpp/fidl.h>
+
+namespace llcpp {
+
+namespace fuchsia {
+namespace device {
+namespace power {
+namespace test {
+
+struct TestDevice_AddDeviceWithPowerArgs_Response;
+struct TestDevice_AddDeviceWithPowerArgs_Result;
+class TestDevice;
+
+
+
+struct TestDevice_AddDeviceWithPowerArgs_Response {
+  static constexpr const fidl_type_t* Type = nullptr;
+  static constexpr uint32_t MaxNumHandles = 0;
+  static constexpr uint32_t PrimarySize = 1;
+  [[maybe_unused]]
+  static constexpr uint32_t MaxOutOfLine = 0;
+
+  uint8_t __reserved = {};
+};
+
+extern "C" const fidl_type_t fuchsia_device_power_test_TestDevice_AddDeviceWithPowerArgs_ResultTable;
+
+struct TestDevice_AddDeviceWithPowerArgs_Result {
+  enum class Tag : fidl_union_tag_t {
+    kResponse = 0,
+    kErr = 1,
+    Invalid = ::std::numeric_limits<::fidl_union_tag_t>::max(),
+  };
+
+  TestDevice_AddDeviceWithPowerArgs_Result();
+  ~TestDevice_AddDeviceWithPowerArgs_Result();
+
+  TestDevice_AddDeviceWithPowerArgs_Result(TestDevice_AddDeviceWithPowerArgs_Result&& other) {
+    tag_ = Tag::Invalid;
+    if (this != &other) {
+      MoveImpl_(std::move(other));
+    }
+  }
+
+  TestDevice_AddDeviceWithPowerArgs_Result& operator=(TestDevice_AddDeviceWithPowerArgs_Result&& other) {
+    if (this != &other) {
+      MoveImpl_(std::move(other));
+    }
+    return *this;
+  }
+
+  bool has_invalid_tag() const { return tag_ == Tag::Invalid; }
+
+  bool is_response() const { return tag_ == Tag::kResponse; }
+
+  TestDevice_AddDeviceWithPowerArgs_Response& mutable_response();
+
+  template <typename T>
+  std::enable_if_t<std::is_convertible<T, TestDevice_AddDeviceWithPowerArgs_Response>::value && std::is_copy_assignable<T>::value>
+  set_response(const T& v) {
+    mutable_response() = v;
+  }
+
+  template <typename T>
+  std::enable_if_t<std::is_convertible<T, TestDevice_AddDeviceWithPowerArgs_Response>::value && std::is_move_assignable<T>::value>
+  set_response(T&& v) {
+    mutable_response() = std::move(v);
+  }
+
+  TestDevice_AddDeviceWithPowerArgs_Response const & response() const { return response_; }
+
+  bool is_err() const { return tag_ == Tag::kErr; }
+
+  int32_t& mutable_err();
+
+  template <typename T>
+  std::enable_if_t<std::is_convertible<T, int32_t>::value && std::is_copy_assignable<T>::value>
+  set_err(const T& v) {
+    mutable_err() = v;
+  }
+
+  template <typename T>
+  std::enable_if_t<std::is_convertible<T, int32_t>::value && std::is_move_assignable<T>::value>
+  set_err(T&& v) {
+    mutable_err() = std::move(v);
+  }
+
+  int32_t const & err() const { return err_; }
+
+  Tag which() const { return tag_; }
+
+  static constexpr const fidl_type_t* Type = &fuchsia_device_power_test_TestDevice_AddDeviceWithPowerArgs_ResultTable;
+  static constexpr uint32_t MaxNumHandles = 0;
+  static constexpr uint32_t PrimarySize = 8;
+  [[maybe_unused]]
+  static constexpr uint32_t MaxOutOfLine = 0;
+
+ private:
+  void Destroy();
+  void MoveImpl_(TestDevice_AddDeviceWithPowerArgs_Result&& other);
+  static void SizeAndOffsetAssertionHelper();
+  Tag tag_;
+  union {
+    TestDevice_AddDeviceWithPowerArgs_Response response_;
+    int32_t err_;
+  };
+};
+
+extern "C" const fidl_type_t fuchsia_device_power_test_TestDeviceAddDeviceWithPowerArgsRequestTable;
+extern "C" const fidl_type_t fuchsia_device_power_test_TestDeviceAddDeviceWithPowerArgsResponseTable;
+
+class TestDevice final {
+  TestDevice() = delete;
+ public:
+
+  struct AddDeviceWithPowerArgsResponse final {
+    FIDL_ALIGNDECL
+    fidl_message_header_t _hdr;
+    TestDevice_AddDeviceWithPowerArgs_Result result;
+
+    static constexpr const fidl_type_t* Type = &fuchsia_device_power_test_TestDeviceAddDeviceWithPowerArgsResponseTable;
+    static constexpr uint32_t MaxNumHandles = 0;
+    static constexpr uint32_t PrimarySize = 24;
+    static constexpr uint32_t MaxOutOfLine = 0;
+    static constexpr bool HasFlexibleEnvelope = false;
+    static constexpr ::fidl::internal::TransactionalMessageKind MessageKind =
+        ::fidl::internal::TransactionalMessageKind::kResponse;
+  };
+  struct AddDeviceWithPowerArgsRequest final {
+    FIDL_ALIGNDECL
+    fidl_message_header_t _hdr;
+    ::fidl::VectorView<::llcpp::fuchsia::device::DevicePowerStateInfo> info;
+
+    static constexpr const fidl_type_t* Type = &fuchsia_device_power_test_TestDeviceAddDeviceWithPowerArgsRequestTable;
+    static constexpr uint32_t MaxNumHandles = 0;
+    static constexpr uint32_t PrimarySize = 32;
+    static constexpr uint32_t MaxOutOfLine = 4294967295;
+    static constexpr bool HasFlexibleEnvelope = false;
+    static constexpr ::fidl::internal::TransactionalMessageKind MessageKind =
+        ::fidl::internal::TransactionalMessageKind::kRequest;
+    using ResponseType = AddDeviceWithPowerArgsResponse;
+  };
+
+
+  // Collection of return types of FIDL calls in this interface.
+  class ResultOf final {
+    ResultOf() = delete;
+   private:
+    template <typename ResponseType>
+    class AddDeviceWithPowerArgs_Impl final : private ::fidl::internal::OwnedSyncCallBase<ResponseType> {
+      using Super = ::fidl::internal::OwnedSyncCallBase<ResponseType>;
+     public:
+      AddDeviceWithPowerArgs_Impl(zx::unowned_channel _client_end, ::fidl::VectorView<::llcpp::fuchsia::device::DevicePowerStateInfo> info);
+      ~AddDeviceWithPowerArgs_Impl() = default;
+      AddDeviceWithPowerArgs_Impl(AddDeviceWithPowerArgs_Impl&& other) = default;
+      AddDeviceWithPowerArgs_Impl& operator=(AddDeviceWithPowerArgs_Impl&& other) = default;
+      using Super::status;
+      using Super::error;
+      using Super::ok;
+      using Super::Unwrap;
+      using Super::value;
+      using Super::operator->;
+      using Super::operator*;
+    };
+
+   public:
+    using AddDeviceWithPowerArgs = AddDeviceWithPowerArgs_Impl<AddDeviceWithPowerArgsResponse>;
+  };
+
+  // Collection of return types of FIDL calls in this interface,
+  // when the caller-allocate flavor or in-place call is used.
+  class UnownedResultOf final {
+    UnownedResultOf() = delete;
+   private:
+    template <typename ResponseType>
+    class AddDeviceWithPowerArgs_Impl final : private ::fidl::internal::UnownedSyncCallBase<ResponseType> {
+      using Super = ::fidl::internal::UnownedSyncCallBase<ResponseType>;
+     public:
+      AddDeviceWithPowerArgs_Impl(zx::unowned_channel _client_end, ::fidl::BytePart _request_buffer, ::fidl::VectorView<::llcpp::fuchsia::device::DevicePowerStateInfo> info, ::fidl::BytePart _response_buffer);
+      ~AddDeviceWithPowerArgs_Impl() = default;
+      AddDeviceWithPowerArgs_Impl(AddDeviceWithPowerArgs_Impl&& other) = default;
+      AddDeviceWithPowerArgs_Impl& operator=(AddDeviceWithPowerArgs_Impl&& other) = default;
+      using Super::status;
+      using Super::error;
+      using Super::ok;
+      using Super::Unwrap;
+      using Super::value;
+      using Super::operator->;
+      using Super::operator*;
+    };
+
+   public:
+    using AddDeviceWithPowerArgs = AddDeviceWithPowerArgs_Impl<AddDeviceWithPowerArgsResponse>;
+  };
+
+  class SyncClient final {
+   public:
+    explicit SyncClient(::zx::channel channel) : channel_(std::move(channel)) {}
+    ~SyncClient() = default;
+    SyncClient(SyncClient&&) = default;
+    SyncClient& operator=(SyncClient&&) = default;
+
+    const ::zx::channel& channel() const { return channel_; }
+
+    ::zx::channel* mutable_channel() { return &channel_; }
+
+    // Add Test Device with some powerargs
+    // Allocates 24 bytes of response buffer on the stack. Request is heap-allocated.
+    ResultOf::AddDeviceWithPowerArgs AddDeviceWithPowerArgs(::fidl::VectorView<::llcpp::fuchsia::device::DevicePowerStateInfo> info);
+
+    // Add Test Device with some powerargs
+    // Caller provides the backing storage for FIDL message via request and response buffers.
+    UnownedResultOf::AddDeviceWithPowerArgs AddDeviceWithPowerArgs(::fidl::BytePart _request_buffer, ::fidl::VectorView<::llcpp::fuchsia::device::DevicePowerStateInfo> info, ::fidl::BytePart _response_buffer);
+
+   private:
+    ::zx::channel channel_;
+  };
+
+  // Methods to make a sync FIDL call directly on an unowned channel, avoiding setting up a client.
+  class Call final {
+    Call() = delete;
+   public:
+
+    // Add Test Device with some powerargs
+    // Allocates 24 bytes of response buffer on the stack. Request is heap-allocated.
+    static ResultOf::AddDeviceWithPowerArgs AddDeviceWithPowerArgs(zx::unowned_channel _client_end, ::fidl::VectorView<::llcpp::fuchsia::device::DevicePowerStateInfo> info);
+
+    // Add Test Device with some powerargs
+    // Caller provides the backing storage for FIDL message via request and response buffers.
+    static UnownedResultOf::AddDeviceWithPowerArgs AddDeviceWithPowerArgs(zx::unowned_channel _client_end, ::fidl::BytePart _request_buffer, ::fidl::VectorView<::llcpp::fuchsia::device::DevicePowerStateInfo> info, ::fidl::BytePart _response_buffer);
+
+  };
+
+  // Messages are encoded and decoded in-place when these methods are used.
+  // Additionally, requests must be already laid-out according to the FIDL wire-format.
+  class InPlace final {
+    InPlace() = delete;
+   public:
+
+    // Add Test Device with some powerargs
+    static ::fidl::DecodeResult<AddDeviceWithPowerArgsResponse> AddDeviceWithPowerArgs(zx::unowned_channel _client_end, ::fidl::DecodedMessage<AddDeviceWithPowerArgsRequest> params, ::fidl::BytePart response_buffer);
+
+  };
+
+  // Pure-virtual interface to be implemented by a server.
+  class Interface {
+   public:
+    Interface() = default;
+    virtual ~Interface() = default;
+    using _Outer = TestDevice;
+    using _Base = ::fidl::CompleterBase;
+
+    class AddDeviceWithPowerArgsCompleterBase : public _Base {
+     public:
+      void Reply(TestDevice_AddDeviceWithPowerArgs_Result result);
+      void Reply(::fidl::BytePart _buffer, TestDevice_AddDeviceWithPowerArgs_Result result);
+      void Reply(::fidl::DecodedMessage<AddDeviceWithPowerArgsResponse> params);
+
+     protected:
+      using ::fidl::CompleterBase::CompleterBase;
+    };
+
+    using AddDeviceWithPowerArgsCompleter = ::fidl::Completer<AddDeviceWithPowerArgsCompleterBase>;
+
+    virtual void AddDeviceWithPowerArgs(::fidl::VectorView<::llcpp::fuchsia::device::DevicePowerStateInfo> info, AddDeviceWithPowerArgsCompleter::Sync _completer) = 0;
+
+  };
+
+  // Attempts to dispatch the incoming message to a handler function in the server implementation.
+  // If there is no matching handler, it returns false, leaving the message and transaction intact.
+  // In all other cases, it consumes the message and returns true.
+  // It is possible to chain multiple TryDispatch functions in this manner.
+  static bool TryDispatch(Interface* impl, fidl_msg_t* msg, ::fidl::Transaction* txn);
+
+  // Dispatches the incoming message to one of the handlers functions in the interface.
+  // If there is no matching handler, it closes all the handles in |msg| and closes the channel with
+  // a |ZX_ERR_NOT_SUPPORTED| epitaph, before returning false. The message should then be discarded.
+  static bool Dispatch(Interface* impl, fidl_msg_t* msg, ::fidl::Transaction* txn);
+
+  // Same as |Dispatch|, but takes a |void*| instead of |Interface*|. Only used with |fidl::Bind|
+  // to reduce template expansion.
+  // Do not call this method manually. Use |Dispatch| instead.
+  static bool TypeErasedDispatch(void* impl, fidl_msg_t* msg, ::fidl::Transaction* txn) {
+    return Dispatch(static_cast<Interface*>(impl), msg, txn);
+  }
+
+};
+
+}  // namespace test
+}  // namespace power
+}  // namespace device
+}  // namespace fuchsia
+}  // namespace llcpp
+
+namespace fidl {
+
+template <>
+struct IsFidlType<::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Response> : public std::true_type {};
+static_assert(std::is_standard_layout_v<::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Response>);
+static_assert(offsetof(::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Response, __reserved) == 0);
+static_assert(sizeof(::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Response) == ::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Response::PrimarySize);
+
+template <>
+struct IsFidlType<::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Result> : public std::true_type {};
+static_assert(std::is_standard_layout_v<::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Result>);
+
+template <>
+struct IsFidlType<::llcpp::fuchsia::device::power::test::TestDevice::AddDeviceWithPowerArgsRequest> : public std::true_type {};
+template <>
+struct IsFidlMessage<::llcpp::fuchsia::device::power::test::TestDevice::AddDeviceWithPowerArgsRequest> : public std::true_type {};
+static_assert(sizeof(::llcpp::fuchsia::device::power::test::TestDevice::AddDeviceWithPowerArgsRequest)
+    == ::llcpp::fuchsia::device::power::test::TestDevice::AddDeviceWithPowerArgsRequest::PrimarySize);
+static_assert(offsetof(::llcpp::fuchsia::device::power::test::TestDevice::AddDeviceWithPowerArgsRequest, info) == 16);
+
+template <>
+struct IsFidlType<::llcpp::fuchsia::device::power::test::TestDevice::AddDeviceWithPowerArgsResponse> : public std::true_type {};
+template <>
+struct IsFidlMessage<::llcpp::fuchsia::device::power::test::TestDevice::AddDeviceWithPowerArgsResponse> : public std::true_type {};
+static_assert(sizeof(::llcpp::fuchsia::device::power::test::TestDevice::AddDeviceWithPowerArgsResponse)
+    == ::llcpp::fuchsia::device::power::test::TestDevice::AddDeviceWithPowerArgsResponse::PrimarySize);
+static_assert(offsetof(::llcpp::fuchsia::device::power::test::TestDevice::AddDeviceWithPowerArgsResponse, result) == 16);
+
+}  // namespace fidl
diff --git a/zircon/system/dev/test/ddk-power/test-driver-child.cc b/zircon/system/dev/test/ddk-power/test-driver-child.cc
new file mode 100644
index 0000000..7b8b89c
--- /dev/null
+++ b/zircon/system/dev/test/ddk-power/test-driver-child.cc
@@ -0,0 +1,110 @@
+// Copyright 2019 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 <ddk/binding.h>
+#include <ddk/debug.h>
+#include <ddk/device.h>
+#include <ddk/driver.h>
+#include <ddk/platform-defs.h>
+#include <ddktl/device.h>
+#include <ddktl/fidl.h>
+#include <fbl/alloc_checker.h>
+#include <fbl/auto_call.h>
+#include <fuchsia/device/power/test/llcpp/fidl.h>
+#include <lib/fdio/fd.h>
+#include <lib/fdio/fdio.h>
+#include <lib/fdio/namespace.h>
+#include <lib/fdio/spawn.h>
+#include <lib/fdio/unsafe.h>
+#include <lib/fdio/watcher.h>
+
+using llcpp::fuchsia::device::DevicePowerStateInfo;
+using llcpp::fuchsia::device::power::test::TestDevice;
+
+class TestPowerDriverChild;
+using DeviceType = ddk::Device<TestPowerDriverChild, ddk::Unbindable, ddk::Messageable>;
+class TestPowerDriverChild : public DeviceType,
+                             public TestDevice::Interface {
+ public:
+  TestPowerDriverChild(zx_device_t* parent) : DeviceType(parent) {}
+  static zx_status_t Create(void* ctx, zx_device_t* device);
+  zx_status_t Bind();
+  void DdkUnbind() {
+    DdkRemove();
+  }
+
+  void AddDeviceWithPowerArgs(::fidl::VectorView<DevicePowerStateInfo> info,
+                                     AddDeviceWithPowerArgsCompleter::Sync completer) override;
+
+  zx_status_t DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn) {
+    DdkTransaction transaction(txn);
+    ::llcpp::fuchsia::device::power::test::TestDevice::Dispatch(this, msg, &transaction);
+    return transaction.Status();
+  }
+
+  void DdkRelease() { delete this; }
+};
+
+void TestPowerDriverChild::AddDeviceWithPowerArgs(::fidl::VectorView<DevicePowerStateInfo> info,
+                                                  AddDeviceWithPowerArgsCompleter::Sync completer) {
+  ::llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Result response;
+  fbl::AllocChecker ac;
+  auto child2 = fbl::make_unique_checked<TestPowerDriverChild>(&ac, this->parent());
+  if (!ac.check()) {
+    response.set_err(ZX_ERR_NO_MEMORY);
+    completer.Reply(std::move(response));
+    return;
+  }
+
+  auto state_info = info.data();
+  auto states = std::make_unique<device_power_state_info_t[]>(info.count());
+  auto count = static_cast<uint8_t>(info.count());
+  for (uint8_t i = 0; i < count; i++) {
+    states[i].state_id = static_cast<fuchsia_device_DevicePowerState>(state_info[i].state_id);
+    states[i].restore_latency = state_info[i].restore_latency;
+    states[i].wakeup_capable = state_info[i].wakeup_capable;
+    states[i].system_wake_state = state_info[i].system_wake_state;
+  }
+  zx_status_t status = child2->DdkAdd("test-power-child-2", 0, nullptr, 0, 0, nullptr,
+                       ZX_HANDLE_INVALID, states.get(), count);
+  if (status != ZX_OK) {
+    response.set_err(status);
+  } else {
+    response.set_response(
+        llcpp::fuchsia::device::power::test::TestDevice_AddDeviceWithPowerArgs_Response{});
+  }
+  completer.Reply(std::move(response));
+}
+
+zx_status_t TestPowerDriverChild::Bind() {
+  return DdkAdd("power-test-child");
+}
+
+zx_status_t TestPowerDriverChild::Create(void* ctx, zx_device_t* device) {
+  fbl::AllocChecker ac;
+  auto dev = fbl::make_unique_checked<TestPowerDriverChild>(&ac, device);
+
+  if (!ac.check()) {
+    return ZX_ERR_NO_MEMORY;
+  }
+  auto status = dev->Bind();
+  if (status == ZX_OK) {
+    // devmgr is now in charge of the memory for dev
+    __UNUSED auto ptr = dev.release();
+  }
+  return status;
+}
+
+static zx_driver_ops_t test_power_child_driver_ops = []() -> zx_driver_ops_t {
+  zx_driver_ops_t ops;
+  ops.version = DRIVER_OPS_VERSION;
+  ops.bind = TestPowerDriverChild::Create;
+  return ops;
+}();
+
+// clang-format off
+ZIRCON_DRIVER_BEGIN(TestPowerChild, test_power_child_driver_ops, "zircon", "0.1", 1)
+  BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_TEST_POWER_CHILD),
+ZIRCON_DRIVER_END(TestPowerChild)
+// clang-format on
diff --git a/zircon/system/dev/test/ddk-power/test-driver.cc b/zircon/system/dev/test/ddk-power/test-driver.cc
new file mode 100644
index 0000000..b6eabbe7
--- /dev/null
+++ b/zircon/system/dev/test/ddk-power/test-driver.cc
@@ -0,0 +1,55 @@
+// Copyright 2019 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 <ddk/binding.h>
+#include <ddk/debug.h>
+#include <ddk/device.h>
+#include <ddk/driver.h>
+#include <ddk/platform-defs.h>
+#include <ddktl/device.h>
+#include <ddktl/protocol/empty-protocol.h>
+#include <fbl/alloc_checker.h>
+
+class TestPowerDriver;
+using DeviceType = ddk::Device<TestPowerDriver, ddk::Unbindable>;
+class TestPowerDriver : public DeviceType,
+                                    public ddk::EmptyProtocol<ZX_PROTOCOL_TEST_POWER_CHILD> {
+ public:
+  TestPowerDriver(zx_device_t* parent) : DeviceType(parent) {}
+  zx_status_t Bind();
+  void DdkUnbind() { DdkRemove(); }
+  void DdkRelease() { delete this; }
+};
+
+zx_status_t TestPowerDriver::Bind() {
+  return DdkAdd("power-test");
+}
+
+zx_status_t test_power_hook_bind(void* ctx, zx_device_t* device) {
+  fbl::AllocChecker ac;
+  auto dev = fbl::make_unique_checked<TestPowerDriver>(&ac, device);
+  if (!ac.check()) {
+    return ZX_ERR_NO_MEMORY;
+  }
+  auto status = dev->Bind();
+  if (status == ZX_OK) {
+    // devmgr is now in charge of the memory for dev
+    __UNUSED auto ptr = dev.release();
+  }
+  return status;
+}
+
+static zx_driver_ops_t test_power_hook_driver_ops = []() -> zx_driver_ops_t {
+  zx_driver_ops_t ops;
+  ops.version = DRIVER_OPS_VERSION;
+  ops.bind = test_power_hook_bind;
+  return ops;
+}();
+
+// clang-format off
+ZIRCON_DRIVER_BEGIN(TestPower, test_power_hook_driver_ops, "zircon", "0.1", 2)
+    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_TEST),
+    BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_POWER_TEST),
+ZIRCON_DRIVER_END(TestPower)
+// clang-format on
diff --git a/zircon/system/dev/test/ddk-power/test.cc b/zircon/system/dev/test/ddk-power/test.cc
new file mode 100644
index 0000000..707e51c
--- /dev/null
+++ b/zircon/system/dev/test/ddk-power/test.cc
@@ -0,0 +1,260 @@
+// Copyright 2019 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 <ddk/platform-defs.h>
+#include <fuchsia/device/c/fidl.h>
+#include <fuchsia/device/manager/c/fidl.h>
+#include <fuchsia/device/power/test/llcpp/fidl.h>
+#include <fuchsia/device/llcpp/fidl.h>
+#include <lib/driver-integration-test/fixture.h>
+#include <lib/fdio/fdio.h>
+#include <zircon/processargs.h>
+#include <zircon/syscalls.h>
+#include <zxtest/zxtest.h>
+
+using driver_integration_test::IsolatedDevmgr;
+using llcpp::fuchsia::device::DevicePowerStateInfo;
+using llcpp::fuchsia::device::DevicePowerState;
+using llcpp::fuchsia::device::MAX_DEVICE_POWER_STATES;
+using llcpp::fuchsia::device::power::test::TestDevice;
+
+TEST(DeviceControllerIntegrationTest, InvalidDevicePowerCaps_Less) {
+  IsolatedDevmgr devmgr;
+  IsolatedDevmgr::Args args;
+  args.load_drivers.push_back("/boot/driver/ddk-power-test.so");
+  args.load_drivers.push_back("/boot/driver/ddk-power-test-child.so");
+
+  board_test::DeviceEntry dev = {};
+  dev.vid = PDEV_VID_TEST;
+  dev.pid = PDEV_PID_POWER_TEST;
+  dev.did = 0;
+  args.device_list.push_back(dev);
+
+  zx_status_t status = IsolatedDevmgr::Create(&args, &devmgr);
+  ASSERT_OK(status);
+  fbl::unique_fd parent_fd, child_fd;
+  ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(
+      devmgr.devfs_root(), "sys/platform/11:0b:0/power-test", &parent_fd));
+  ASSERT_GT(parent_fd.get(), 0);
+
+  ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(
+      devmgr.devfs_root(), "sys/platform/11:0b:0/power-test/power-test-child",
+      &child_fd));
+  ASSERT_GT(child_fd.get(), 0);
+
+  zx::channel child_device_handle;
+  ASSERT_OK(
+      fdio_get_service_handle(child_fd.release(), child_device_handle.reset_and_get_address()));
+  ASSERT_NE(child_device_handle.get(), ZX_HANDLE_INVALID);
+
+  DevicePowerStateInfo states[1];
+  states[0].state_id = DevicePowerState::DEVICE_POWER_STATE_D1;
+  states[0].is_supported = true;
+  auto power_states = ::fidl::VectorView<DevicePowerStateInfo>(1,
+                       reinterpret_cast<DevicePowerStateInfo *>(states));
+  auto response =
+    TestDevice::Call::AddDeviceWithPowerArgs(zx::unowned_channel(child_device_handle),
+    power_states);
+  ASSERT_OK(response.status());
+  zx_status_t call_status = ZX_OK;
+  if (response->result.is_err()) {
+    call_status = response->result.err();
+  }
+  ASSERT_STATUS(call_status, ZX_ERR_INVALID_ARGS);
+}
+
+TEST(DeviceControllerIntegrationTest, InvalidDevicePowerCaps_More) {
+  IsolatedDevmgr devmgr;
+  IsolatedDevmgr::Args args;
+  args.load_drivers.push_back("/boot/driver/ddk-power-test.so");
+  args.load_drivers.push_back("/boot/driver/ddk-power-test-child.so");
+
+  board_test::DeviceEntry dev = {};
+  dev.vid = PDEV_VID_TEST;
+  dev.pid = PDEV_PID_POWER_TEST;
+  dev.did = 0;
+  args.device_list.push_back(dev);
+
+  zx_status_t status = IsolatedDevmgr::Create(&args, &devmgr);
+  ASSERT_OK(status);
+  fbl::unique_fd parent_fd, child_fd;
+  ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(
+      devmgr.devfs_root(), "sys/platform/11:0b:0/power-test", &parent_fd));
+  ASSERT_GT(parent_fd.get(), 0);
+
+  ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(
+      devmgr.devfs_root(), "sys/platform/11:0b:0/power-test/power-test-child",
+      &child_fd));
+  ASSERT_GT(child_fd.get(), 0);
+
+  zx::channel child_device_handle;
+  ASSERT_OK(
+      fdio_get_service_handle(child_fd.release(), child_device_handle.reset_and_get_address()));
+  ASSERT_NE(child_device_handle.get(), ZX_HANDLE_INVALID);
+
+  DevicePowerStateInfo states[MAX_DEVICE_POWER_STATES + 1];
+  for (uint8_t i = 0; i < MAX_DEVICE_POWER_STATES + 1; i++) {
+    states[i].state_id = DevicePowerState::DEVICE_POWER_STATE_D1;
+    states[i].is_supported = true;
+  }
+  auto power_states = ::fidl::VectorView<DevicePowerStateInfo>(MAX_DEVICE_POWER_STATES + 1,
+    reinterpret_cast<DevicePowerStateInfo *>(states));
+  auto response =
+    TestDevice::Call::AddDeviceWithPowerArgs(zx::unowned_channel(child_device_handle),
+                                             power_states);
+  ASSERT_OK(response.status());
+  zx_status_t call_status = ZX_OK;
+  if (response->result.is_err()) {
+    call_status = response->result.err();
+  }
+
+  ASSERT_STATUS(call_status, ZX_ERR_INVALID_ARGS);
+}
+
+TEST(DeviceControllerIntegrationTest, InvalidDevicePowerCaps_MissingRequired) {
+  IsolatedDevmgr devmgr;
+  IsolatedDevmgr::Args args;
+  args.load_drivers.push_back("/boot/driver/ddk-power-test.so");
+  args.load_drivers.push_back("/boot/driver/ddk-power-test-child.so");
+
+  board_test::DeviceEntry dev = {};
+  dev.vid = PDEV_VID_TEST;
+  dev.pid = PDEV_PID_POWER_TEST;
+  dev.did = 0;
+  args.device_list.push_back(dev);
+
+  zx_status_t status = IsolatedDevmgr::Create(&args, &devmgr);
+  ASSERT_OK(status);
+  fbl::unique_fd parent_fd, child_fd;
+  ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(
+      devmgr.devfs_root(), "sys/platform/11:0b:0/power-test", &parent_fd));
+  ASSERT_GT(parent_fd.get(), 0);
+
+  ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(
+      devmgr.devfs_root(), "sys/platform/11:0b:0/power-test/power-test-child",
+      &child_fd));
+  ASSERT_GT(child_fd.get(), 0);
+
+  zx::channel child_device_handle;
+  ASSERT_OK(
+      fdio_get_service_handle(child_fd.release(), child_device_handle.reset_and_get_address()));
+  ASSERT_NE(child_device_handle.get(), ZX_HANDLE_INVALID);
+
+  DevicePowerStateInfo states[MAX_DEVICE_POWER_STATES];
+  for (uint8_t i = 0; i < MAX_DEVICE_POWER_STATES; i++) {
+    //Missing D0 and D3COLD
+    states[i].state_id = DevicePowerState::DEVICE_POWER_STATE_D1;
+    states[i].is_supported = true;
+  }
+  auto power_states = ::fidl::VectorView<DevicePowerStateInfo>(MAX_DEVICE_POWER_STATES,
+                                             reinterpret_cast<DevicePowerStateInfo *>(states));
+  auto response =
+    TestDevice::Call::AddDeviceWithPowerArgs(zx::unowned_channel(child_device_handle),
+                                             power_states);
+  ASSERT_OK(response.status());
+  zx_status_t call_status = ZX_OK;
+  if (response->result.is_err()) {
+    call_status = response->result.err();
+  }
+
+  ASSERT_STATUS(call_status, ZX_ERR_INVALID_ARGS);
+}
+
+TEST(DeviceControllerIntegrationTest, InvalidDevicePowerCaps_DuplicateCaps) {
+  IsolatedDevmgr devmgr;
+  IsolatedDevmgr::Args args;
+  args.load_drivers.push_back("/boot/driver/ddk-power-test.so");
+  args.load_drivers.push_back("/boot/driver/ddk-power-test-child.so");
+
+  board_test::DeviceEntry dev = {};
+  dev.vid = PDEV_VID_TEST;
+  dev.pid = PDEV_PID_POWER_TEST;
+  dev.did = 0;
+  args.device_list.push_back(dev);
+
+  zx_status_t status = IsolatedDevmgr::Create(&args, &devmgr);
+  ASSERT_OK(status);
+  fbl::unique_fd parent_fd, child_fd;
+  ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(
+      devmgr.devfs_root(), "sys/platform/11:0b:0/power-test", &parent_fd));
+  ASSERT_GT(parent_fd.get(), 0);
+
+  ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(
+      devmgr.devfs_root(), "sys/platform/11:0b:0/power-test/power-test-child",
+      &child_fd));
+  ASSERT_GT(child_fd.get(), 0);
+
+  zx::channel child_device_handle;
+  ASSERT_OK(
+      fdio_get_service_handle(child_fd.release(), child_device_handle.reset_and_get_address()));
+  ASSERT_NE(child_device_handle.get(), ZX_HANDLE_INVALID);
+
+  DevicePowerStateInfo states[MAX_DEVICE_POWER_STATES];
+  states[0].state_id = DevicePowerState::DEVICE_POWER_STATE_D0;
+  states[0].is_supported = true;
+  states[1].state_id = DevicePowerState::DEVICE_POWER_STATE_D3COLD;
+  states[1].is_supported = true;
+  // Repeat
+  states[2].state_id = DevicePowerState::DEVICE_POWER_STATE_D3COLD;
+  states[2].is_supported = true;
+  auto power_states = ::fidl::VectorView<DevicePowerStateInfo>(MAX_DEVICE_POWER_STATES,
+                                             reinterpret_cast<DevicePowerStateInfo *>(states));
+  auto response =
+    TestDevice::Call::AddDeviceWithPowerArgs(zx::unowned_channel(child_device_handle),
+                                             power_states);
+  ASSERT_OK(response.status());
+  zx_status_t call_status = ZX_OK;
+  if (response->result.is_err()) {
+    call_status = response->result.err();
+  }
+
+  ASSERT_STATUS(call_status, ZX_ERR_INVALID_ARGS);
+}
+
+TEST(DeviceControllerIntegrationTest, GetDevicePowerCaps_Success) {
+  IsolatedDevmgr devmgr;
+  IsolatedDevmgr::Args args;
+  args.load_drivers.push_back("/boot/driver/ddk-power-test.so");
+  args.load_drivers.push_back("/boot/driver/ddk-power-test-child.so");
+
+  board_test::DeviceEntry dev = {};
+  dev.vid = PDEV_VID_TEST;
+  dev.pid = PDEV_PID_POWER_TEST;
+  dev.did = 0;
+  args.device_list.push_back(dev);
+
+  zx_status_t status = IsolatedDevmgr::Create(&args, &devmgr);
+  ASSERT_OK(status);
+  fbl::unique_fd parent_fd, child_fd;
+  ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(
+      devmgr.devfs_root(), "sys/platform/11:0b:0/power-test", &parent_fd));
+  ASSERT_GT(parent_fd.get(), 0);
+
+  ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(
+      devmgr.devfs_root(), "sys/platform/11:0b:0/power-test/power-test-child",
+      &child_fd));
+  ASSERT_GT(child_fd.get(), 0);
+
+  zx::channel child_device_handle;
+  ASSERT_OK(
+      fdio_get_service_handle(child_fd.release(), child_device_handle.reset_and_get_address()));
+  ASSERT_NE(child_device_handle.get(), ZX_HANDLE_INVALID);
+
+  DevicePowerStateInfo states[2];
+  states[0].state_id = DevicePowerState::DEVICE_POWER_STATE_D0;
+  states[0].is_supported = true;
+  states[1].state_id = DevicePowerState::DEVICE_POWER_STATE_D3COLD;
+  states[1].is_supported = true;
+  auto power_states = ::fidl::VectorView<DevicePowerStateInfo>(2,
+                       reinterpret_cast<DevicePowerStateInfo *>(states));
+  auto response = TestDevice::Call::AddDeviceWithPowerArgs(zx::unowned_channel(child_device_handle),
+                                                           power_states);
+  ASSERT_OK(response.status());
+  zx_status_t call_status = ZX_OK;
+  if (response->result.is_err()) {
+    call_status = response->result.err();
+  }
+
+  ASSERT_STATUS(call_status, ZX_OK);
+}
diff --git a/zircon/system/dev/test/ddk-power/test.fidl b/zircon/system/dev/test/ddk-power/test.fidl
new file mode 100644
index 0000000..8936ed1
--- /dev/null
+++ b/zircon/system/dev/test/ddk-power/test.fidl
@@ -0,0 +1,13 @@
+// Copyright 2019 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.
+
+library fuchsia.device.power.test;
+
+using zx;
+using fuchsia.device;
+
+protocol TestDevice {
+    /// Add Test Device with some powerargs
+    AddDeviceWithPowerArgs(vector<fuchsia.device.DevicePowerStateInfo> info) -> () error zx.status;
+};
diff --git a/zircon/system/dev/test/ddk-runcompatibilityhook/test-driver-child.cc b/zircon/system/dev/test/ddk-runcompatibilityhook/test-driver-child.cc
index ce74b92..dfe9e64 100644
--- a/zircon/system/dev/test/ddk-runcompatibilityhook/test-driver-child.cc
+++ b/zircon/system/dev/test/ddk-runcompatibilityhook/test-driver-child.cc
@@ -66,6 +66,6 @@
 
 // clang-format off
 ZIRCON_DRIVER_BEGIN(TestCompatibilityHookChild, test_compatibility_hook_child_driver_ops, "zircon", "0.1", 1)
-    BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_TEST_CHILD),
+    BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_TEST_COMPAT_CHILD),
 ZIRCON_DRIVER_END(TestCompatibilityHookChild)
     // clang-format on
diff --git a/zircon/system/dev/test/ddk-runcompatibilityhook/test-driver.cc b/zircon/system/dev/test/ddk-runcompatibilityhook/test-driver.cc
index e4680eb..91d4b26 100644
--- a/zircon/system/dev/test/ddk-runcompatibilityhook/test-driver.cc
+++ b/zircon/system/dev/test/ddk-runcompatibilityhook/test-driver.cc
@@ -17,7 +17,7 @@
 class TestCompatibilityHookDriver;
 using DeviceType = ddk::Device<TestCompatibilityHookDriver, ddk::Unbindable>;
 class TestCompatibilityHookDriver : public DeviceType,
-                                    public ddk::EmptyProtocol<ZX_PROTOCOL_TEST_CHILD> {
+                                    public ddk::EmptyProtocol<ZX_PROTOCOL_TEST_COMPAT_CHILD> {
  public:
   TestCompatibilityHookDriver(zx_device_t* parent) : DeviceType(parent) {}
   zx_status_t Bind();
diff --git a/zircon/system/fidl/fuchsia-device/controller.fidl b/zircon/system/fidl/fuchsia-device/controller.fidl
index 15fb0e4d..ff685aa 100644
--- a/zircon/system/fidl/fuchsia-device/controller.fidl
+++ b/zircon/system/fidl/fuchsia-device/controller.fidl
@@ -14,6 +14,10 @@
 const uint64 MAX_DRIVER_NAME_LEN = 32;
 /// Maximum length for a driver path
 const uint64 MAX_DRIVER_PATH_LEN = 1024;
+/// Maximum device power states. In future this should account
+/// for performant states.
+const uint32 MAX_DEVICE_POWER_STATES = 5;
+const uint32 MIN_DEVICE_POWER_STATES = 2; // D0 & D3COLD
 
 /// Signal that will be active on a device event handle if the device's read() method
 /// will return data.
@@ -32,6 +36,69 @@
 /// This is primarily used by the PTY support.
 const uint32 DEVICE_SIGNAL_HANGUP = 0x10000000; // ZX_USER_SIGNAL_4
 
+
+enum DevicePowerState : uint8 {
+  /// Mandatory Working state. Device is fully functional, can take I/O,
+  /// issue interrrupts. This state is mandatory for all devices
+  /// The device enters into this state by default, when powered on.
+  DEVICE_POWER_STATE_D0 = 0;
+  /// [OPTIONAL] Device is not working when in this state. It cannot process
+  /// I/O nor issue interrupts, unless it is armed for some special interrupts
+  /// that can wake up the system/device. When in this state, the restore time
+  /// of getting back to working state is less than other low-power states
+  /// Power savings in this state are lesser than other low power states.
+  /// Device may retain some hardware context and full initialization
+  /// may not be needed when resuming from this state.
+  DEVICE_POWER_STATE_D1 = 1;
+  /// [OPTIONAL] Device is not working when in this state. It cannot process
+  /// I/O nor issue interrupts, unless it is armed for some special interrupts
+  /// that can wake up the system/device. When in this state, the restore time
+  /// of getting back to working state is more than DEVICE_POWER_STATE_D1 and
+  /// less than restore time of getting back from DEVICE_POWER_STATE_D3HOT,
+  /// DEVICE_POWER_STATE_D3COLD. Power savings in this state are lesser
+  /// than DEVICE_POWER_STATE_D3COLD, DEVICE_POWER_STATE_D3HOT.
+  /// Device may retain some hardware context and full initialization
+  /// may not be needed when resuming from this state.
+  DEVICE_POWER_STATE_D2 = 2;
+  /// [OPTIONAL] Device is not working when in this state. It cannot process
+  /// I/O nor issue interrupts, unless it is armed for some special interrupts
+  /// that can wake up the system/device. When in this state, the restore time
+  /// of getting back to working state is more than DEVICE_POWER_STATE_D1,
+  /// DEVICE_POWER_STATE_D3HOT and less than restore time of getting back from
+  /// DEVICE_POWER_STATE_D3COLD. Power savings in this state are lesser
+  /// than DEVICE_POWER_STATE_D3COLD. Device has no context and full initialization
+  /// by the device driver when resuming from this state.
+  /// Although the device is completely off, it is still powered on and is enumerable.
+  DEVICE_POWER_STATE_D3HOT = 3;
+  /// [MANDATORY] Device is not working when in this state. It cannot process
+  /// I/O nor issue interrupts, unless it is armed for some special interrupts
+  /// that can wake up the system/device. When in this state, the restore time
+  /// of getting back to working state is more than all other low power states.
+  /// Power savings are more compared to all other low-power states.
+  /// Device has no context and full initialization by the device driver when
+  /// resuming from this state. In this state, the power to this device is turned off.
+  /// Device may be powered by other auxiliary supplies to support wake capability.
+  DEVICE_POWER_STATE_D3COLD = 4;
+};
+
+//TODO(ravoorir): This should be table when the Controller protocol moves off of
+//simple layout.
+struct DevicePowerStateInfo {
+  DevicePowerState state_id;
+
+  /// Is this state supported?
+  bool is_supported;
+
+  /// Restore time for coming out of this state to working D0 state.
+  int64 restore_latency;
+
+  /// Is this device wakeup_capable?
+  bool wakeup_capable;
+
+  /// Deepest system system sleep state that the device can wake the system from.
+  int32 system_wake_state;
+};
+
 /// Interface for manipulating a device in a devhost
 [Layout = "Simple"]
 protocol Controller {
@@ -64,9 +131,9 @@
     /// Debug command: execute the device's resume hook
     DebugResume() -> (zx.status status);
 
-    /// RunCompatibilityTests: Runs compatibility tests for the driver that binds to this device.
-    /// The |hook_wait_time| is the time that the driver expects to take for each device hook in
-    /// nanoseconds.
+    /// Runs compatibility tests for the driver that binds to this device.
+    /// The |hook_wait_time| is the time that the driver expects to take for
+    /// each device hook in nanoseconds.
     /// Returns whether the driver passed the compatibility check.
     RunCompatibilityTests(int64 hook_wait_time) -> (uint32 status);
 };
diff --git a/zircon/system/fidl/fuchsia-device/gen/llcpp/include/fuchsia/device/llcpp/fidl.h b/zircon/system/fidl/fuchsia-device/gen/llcpp/include/fuchsia/device/llcpp/fidl.h
index f9e241e..7abb684 100644
--- a/zircon/system/fidl/fuchsia-device/gen/llcpp/include/fuchsia/device/llcpp/fidl.h
+++ b/zircon/system/fidl/fuchsia-device/gen/llcpp/include/fuchsia/device/llcpp/fidl.h
@@ -24,6 +24,16 @@
 struct NameProvider_GetDeviceName_Response;
 struct NameProvider_GetDeviceName_Result;
 class NameProvider;
+enum class DevicePowerState : uint8_t {
+  DEVICE_POWER_STATE_D0 = 0u,
+  DEVICE_POWER_STATE_D1 = 1u,
+  DEVICE_POWER_STATE_D2 = 2u,
+  DEVICE_POWER_STATE_D3HOT = 3u,
+  DEVICE_POWER_STATE_D3COLD = 4u,
+};
+
+
+struct DevicePowerStateInfo;
 
 extern "C" const fidl_type_t fuchsia_device_ControllerBindRequestTable;
 extern "C" const fidl_type_t fuchsia_device_ControllerBindResponseTable;
@@ -740,16 +750,16 @@
     // Caller provides the backing storage for FIDL message via request and response buffers.
     UnownedResultOf::DebugResume DebugResume(::fidl::BytePart _response_buffer);
 
-    // RunCompatibilityTests: Runs compatibility tests for the driver that binds to this device.
-    // The |hook_wait_time| is the time that the driver expects to take for each device hook in
-    // nanoseconds.
+    // Runs compatibility tests for the driver that binds to this device.
+    // The |hook_wait_time| is the time that the driver expects to take for
+    // each device hook in nanoseconds.
     // Returns whether the driver passed the compatibility check.
     // Allocates 48 bytes of message buffer on the stack. No heap allocation necessary.
     ResultOf::RunCompatibilityTests RunCompatibilityTests(int64_t hook_wait_time);
 
-    // RunCompatibilityTests: Runs compatibility tests for the driver that binds to this device.
-    // The |hook_wait_time| is the time that the driver expects to take for each device hook in
-    // nanoseconds.
+    // Runs compatibility tests for the driver that binds to this device.
+    // The |hook_wait_time| is the time that the driver expects to take for
+    // each device hook in nanoseconds.
     // Returns whether the driver passed the compatibility check.
     // Caller provides the backing storage for FIDL message via request and response buffers.
     UnownedResultOf::RunCompatibilityTests RunCompatibilityTests(::fidl::BytePart _request_buffer, int64_t hook_wait_time, ::fidl::BytePart _response_buffer);
@@ -849,16 +859,16 @@
     // Caller provides the backing storage for FIDL message via request and response buffers.
     static UnownedResultOf::DebugResume DebugResume(zx::unowned_channel _client_end, ::fidl::BytePart _response_buffer);
 
-    // RunCompatibilityTests: Runs compatibility tests for the driver that binds to this device.
-    // The |hook_wait_time| is the time that the driver expects to take for each device hook in
-    // nanoseconds.
+    // Runs compatibility tests for the driver that binds to this device.
+    // The |hook_wait_time| is the time that the driver expects to take for
+    // each device hook in nanoseconds.
     // Returns whether the driver passed the compatibility check.
     // Allocates 48 bytes of message buffer on the stack. No heap allocation necessary.
     static ResultOf::RunCompatibilityTests RunCompatibilityTests(zx::unowned_channel _client_end, int64_t hook_wait_time);
 
-    // RunCompatibilityTests: Runs compatibility tests for the driver that binds to this device.
-    // The |hook_wait_time| is the time that the driver expects to take for each device hook in
-    // nanoseconds.
+    // Runs compatibility tests for the driver that binds to this device.
+    // The |hook_wait_time| is the time that the driver expects to take for
+    // each device hook in nanoseconds.
     // Returns whether the driver passed the compatibility check.
     // Caller provides the backing storage for FIDL message via request and response buffers.
     static UnownedResultOf::RunCompatibilityTests RunCompatibilityTests(zx::unowned_channel _client_end, ::fidl::BytePart _request_buffer, int64_t hook_wait_time, ::fidl::BytePart _response_buffer);
@@ -904,9 +914,9 @@
     // Debug command: execute the device's resume hook
     static ::fidl::DecodeResult<DebugResumeResponse> DebugResume(zx::unowned_channel _client_end, ::fidl::BytePart response_buffer);
 
-    // RunCompatibilityTests: Runs compatibility tests for the driver that binds to this device.
-    // The |hook_wait_time| is the time that the driver expects to take for each device hook in
-    // nanoseconds.
+    // Runs compatibility tests for the driver that binds to this device.
+    // The |hook_wait_time| is the time that the driver expects to take for
+    // each device hook in nanoseconds.
     // Returns whether the driver passed the compatibility check.
     static ::fidl::DecodeResult<RunCompatibilityTestsResponse> RunCompatibilityTests(zx::unowned_channel _client_end, ::fidl::DecodedMessage<RunCompatibilityTestsRequest> params, ::fidl::BytePart response_buffer);
 
@@ -1359,18 +1369,48 @@
 
 };
 
+constexpr uint32_t MIN_DEVICE_POWER_STATES = 2u;
+
 // Maximum length for a driver path
 constexpr uint64_t MAX_DRIVER_PATH_LEN = 1024u;
 
 // Maxmium length for a driver name
 constexpr uint64_t MAX_DRIVER_NAME_LEN = 32u;
 
+// Maximum device power states. In future this should account
+// for performant states.
+constexpr uint32_t MAX_DEVICE_POWER_STATES = 5u;
+
 // Maximum length of a device path
 constexpr uint64_t MAX_DEVICE_PATH_LEN = 1024u;
 
 // Maxmium length for a device name
 constexpr uint64_t MAX_DEVICE_NAME_LEN = 32u;
 
+extern "C" const fidl_type_t fuchsia_device_DevicePowerStateInfoTable;
+
+struct DevicePowerStateInfo {
+  static constexpr const fidl_type_t* Type = &fuchsia_device_DevicePowerStateInfoTable;
+  static constexpr uint32_t MaxNumHandles = 0;
+  static constexpr uint32_t PrimarySize = 24;
+  [[maybe_unused]]
+  static constexpr uint32_t MaxOutOfLine = 0;
+
+  DevicePowerState state_id = {};
+
+  // Is this state supported?
+  bool is_supported = {};
+
+  // Restore time for coming out of this state to working D0 state.
+  int64_t restore_latency = {};
+
+  // Is this device wakeup_capable?
+  bool wakeup_capable = {};
+
+  // Deepest system system sleep state that the device can wake the system from.
+  int32_t system_wake_state = {};
+};
+
 // Signal that will be active on a device event handle if the device's write() method
 // will accept data.
 constexpr uint32_t DEVICE_SIGNAL_WRITABLE = 67108864u;
@@ -1535,4 +1575,14 @@
     == ::llcpp::fuchsia::device::NameProvider::GetDeviceNameResponse::PrimarySize);
 static_assert(offsetof(::llcpp::fuchsia::device::NameProvider::GetDeviceNameResponse, result) == 16);
 
+template <>
+struct IsFidlType<::llcpp::fuchsia::device::DevicePowerStateInfo> : public std::true_type {};
+static_assert(std::is_standard_layout_v<::llcpp::fuchsia::device::DevicePowerStateInfo>);
+static_assert(offsetof(::llcpp::fuchsia::device::DevicePowerStateInfo, state_id) == 0);
+static_assert(offsetof(::llcpp::fuchsia::device::DevicePowerStateInfo, is_supported) == 1);
+static_assert(offsetof(::llcpp::fuchsia::device::DevicePowerStateInfo, restore_latency) == 8);
+static_assert(offsetof(::llcpp::fuchsia::device::DevicePowerStateInfo, wakeup_capable) == 16);
+static_assert(offsetof(::llcpp::fuchsia::device::DevicePowerStateInfo, system_wake_state) == 20);
+static_assert(sizeof(::llcpp::fuchsia::device::DevicePowerStateInfo) == ::llcpp::fuchsia::device::DevicePowerStateInfo::PrimarySize);
+
 }  // namespace fidl
diff --git a/zircon/system/ulib/ddk/BUILD.gn b/zircon/system/ulib/ddk/BUILD.gn
index 1d16e83..8d0ac1b 100644
--- a/zircon/system/ulib/ddk/BUILD.gn
+++ b/zircon/system/ulib/ddk/BUILD.gn
@@ -16,6 +16,7 @@
     "ddk/binding.h",
     "ddk/debug.h",
     "ddk/device.h",
+    "ddk/device-power-states.h",
     "ddk/driver.h",
     "ddk/io-buffer.h",
     "ddk/metadata.h",
@@ -47,6 +48,8 @@
     # <ddk/usb-peripheral-config.h> has #include <fuchsia/hardware/usb/peripheral/c/fidl.h>.
     "$zx/system/fidl/fuchsia-hardware-usb-peripheral:c.headers",
 
+    "$zx/system/fidl/fuchsia-device:c.headers",
+
     # TODO(BLD-353): Exporting this dependency causes problems in the legacy
     # build.  Instead, users of the ddk library that actually use this header
     # have to also express a dependency on the trace library.
diff --git a/zircon/system/ulib/ddk/include/ddk/device-power-states.h b/zircon/system/ulib/ddk/include/ddk/device-power-states.h
new file mode 100644
index 0000000..d3220c2
--- /dev/null
+++ b/zircon/system/ulib/ddk/include/ddk/device-power-states.h
@@ -0,0 +1,10 @@
+// Copyright 2019 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 DEVICE_POWER_STATES_H_
+#define DEVICE_POWER_STATES_H_
+
+#include <fuchsia/device/c/fidl.h>
+
+#endif  // DEVICE_POWER_STATES_H_
diff --git a/zircon/system/ulib/ddk/include/ddk/driver.h b/zircon/system/ulib/ddk/include/ddk/driver.h
index e24a066..c23d47f 100644
--- a/zircon/system/ulib/ddk/include/ddk/driver.h
+++ b/zircon/system/ulib/ddk/include/ddk/driver.h
@@ -10,6 +10,8 @@
 #include <zircon/compiler.h>
 #include <stdint.h>
 
+#include <ddk/device-power-states.h>
+
 __BEGIN_CDECLS
 
 typedef struct zx_device zx_device_t;
@@ -75,6 +77,17 @@
 };
 
 // Device Manager API
+
+typedef struct device_power_state_info {
+  fuchsia_device_DevicePowerState state_id;
+  // Restore time for coming out of this state to working D0 state.
+  zx_duration_t restore_latency;
+  // Is this device wakeup_capable?
+  bool wakeup_capable;
+  // Deepest system system sleep state that the device can wake the system from.
+  int32_t system_wake_state;
+} device_power_state_info_t;
+
 typedef struct device_add_args {
   // DEVICE_ADD_ARGS_VERSION
   uint64_t version;
@@ -97,12 +110,18 @@
   // Number of device properties
   uint32_t prop_count;
 
+  // List of power_states that the device supports.
+  // List cannot be more than MAX_DEVICE_POWER_STATES size.
+  const device_power_state_info_t* power_states;
+
+  // Number of power states in the list
+  uint8_t power_state_count;
+
   // Optional custom protocol for this device
   uint32_t proto_id;
 
   // Optional custom protocol operations for this device
   void* proto_ops;
-
   // Arguments used with DEVICE_ADD_MUST_ISOLATE
   // these will be passed to the create() driver op of
   // the proxy device in the new devhost
diff --git a/zircon/system/ulib/ddk/include/ddk/platform-defs.h b/zircon/system/ulib/ddk/include/ddk/platform-defs.h
index 22d23b5..08a062c 100644
--- a/zircon/system/ulib/ddk/include/ddk/platform-defs.h
+++ b/zircon/system/ulib/ddk/include/ddk/platform-defs.h
@@ -208,6 +208,7 @@
 #define PDEV_PID_PCI_TEST           8
 #define PDEV_PID_DDKFIDL_TEST       9
 #define PDEV_PID_COMPATIBILITY_TEST 10
+#define PDEV_PID_POWER_TEST         11
 
 #define PDEV_DID_TEST_PARENT        1
 #define PDEV_DID_TEST_CHILD_1       2
diff --git a/zircon/system/ulib/ddk/include/ddk/protodefs.h b/zircon/system/ulib/ddk/include/ddk/protodefs.h
index f513e41..c2ed215 100644
--- a/zircon/system/ulib/ddk/include/ddk/protodefs.h
+++ b/zircon/system/ulib/ddk/include/ddk/protodefs.h
@@ -88,7 +88,8 @@
 DDK_PROTOCOL_DEF(IHDA_DSP,       'piHD', "intel-hda-dsp", 0)
 DDK_PROTOCOL_DEF(AUDIO_CODEC,    'pAUC', "audio-codec", 0)
 DDK_PROTOCOL_DEF(TEST,           'pTST', "test", 0)
-DDK_PROTOCOL_DEF(TEST_CHILD,     'pTCC', "test-child", 0)
+DDK_PROTOCOL_DEF(TEST_COMPAT_CHILD, 'pTCC', "test-compat-child", 0)
+DDK_PROTOCOL_DEF(TEST_POWER_CHILD,  'pTPC', "test-power-child", 0)
 DDK_PROTOCOL_DEF(TEST_PARENT,    'pTSP', "test-parent", PF_NOPUB)
 DDK_PROTOCOL_DEF(PBUS,           'pPBU', "platform-bus", 0)
 DDK_PROTOCOL_DEF(PDEV,           'pPDV', "platform-dev", 0)
diff --git a/zircon/system/ulib/ddktl/include/ddktl/device.h b/zircon/system/ulib/ddktl/include/ddktl/device.h
index 4dfc653..c83c936 100644
--- a/zircon/system/ulib/ddktl/include/ddktl/device.h
+++ b/zircon/system/ulib/ddktl/include/ddktl/device.h
@@ -280,7 +280,9 @@
   zx_status_t DdkAdd(const char* name, uint32_t flags = 0, zx_device_prop_t* props = nullptr,
                      uint32_t prop_count = 0, uint32_t proto_id = 0,
                      const char* proxy_args = nullptr,
-                     zx_handle_t client_remote = ZX_HANDLE_INVALID) {
+                     zx_handle_t client_remote = ZX_HANDLE_INVALID,
+                     const device_power_state_info_t* power_states = nullptr,
+                     const uint8_t power_state_count = 0) {
     if (this->zxdev_ != nullptr) {
       return ZX_ERR_BAD_STATE;
     }
@@ -298,6 +300,8 @@
     args.proto_id = proto_id;
     args.proxy_args = proxy_args;
     args.client_remote = client_remote;
+    args.power_states = power_states;
+    args.power_state_count = power_state_count;
     AddProtocol(&args);
 
     return device_add(this->parent_, &args, &this->zxdev_);