\ Rozbor USB

Rozbor USB
Universal Serial Bus je standard organizace USB Implementers Forum. Definuje průmyslový standard a popisuje vlastnosti sběrnice, protokol, typy přenosů, hospodaření s prostředky a potřebnou programovou podporu. Existují tři verze specifikací: USB 1.0, USB 1.1 a USB 2.0. V této práci se budeme zabývat výhradně verzí USB 1.1, jejíž specifikace je k dispozici na http://smrz.chrudim.cz/usb/usbspec.zip. Mezi materiály, které vysvětlují USB 1.1 patří také dokument USB in a Nutshell, který více didakticky popisuje principy komunikace po USB.

Vlastnosti USB 1.1

  • sériová poloduplexní sběrnice s přenosovou rychlostí 1,5 Mbaud (low speed) a 12Mbaud (full speed)
  • elektricky je signál přenášen po diferenciálním vedení a je galvanicky spojen s napájecím (Vbus) a zemnícím (GND) vodičem
  • zařízení mohou být napájena přímo přes vodiče Vbus a GND, ale musejí mít omezenou spotřebu (<500mA) a schopnost úsporného režimu.
  • linková vrstva používá NRZI kódování a bit stuffing, pakety jsou chráněny CRC16 nebo CRC5
  • de facto se nejedná o sběrnici, ale o propojení PTP (point-to-point) mezi hostitelem (host) nebo hubem a zařízením (device)
  • síťová topologie je stromového typu, v každém uzlu je umístěn přepínač (switch, ale nazýván USB hub)
  • z hlediska transportní vrstvy (roura mezi odesílatelem a příjemcem) je celé přenosové médium transparentní
  • zařízení mohou obsahovat až 7 rour (pipe) a více konfigurací, zařízení se sama enumerují a splňují specifikaci Plug and Play
  • maximální počet USB zařízení připojených k jednomu kořenovému přepínači je 127

Elektrická vrstva

Diferenciální vedení o impedanci 90 ohm slouží pro obousměrnou komunikaci. Přístup k médiu je deterministický a nemůže na něm dojít ke kolizi, protože komunikace je vždy iniciována hostitelem. Oba diferenciální vodiče jsou na obou stranách vedení buzeny CMOS hradly (obr. 1) se sériově zapojenými rezistory (28 až 44 ohm), sloužícími jako impedanční přizpůsobení. Hostitel i zařízení mají možnost odpojení budicího hradla od linky pro případ, kdy přecházejí z vysílacího do přijímacího režimu.
obr. 1 - Budiče linky

Úrovně jsou na diferenciálních vodičích pro klidový stav (hostitel i zařízení jsou odpojené) na straně hostitele určeny dvěma pull-down rezistory a na straně zařízení jedním pull-up rezistorem. Připojením pull-up rezistoru k D+ nebo D- vodiči se zařízení identifikuje buď jako full-speed nebo jako low-speed (tabulka 1). S tím se mění také počáteční podmínka pro výpočet NRZI (tabulka 2).

pull up D+pull up D-
low-speed-1,5k
full-speed1,5k-
Tabulka 1.

D+D-počáteční stav D+
low-speed0V3,3V0
full-speed3,3V0V1
Tabulka 2.

Přítomnost společného potenciálu na obou diferenciálních vodičích zároveň po dobu alespoň dvou bitů znamená konec paketu a je označována jako SE0.

Mechanická část

Diferenciální vedení se realizují kroucenou dvojlinkou se zemněným pláštěm (obr. 2).
obr. 2 - kabel

Konektory se na straně hostitele a na straně zařízení liší, takže nelze spojit dva hostitele nebo dvě zařízení k sobě. Typ A (samice) je na straně hositele a tyb B (samice) je na straně zařízení. U miniaturních zařízení se nemusí používat propojovací A-B kabel, ale zařízení je přímo opatřeno konektorem typu A (samec).

Linková vrstva

