找出服務執行個體

本指南可協助您移除使用固定路徑名稱的驅動程式庫程式例項識別方式,以免造成反模式。

本指南特別適用於執行下列任一操作的非驅動程式庫元件用戶端:

  • 使用拓樸路徑,例如 /dev/sys/platform/00:00:2e/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",
],

然後使用服務目錄和執行個體名稱呼叫 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()

您也必須從 Driver Manager 將 fuchsia.driver.token.NodeBusTopology 通訊協定路由至用戶端。

如有任何問題或想瞭解最新進度,請與我們聯絡: