Alla classe HID appartengono dispositivi comuni per applicazioni PC quali mouse e tastiere ma anche sensori di misura analogica o schede I/O. Il vantaggio è che i server per un dispositivo HID sono già disponibili in Windows e non necessitano di uno sviluppo specifico.
Introduzione
Nella specifica del bus USB sono state definite numerose classi che raggruppano i diversi dispositivi dotati di interfacce USB. I dispositivi appartenenti ad una classe non necessariamente hanno caratteristiche comuni ma forniscono la stessa interfaccia di comunicazione sul bus USB. La classe HID, acronimo di Human Interface Device, è quella facilmente realizzabile anche in un dispositivo embedded. A questa classe appartengono dispositivi comuni per applicazioni PC quali ad esempio mouse e tastiere ma possono anche appartenere sensori di misura analogica o schede di I/O digitale. Il sistema operativo windows include un driver standard per la comunicazione con dispositivi della classe HID.
Questo è un vantaggio per chi produce delle periferiche con porta USB in piccole serie perché non deve sviluppare un driver proprietario e può garantire la compatibilità del proprio dispositivo con tutti i software di comunicazione che si appoggiano al driver HID. Ci sono anche altre considerazioni che favoriscono l’utilizzo dell’interfaccia HID per la propria periferica: l’interfaccia HID infatti richiede, oltre all’endpoint0, un solo endpoint di tipo IN e può essere facilmente implementata nella SIE di un microcontrollore. Un dispositivo HID, inoltre, può definire un protocollo di comunicazione personalizzato specifico per la propria applicazione. Vedremo in seguito come la periferica sia in grado di fornire all’host tutte le informazioni sulla struttura del protocollo utilizzato e sul formato dei dati inviati/ricevuti.
GLI ENDPOINT DELLA CLASSE HID
In un dispositivo della classe HID oltre al control endpoint0 comune a tutti i dispositivi USB è presente un solo endpoint di tipo IN, in grado cioè di trasferire dati dal dispositivo verso l’host. Per i dispositivi HID conformi alla versione 1.1 della specifica USB è possibile attivare anche una comunicazione con un endpoint di tipo OUT per ricevere dati dall’host. L’endpoint OUT è opzionale e non sempre è presente. L’informazione sugli endpoint presenti nel dispositivo è memorizzata nei descrittori standard di interfaccia e configurazione che l’host acquisisce tramite l’endpoint0 durante il processo di enumerazione.
In mancanza dell’endpoint OUT la comunicazione da host a dispositivo avviene sfruttando le richieste standard specifiche della classe HID dirette all’endpoint0. Gli endpoint IN e OUT utilizzano le interrupt transfer per inviare e ricevere dati. Questa modalità ricorda la comunicazione seriale a polling con invio periodico di messaggi tra master e slave. Ogni transfer è suddivisa in più transactions e ogni transaction contiene un determinato numero di byte dati come indicato in tabella 1.
La interrupt transfer è perciò composta da più transaction che vengono inviate sul bus con una periodicità stabilita. L’intervallo di tempo tra due transaction è definito nei descrittori standard del dispositivo USB e va inteso come tempo massimo entro il quale il bus gestirà la prossima transaction. Nella seconda riga della tabella 1 sono indicate le periodicità minime della interrupt transfer nelle tre configurazioni possibili del bus USB, mentre nella terza riga sono riportate le velocità massime raggiungibili in termini di Byte/sec per una comunicazione USB di tipo interrupt transfer. Per un confronto con la comunicazione seriale si vede che il baud rate minimo è di 64Kbit/sec.
I DESCRITTORI DEL DISPOSITIVO HID
Ogni dispositivo USB deve contenere i descrittori standard. Questi descrittori informano l’host sulle caratteristiche della periferica, in particolare se il campo bInterfaceClass del descrittore d’interfaccia ha valore 3 l’interfaccia viene riconosciuta come interfaccia HID. Quando l’host apprende che il dispositivo appartiene alla classe HID deve acquisire le informazioni specifiche per la classe che sono memorizzate nell’HID descriptor e nei descrittori subordinati report descriptor e physical descriptor. La struttura dell’HID descriptor è riportata in tabella 2.
Come ogni descrittore anche l’HID descriptor inizia con un’intestazione che contiene la lunghezza del descrittore e l’identificativo bDescriptorType del descrittore. Nel caso di un HID descriptor bDescriptorType = 0x21. Il parametro bCountryCode localizza il dispositivo per una determinata area geografica. Questa opzione potrebbe essere utile ad esempio per identificare il layout di una tastiera con interfaccia USB. Il parametro bNumDescriptors indica quanti descrittori subordinati all’HID descriptor sono contenuti nel dispositivo. Descrittori che possono essere report o physical. I primi descrivono in dettaglio il tipo dati e la struttura dei messaggi inviati/ricevuti dalla periferica. I physical descriptors invece definiscono l’iterazione della periferica con determinate parti del corpo. Ad esempio un mouse può essere configurato per l’utilizzo con la mano destra o con la mano sinistra.
I descrittori subordinati sono opzionali ma le specifiche della classe HID prevedono che sia definito almeno un report descriptor. Il parametro bNumDescriptors perciò ha valore minimo 1 ed è seguito da coppie di valori wDescriptorLength e bDescriptorType. Ogni coppia di valori definisce la lunghezza del descrittore e il tipo, report o physical, del descrittore. Il descrittore HID contiene tante coppie di valori quanti sono i descrittori subordinati definiti in bNumDescriptors. Per definire completamente i descrittori di una periferica USB che appartiene alla classe HID si deve compilare almeno un report descriptor. La scrittura di un report descriptor non è semplice poiché non si tratta di una tabella come i descrittori visti in precedenza. Il report descriptor ha una propria sintassi e una struttura che può essere molto complessa e ricca di dettagli.
LE RICHIESTE STANDARD PER LA CLASSE HID
In aggiunta alle undici richieste standard comuni a tutti i dispositivi USB, un dispositivo della classe HID deve anche gestire sei richieste standard specifiche per la classe che sono elencate in tabella 3.
Si ricorda che le richieste standard sono sempre dirette all‘endpoint0 del dispositivo e che il dispositivo non deve necessariamente gestirle tutte. Alle richieste non gestite il dispositivo risponde con l’handshake packet STALL. La specifica USB prevede che un dispositivo della classe HID risponda sempre alla richiesta Get_Report mentre la gestione delle altre richieste è opzionale. In realtà se il dispositivo non ha un endpoint OUT, per ricevere dati dall’host si deve gestire anche la Set_Report. Le richieste standard Get_Idle e Set_Idle permettono di ottimizzare la trasmissione sul bus USB evitando le trasmissioni di messaggi quando i dati non sono cambiati rispetto all’ultimo report inviato. Il parametro della Set_Idle configura l’intervallo di tempo, espresso in multipli di 4ms, tra l’invio di due report successivi. Il dispositivo dopo aver inviato un report risponderà con NAK a tutte le richieste che giungeranno nel periodo di tempo impostato da Set_Idle. Alla prima richiesta successiva al timeout invierà comunque un nuovo report anche se i dati non sono cambiati. Se il periodo di Idle è uguale a zero il dispositivo invierà un nuovo report solo se i dati sono cambiati rispondendo NAK a tutte le richieste successive alla prima. Le richieste Get_Protocol e Set_Protocol sono utilizzate da periferiche HID che si dichiarano di tipo boot nel campo subclass dell’interface descriptor. Sono periferiche che possono comunicare con l’host anche in assenza del driver standard. Per una periferica HID di tipo boot è necessario specificare, sempre nell’interface descriptor, anche il protocollo da utilizzare per la comunicazione, protocollo che ovviamente dovrà essere noto all’host.
IL SOFTWARE TEMPLATE SILABS
Per la realizzazione del firmware del driver di comunicazione UBS del dispositivo HID è stato utilizzato il software template scritto da Silabs. L’applicazione è stata poi realizzata su un micro C8051F340 con core 8051 che integra una periferica USB costituita da una SIE in grado di gestire quattro endpoint con memoria buffer fifo riservata nell’area XRAM del micro. Il software template è scritto interamente in linguaggio C che contiene le procedure per la configurazione delle periferiche del microcontrollore e ovviamente è specifico per il micro utilizzato. Le procedure configurano i registri delle periferiche del micro, le porte di I/O, l’oscillatore, i timers hardware. Le altre funzioni implementano la parte a basso livello del driver che gestisce direttamente la SIE della periferica USB e il driver di comunicazione dell’endpoint0. È compito del progettista compilare correttamente i descrittori standard, il descrittore della classe HID e aggiungere i report descriptor previsti per la propria applicazione. Il progettista dovrà quindi scrivere una procedura per ogni report in grado di codificare o decodificare i dati così come previsto dal report descriptor. L’entry point della procedura dovrà poi essere memorizzato in una delle due vectortable.
IL PROGRAMMA HOST DI COMUNICAZIONE
Per realizzare il programma host si utilizza la libreria opensource LibUSB. Questa libreria è disponibile sia per Linux che per Windows nella versione LibUSB-WIN32. Si segnala inoltre che per l’ambiente Windows esiste anche il porting della libreria per il linguaggio C#. La LibUSB può essere installata come filtro (file libusb-win32-filter-bin), oppure può essere utilizzata come driver della periferica USB. In questo caso quando la periferica viene riconosciuta per la prima volta dal sistema operativo si deve indicare alla procedura d’installazione il driver da utilizzare. Il disco driver può essere realizzato con lo script driver_installer_template.iis fornito con i sorgenti della libreria. Lo script template contiene le istruzioni dettagliate per la realizzazione del programma di setup del driver. Dopo aver installato il driver per la nuova periferica, nella gestione risorse del sistema la periferica USB, se collegata, sarà descritta come in figura 1.
La procedura USBinit() dev’essere richiamata all’inizio del programma per individuare tutte le periferiche USB connesse al PC. I dati raccolti da ogni periferica sono memorizzati sottoforma di liste concatenate. Per comunicare con una determinata periferica si deve cercare tra le liste di tutte le periferiche collegate quella identificata dal proprio vendorID e productID. Per la comunicazione con dispositivi HID non è sufficiente aprire il canale di comunicazione con il dispositivo USB con la usb_open() ma si deve anche selezionare la configurazione e l’interfaccia da utilizzare nella comunicazione con il dispositivo. Ogni dispositivo USB, infatti, può proporre diverse configurazioni con diverse interfacce. Dopo ogni chiamata alle funzioni di libreria è possibile controllare lo stato dell’operazione eseguita sia con il valore restituito dalla funzione che è negativo in caso di errore, oppure si può ottenere l’ultimo messaggio d’errore generato dalla libreria con al procedura usb_strerror().
Il protocollo HID rende l’implementazione dei dispositivi molto semplice. I dispositivi definiscono in pratica un descrittore HID che è una matrice codificata di byte relativamente ai pacchetti dati del dispositivo.
La classe HID (Human Interface Device) è perfetta per interfacciare piccoli microcontrollori, inoltre, poichè durante l’enumerazione dei dispositivi HID il sistema legge una struttura dati chiamata HID report descriptor, che descrive quanti dati trasferire e come devono essere interpretati, è possibile avere una grande flessibilità sul tipo di periferica e sui dati trasferiti; in questo modo si possono definire nuovi tipi di dispositivo e in generale combinazioni non previste espressamente dal sistema operativo.