Přenášené elementární datové struktury se nazývají pakety. MTU (maximal transfer unit) neboli maximální počet bytů v paketu (bez hlaviček a CRC) je pro 0. endpoint 8,16,32 nebo 64 bytů, pro ostatní endpointy je volitelný v pomocí Endpoint descriptoru. Čistá data je tedy třeba rozdělit více paketů, opatřit je hlavičkami a cyklickou redundantní kontrolou. Dále je nutné provést bit stuffing (za každou 6. jedničku musí být vložena nula), čímž může vzniknout neceločíselný počet bytů k přenesení. Poté se data zakódují kanálovým kódováním NRZI (Non-Return to Zero Invert), které nulu kóduje jako změnu úrovně a jedničku jako setrvání v úrovni. Pro bit v čase t platí line(t)=line(t-1) xor (!bit(t)). Realizace může proběhnout pomocí D klopného obvodu (DFF) na obr. 3.
obr. 3 - NRZI kodér

Procesy linkové vrstvy (bit stuffing a NRZI) jsou zobrazené na obr. 4.
obr. 4 - příklad bit stuffingu a NRZI kódování

Všechny operace jsou bitově orientované a lze je tedy přepsat do kaskády proudových filtrů (obr. 5), které mají za vstup bitový tok dat (LSb i LSB se posílají nejdříve). Skutečnost, že data se zpracovávají bit po bitu nahrává realizaci USB řadiče na hradlovém poli nebo pomocí bitového stavového automatu. Rozhodně však nelze data zpracovávat instrukcemi procesoru po celých bytech. Budeme-li například vstupní proud zpracovávat procesorem, bude se jednat o operace nad každým jedním bitem a teprve až po provedení všech dekódování a CRC výpočtů bude možné data deserializovat a skladovat po bytech.
obr. 5 - proudové filtry výstupní části řadiče

V případě, že odesílaná data jsou dopředu známa, je možné vyloučit z kaskády filtrů výpočet CRC16 a jeho předpočítanou hodnotu zařadit za data paketu. Pro náš úkol je zajímavá i možnost předpočítat si data úplně celá (pro oba diferenciální vodiče) a pak je pouze po dvojicích serializovat a zapisovat na linku. Takový přístup sice není elegantní, přesto se s ním v naší práci později setkáme.

Cyklická redundantní kontrola

Obsah paketů je doplněn 16-bitovým číslem, takzvanou cyklickou redundantní kontrolou (CRC). Odesílatel počítá CRC pro odeslanou zprávu a hodnotu CRC přikládá na konec paketu. Příjemce má dvě možnosti, jak zkontrolovat správnost paketu:
  1. příjemce přečte paket, který se skládá z dat (payload) a z dvoubytového CRC. Příjemce pro data spočítá CRC a porovná ji s přijatou CRC. Pokud se obě čísla shodují, paket dorazil správně. Příjemce buď potřebuje vědět dopředu, jak dlouhá jsou data (to je na USB sběrnici nemožné), a nebo bude počítat CRC s dvoubytovým zpožděním a při konci paketu bude jisté, že výpočet proběhl pouze nad daty a ne nad přijímaným CRC.
  2. příjemce přečte paket a CRC spočítá z celého jeho obsahu. Cyklická redundantní kontrola obecně má tu vlastnost, že přijímáme-li stejnou posloupnost bitů, jaká byla obsahem CRC registru, pak dostaneme vždy stejný výsledek (tzv. zbytek, residual). CRC spočítané z celého paketu tedy musí vyjít pro každý správně přijatý paket vždy stejně. Pro náš případ je zbytek 0x800D, zrcadlově 0x4FFE.
USB specifikace definuje pořadí odesílaných bitů jako LSb first, tedy nejméně významný bit je odesílán nejdříve. Pokud máme data uložena v registru, musíme provést pravou rotaci (rotate through carry right). Tím se vždy nejméně významný bit odebere a zpracuje. Po provedení sedmi rotací zbyde v registru nejvíce významný bit - a ke zpracování se tedy dostane jako poslední. Je výhodné, aby reprezentace všech čísel, která se chystáme po USB sběrnici odeslat, měla shodné pořadí bitů. Zvykem je, že binární čísla (a obecně čísla) se zapisují zleva doprava od MSb po LSb.
Výpočet CRC je možno provést dvěma způsoby. První je uveden v USB specifikaci a používá generující polynom x16+x15+x2+1 s binárním vyjádřením 0x8005 a zbytkem 0x800D. Při generování se ale nepochopitelně používá levá rotace (přestože všechna data jsou rotována doprava) a tím vzniká potřeba před odesláním CRC zrcadlově převrátit. Druhý způsob výpočtu používá stejný generující polynom, ale protože používá správně pravou rotaci, musí být jeho binární vyjádření jiné, tedy 0xA001 a zbytek 0x4FFE. Rozdíl mezi metodami je kromě pořadí bitů také v počátečních hodnotách registrů.

