This guide walks through the tasks of serving Banjo protocols in a DFv2 driver and connecting to its Banjo server from a DFv1 child driver.
Banjo protocols, primarily used in DFv1 drivers, are defined in FIDL library
annotated with the @transport("Banjo")
and @banjo_layout("ddk-protocol")
lines, for example:
/// The protocol provides access to functions of the driver.
@transport("Banjo")
@banjo_layout("ddk-protocol")
closed protocol Misc {
/// Returns a unique identifier for this device.
strict GetHardwareId() -> (struct {
status zx.Status;
response uint32;
});
/// Returns the current device firmware version
strict GetFirmwareVersion() -> (struct {
status zx.Status;
major uint32;
minor uint32;
});
};
(Source: gizmo.test.fidl
)
To enable a DFv2 driver to use Banjo protocols, see the tasks below:
Serve a Banjo protocol from a DFv2 driver: Implement and serve Banjo protocols in a DFv2 driver, which sets up the driver to communicate with DFv1 child drivers.
Connect to a Banjo server from a DFv1 driver: Connect to a DFv2 parent driver's Banjo server from a DFv1 child driver and start using Banjo protocols.
Serve a Banjo protocol from a DFv2 driver
This section walks through implementing a Banjo protocol in a DFv2 driver
and serving the protocol to a DFv1 child driver. This walkthrough is based
on the Banjo Transport example, which implements
the Misc
Banjo protocol in the gizmo.test
FIDL library.
The steps are:
1. Set up the Banjo protocol
To set up the Misc
Banjo protocol in a DFv2 driver, do the following:
In the
BUILD.gn
file, add the Banjo library as a dependency in thefuchsia_driver
target, for example:fuchsia_driver("parent_driver") { output_name = "banjo_transport_parent" sources = [ "parent-driver.cc" ] deps = [ "//examples/drivers/bind_library:gizmo.example_cpp", "//examples/drivers/transport/banjo:fuchsia.examples.gizmo_banjo_cpp", ... ] }
In the driver's C++ header file, include the Banjo library's C++ header, for example:
#include <fuchsia/examples/gizmo/cpp/banjo.h> ... namespace banjo_transport { ...
(Source:
parent-driver.h
)To inherit from the Banjo protocol bindings, update the driver class using the following format:
ddk::<PROTOCOL_NAME>Protocol<<YOUR_DRIVER_CLASS>>
Replace
PROTOCOL_NAME
with the name of the Banjo protocol andYOUR_DRIVER_CLASS
is the class of your driver, both in camel case, for example:class ParentBanjoTransportDriver : public fdf::DriverBase, public ddk::MiscProtocol<ParentBanjoTransportDriver> { ... };
(Source:
parent-driver.h
)
2. Implement the Banjo protocol
In the driver class, define and implement each function in the Banjo protocol.
For instance, the example below shows a Banjo protocol named ProtocolName
:
@transport("Banjo")
@banjo_layout("ddk-protocol")
closed protocol ProtocolName {
/// Returns a unique identifier for this device.
strict FunctionName() -> (struct {
status zx.Status;
response_1 response_1_type;
response_2 response_2_type;
});
};
For this ProtocolName
Banjo protocol, the C++ binding for the
FunctionName
function looks like below:
zx_status_t ProtocolNameFunctionName(response_1_type* response_1, response_2_type* response_2);
And you can find the C++ bindings of the existing Banjo protocols in the following path of the Fuchsia source checkout:
<OUT_DIRECTORY>/fidling/gen/<PATH_TO_THE_FIDL_LIBRARY>/banjo
For instance, if your out
directory is out/default
and the FIDL
library is located in the examples/drivers/transport
directory,
then the C++ bindings are located in the following directory:
out/default/fidling/gen/examples/drivers/transport/banjo
See the following implementation in the Banjo Transport example:
The
Misc
protocol contains the functions below:/// Returns a unique identifier for this device. strict GetHardwareId() -> (struct { status zx.Status; response uint32; }); /// Returns the current device firmware version strict GetFirmwareVersion() -> (struct { status zx.Status; major uint32; minor uint32; });
The
ParentBanjoTransportDriver
class defines these functions as below:class ParentBanjoTransportDriver : public fdf::DriverBase, public ddk::MiscProtocol<ParentBanjoTransportDriver> { public: ... // MiscProtocol implementation. zx_status_t MiscGetHardwareId(uint32_t* out_response); zx_status_t MiscGetFirmwareVersion(uint32_t* out_major, uint32_t* out_minor); ... };
(Source:
parent-driver.h
)The functions are implemented as below:
zx_status_t ParentBanjoTransportDriver::MiscGetHardwareId(uint32_t* out_response) { *out_response = 0x1234ABCD; return ZX_OK; } zx_status_t ParentBanjoTransportDriver::MiscGetFirmwareVersion(uint32_t* out_major, uint32_t* out_minor) { *out_major = 0x0; *out_minor = 0x1; return ZX_OK; }
(Source:
parent-driver.cc
)
3. Serve the Banjo protocol
Once the Banjo protocol is implemented in a DFv2 driver, you need to serve the protocol to a DFv1 child node using a compat device server configured with Banjo.
To do so, complete the following tasks in the Set up the compat device server in a DFv2 driver guide:
Connect to a Banjo server from a DFv1 driver
This section uses the Banjo transport example to walk through the task of connecting a DFv1 child driver to a DFv2 parent driver that serves a Banjo protocol.
The steps are:
1. Connect the child driver to the parent driver
To be able to connect a child driver to a parent driver for using Banjo
protocols, the child must be co-located in the same driver host as the parent
and both drivers need to use the compat banjo_client
library.
To connect the child driver to the parent driver, do the following:
In the child driver's component manifest (
.cml
), set thecolocate
field totrue
, for example:{ include: [ 'syslog/client.shard.cml' ], program: { runner: 'driver', binary: 'driver/banjo_transport_child.so', bind: 'meta/bind/child-driver.bindbc', // Run in the same driver host as the parent driver colocate: 'true', }, use: [ { service: 'fuchsia.driver.compat.Service' }, ], }
(Source:
child-driver.cml
)In the driver's
BUILD.gn
file, add the Banjo library as a dependency in thefuchsia_driver
target, for example:fuchsia_driver("child_driver") { output_name = "banjo_transport_child" sources = [ "child-driver.cc" ] deps = [ "//examples/drivers/transport/banjo:fuchsia.examples.gizmo_banjo_cpp", "//sdk/lib/driver/component/cpp:cpp", "//src/devices/lib/driver:driver_runtime", ] }
(Source:
BUILD.gn
)In the driver's C++ header file, include the Banjo library's C++ header, for example:
#include <fuchsia/examples/gizmo/cpp/banjo.h> ... namespace banjo_transport { ...
(Source:
child-driver.h
)In the driver's
BUILD.gn
file, add the compatbanjo_client
library as a dependency in thefuchsia_driver
target, for example:fuchsia_driver("child_driver") { output_name = "banjo_transport_child" sources = [ "child-driver.cc" ] deps = [ "//examples/drivers/transport/banjo:fuchsia.examples.gizmo_banjo_cpp", "//sdk/lib/driver/compat/cpp", "//sdk/lib/driver/component/cpp:cpp", "//src/devices/lib/driver:driver_runtime", ] }
(Source:
BUILD.gn
)In the driver's C++ source file, include the compat
banjo_client
library's C++ header, for example:#include "examples/drivers/transport/banjo/v2/child-driver.h" #include <lib/driver/compat/cpp/compat.h> #include <lib/driver/component/cpp/driver_export.h> #include <lib/driver/logging/cpp/structured_logger.h> namespace banjo_transport { zx::result<> ChildBanjoTransportDriver::Start() { ...
(Source:
child-driver.cc
)In the driver's C++ source file, set up a Banjo client with the
compat::ConnectBanjo()
function, for example:zx::result<Client> ConnectBanjo(const std::shared_ptr<fdf::Namespace>& incoming, std::string_view parent_name = "default") {
In the Banjo Transport example, the child driver does the following to connect to the
Misc
protocol:zx::result<ddk::MiscProtocolClient> client = compat::ConnectBanjo<ddk::MiscProtocolClient>(incoming()); if (client.is_error()) { FDF_SLOG(ERROR, "Failed to connect client", KV("status", client.status_string())); return client.take_error(); }
(Source:
child-driver.cc
)
2. Use the Banjo protocol
In the child driver, use the protocol client to invoke the Banjo functions.
For instance, the example below shows a Banjo protocol named
ProtocolName
:
@transport("Banjo")
@banjo_layout("ddk-protocol")
closed protocol ProtocolName {
/// Returns a unique identifier for this device.
strict FunctionName() -> (struct {
status zx.Status;
response_1 response_1_type;
response_2 response_2_type;
});
};
For this ProtocolName
Banjo protocol, the C++ binding for the
FunctionName
function looks like below:
zx_status_t ProtocolNameFunctionName(response_1_type* response_1, response_2_type* response_2);
In the Banjo Transport example,
the GetHardwareId()
function is defined as below:
/// Returns a unique identifier for this device.
strict GetHardwareId() -> (struct {
status zx.Status;
response uint32;
});
(Source: gizmo.test.fidl
)
With the Banjo client stored in the client_
object
and the hardware_id_
variable defined as
a uint32_t
, you can call the GetHardwareId()
function
in the following way:
zx_status_t status = client_.GetHardwareId(&hardware_id_);
if (status != ZX_OK) {
return status;
}
(Source: child-driver.cc
)