# USB ovladače bez bolesti: od teorie k kódu na libusb
Psaní ovladačů pro USB zařízení často děsí vývojáře složitostí low-level kódu. S vhodným nástrojem, jako je libusb, se však proces stává srovnatelně obtížným se síťovým programováním přes sokety. V tomto článku si rozebereme, jak vytvořit ovladač pro USB zařízení na příkladu Android zařízení v režimu bootloaderu.
Základy USB: co by měl vědět vývojář
USB specifikace definuje standardní mechanismus identifikace zařízení prostřednictvím VID (Vendor ID) a PID (Product ID). Tyto identifikátory přiděluje výrobci (VID – USB-IF) a konkrétnímu produktu (PID – samotný výrobce). Při připojení zařízení host požaduje deskriptory – binární struktury ve firmwaru obsahující informace o třídě zařízení, jeho schopnostech a požadovaném ovladači. Klíčový bod: většinu úkolů lze vyřešit v uživatelském prostoru prostřednictvím standardních ovladačů jako Winusb.sys (Windows) nebo usbfs (Linux), bez práce s kódem jádra.
Důležité je pochopit terminologii:
- Koncový bod (Endpoint): Logický kanál pro přenos dat (Control, Bulk, Interrupt, Isochronous).
- Deskriptor zařízení: Kořenová struktura s VID/PID a základními parametry.
- Třída zařízení: Standardizovaná funkčnost (HID, Mass Storage), ale mnohá zařízení používají Vendor Specific Class.
Příprava zařízení: výběr a nastavení
Jako příklad vezmeme Android zařízení v režimu Bootloader (fastboot). Tento výběr je opodstatněný:
- Dostupnost: většina chytrých telefonů podporuje přechod do fastboot.
- Jednoduchost protokolu: dokumentace k fastboot je otevřená a minimalistická.
- Absence předinstalovaných ovladačů: OS nepřebírá komunikaci.
Přepnutí do režimu Bootloader obvykle vyžaduje podržení kombinace tlačítek při zapnutí (např. tlačítko zesílení hlasitosti + napájení). Pro identifikaci zařízení v Linuxu použijte lsusb:
$ lsusb
Bus 008 Device 014: ID 18d1:4ee0 Google Inc. Nexus/Pixel Device (fastboot)
Zde je 18d1 VID společnosti Google, 4ee0 PID pro fastboot. Ve Windows poskytuje analogické informace USB Device Tree Viewer. Klíčový bod: pokud OS načte ovladač (např. v Správci zařízení se zobrazí se symbolem ⚠️), bude nutná výměna za Winusb.sys přes Zadig.
Zjišťování zařízení: od ruční analýzy k programové realizaci
Proces zjišťování (enumeration) se spustí automaticky při připojení. OS analyzuje VID/PID a třídu zařízení pro výběr ovladače. Pro uživatelský prostor tento proces realizujeme přes libusb. Podívejme se na kód registrace obslužné rutiny připojení:
#include <print>
#include <libusb-1.0/libusb.h>
auto hotplug_callback(
libusb_context *ctx,
libusb_device *device,
libusb_hotplug_event event,
void *user_data
) -> int {
std::println("Device plugged in!");
return 0;
}
auto main() -> int {
libusb_context *context = nullptr;
libusb_init(&context);
libusb_hotplug_callback_handle handle;
libusb_hotplug_register_callback(
context,
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
LIBUSB_HOTPLUG_ENUMERATE,
0x18d1, 0x4ee0,
LIBUSB_HOTPLUG_MATCH_ANY,
hotplug_callback, nullptr,
&handle
);
while (true) {
if (libusb_handle_events(context) < 0) break;
}
libusb_hotplug_deregister_callback(context, handle);
libusb_exit(context);
}
Tento kód inicializuje libusb, zaregistruje callback pro zařízení s VID=0x18d1 a PID=0x4ee0 a čeká na připojení. Při úspěšném spuštění vypíše "Device plugged in!". Na Windows může být nutné odpojení jádra:
libusb_detach_kernel_driver(handle, 0);
Komunikace se zařízením přes Control Endpoint
Pro základní interakci použijeme Control Endpoint (ID 0x00) – standardní kanál pro servisní požadavky. Realizujeme odeslání požadavku GET_STATUS:
libusb_device_handle *handle = nullptr;
libusb_open(device, &handle);
std::vector<std::uint8_t> data(0xFF);
const auto result = libusb_control_transfer(
handle,
LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_DEVICE | LIBUSB_REQUEST_TYPE_STANDARD,
LIBUSB_REQUEST_GET_STATUS,
0x00, 0x00,
data.data(), data.size(),
1000
);
if (result >= 0) print_bytes(std::span(data).subspan(0, result));
libusb_close(handle);
Úspěšná odpověď (např. 01 00) se dekóduje podle specifikace: první bajt – zdroj napájení (1 = z baterie), druhý – podpora vzdáleného probuzení (0 = nepodporováno). Control Endpoint umožňuje odesílat i další standardní požadavky (GET_DESCRIPTOR, SET_CONFIGURATION), což je klíčové pro získání deskriptorů.
Požadavek na deskriptory: klíč k pochopení zařízení
Deskriptory jsou základem interakce s USB zařízením. Požádáme o deskriptor zařízení přes GET_DESCRIPTOR:
const auto result = libusb_control_transfer(
handle,
LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_DEVICE | LIBUSB_REQUEST_TYPE_STANDARD,
LIBUSB_REQUEST_GET_DESCRIPTOR,
(LIBUSB_DT_DEVICE << 8), // Typ deskriptoru
0x00,
data.data(), data.size(),
1000
);
Získaná data obsahují strukturu libusb_device_descriptor s poli:
bcdUSB: Verze USB specifikacebMaxPacketSize0: Maximální velikost paketu pro Control EndpointidVendor/idProduct: VID a PIDbNumConfigurations: Počet konfigurací
Analýza deskriptorů umožňuje určit podporované rozhraní a koncové body. Pro získání konfiguračního deskriptoru změňte typ požadavku na LIBUSB_DT_CONFIG a uveďte index konfigurace v wIndex.
Co je důležité
- Práce v uživatelském prostoru: Používejte libusb místo kódu jádra – snadnější ladění a bezpečnější.
- VID/PID – hlavní identifikátory: Na nich je postaveno přiřazení ovladače, třída zařízení je často neinformative (Vendor Specific Class).
- Control Endpoint – základní nástroj: Přes něj získáváte deskriptory a ovládáte zařízení před nastavením dalších endpointů.
- Deskriptory – zdroj pravdy: Jejich struktura je popsána v USB specifikaci, dekódování poskytuje úplný přehled o schopnostech zařízení.
— Editorial Team
Zatím žádné komentáře.