1. metoda:
  1. počáteční hodnota CRC je 0xFFFF
  2. CRC=(CRC shl 1), přičemž vysunutý bit (MSb) je předmětem dalšího výpočtu
  3. pokud je MSb různý od vstupního bitu (xor je 1), pak CRC=CRC xor 0x8005
  4. pro všechny vstupní bity opakovat od bodu 2, jinak skončit
  5. budeme-li CRC odesílat, musíme jej zrcadlově převrátit (15. bit na 0., 14. bit na 1., atd.) a znegovat (xor 0xFFFF)
  6. byl-li CRC počítán pro kontrolu příchozího paketu, musí se pro bezchybný paket rovnat zbytku 0x800D
Tento postup je ale nepříjemný kvůli bodu 5), kdy je potřeba zrcadlově převracet a negovat CRC. 2. metoda (rozepsaná ve funkci crcpipe()) je ekvivalentní, zrcadlově převrácená od začátku a méně náročná na výpočet.

2. metoda:
  1. počáteční hodnota CRC je 0x0000
  2. CRC=(CRC shr 1) or 0x8000, přičemž vysunutý bit (LSb) je předmětem dalšího výpočtu
  3. pokud je LSb stejný jako vstupní bit (xor je 0), pak CRC=CRC xor 0xA001
  4. pro všechny vstupní bity opakovat od bodu 2, jinak skončit
  5. budeme-li CRC odesílat, můžeme jej použít bez jakékoliv úpravy
  6. byl-li CRC počítán pro kontrolu příchozího paketu, musí se pro bezchybný paket rovnat zbytku 0x4FFE
Výpočet CRC je možné hardwarově realizovat shift registrem, jehož některé prvky mají možnost znegovat svůj obsah (funkce XOR - negace na masku). U takového CRC shift registru nemá smysl hovořit o levé nebo pravé rotaci, protože pořadí dat vstupu a výstupu je zaručeně stejné a nevzniká zde zmatek ohledně pořadí bitů. Shift regist pro počáteční hodnotu 0x0000 je uveden na obrázku 5.
Obr. 5 - výpočet CRC16

Programově lze výpočet CRC nahradit voláním procedury crcpipe() pro každý přijatý bit:
var crc:word; {pocatecni hodnota je 0}
procedure crcpipe(bit:byte);
var lsb:byte;
begin
 lsb:=crc and 1;
 crc:=(crc shr 1) or $8000;
 if lsb=bit then begin
  crc:=crc xor $A001;
 end;
