12.3. O formato do pacote

Todo byte que cruza a transmissão entre a câmera e o host faz parte de um pacote. Um pacote começa com um cabeçalho de 10 bytes, segue com uma carga de comprimento variável e termina com um CRC final de 4 bytes. Nenhum outro byte aparece na transmissão – uma vez que o host tenha visto a palavra de sincronização de 2 bytes, os próximos bytes são um cabeçalho nesta sequência exata.

Um layout horizontal de um pacote de protocolo mostrando o cabeçalho de 10 bytes (palavra de sincronização, número de sequência, ID de canal, flags, opcode, comprimento da carga, CRC do cabeçalho) seguido pela carga de comprimento variável e um CRC de carga de 4 bytes.

12.3.1. O cabeçalho

Dez bytes, empacotados sem preenchimento. Cada campo:

  • sync – a palavra de 16 bits 0xD5AA em ordem little-endian. O byte 0 na transmissão é 0xAA, o byte 1 é 0xD5. Um receptor que varre os bytes pode encontrar o início de um pacote procurando pelo par AA D5; qualquer coisa antes dele é tratada como lixo. A escolha do valor é deliberada: 0xAA e 0xD5 raramente aparecem em texto imprimível, e o par dificilmente ocorre por acidente no meio de uma carga.

  • seq – um byte. Um contador que incrementa em um para cada pacote enviado em uma dada direção. O receptor verifica se o número de sequência do próximo pacote é o esperado; se não for, a camada de confiabilidade pede uma retransmissão.

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

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

    • bit 0 ACK – este pacote é uma confirmação 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 confirmado.

    • bit 4 FRAGMENT – mais fragmentos seguem este em uma mensagem maior.

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

    • os bits 6 e 7 são reservados.

  • opcode – um byte. O código de comando ou resposta. A biblioteca de protocolo reserva faixas 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 carga que seguem o cabeçalho. Um comprimento de zero é válido – muitas confirmações e comandos pequenos não carregam carga alguma.

  • crc – dois bytes. Um CRC-16 sobre os oito bytes anteriores do cabeçalho. Um receptor que recebe um cabeçalho com CRC inválido descarta o pacote inteiro sem sequer olhar a carga.

12.3.2. A carga

Zero ou mais bytes, tratados como opacos pela camada de enquadramento. O que há na carga 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 quer que o host tenha enviado.

O tamanho máximo da carga depende do tamanho do buffer de protocolo da câmera (consulte a tabela por placa em protocol.init()). Mensagens mais longas que o limite são divididas em fragmentos com a flag FRAGMENT ativada em todos, exceto no último.

12.3.3. O CRC final

Quatro bytes, um CRC-32 sobre a carga. Captura corrupções que o CRC do cabeçalho não consegue ver, particularmente em cargas longas onde um erro de um único bit no meio do quadro de outra forma passaria despercebido.

Dividir a verificação de integridade entre dois CRCs é deliberado. O CRC do cabeçalho protege os próprios campos de enquadramento – particularmente o comprimento da carga. Sem um CRC de cabeçalho separado, a inversão de um único bit no byte de comprimento faria o receptor ler o número errado de bytes da carga e dessincronizar completamente do fluxo de bytes; com ele, um cabeçalho danificado é rejeitado de imediato e o receptor revarre em busca da próxima palavra de sincronização. O CRC da carga então protege o corpo da mensagem como uma preocupação separada, de modo que a inversão de um bit nos dados é reportada como uma carga corrompida, em vez de confundida com um erro de enquadramento.

O formato é pequeno o bastante para ser percorrido byte a byte, e o fato de que todo pacote tem o mesmo layout – sincronização, então cabeçalho, então carga, então CRC – significa que um parser feito à mão cabe em uma tela de código. É por isso que uma pequena implementação de host em C, Python ou Rust é um projeto de fim de semana; a biblioteca de protocolo é a versão Python mantida em cada lado.