簡介
在本節中,我們將介紹「複合型裝置」。複合裝置是指由其他裝置組成的裝置。
這些裝置處理硬體層級組合的情況,其中「裝置」(從使用者的角度來看),會由多個不同的硬體區塊實作。
例如:
- 由 I2C 裝置和 GPIO 組成的觸控面板
- 由 MAC 晶片和一或多個 PHY 組成的乙太網路裝置;或
- 由音訊控制器和一組轉碼器組成的音訊裝置。
在這些情況下,主機驅動程式庫會在啟動時得知硬體的關係 (透過靜態方式或透過動態方式,例如 ACPI)。
以下範例會使用 astro-audio
裝置:
這部裝置的特色如下:
- I2C 匯流排介面
- 兩組 GPIO (一個用於錯誤,一個用於啟用)
- 適用於大量資料移轉的 MMIO (記憶體對應 I/O),以及
- IRQ (中斷要求) 行,對驅動程式庫產生中斷。
請注意,ZX_PROTOCOL_I2C
和 ZX_PROTOCOL_GPIO
通訊協定是用來移轉資料,也就是透過個別的驅動程式收發及接收 I2C 訊息和 GPIO PIN 碼狀態。
ZX_PROTOCOL_PDEV
部分不同。這裡的通訊協定只會用於將存取權授予 MMIO 和 IRQ;實際的 MMIO 資料和中斷情形「並非」由 PDEV
處理,而是由 astro-audio
驅動程式庫自行處理。
建立複合型裝置
如要建立複合型裝置,請設定一些資料結構。
繫結操作說明
我們需要一些繫結操作說明 (zx_bind_inst_t
),指示我們比對哪些裝置。
針對 astro-audio
裝置,我們有:
static const zx_bind_inst_t i2c_match[] = {
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_I2C),
BI_ABORT_IF(NE, BIND_I2C_BUS_ID, ASTRO_I2C_3),
BI_MATCH_IF(EQ, BIND_I2C_ADDRESS, I2C_AUDIO_CODEC_ADDR),
};
static const zx_bind_inst_t fault_gpio_match[] = {
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_GPIO),
BI_MATCH_IF(EQ, BIND_GPIO_PIN, GPIO_AUDIO_SOC_FAULT_L),
};
static const zx_bind_inst_t enable_gpio_match[] = {
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_GPIO),
BI_MATCH_IF(EQ, BIND_GPIO_PIN, GPIO_SOC_AUDIO_EN),
};
這些繫結操作說明可用來尋找裝置。
我們有三個繫結指令陣列,分別是 I2C (i2c_match[]
) 裝置和兩個 GPIO (fault_gpio_match[]
和 enable_gpio_match[]
)。
這些指示隨後會放置於結構陣列 (device_fragment_part_t
),以定義每個片段:
astro-audio
裝置包含:
static const device_fragment_part_t i2c_fragment[] = {
{ countof(i2c_match), i2c_match },
};
static const device_fragment_part_t fault_gpio_fragment[] = {
{ countof(fault_gpio_match), fault_gpio_match },
};
static const device_fragment_part_t enable_gpio_fragment[] = {
{ countof(enable_gpio_match), enable_gpio_match },
};
此時,有三個片段裝置,分別為 i2c_fragment[]
、fault_gpio_fragment[]
和 enable_gpio_fragment[]
。
Fragment 裝置比對規則
輸入的規則如下:
- 最後一個元素必須描述目標裝置本身。
- 其餘元素必須依序與根層級到目標裝置的路徑上裝置相符。系統可能會略過部分「裝置」,但每個「元素」都必須相符。
最後,我們會將其合併為名為 fragments[]
的匯總類型,類型為 device_fragment_t
:
現在會提供單一 ID fragments[]
,我們在建立複合裝置時可以使用。
在 astro-audio
中,這看起來如下:
static const device_fragment_t fragments[] = {
{ "i2c", countof(i2c_fragment), i2c_fragment },
{ "gpio-fault", countof(fault_gpio_fragment), fault_gpio_fragment },
{ "gpio-enable", countof(enable_gpio_fragment), enable_gpio_fragment },
};
正在建立裝置
如果是簡單的 (非複合) 裝置,我們使用了 device_add()。
如果是複合型裝置,我們使用 device_add_composite_deprecated():
zx_status_t device_add_composite_deprecated(
zx_device_t* dev,
const char* name,
const zx_device_prop_t* props,
size_t props_count,
const device_fragment_t* fragments,
size_t fragments_count,
uint32_t coresident_device_index);
引數如下:
引數 | 意義 |
---|---|
dev |
家長裝置 |
name |
裝置名稱 |
props |
屬性 (請參閱「宣告驅動程式」) |
props_count |
「props 」中的項目數量 |
fragments |
個別片段裝置 |
fragments_count |
「fragments 」中的項目數量 |
coresident_device_index |
要使用的驅動程式代管程序 |
dev
值必須是與「sys
」裝置 (即平台匯流排驅動程式) 對應的 zx_device_t
。
請注意,coresident_device_index
是用來指出新裝置應使用的驅動程式代管程序。
如果指定 UINT32_MAX
,裝置就會存放在新的驅動程式代管程序中。
請注意,
astro-audio
使用 pbus_composite_device_add() 而非 pbus_composite_device_add()。不同之處在於 pbus_composite_device_add() 是由平台匯流排驅動程式提供的 API,可納入 pbus_composite_device_add(),並插入額外的 IR 片段,以便透過直接存取
使用複合型裝置
從程式設計的角度來看,複合裝置如同一般裝置,但沒有 banjo 通訊協定。每個內建片段都能提供通訊協定和中繼資料,但為了要提高相容性,請勿直接存取片段。
但可以呼叫 device_get_fragment_protocol() 和 device_get_fragment_metadata() 以直接存取每個片段的通訊協定和中繼資料。
bool device_get_fragment_protocol (
zx_device_t* parent,
const char* fragment_name,
uint32_t proto_id, void* out);
引數如下:
引數 | 意義 |
---|---|
parent |
代表父項的 zx_device_t 指標 |
fragment_name |
要擷取的片段名稱 |
proto_id |
要擷取的通訊協定 ID |
out |
要傳回的通訊協定指標 |
foo_protocol_t proto;
auto status = device_get_fragment_protocol(&composite, "fragment-name", ZX_PROTOCOL_FOO, &proto);
if (status != ZX_OK) {
zxlogf(ERROR, "could not get protocol");
return status;
}
中繼資料也是如此:
bool device_get_fragment_metadata (
zx_device_t* parent,
const char* fragment_name,
uint32_t type, void* buf,
size_t buflen, size_t* actual);
引數如下:
引數 | 意義 |
---|---|
parent |
代表父項的 zx_device_t 指標 |
fragment_name |
要擷取的片段名稱 |
type |
要擷取的通訊協定 ID |
buf |
指向要填入資料集的指標 |
buflen |
可寫入 buf 的位元組數上限 |
actual |
指向已填滿實際大小的 size_t 指標。 |
std::vector<uint8_t> data(50);
size_t actual = 0;
auto status = device_get_fragment_metadata(&composite, "fragment-name",
DEVICE_METADATA_FOO, data.data(),
data.size(), &actual);
if (status != ZX_OK) {
zxlogf(ERROR, "could not get metadata");
return status;
}
提供給 device_get_fragment_protocol() 和 device_get_fragment_metadata() 的片段名稱,與提供給主面板驅動程式庫程式的 device_add_Composite_Deprecatedd() 項目中的 device_fragment_t 項目相同。
進階主題
我們在此討論一些專門 / 進階的主題。
複合裝置和 Proxy
astro-audio
驅動程式庫的實際執行方式比原本顯示的還算複雜一點:
片段會繫結至內部驅動程式庫 (位於片段目錄中)。
驅動程式會視需要處理跨程序邊界的 Proxy 處理。此 Proxy 會使用 DEVICE_ADD_MUST_ISOLATE
機制 (詳情請參閱「隔離裝置」一節)。
使用 DEVICE_ADD_MUST_ISOLATE
新增裝置時,系統建立兩個裝置:一般裝置,與父項及 Proxy 相同。
Proxy 會在新的驅動程式代管程序中建立;如果一般裝置的驅動程式庫為 normal.so
,則其驅動程式庫為 normal.proxy.so
。此驅動程式庫應實作 create() 方法,該方法會呼叫 device_add() 並保留指定的 IPC 管道。管道稍後將用於與一般裝置通訊,以便滿足 Proxy 的子項要求。
一般裝置會實作 rxrpc
掛鉤,每當從與 Proxy 共用的管道收到訊息時,驅動程式庫執行階段就會叫用該掛鉤。
因此,如要實作新的通訊協定 Proxy,必須修改 fragment.proxy.so
驅動程式以傳送訊息至一般裝置,以處理所需的通訊協定,並修改 fragment.so
驅動程式庫,以便妥善服務這些訊息。
片段 Proxy 是在 fragment-proxy.cc 中實作,而另一個則是 fragment.cc。