end;
Pro zkušební výpočty CRC slouží program data2usb.pas (Pascal). Ve zdrojovém kódu programu je vidět praktický výpočet CRC. Vývojové prostředí Turbo Pascalu se skládá ze třech souborů, které je potřeba stáhnout do jednoho adresáře a spustit turbo.exe. Soubory jsou k dispozici ke stažení jako turbo.exe, turbo.tpl, turbo.tph.
Dalším programem je usbprep.exe (příprava USB dat, zdroj usbprep.pas), který přijímá jako parametr zprávu, kterou chceme zakódovat. Zpráva může být libovolně dlouhá. Program zprávu rozdělí na 8 bytové pakety, vypočítá CRC16 a opatří je bit stuffingem. Zpráva se předává programu jako parametr na příkazové řádce, mezerou se oddělují jednotlivé byty nebo wordy. Wordy jsou programem automaticky rozdělelny na byty a položeny LSB napřed. Pokud je počet bitů v paketu neceločíselným násobkem 8 (po doplnění bit stuffingem je to běžné) a přesto potřebujeme odesílací rutině předat data po bytech, musíme bitový tok zarovnat doprava a v prvním bytu doplnit chybějící pozice jedničkami. Při odesílání se úvodní jedničky nijak neprojeví (vyplývá to z vlastností NRZI kanálového kódování), pouze se prodlouží doba před synchronizační sekvencí 0x80. Pokud by se chybějící pozice doplňovaly nulami, prodloužila by se synchronizační sekvence, což není v souladu se specifikací pro low-speed USB (ve specifikaci je délka synchronizačního bytu stanovena na 0x80). U odesílacích rutin, které přijímají data včetně bit stuffingu (9MHz a 10,5 MHz) je vhodnější úvodní jedničky ignorovat, aby se zbytečně neprodlužovala doba mezi posledním přijatým a následujícím odeslaným paketem. Většínou ale rutiny (12MHz, 15MHz, 18MHz a 19,5MHz) očekávají data bez bit stuffingu a celá operace přípravy dat se jednodušuje pouze na dělení na pakety a výpočet CRC.

Ukázka použití programu usbprep.exe (zprávou je zde Device Descriptor):
c:\>usbprep 12 01 0100 00 00 00 08 065d 1031 0004 00 00 00 01

USB prep - rozdeli zpravu na pakety a opatri je CRC16 a bit stuffingem
Prevod zpravy:  12 01 0100 00 00 00 08 065d 1031 0004 00 00 00 01
Info: bitove toky jsou LSb (least significant bit) first, hex cisla LSB (least s
ignificant byte) first.
------
data1: 0100100010000000000000001000000000000000000000000000000000010000
crc: 0xE713
00000001110100100100100010000000000000001000000000000000000000000000000000010000
1100100011100111
delka paketu: 96 b
paket: 80 4B 12 01 00 01 00 00 00 08 13 E7
delka paketu s bit stuffingem: 96 b
delka paketu s bit stuffingem mod 8: 0
paket s bit stuffingem: 80 4B 12 01 00 01 00 00 00 08 13 E7
prvni byte, doplneno jednickami zpredu (LSB): 00000001
------
data0: 1011101001100000100011000000100000100000000000000000000000000000
crc: 0x83D9
00000001110000111011101001100000100011000000100000100000000000000000000000000000
1001101111000001
delka paketu: 96 b
paket: 80 C3 5D 06 31 10 04 00 00 00 D9 83
delka paketu s bit stuffingem: 96 b
delka paketu s bit stuffingem mod 8: 0
paket s bit stuffingem: 80 C3 5D 06 31 10 04 00 00 00 D9 83
prvni byte, doplneno jednickami zpredu (LSB): 00000001
------
data1: 0000000010000000
crc: 0x8F3F
000000011101001000000000100000001111110011110001
delka paketu: 48 b
paket: 80 4B 00 01 3F 8F
delka paketu s bit stuffingem: 49 b
delka paketu s bit stuffingem mod 8: 1
paket s bit stuffingem: 7F C0 25 80 80 1F 8F
prvni byte, doplneno jednickami zpredu (LSB): 11111110

Transportní vrstva

Na této úrovni řešíme vyměňování paketů mezi hostitelem a zařízením za účelem:
  1. přenést užitečná data (payload) - rourový přístup
  2. provést enumeraci (počáteční vyjednávání) speciální rourou (endpoint 0)
  3. řídit stavy zařízení
  4. korigovat chyby při přenosu, zařizovat handshaking
Existuje 16 typů paketů rozdělěných do čtyřech skupin:
  • potvrzovací (handshake) - ACK, NAK, STALL, NYET (No Response Yet)
  • pověřovací (token) - IN, OUT, SETUP, SOF
  • datové (data) - DATA0, DATA1, DATA2, MDATA
  • speciální (special) - PREamble, ERR, Split, Ping
Identifikátor paketu (PID) je prvním bytem v paketu (po synchronizační sekvenci SYNC 0x80) a skládá se z čísla paketu (v dolní polovině bytu) a invertovaného čísla paketu (v horní polovině bytu):

7 - MSb

PID3

