12.3. O formato de pacote

Cada byte que cruza a linha entre a câmara e o anfitrião faz parte de um pacote. Um pacote começa com um cabeçalho de 10 bytes, tem um payload de comprimento variável, e termina com um CRC final de 4 bytes. Não aparecem outros bytes na linha – assim que o anfitrião viu a palavra de sincronização de 2 bytes, os próximos bytes são um cabeçalho nesta sequência exata.

A horizontal layout of a protocol packet showing the 10-byte header (sync word, sequence number, channel ID, flags, opcode, payload length, header CRC) followed by the variable-length payload and a 4-byte payload CRC.

12.3.1. O cabeçalho

Dez bytes, compactados sem preenchimento. Cada campo:

  • sync – a palavra de 16 bits 0xD5AA em ordem little-endian. O byte 0 na linha é 0xAA, o byte 1 é 0xD5. Um recetor a analisar bytes pode encontrar o início de um pacote procurando o par AA D5; qualquer coisa anterior é tratada como lixo. A escolha do valor é deliberada: 0xAA e 0xD5 raramente aparecem em texto imprimível, e é improvável que o par ocorra por acidente no meio de um payload.

  • seq – um byte. Um contador que incrementa um por cada pacote enviado numa dada direção. O recetor verifica se o número de sequência do próximo pacote é o esperado; caso contrário, a camada de fiabilidade pede uma retransmissão.

  • chan – um byte. O ID do canal a que este pacote pertence. Os canais 0..31 são utilizáveis; os canais integrados stdin, stdout, stream e (opcionalmente) profile utilizam IDs fixos que a câmara reserva.

  • flags – um byte. Um campo de bits que indica ao recetor como interpretar o pacote:

    • bit 0 ACK – este pacote é um reconhecimento de um anterior.

    • bit 1 NAK – este pacote rejeita um anterior.

    • bit 2 RTX – este pacote é uma retransmissão.

    • bit 3 ACK_REQ – o remetente quer que este pacote seja reconhecido.

    • bit 4 FRAGMENT – seguem-se mais fragmentos a este numa mensagem maior.

    • bit 5 EVENT – este pacote transporta um evento de canal em vez de dados.

    • os bits 6 e 7 estão reservados.

  • opcode – um byte. O código de comando ou resposta. A biblioteca de protocolo reserva intervalos de opcode por finalidade:

    • 0x00..0x0F – comandos de protocolo (SYNC, GET_CAPS, SET_CAPS, STATS, VERSION).

    • 0x10..0x1F – comandos de sistema (RESET, BOOT, INFO, EVENT, MEMORY).

    • 0x20..0x2F – comandos de canal (LIST, POLL, LOCK, UNLOCK, SHAPE, SIZE, READ, WRITE, IOCTL, EVENT).

  • len – dois bytes, little-endian. O número de bytes de payload que se seguem ao cabeçalho. Um comprimento de zero é válido – muitos reconhecimentos e comandos pequenos não transportam payload.

  • crc – dois bytes. Um CRC-16 sobre os oito bytes de cabeçalho anteriores. Um recetor que receba um cabeçalho com um CRC incorreto descarta o pacote inteiro sem sequer olhar para o payload.

12.3.2. O payload

Zero ou mais bytes, tratados como opacos pela camada de enquadramento. O que está no payload depende do opcode: para uma resposta CHANNEL_READ são os dados reais do canal; para uma resposta GET_CAPS é uma pequena estrutura fixa; para uma escrita de canal é o que o anfitrião enviou.

O tamanho máximo do payload depende do tamanho do buffer de protocolo da câmara (consulte a tabela por placa em protocol.init()). As mensagens mais longas do que o limite são divididas em fragmentos com o indicador FRAGMENT definido em todos exceto o último.

12.3.3. O CRC final

Quatro bytes, um CRC-32 sobre o payload. Deteta corrupção que o CRC do cabeçalho não consegue ver, particularmente em payloads longos onde um erro de um único bit a meio do fotograma passaria de outra forma despercebido.

A divisão da verificação de integridade em dois CRCs é deliberada. O CRC do cabeçalho protege os próprios campos de enquadramento – particularmente o comprimento do payload. Sem um CRC de cabeçalho separado, um único bit invertido no byte de comprimento faria com que o recetor lesse um número errado de bytes para o payload e perdesse a sincronização do fluxo de bytes por completo; com ele, um cabeçalho danificado é rejeitado imediatamente e o recetor volta a procurar a próxima palavra de sincronização. O CRC do payload protege então o corpo da mensagem como preocupação separada, para que um bit invertido nos dados seja reportado como payload corrompido em vez de ser confundido com um erro de enquadramento.

O formato é suficientemente pequeno para percorrer byte a byte, e o facto de cada pacote ter o mesmo esquema – sincronização, depois cabeçalho, depois payload, depois CRC – significa que um analisador escrito à mão cabe num ecrã de código. É por isso que uma implementação de anfitrião pequena em C, Python ou Rust é um projeto de fim de semana; a biblioteca de protocolo é a versão Python mantida de cada lado.