12.4. 交握與能力協商

相機與主機到達傳輸層時,各自對協定該如何運作抱持著自己的想法:使用哪些 CRC 模式、是否要求 ACK、它們能緩衝的最大酬載是多少。在真正的流量開始之前,它們會交換一次交握(handshake),把這些參數固定下來,供後續整個工作階段使用。

12.4.1. 主機開啟連線

相機端會在開機時啟動協定堆疊(或由應用程式以 protocol.init() 重新啟動它以變更參數),然後安靜地等待主機。從相機的角度來看,在封包抵達之前都無事可做。

主機端會開啟傳輸層——USB 連接埠或 UART——並立即送出一個 PROTO_SYNC 封包(opcode 0x00)。這個封包帶有一個魔術酬載(magic payload),即使雙方都失去同步,也能讓相機辨識出它;而且在能力協商完成之前,這是相機唯一會回應的封包。

如果相機在重傳逾時內沒有回覆,主機會再次送出 PROTO_SYNC,最多 rtx_retries 次。在那之後它就放棄並回報連線失敗。正是這項重試讓「拔除、重插、重新啟動主機指令碼」得以運作,而相機完全不需要知道主機曾經離開過。

12.4.2. 能力交換

一旦相機確認了同步,主機就送出 PROTO_GET_CAPS(opcode 0x01),詢問相機支援哪些功能。回覆酬載會回報:

  • CRC 驗證已啟用或已停用

  • 序號追蹤已啟用或已停用

  • ACK 已啟用或已停用

  • 事件通知已啟用或已停用

  • 相機的最大酬載大小(位元組)

  • 目前的重傳重試次數、逾時與輪詢參數

主機會將這些與自己的組態比對。如果主機需要 變更 其中任何一項——例如,因為它的接收緩衝區比相機的小,而需要協商出一個較小的最大酬載——它就會送出帶有新值的 PROTO_SET_CAPS(opcode 0x02)。相機會重新設定其堆疊並予以確認。自此之後,每個跨越線路的封包都遵循那份共享的契約。

如果主機不覆寫任何項目,預設值就全部啟用:CRC 驗證、序號追蹤、ACK 與事件通知。預設的最大酬載是相機的逐板緩衝區大小減去 14 個位元組的封包切分額外負擔(10 個位元組的標頭加上 4 個位元組的尾端酬載 CRC)。對於大多數相機對筆電的工作而言,預設值就是恰當的起點;可靠性那一頁會說明應用程式何時以及為何要關掉其中某些部分。

12.4.3. 通道探索

在能力協商之後,主機送出 CHANNEL_LIST(opcode 0x20)。相機回覆一份已註冊通道的清單——四個內建通道(stdinstdoutstreamprofile)加上任何由應用程式以 protocol.register() 註冊的通道。每一筆項目都帶有該通道的 ID、名稱以及它的能力旗標(唯讀、唯寫、可上鎖)。

主機會收藏這份清單,待稍後應用程式碼要求 channel_read("frame")channel_write("config", ...) 時使用——名稱會被查找一次以對應到通道 ID,之後該通道上的每一個後續封包都直接使用該 ID。

如果相機在初始清單之後註冊了一個新通道——當應用程式在交握之後才開始執行時,這很常見——相機會發出一個 CHANNEL_REGISTERED 事件封包。主機會監聽這些封包並更新其內部通道清單,因此一個早期就接上的主機指令碼,不必重新啟動就能看到新註冊的通道出現。

交握在連線建立時會耗費幾個往返,之後就再也不重複。穩態流量就只是封包而已:切分後的位元組進、切分後的位元組出,通道 ID 兩端都已知曉。