6

PID2

5

PID1

4

PID0

3

PID3

2

PID2

1

PID1

0 - LSb

PID0


Typy paketů:
PID3 - PID0PIDpakettyp
00010xE1OUT TokenToken
10010x69IN Token
01010xA5SOF Token
11010x2DSETUP Token
00110xC3DATA0Data
10110x4BDATA1
01110x87DATA2
11110x0FMDATA
00100xD2ACKHandshake
10100x5ANAK
11100x1ESTALL
01100x96NYET
11000x3CPREambleSpecial
11000x3CERR
10000x78Split
01000xB4Ping

Formát paketu je závislý na jeho typu. Společné pro všechny pakety jsou úvodní synchronizace (SYNC), identifikace paketu (PID) a konec paketu (EOP, SE0 po dobu 2-3 bitů).
data packet
token packet
SOF packet
handshake packet

Na USB zařízení se stále snažíme dívat jako na rouru, která spojuje hostitele s koncovým bodem (endpointem). Přenos těchto užitečných dat se děje pomocí DATA paketů. Přenášený obsah v DATA paketech není sběrnicí interpretován a může obsahovat libovoná data. K řízení přenosu a k ovlivňování koncového bodu slouží jiné druhy paketů. Pověřovací pakety mají za úkol řídit směr komunikace (IN, OUT, SETUP) a potvrzovací (handshakové) pakety informovat o úspěšnosti přenosu. Specifikace vyžaduje, aby každé zařízení bylo schopno reagovat kromě datových paketů (s užitečným obsahem) také na požadavky sběrnice (requesty). Paket SETUP uvádí zařízení režimu vyřizování požadavků. Po SETUP paketu přichází DATA paket, jehož obsah speficikuje úkon, který se od zařízení očekává. Zařízení poté v DATA paketech odesílá hostiteli výsledek požadavku. V průběhu úvodní identifikace zařízení (enumerace) patří mezi nejčastější požadavky čtení Device Descriptoru, Configuration Descriptoru a jiných datových struktur zařízení. USB specifikace umožňuje zařízení složitě se identifikovat, tvořit více konfigurací, podporovat různé jazyky a definovat násobné endpointy. Pro naši úlohu je naopak potřeba vynechat všechny volitelné možnosti a zachovat pouze základní schopnosti zařízení.

Nutné schopnosti zařízení:
  1. Odeslat 18 bytů Device Descriptoru
  2. Odeslat 9 bytů Configuration Descriptoru
  3. Odeslat 25 bytů descriptor komplexu (Configuration, Interface a Endpoint Descriptor)
  4. Přijmout příkaz Set Address
  5. Přijmout příkaz Set Configuration
  6. Provést vlastní reset na základě SE0 trvajícího 10ms
  7. Přijmout data pomocí posloupnosti OUT a DATA paketů

Ukázková komunikace na USB sběrnici

Pro získání představy, jak probíhá komunikace na USB sběrnici, je vhodné přímo změřit průběhy napětí na diferenciálních vodičích sběrnice. Je možné též použít softwarové monitory USB. Oba způsoby jsou vhodné k jinému účelu. Sledování osciloskopem umožňuje pochopit elektrickou vrstvu, kanálové kódování a doby trvání SE0. Softwarový monitoring podává obraz o vyměňovaných datech, ale z principu nedovede zobrazit jevy, o které se stará řadič USB (chyby přenosu, časování signálů atd.) Zařízení, která pasivně poslouchají na sběrnici a poskytují kompletní obraz o průbězích a datech jsou ukázána na adrese http://www.beyondlogic.org/usb/protocolanalysers.htm.

Komunikace myši s operačním systémem Windows XP
Tabulka barevně značí, o jaké typy paketů se jedná. Slovo sync znamená synchronizační sekvenci na začátku paketu 0x80 a slovo eop značí přítomnost SE0 na diferenciálních vodičích po dobu dvou až třech bitů. Data jsou zobrazena v dekódované podobě (bez NRZI a bit stuffingu). Pakety DATA0 a DATA1 by se měly periodicky střídat.

