This guide explains how to export a FIDL service from a parent driver and
utilize it in a child driver. This guide assumes familiarity with the following
concepts:

- [FIDL](https://fuchsia.dev/docs/development/languages/fidl/README)
- [Driver binding](https://fuchsia.dev/docs/concepts/drivers/driver_binding)
- [Write a minimal driver](https://fuchsia.dev/docs/development/drivers/developer_guide/write-a-minimal-dfv2-driver)
- [Driver communication](https://fuchsia.dev/docs/concepts/drivers/driver_communication)
- [C++ FIDL Bindings](https://fuchsia.dev/docs/development/languages/fidl/tutorials/cpp/README)

For a complete working example in the Fuchsia source tree, see the
[Zircon transport example](https://cs.opensource.google/fuchsia/fuchsia/+/main:/examples/drivers/transport/zircon/v2/).

## FIDL Protocol Definition

The following snippets utilize this FIDL definition:

    library fidl.examples.echo;

    const MAX_STRING_LENGTH uint64 = 32;

    @discoverable
    closed protocol Echo {
        /// Returns the input.
        strict EchoString(struct {
            value string:<MAX_STRING_LENGTH, optional>;
        }) -> (struct {
            response string:<MAX_STRING_LENGTH, optional>;
        });
    };

    service EchoService {
        echo client_end:Echo;
    };

## Parent Driver (The Server)

The parent driver implements the `fidl.examples.echo/Echo` protocol, serves
`fidl.examples.echo.EchoService` in its outgoing directory, and offers that
service when adding a child node.

### Driver Manifest

In the parent driver's manifest (`.dml`), declare and expose the FIDL service
capability:

    capabilities: [
        { service: "fidl.examples.echo.EchoService" },
    ],
    expose: [
        {
            service: "fidl.examples.echo.EchoService",
            from: "self",
        },
    ],

### Server Code

    #include <fidl/fidl.examples.echo/cpp/wire.h>
    #include <lib/driver/component/cpp/driver_base2.h>
    #include <lib/driver/component/cpp/driver_export2.h>
    #include <lib/driver/component/cpp/node_add_args.h>
    #include <lib/driver/logging/cpp/logger.h>

    // This class implements the fidl.examples.echo/Echo FIDL protocol using the
    // C++ Wire bindings.
    class ParentDriver : public fdf::DriverBase2,
                         public fidl::WireServer<fidl_examples_echo::Echo> {
     public:
      ParentDriver() : fdf::DriverBase2("parent") {}

      zx::result<> Start(fdf::DriverContext context) override {
        // Add the FIDL service we wish to export to our child to our outgoing
        // directory. When a connection is attempted we bind the server end of
        // the channel pair to our server implementation using the driver's default
        // dispatcher.
        fidl_examples_echo::EchoService::InstanceHandler handler({
            .echo = bindings_.CreateHandler(this, dispatcher(),
                                            fidl::kIgnoreBindingClosure),
        });

        zx::result result =
            outgoing()->AddService<fidl_examples_echo::EchoService>(std::move(handler));
        if (result.is_error()) {
          fdf::error("Failed to add EchoService to outgoing directory: {}", result);
          return result.take_error();
        }

        // Declare our outgoing service offer and add the child node. The driver
        // framework uses this offer to route the service to the child and
        // automatically populate node properties for binding.
        std::vector<fuchsia_driver_framework::NodeProperty2> properties = {};
        zx::result child_result = AddChild(
            "echo_child", properties,
            std::array{fdf::MakeOffer2<fidl_examples_echo::EchoService>()});
        if (child_result.is_error()) {
          fdf::error("Failed to add child node: {}", child_result);
          return child_result.take_error();
        }
        controller_.Bind(std::move(child_result.value()), dispatcher());

        return zx::ok();
      }

     private:
      // Implementation of the method required by our FIDL protocol.
      void EchoString(EchoStringRequestView request,
                      EchoStringCompleter::Sync& completer) override {
        completer.Reply(request->value);
      }

      fidl::WireClient<fuchsia_driver_framework::NodeController> controller_;
      // Ensures that the FIDL connections do not outlive the driver instance.
      fidl::ServerBindingGroup<fidl_examples_echo::Echo> bindings_;
    };

    FUCHSIA_DRIVER_EXPORT2(ParentDriver);

## Child Driver (The Client)

### Driver Manifest

In the child driver's manifest (`.dml`), declare that the driver uses the
service:

    use: [
        { service: "fidl.examples.echo.EchoService" },
    ],

Note that the child driver's bind rules are automatically generated from its
DML.

### Client Code

The following code snippet shows how a child driver that has bound to the parent
node connects to and calls the `Echo` protocol:

    #include <fidl/fidl.examples.echo/cpp/wire.h>
    #include <lib/driver/component/cpp/driver_base2.h>
    #include <lib/driver/component/cpp/driver_export2.h>
    #include <lib/driver/logging/cpp/logger.h>

    class ChildDriver : public fdf::DriverBase2 {
     public:
      ChildDriver() : fdf::DriverBase2("child") {}

      zx::result<> Start(fdf::DriverContext context) override {
        // Connect to the protocol offered by our parent driver through our incoming
        // namespace. We do not need to specify the service or protocol name string
        // because Connect is templated on the generated service member type.
        zx::result client_end =
            context.incoming().Connect<fidl_examples_echo::EchoService::Echo>();
        if (client_end.is_error()) {
          fdf::error("Failed to connect to Echo protocol: {}", client_end);
          return client_end.take_error();
        }

        // Turn the client side of the endpoint pair into a synchronous client.
        fidl::WireSyncClient client{std::move(client_end.value())};

        // Utilize our client to make calls.
        constexpr std::string_view kInput = "Test String";

        fidl::WireResult result =
            client->EchoString(fidl::StringView::FromExternal(kInput));
        if (!result.ok()) {
          fdf::error("Failed to call EchoString: {}", result.error());
          return zx::error(result.status());
        }
        if (result->response.get() != kInput) {
          fdf::error("Unexpected response: Actual: \"{}\", Expected: \"{}\"",
                     result->response.get(), kInput);
          return zx::error(ZX_ERR_INTERNAL);
        }

        return zx::ok();
      }
    };

    FUCHSIA_DRIVER_EXPORT2(ChildDriver);

[](https://issues.fuchsia.dev/issues/new?component=1439230&template=1881640&description=Issue+on:+https://fuchsia.dev/docs/development/drivers/tutorials/fidl-tutorial.md "Report a bug or suggest improvements on this page") [](https://cs.opensource.google/fuchsia/fuchsia/+/main:docs/development/drivers/tutorials/fidl-tutorial.md "View the source code of this page") [](https://ci.android.com/edit?repo=fuchsia/fuchsia/main&file=docs/development/drivers/tutorials/fidl-tutorial.md&cloneRepo=fuchsia/fuchsia:refs/heads/main&fullPath=fuchsia/docs/development/drivers/tutorials/fidl-tutorial.md "Edit the source code of this page")