本指南說明如何新增 並搭配其他驅動程式庫使用本指南假設您熟悉 以下概念:
FIDL 通訊協定定義
下列程式碼片段使用這個 FIDL 通訊協定:
library fidl.examples.echo;
const MAX_STRING_LENGTH uint64 = 32;
// The discoverable annotation is required, otherwise the protocol bindings
// will not have a name string generated.
@discoverable
protocol Echo {
/// Returns the input.
EchoString(struct {
value string:<MAX_STRING_LENGTH, optional>;
}) -> (struct {
response string:<MAX_STRING_LENGTH, optional>;
});
};
service EchoService {
echo client_end:Echo;
};
父項驅動程式 (伺服器)
我們估算了父項驅動程式庫通訊協定如何實作 都會寫入雖然未顯示,但我們假設這個類別 第一種是使用 DDKTL。
// This class implement the fuchsia.examples.echo/Echo FIDL protocol using the
// new C++ FIDL bindings
class Device : public fidl::WireServer<fidl_examples_echo::Echo> {
// This is the main entry point for the driver.
static zx_status_t Bind(void* ctx, zx_device_t* parent) {
// When creating the device, we initialize it with a dispatcher provided by
// the driver framework. This dispatcher is allows us to schedule
// asynchronous work on the same thread as other drivers. You may opt to
// create your own dispatcher which is serviced on a thread you spawn if you
// desire instead.
auto* dispatcher = fdf::Dispatcher::GetCurrent()->async_dispatcher();
auto device = std::make_unique<Device>(parent, dispatcher);
// We add the FIDL protocol we wish to export to our child to our outgoing
// directory. When a connection is attempted we will bind the server end of
// the channel pair to our server implementation.
zx::result = device->outgoing_.AddService<fidl_examples_echo::EchoService>(
fidl_examples_echo::EchoService::InstanceHandler({
.echo = device->bindings_.CreateHandler(device.get(), dispatcher,
fidl::kIgnoreBindingClosure),
}));
// Utilizing the server end of the endpoint pair we created above, we bind
// it to our outgoing directory.
result = device->outgoing_.Serve(std::move(endpoints->server));
if (result.is_error()) {
zxlogf(ERROR, "Failed to service the outgoing directory");
return result.status_value();
}
// We declare our outgoing protocols here. These will be utilize to
// help the framework populate node properties which can be used for
// binding.
std::array offers = {
fidl_examples_echo::Service::Name,
};
status = device->DdkAdd(ddk::DeviceAddArgs("parent")
// The device must be spawned in a separate
// driver host.
.set_flags(DEVICE_ADD_MUST_ISOLATE)
.set_fidl_service_offers(offers)
// The client side of outgoing directory is
// provided to the framework. This will be
// forwarded to the new driver host that spawns to
// allow the child driver which binds the ability
// to connect to our outgoing FIDL protocols.
.set_outgoing_dir(endpoints->client.TakeChannel()));
if (status == ZX_OK) {
[[maybe_unused]] auto ptr = device.release();
} else {
zxlogf(ERROR, "Failed to add device");
}
return status;
}
private:
// This is the implementation of the only method our FIDL protocol requires.
void EchoString(EchoStringRequestView request, EchoStringCompleter::Sync& completer) override {
completer.Reply(request->value);
}
// This is a helper class which we use to serve the outgoing directory.
component::OutgoingDirectory outgoing_;
// This ensures that the fidl connections don't outlive the device object.
fidl::ServerBindingGroup<fidl_examples_echo::Echo> bindings_;
};
子項駕駛 (用戶端)
裝訂
第一個要討論的重點是子項驅動程式庫的繫結方式。這項服務可以 繫結至任意數量的節點屬性 而非父項檔案 FIDL 通訊協定,則需要繫結程式庫 FIDL 程式庫自動產生版本 (詳情請參閱產生的繫結程式庫)。
您將依附並在驅動程式庫的繫結規則中使用這個繫結程式庫:
using fidl.examples.echo;
fidl.examples.echo.Echo == fidl.examples.echo.Echo.ZirconTransport;
ZirconTransport 是家長驅動程式庫用來 為子項提供 Echo FIDL 通訊協定
您可以視需要新增其他繫結限制條件。請注意, 只有在父項驅動程式庫宣告時,系統才會新增此處描述的屬性 在新增裝置時提供的 FIDL 通訊協定。
用戶端代碼
下列程式碼片段位於成功執行的子項驅動程式中 繫結至上述的父項驅動程式庫,
zx_status_t CallEcho() {
// The following method allows us to connect to the protocol we desire. This
// works by providing the server end of our endpoint pair to the framework. It
// will push this channel through the outgoing directory to our parent driver
// which will then bind it to its server implementation. We do not need to
// name the protocol because the method is templated on the channel type and
// it is able to automatically derive the name from the type.
zx::result client_end = DdkConnectFidlProtocol<fidl_examples_echo::EchoService::Echo>();
if (client_end.is_error()) {
zxlogf(ERROR, "Failed to connect fidl protocol: %s", client_end.status_string());
return client_end.status_value();
}
// We turn the client side of the endpoint pair into a synchronous client.
fidl::WireSyncClient client{std::move(client_end.value())};
// We can now utilize our client to make calls!
constexpr std::string_view kInput = "Test String";
auto result = client->EchoString(fidl::StringView::FromExternal(cpp17::string_view(kInput)));
if (!result.ok()) {
zxlogf(ERROR, "Failed to call EchoString");
return result.status();
}
if (result->response.get() != kInput) {
zxlogf(ERROR, "Unexpected response: Actual: \"%.*s\", Expected: \"%.*s\"",
static_cast<int>(result->response.size()), result->response.data(),
static_cast<int>(kInput.size()), kInput.data());
return ZX_ERR_INTERNAL;
}
return ZX_OK;
}
產生的繫結程式庫
所有 FIDL 程式庫都會取得根據這些程式庫所建立自動產生的繫結程式庫。這是協助驅動程式庫 作者會根據父項提供的 FIDL 通訊協定與服務建立繫結規則,以及 供父項使用的傳輸方法
繫結程式庫
以下繫結程式庫提供三種可能的傳輸方法:Banjo
、ZirconTransport
、
和 DriverTransport
。目前可以放心假設值為 ZirconTransport
(只是 Zircon 頻道上一般的 FIDL)?DriverTransport
(這是在同一位置驅動程式的處理中通訊堆疊)。
繫結程式庫包含通訊協定和這些傳輸方法的常數。
FIDL 程式庫中定義的各項服務和可探索通訊協定都會在 將列舉值繫結程式庫,列舉的值為三種傳輸方法。
在以下示例中,FIDL 程式庫包含一個可探索的通訊協定:
protocol.fidl
library fuchsia.gizmo.protocol;
@discoverable
closed protocol TestingProtocol {
strict Get();
};
產生的 lib
// WARNING: This file is machine generated by bindc.
library fuchsia.gizmo.protocol;
enum TestingProtocol {
Banjo,
ZirconTransport,
DriverTransport,
};
建構目標
產生的繫結程式庫將以 FIDL 程式庫的
《library_name
》和《target_name
》。繫結程式庫的目標名稱會是
{fidl_target_name}_bindlib
及其 library_name
與 FIDL 相同。
舉例來說,如果 FIDL 程式庫目標為 //sdk/fidl/fidl.examples.echo:myecholibrary
,
自動產生的繫結庫目標就是
//sdk/fidl/fidl.examples.echo:myecholibrary_bindlib
。
實際上,大多數 FIDL 程式庫的 target_name
都與所在的資料夾相同,
通常是程式庫名稱舉例來說,如果 FIDL 程式庫
//sdk/fidl/fidl.examples.echo
,自動產生的繫結程式庫目標為
//sdk/fidl/fidl.examples.echo:fidl.examples.echo_bindlib
。
產生的程式碼目標
這些產生的繫結程式庫的運作方式,與編寫使用者時完全相同 繫結程式庫。如要進一步瞭解如何產生使用者編寫繫結程式庫的程式碼,請參閱 繫結程式庫程式碼產生教學課程。
範例
讓我們以上文所示的 FIDL 程式庫為例來運用。
FIDL (BUILD.gn)
fidl("my_fidl_target") { # The target_name
name = "fuchsia.gizmo.protocol" # The library_name (optional, defaults to
# target_name)
sources = [ "protocol.fidl" ]
}
現在,產生的繫結程式庫會以 :my_fidl_target_bindlib
的目標名稱提供
以及 fuchsia.gizmo.protocol
的程式庫名稱顯示了針對繫結程式庫產生的來源
我們可以利用此方法為子項驅動程式庫建立繫結規則。
子項繫結規則 (BUILD.gn)
driver_bind_rules("bind") {
rules = "meta/child_driver.bind"
bind_output = "child_driver.bindbc"
deps = [ ":my_fidl_target_bindlib" ]
}
child-driver.bind
using fuchsia.gizmo.protocol;
fuchsia.gizmo.protocol.TestingProtocol == fuchsia.gizmo.protocol.TestingProtocol.ZirconTransport;
驅動程式庫在建立子項節點時,會自動為其指派
fuchsia_driver_framework::NodeAddArgs
資料表中的 offers
。因此,父項驅動程式
不需要手動指定這個屬性
舉例來說,如果為節點提供了驅動程式庫運輸型服務能力
我們會使用索引鍵來新增屬性 fuchsia_hardware_gizmo::Service
fuchsia.hardware.gizmo.Service
,值為 fuchsia.hardware.gizmo.Service.DriverTransport
。
這些值會與子項驅動程式庫的對應繫結程式庫變數相符
將用於繫結規則
產生的程式碼在建立複合節點規格時仍然實用。 這通常發生在主機驅動程式庫必須填寫規格的屬性 如果規格希望根據這些規格的節點比對節點,請手動提供優惠資訊 優惠。
我們可以使用自動產生的程式碼目標,存取這個繫結程式庫的常數 產生程式碼
複合節點規格建立者 (BUILD.gn)
C++
source_set("bindlib_usage_cpp") {
sources = [ "bindlib_usage.cc" ]
deps = [ ":my_fidl_target_bindlib_cpp" ]
}
Rust
rustc_binary("bindlib_usage_rust") {
edition = "2021"
source_root = "bindlib_usage.rs"
sources = [ "bindlib_usage.rs" ]
deps = [ ":my_fidl_target_bindlib_rust" ]
}
複合節點規格建立者程式碼
C++
#include <bind/fuchsia/gizmo/protocol/cpp/bind.h>
std::string test_protocol_key = bind_fuchsia_gizmo_protocol::TESTINGPROTOCOL;
std::string test_protocol_value = bind_fuchsia_gizmo_protocol::TESTINGPROTOCOL_ZIRCONTRANSPORT;
Rust
fn main() {
let _test_protocol_key: &str = bind_fuchsia_gizmo_protocol::TESTINGPROTOCOL;
let _test_protocol_value: &str = bind_fuchsia_gizmo_protocol::TESTINGPROTOCOL_ZIRCONTRANSPORT;
}