Interfaccia USB per un dispositivo in classe HID

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.

Tabella 1. Confronto tra le caratteristiche della comunicazione di una periferica della classe HID nelle tre configurazioni del bus USB

Tabella 1: confronto tra le caratteristiche della comunicazione di una periferica della classe HID nelle tre configurazioni del bus USB

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.

Tabella 2. Descrittore dell’ interfaccia HID. Il descrittore contiene le informazioni sull’interfaccia HID e sul numero di descrittori subordinati report o physical che descrivono l’interfaccia. La specifica prevede che la periferica HID contenga almeno un report descriptor

Tabella 2: descrittore dell’ interfaccia HID. Il descrittore contiene le informazioni sull’interfaccia HID e sul numero di descrittori subordinati report o physical che descrivono l’interfaccia. La specifica prevede che la periferica HID contenga almeno un report descriptor

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.

Tabella 3. In aggiunta alle undici richieste standard, l’endpoint0 di una periferica USB che appartiene classe HID deve gestire anche sei richieste specifiche per la classe. La periferica deve sempre gestire la Get_Report mentre alle altre richieste, se non implementate, rispondere con il packet STALL.

Tabella 3: in aggiunta alle undici richieste standard, l’endpoint0 di una periferica USB che appartiene classe HID deve gestire anche sei richieste specifiche per la classe. La periferica deve sempre gestire la Get_Report mentre alle altre richieste, se non implementate, rispondere con il packet STALL.

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.

Figura 1. La periferica USB vista nelle risorse del sistema

Figura 1: la periferica USB vista nelle risorse del sistema

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().

 

 

Una risposta

  1. Maurizio Di Paolo Emilio Maurizio Di Paolo Emilio 3 agosto 2017

Scrivi un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *