Enumerace je proces počátečního vyjednávání, kdy hostitel a host komunikují za účelem vzájemného poznávání a nastavování. Během enumerace posílá zařízení informace o svých hardwarových a softwarových prostředcích hostiteli, zatímco hostitel poskytuje zařízení adresu a mění vnitřní stavy zařízení.
Pokud budeme chápat komunikaci mezi hostitelem a zařízením jako obousměrnou rouru, po které proudí užitečná data, nutně dojdeme k potřebě odlišit data, která jsou předmětem enumerace a ostatní data, která jsou závislá na funkci zařízení, kvůli které bylo sestrojeno. USB specifikace řeší potřebu více komunikačních kanálů zavedením tzv. endpointů. Endpoint je funkční celek, který má k dispozici obousměrnou rouru, po níž komunikuje s hostitelem. Endpoint může být jak konzument, tak producent datového toku. Jedno zařízení může mít až 7 endpointů (rour). Nultý endpoint je určen pro vyřizování enumerace a má vlastnosti pevně dané specifikací. Vlastnosti ostatních endpointů jsou předány hostiteli během enumerace ve speciálních datových strukturách.
Pokud by se komunikace po rouře skládala pouze z užitečných dat (například výsledků měření), neměl by hostitel k dispozici žádný nástroj, kterým by měnil vnitřní stavy zařízení a nemohl by ovlivňovat jeho chování. Aby mohl hostitel posílat kromě dat také příkazy, zavádí USB specifikace zvláštní druh paketů (setup packet), které říkají, že data předaná v následné transakci nejsou určena pro funkčnost zařízení, ale pro řízení stavů zařízení.
Endpoint 0 musí reagovat na setup pakety a odesílat informace, které jsou po zařízení požadovány. Abychom mohli navrhnout program, který bude požadavky vyřizovat, musíme si uvědomit, z jakých atomických operací se komunikace po USB skládá.
Transakce
Jak již bylo uvedeno v rozboru USB, přenosovou jednotkou na sběrnici je paket. Paket může obsahovat sdělení různé povahy:
- lichá část dat, sudá část dat (DATA0, DATA1)
- handshaková odpověď (ACK, NAK)
- oznámení, že následující data jsou příkazem (SETUP packet)
- výzva k odebrání dat (OUT packet)
- výzva k odeslání dat (IN packet)
Problém, který obecně nastává u datových přenosů, je konečná délka paketu. MTU (maximum transfer unit) udává počet bytů, které smějí být přeneseny v rámci jednoho paketu. MTU reflektuje hardwarová omezení, například nepřesnost synchronicity příjemce a odesílatele, velikosti vstupně/výstupních bufferů, účinnost CRC a podobně. Endpoint 0 má maximální délku dat stanovenu na 8 bytů. Jev, který nastává v důsledku omezení velikosti paketů, se nazývá fragmentace (dělení na části). Proces přenosu dat se zesložiťuje a přenos paketů musí být opatřen rozšířením nazvaným transakce.
Transakce je předpis, který stanoví posloupnost různých druhů paketů za účelem přenesení zprávy libovolné délky. Transakce budeme dělit na tři druhy:
- vstupní transakce - přenáší data ze zařízení k hostiteli
- výstupní transakce - přenáší data od hostitele do zařízení
- řídící transakce - posílá řídící data od hostitele do zařízení
Výstupní transakce
Hostitel nejprve uvědomí zařízení, do kterého chce zapisovat, vysláním OUT packetu. Data, která chce hostitel odeslat, si rozdělí na bloky pevné délky (poslední blok může být kratší). Hostitel bez čekání odešle první blok dat v paketu typu DATA0. Zařízení odpovídá handshakovým paketem (ACK nebo NAK). V případě, že zařízení pošle ACK, hostitel vysílá OUT packet následovaný bez prodlení paketem DATA1 s dalším blokem dat a znovu čeká na handshakovou odpověď zařízení. Střídavě se tak přenášejí OUT+DATA0 a OUT+DATA1, dokud nejsou přeneseny všechny části zprávy. Pokud zařízení odpoví na nějaký paket negativně (NAK), zopakuje hostitel své předchozí vysílání (v paketu stejného typu). Když hostitel odešle vše, co potřeboval, nastává posloupnost paketů ukončující celou transakci. Hostitel vyšle IN packet a očekává, že zařízení pošle paket DATA0 s nulovou délkou obsahu (prázdný paket). Hostitel takový paket potvrdí vysláním ACK paketu. Tím se ukončuje transakce a zpráva, která byla předmětem transakce, je považována za platnou. Pokud není dodržena posloupnost paketů jednou ze stran, transakce se ruší a dosud přenesená data se musí zahodit.
Vstupní transakce
Hostitel vyšle do endpointu, ze kterého chce číst zprávu, IN paket. Zařízení odpovídá první částí zprávy v paketu DATA0. Hostitel handshakuje (ACK nebo NAK). Hostitel opakuje IN packet a předpokládá, že zařízení mu nyní odpoví v paketu DATA1 (střídavě). Hostitel přijatá data opět potvrzuje a podle potřeby opakuje vysílání paketu tolikrát, dokud neobdrží buď prázdný paket nebo paket s menším počtem bytů, než je maximum. Tím se hostitel dozví, že zařízení již poslalo celou zprávu a nastane okamžik pro potvrzení platnosti celé transakce. Hostitel vysílá OUT packet a bez čekání pošle i DATA0 packet bez obsahu (prázdný DATA0 packet). Zařízení musí odpovědět ACK packetem.
U obou transakcí se vyskytuje potvrzení transakce, které je realizováno posláním prázdného datového paketu v opačném směru, než ve kterém probíhal přenos zprávy.
Řídící transakce
Řídící transakce začíná okamžikem, kdy hostitel pošle SETUP packet do endpointu. Poté pošle DATA0 packet o délce 8 bytů (rozumí se 8 bytů obsahu). Hostitel očekává ACK od zařízení. Obsah paketu DATA0 má přesně specifikovanou strukturu a zařízení se z ní dozví, zda bude následovat vstupní či výstupní transakce. Zařízení si buď připraví data k odeslání nebo uvolní vstupní buffer pro přijímání a pak provede standardní transakci podle toho, jak bylo uvedeno výše. Řídící transakce je tedy stejná jako vstupní či výstupní transakce s tím rozdílem, že užitečná data, která by byla normálně přenášena, jsou nahrazena daty, o které hostitel požádal v posloupnosti SETUP+DATA0 paketech.
Transakce je nejkomplexnějším útvarem při USB komunikaci. Transakce přenáší zprávy, které jsou připraveny ve vstupně/výstupních bufferech zařízení i hostitele a jejich obsah může být libovolný. Pouze při příchodu SETUP+DATA0 paketu do zařízení jsou tyto buffery nahrazeny odpovědí s přesně specifikovaným významem a strukturou. SETUP paket lze tedy chápat jako přepnutí vnitřního stavu zařízení.
Stavy zařízení
Program, který bude rozebírat přišlé pakety, rozhodovat se o jejich sdělení a odesílat zpět odpověď, bude pracovat podle schématu přijmi-vyhodnoť-odpověz. Rutiny tohoto analyzátoru (parseru) se zavolají jednou po příchodu paketu, provedou se a uvolní procesorový čas. Rutiny nebudou obsahovat žádné čekání. Vzhledem k omezeným možnostem procesorů AVR nebude nikde uložena celá zpráva, která je předmětem transakce. Každý blok (max. 8 bytů) bude do bufferu vložen těsně před odesláním a přijatá data budou ihned po přijetí zpracována. Parser bude tedy pracovat obdobně jako stavový automat, jehož vstupem je příchozí paket a výstupem odchozí paket a změna vnitřního stavu.
Požadavky, které musí zařízení plnit:
- užitečná data (rqData)
- device descriptor (rqDevDesc)
- configuration descriptor (rqConfDesc)
- set address (rqSetAddr)
Fáze transakce (x-action), kterými zařízení prochází:
- klid, výchozí stav (xaNone)
- požadavek na zápis (OUT paket) (xaOut)
- požadavek na čtení (IN paket) (xaIn)
- požadavek na nastavení (SETUP paket) (xaSetup)
- ukončení zápisové transakce (IN paket přicházející při zápisové transakci) (xaWriteEnd)
- ukončení čtecí transakce (OUT paket přicházející při čtecí transakci) (xaReadEnd)
- odmítnutí ukončení čtecí transakce (OUT paket přicházející při čtecí transakci, aniž by byla potvrzena poslední data) (xaReadEndFail)
- čekání na potvrzení datového paketu při čtecí transakci (xaInAckWait)
Stavy musejí být rozšířeny o počítadlo odeslaných bytů a o další pomocné proměnné (spánek, stav bez adresy, úsporný režim).
Stav | Příchozí paket | Nový stav | Odchozí paket |
cokoliv | SETUP | xaSetup | - |
xaNone (klid) | OUT | xaOut | - |
IN | xaInWaitAck | DATA1 |
ACK, NAK | xaNone | - |
DATAn | xaNone | NAK |
xaIn | OUT | xaReadEnd | - |
IN | xaInWaitAck | DATA0 nebo DATA1 |
ACK, NAK | xaNone (transakce zrušena) | - |
DATAn | xaIn | NAK |
xaInWaitAck | OUT | xaReadEndFail | - |
IN | xaInWaitAck | DATA0 nebo DATA1 (opakované odeslání) |
ACK | xaIn (+připravit další paket) | - |
NAK | xaIn | - |
DATAn | xaNone (transakce zrušena) | NAK |
xaReadEndFail | OUT | xaOut (transakce zrušena) | - |
IN | xaInWaitAck (transakce zrušena) | DATA1 |
ACK, NAK | xaNone (transakce zrušena) | - |
DATAn | xaNone (transakce zrušena) | NAK |
xaOut | OUT | xaOut | - |
IN | xaWriteEnd | zero DATA |
ACK, NAK | xaNone (transakce zrušena) | - |
DATAn | xaOut (paket přijat v pořádku) | ACK |
xaWriteEnd | OUT | xaOut (transakce zrušena) | - |
IN | xaWriteEnd | zero DATA |
ACK | xaNone (data odeslána v pořádku) | - |
NAK | xaNone (transakce zrušena) | - |
DATAn | xaNone (transakce zrušena) | NAK |
xaReadEnd | OUT | xaOut (transakce zrušena) | - |
IN | xaInWaitAck (transakce zrušena) | DATA1 |
ACK,NAK | xaNone (transakce zrušena) | - |
DATAn | xaNone (transakce potvrzena) | ACK |
xaSetup | OUT | xaOut (setup zrušen) | - |
IN | xaInWaitAck (setup zrušen) | DATA1 |
ACK, NAK | xaNone (setup zrušen) | - |
DATAn | xaOut nebo xaIn (dle obsahu dat) | ACK |
Požadavky
Zařízení musí na požádání zasílat dvě datové struktury (dvě zprávy). První je popisovač zařízení (Device Descriptor) a druhý je popisovač nastavení (Configuration Descriptor). Obě datové struktury mají více než 8 bytů a proto se rozpadají na více paketů. Dalším požadavkem, který je posílán hositelem je nastavení adresy, ale odpověď je zařízením signalizována tak, že úspěšně ukončí transakci (prázdný paket, zero DATA) a takovou odpověď zařídí stavový automat sám od sebe.
Device Descriptor
Offset | Popis | Hodnota |
0 | bLength | 0x12 |
1 | bDescriptorType | 0x01 |
2,3 | bcdUSB | 0x0100 |
4 | bDeviceClass | 0x00 |
5 | bDeviceSubClass | 0x00 |
6 | bDeviceProtocol | 0x00 |
7 | bMaxPacketSize | 0x08 |
8,9 | idVendor | 0x065d |
10,11 | idProduct | 0x1031 |
12,13 | bcdDevice | 0x0004 |
14 | iManufacturer | 0x00 |
15 | iProduct | 0x00 |
16 | iSerialNumber | 0x00 |
17 | bNumConfigurations | 0x01 |
Configuration Descriptor (spojení Configuration, Interface a Endpoint descriptoru)
Offset | Popis | Hodnota |
Configuration Descriptor |
0 | bLength | 0x09 |
1 | bDescriptorType | 0x02 |
2,3 | wTotalLenght | 0x0020 |
4 | bNumInterfaces | 0x01 |
5 | bConfigurationValue | 0x01 |
6 | iConfiguration | 0x00 |
7 | bmAttributes | 0x80 |
8 | bMaxPower | 0x32 |
Interface Descriptor |
9 | bLength | 0x09 |
10 | bDescriptorType | 0x04 |
11 | bInterfaceNumber | 0x00 |
12 | bAlternateSetting | 0x00 |
13 | bNumEndpoints | 0x02 |
14 | bInterfaceClass | 0x00 |
15 | bInterfaceSubClass | 0x00 |
16 | bInterfaceProtocol | 0x00 |
17 | iInterface | 0x00 |
Endpoint 1 Descriptor |
18 | bLength | 0x07 |
19 | bDescriptionType | 0x05 |
20 | bEndpointAddress | 0x01 |
21 | bmAttributes | 0x00 |
22,23 | wMaxPacketSize | 0x0001 |
24 | bInterval | 0x00 |
Endpoint 2 Descriptor |
25 | bLength | 0x07 |
26 | bDescriptionType | 0x05 |
27 | bEndpointAddress | 0x82 |
28 | bmAttributes | 0x00 |
29,30 | wMaxPacketSize | 0x0001 |
31 | bInterval | 0x00 |
Deskriptory budou odeslány v následujících paketech:
Device Descriptor:
DATA1 packet - CRC 0xe713 - Device Descriptor
80 4b 12 01 00 01 00 00 00 08 13 e7 eop |
DATA0 packet - CRC 0x83d9 - Device Descriptor
80 c3 5d 06 31 10 04 00 00 00 d9 83 eop |
DATA1 packet - CRC 0x8f3f - Device Descriptor
80 4b 00 01 3f 8f eop |
Configuration Descriptors:
DATA1 packet - CRC 0xa20a - Configuration Descriptor
80 4b 09 02 20 00 01 01 00 80 0a a2 eop |
DATA0 packet - CRC 0x7d04 - Configuration Descriptor a Interface Descriptor
80 c3 32 09 04 00 00 02 00 00 04 7d eop |
DATA1 packet - CRC 0x2f72 - Interface Descriptor a Endpoint 1 Descriptor
80 4b 00 00 07 05 01 00 01 00 72 2f eop |
DATA0 packet - CRC 0xbfe0 - Endpoint 1 Descriptor a Endpoint 2 Descriptor
80 c3 00 07 05 82 00 01 00 00 e0 bf eop |
DATA1 packet - CRC 0x0000 - konec
80 4b 00 00 eop |
|