本指南可協助您擺脫使用固定路徑名稱識別驅動程式庫例項的反模式。
具體來說,本指南適用於執行下列任一操作的非驅動程式庫元件用戶端:
- 使用拓撲路徑,例如
/dev/sys/platform/ram-nand/nand-ctl - 使用硬式編碼類別路徑 (例如 /dev/class/camera/000)
- 連線至特定 devfs 項目,然後呼叫
device_controller。GetTopologicalPath()
在上述每種情況下,用戶端都必須從服務/fidl 介面供應商 (或看似) 事先已知,且一律假設服務存在的狀態,遷移至匯總服務,其中會非同步新增及移除執行個體,且執行個體名稱無法用於識別。
選項 1:連線至第一個可用的服務執行個體
這並非一般首選解決方案,因為它會忽略系統的動態特性,而且如果有多個服務執行個體,就會發生錯誤。不過,如果工具和測試較為簡單,且預期只有一個服務供應商,這就已足夠。下列程式碼會無限期等待服務執行個體顯示:
C++
SyncServiceMemberWatcher<fidl_example::Service::Device> watcher;
zx::result<ClientEnd<fidl_example::Echo>> result = watcher.GetNextInstance(false);
荒漠油廠
use fuchsia_component::client::Service;
let client = Service::open(fuchsia_example::EchoServiceMarker)?
.watch_for_any()
.await?
.connect_to_device()?;
選項 2:從每個服務執行個體查詢資訊
用戶端應監控感興趣的服務,然後查詢每個執行個體的識別資訊,而非使用專屬 ID 連線至執行個體來識別。這項資訊可能來自服務提供的現有通訊協定,也可能新增至服務,協助識別。
使用服務提供的現有 fidl 通訊協定
許多 fidl 通訊協定已指定裝置資訊 (例如,fuchsia.camera/Control 通訊協定提供 GetInfo 呼叫)。如果目前放送的通訊協定不含個人識別資訊,可以修改通訊協定,加入這類裝置專屬資訊。
library fuchsia.hardware.something;
protocol Device {
...
GetDeviceId() -> (struct {
device_id uint32;
}) error zx.Status;
}
service Service {
device client_end:Device;
};
為服務新增其他通訊協定
在服務中新增 GetInfo 通訊協定,其中可包含裝置識別資訊。例如:
library fuchsia.hardware.something;
Protocol Device { ... }
Protocol GetInfo {
GetDeviceId() -> (struct {
device_id uint32;
}) error zx.Status;
}
service Service {
device client_end:Device;
info client_end:GetInfo;
};
將身分識別介面與主要通訊介面分開有幾項優點:
- 識別介面大多可由驅動程式庫處理,因為它提供一組靜態資訊。
- 識別介面最好能處理多個同步連線,以利查詢,但大多數驅動程式無法處理其他介面的多個同步連線。
- 將識別邏輯分開,可讓日後的工作更輕鬆地將識別步驟整合到架構中。
如果節點需要拓撲資訊,目前已有存取該資訊的通訊協定,請參閱下一節。
存取服務執行個體的拓撲資訊。
為方便轉換介面,使其不再使用 dev-topological 或使用 fuchsia_device::Controller 介面呼叫 GetTopologicalPath,我們提供工具,可存取 devfs 或服務例項的拓撲路徑。這項工具的設計目的是在遷移至使用 fuchsia.driver.token.NodeToken 通訊協定提供匯流排拓撲權杖的服務時,做為其中一個步驟。
使用 GetTopologicalPath 工具將用戶轉換為服務
使用 fdf_topology::GetTopologicalPath 可讓您停止直接存取拓撲路徑,並將驅動程式轉換為服務
將類別名稱新增至許可清單:
const std::list<std::string> kClassesThatAllowTopologicalPath({
"block",
"example_echo",
"devfs_service_test",
});
新增 device_topology 依附元件:
deps = [
"//src/devices/lib/client:device_topology",
],
然後使用 Service Directory 和執行個體名稱呼叫 GetTopologicalPath:
// Open the service directory
zx::result dir = component::OpenDirectory("/svc/fuchsia.examples.EchoService");
ZX_ASSERT(dir.status_value() == ZX_OK);
// For simplicity in this example, just get the first instance in the service directory:
auto watch_result = device_watcher::WatchDirectoryForItems<std::string>(
*dir, [](std::string_view name) -> std::optional<std::string> { return std::string(name); });
ZX_ASSERT(watch_result.status_value() == ZX_OK);
std::string instance_name = std::move(watch_result.value());
// Now check the topological path of that instance:
zx::result<std::string> path_result = fdf_topology::GetTopologicalPath(*dir, instance_name);
ZX_ASSERT(path_result.is_ok());
if (path_result.value() == kSomePathYouWant) {
//Open the instance
}
遷移程序的這個步驟應該相對機械化,可讓用戶停止使用 devfs。接下來的兩個步驟需要更深入瞭解客戶使用拓撲路徑的原因。
將「NodeToken」加入通訊協定或服務
建議使用 NodeToken 取得公車拓撲資訊:
library fuchsia.example;
using fuchsia.driver.token;
Protocol Device {
compose fuchsia.driver.token.NodeToken;
}
service Service {
device client_end:Device;
};
詳情請參閱「匯流排拓撲」。
遷移至使用匯流排拓撲,而非拓撲路徑
這裡的實作方式取決於您需要使用拓撲資訊的原因。舉例來說,如需連線至匯流排上的特定裝置位址,您可以比對匯流排類型和位址號碼。
以下範例使用 ServiceMemberWatcher 篩選特定拓撲:
// Define a callback function to be called when the correct device is found
void OnInstanceFound(ClientEnd<fuchsia_examples::Echo> client_end) {}
// Define a filter for choosing the correct device:
void FilterOnBusTopology(ClientEnd<fuchsia_examples::Echo> client_end) {
fidl::WireResult result = fidl::WireCall(client_end)->Get();
ZX_ASSERT(result.ok());
zx::result topo_client = component::Connect<fuchsia.driver.token.NodeBusTopology>();
ZX_ASSERT(topo_client.is_ok());
fidl::WireResult topo_result = fidl::WireCall(topo_client.value())->Get(result.value().value()->token.get());
ZX_ASSERT(topo_result.ok());
// Check topology:
std::vector<fuchsia.driver.framework.BusInfo> bus_info
= topo_result.value().value()->path.get();
if (bus_info[0].bus() == fuchsia_driver_framework::BusType::I2C &&
bus_info[0].address() == 3) {
OnInstanceFound(std::move(client_end));
}
}
// Optionally define an idle function, which will be called when all
// existing instances have been enumerated:
void AllExistingEnumerated() {...}
// Create the ServiceMemberWatcher:
ServiceMemberWatcher<fuchsia_examples::EchoService::MyDevice> watcher;
watcher.Begin(get_default_dispatcher(), &FilterOnBusTopology, &AllExistingEnumerated);
// If you want to stop watching for new service entries:
watcher.Cancel()
此外,您也必須從驅動程式管理工具將 fuchsia.driver.token.NodeBusTopology 通訊協定傳送至用戶端。
如有任何問題或想瞭解最新進度,請與我們聯絡: