← Todos os artigos

· 3 min de leitura

Fazer funcionar no Linux um dispositivo sem documentação

Um controle USB sem suporte oficial, um capturador de pacotes e um workspace de Rust. Como se ataca um protocolo que ninguém publicou.

RustUSBLinux

Comprei um controle de botões pensado para streaming. Funcionava no Windows com o software do fabricante. No Linux, nada: o sistema o via como um dispositivo HID genérico e os botões não faziam absolutamente nada de útil.

Não havia documentação do protocolo. Não havia SDK. Fabricante não responde esse tipo de pergunta.

Acabei escrevendo o driver. Conto porque o método serve para qualquer integração em que a outra ponta é uma caixa-preta, e isso acontece bem mais do que parece: uma central telefônica antiga, uma leitora de cartões, uma balança industrial, um ERP sem API.

Primeiro olhar, depois escrever

A tentação é abrir o editor. É o erro.

A primeira coisa é capturar tráfego real. No Linux, o usbmon mostra tudo o que entra e sai de uma porta USB, e o Wireshark lê isso direto. Com o dispositivo funcionando numa máquina virtual com Windows, você aperta botões e observa quais pacotes saem.

O que você procura nessa montanha de bytes são padrões:

Esse último é o tipo de descoberta que muda todo o desenho do código. E não se deduz lendo: deduz-se apertando botões e anotando.

Anote tudo, inclusive o que você não entende

Na minha captura havia um byte que mudava sem que eu tocasse em nada. Perdi algumas horas tentando dar sentido a ele até entender que era um contador de sequência: subia sozinho, reiniciava em 255 e não significava mais nada.

Deixei isso escrito no repositório, em texto puro, ao lado do código. Não porque seja elegante, mas porque daqui a seis meses eu mesmo não vou lembrar, e porque outra pessoa vai querer entender por que esse byte é ignorado.

Um protocolo reconstruído sem anotações é um protocolo que vai ter que ser reconstruído de novo.

Por que Rust, e por que três crates

Escolhi Rust por um motivo concreto: num driver, os erros são de bytes e de tempo, e o compilador tira do seu caminho toda uma família de erros de memória enquanto você briga com os outros dois.

Dividi em três partes:

  1. O protocolo, sem nada de entrada e saída. Recebe bytes, devolve eventos. Dá para testar sem o dispositivo plugado.
  2. O transporte, que fala com o USB de verdade.
  3. A aplicação, que traduz eventos em ações do sistema.

Essa separação não é cerimônia. É o que permite ter testes de verdade: 90% da lógica é testada com bytes guardados num arquivo, sem hardware. Se tudo estivesse junto, testar qualquer coisa exigiria ter o aparelho plugado, e os testes nunca seriam escritos.

Teste onde você não escreveu

O driver roda em CI contra Fedora, Debian, Ubuntu e Arch. Não é excesso de zelo: as permissões de acesso ao USB e as regras do udev mudam entre distribuições, e é exatamente o tipo de coisa que funciona na sua máquina e falha na de quem instala.

Se você vai publicar algo que toca o sistema, teste em mais de um sistema. É barato e te poupa metade das issues.

O que fica

Quando um fornecedor diz “não tem API”, o que quase sempre quer dizer é “não tem API documentada”. Se existe um cliente que funciona, existe um protocolo, e um protocolo pode ser observado.

Nem sempre compensa: leva tempo, e se o fabricante mudar o firmware você recomeça. Mas quando a alternativa é trocar todo o parque de equipamentos, duas semanas de engenharia reversa saem bem mais baratas.

O código está publicado com licença MIT, caso o ponto de partida seja útil para alguém.

Acontece algo parecido com você? Conte o seu caso

Setas para mover, Enter para escolher, Esc para fechar