HostitelZařízení
SETUP packet
sync 2d 00 10 eop
DATA0 packet - Get Device Descriptor request
sync c3 80 06 00 01 00 00 40 00 dd 94 eop
ACK
sync d2 eop
IN packet
sync 69 00 10 eop
DATA1 packet - Device Descriptor
sync 4b 12 01 00 01 00 00 00 08 13 e7 eop
ACK
sync d2 eop
OUT packet
sync e1 00 10 eop
DATA1 packet - Transaction Complete
sync 4b 00 00 eop
ACK
sync d2 eop

BUS RESET - SE0 po dobu 10ms

SETUP packet
sync 2d 00 10 eop
DATA0 packet - Set Address request
sync c3 00 05 02 00 00 00 00 00 eb 16 eop
ACK
sync d2 eop
IN packet
sync 69 00 10 eop
DATA1 packet - Transaction Complete
sync 4b 00 00 eop
ACK
sync d2 eop

následuje čtení již celého Device Descriptoru. Změnila se též adresa zařízení na 0x02

SETUP packet
sync 2d 02 a8 eop
DATA0 packet - Get Device Descriptor request
sync c3 80 06 00 01 00 00 12 00 e0 f4 eop
ACK
sync d2 eop
IN packet
sync 69 02 a8 eop
DATA1 packet - Device Descriptor
sync 4b 12 01 00 01 00 00 00 08 13 e7 eop
ACK
sync d2 eop
IN packet
sync 69 02 a8 eop
DATA0 packet - Device Descriptor
sync c3 5d 05 30 10 04 00 00 00 eb 52 eop
ACK
sync d2 eop
IN packet
sync 69 02 a8 eop
DATA1 packet - Device Descriptor
sync 4b 00 01 3f 8f eop
ACK
sync d2 eop
OUT packet
sync e1 02 a8 eop
DATA1 packet - Transaction Complete
sync 4b 00 00 eop
ACK
sync d2 eop

čtení Configuration Descriptoru

SETUP packet
sync 2d 02 a8 eop
DATA0 packet - Get Configuration Descriptor request
sync c3 80 06 00 02 00 00 09 00 ae 04 eop
ACK
sync d2 eop
IN packet
sync 69 02 a8 eop
DATA1 packet - Configuration Descriptor
sync 4b 09 02 22 00 01 01 00 a0 0a 98 eop
ACK
sync d2 eop
IN packet
sync 69 a2 a8 eop
DATA0 packet - Configuration Descriptor
sync c3 32 c1 6a eop
ACK
sync d2 eop
OUT packet
sync e1 02 a8 eop
DATA1 packet - Transaction Complete
sync 4b 00 00 eop
ACK
sync d2 eop

čtení Configuration, Interface, HID a Endpoint Descriptoru o délce 34 bytů

SETUP packet
sync 2d 02 a8 eop
DATA0 packet - Get Configuration Descriptor request
sync c3 80 06 00 02 00 00 ff 00 e9 a4 eop
ACK
sync d2 eop
IN packet
sync 69 02 a8 eop
DATA1 packet - Configuration Descriptor
sync 4b 09 02 22 00 01 01 00 a0 0a 98 eop
ACK
sync d2 eop
IN packet
sync 69 02 a8 eop
DATA0 packet - Configuration Descriptor
sync c3 32 09 04 00 00 01 03 01 35 4d eop
ACK
sync d2 eop
IN packet
sync 69 02 a8 eop
DATA1 packet - Configuration Descriptor
sync 4b 02 00 09 21 00 01 00 01 12 b3 eop
ACK
sync d2 eop
IN packet
sync 69 02 a8 eop
DATA0 packet - Configuration Descriptor
sync c3 22 4a 00 07 05 81 03 04 33 e6 eop
ACK
sync d2 eop
IN packet
sync 69 02 a8 eop
DATA1 packet - Configuration Descriptor
sync 4b 00 0a 7e 48 eop
ACK
sync d2 eop
OUT packet
sync e1 02 a8 eop
DATA1 packet - Transaction Complete
sync 4b 00 00 eop
ACK
sync d2 eop

Tím končí enumerace a pokračují další transakce, které odpovídají funkci zařízení.