12.4. 握手与能力协商

摄像头和主机来到传输层时,各自对协议应当如何运行都有自己的想法:使用哪些 CRC 模式、是否要求 ACK、它们能缓冲的最大载荷是多少。在真正的流量开始之前,它们会交换一次握手,为本次会话其余部分确定这些参数。

12.4.1. 主机打开连接

摄像头一侧在启动时就开始运行协议栈(或者应用通过 protocol.init() 重新启动它以更改参数),然后安静地等待主机。从摄像头的角度看,在数据包到达之前无事可做。

主机一侧打开传输——USB 端口或 UART——并立即发送一个 PROTO_SYNC 数据包(操作码 0x00)。这个数据包带有一个魔法载荷,即使两端失去了同步,也能让摄像头识别出它,而且它是能力协商之前摄像头唯一会回应的数据包。

如果摄像头在重传超时内没有回复,主机就再次发送 PROTO_SYNC,最多发送 rtx_retries 次。此后它放弃并报告连接失败。正是这种重试让“拔下、重插、重启主机脚本”能够正常工作,而无需摄像头知道主机曾经离开过。

12.4.2. 能力交换

一旦摄像头确认了同步,主机就发送 PROTO_GET_CAPS(操作码 0x01)以询问摄像头支持什么。回复载荷报告:

  • CRC 校验已启用或已禁用

  • 序列号跟踪已启用或已禁用

  • ACK 已启用或已禁用

  • 事件通知已启用或已禁用

  • 摄像头以字节为单位的最大载荷大小

  • 当前的重传重试次数、超时以及轮询参数

主机将这些与它自己的配置进行比较。如果主机需要更改其中任何一项——例如,由于其接收缓冲区比摄像头的小而要协商一个更小的最大载荷——它就发送带有新值的 PROTO_SET_CAPS(操作码 0x02)。摄像头重新配置其协议栈并予以确认。从此以后,每一个跨越线缆的数据包都遵循这份共享契约。

如果主机不覆盖任何内容,那么默认值全部为开启:CRC 校验、序列号跟踪、ACK 以及事件通知。默认的最大载荷是摄像头的逐板缓冲区减去 14 字节的分帧开销(10 字节的报头加上 4 字节的载荷尾部 CRC)。对于大多数摄像头到笔记本电脑的工作,默认值是正确的起点;可靠性页面会讲到应用在何时以及为何要关闭其中的某些部分。

12.4.3. 通道发现

能力协商之后,主机发送 CHANNEL_LIST(操作码 0x20)。摄像头回复一个已注册通道的列表——四个内置通道(stdinstdoutstreamprofile)加上应用通过 protocol.register() 注册的任何通道。每一项都携带该通道的 ID、名称以及能力标志(只读、只写、可锁定)。

主机把这个列表收好,稍后当应用代码请求 channel_read("frame")channel_write("config", ...) 时再使用它——名称会被查找为一个通道 ID 一次,此后该通道上的每一个数据包都直接使用该 ID。

如果摄像头在初始列表之后注册了新通道——当应用在握手之后才开始运行时这很常见——摄像头会发出一个 CHANNEL_REGISTERED 事件数据包。主机会监听这些事件并刷新其内部的通道列表,这样一个较早连接上的主机脚本就能看到新注册的通道出现,而无需重启。

握手在连接建立时占用几次往返,此后便不再重复。稳态流量就只是数据包:分帧字节进、分帧字节出,通道 ID 两端都已知晓。